diff --git a/backend/server.ts b/backend/server.ts index 7e7399e..2530fe2 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -74,6 +74,7 @@ async function startServer() { app.use('/api/uploads/foodmenu', express.static(path.join(__dirname, '/uploads/foodmenu'))); app.use('/api/uploads/plannings', express.static(path.join(__dirname, '/uploads/plannings'))); app.use('/api/exports/bus', express.static(path.join(__dirname, '/exports/bus'))); + app.use('/api/exports/teammembers', express.static(path.join(__dirname, '/exports/teammembers'))); // Démarrage du serveur app.listen(server_port, () => { diff --git a/backend/src/controllers/im_export.controller.ts b/backend/src/controllers/im_export.controller.ts index 979e6d1..668c40f 100644 --- a/backend/src/controllers/im_export.controller.ts +++ b/backend/src/controllers/im_export.controller.ts @@ -172,6 +172,15 @@ export const exportUsersCSV: AppRequestHandler = async (_req, res) => { } }; +export const exportTeamMembersCSV: AppRequestHandler = async (_req, res) => { + try { + await export_service.exportTeamMembersToCSV(); + Ok(res, { msg: "CSV des membres de l'équipe généré" }); + } catch (error) { + console.error(error); + Error(res, { msg: "Erreur lors de l'export CSV" }); + } +}; export const getUploadedDocumentStatus: AppRequestHandler = async ( req, res, diff --git a/backend/src/routes/im_export.routes.ts b/backend/src/routes/im_export.routes.ts index 38077cd..7dbb04a 100644 --- a/backend/src/routes/im_export.routes.ts +++ b/backend/src/routes/im_export.routes.ts @@ -1,20 +1,27 @@ import express from 'express'; import * as imexportController from '../controllers/im_export.controller'; -import { createUploadMiddleware } from "../middlewares/multer.middleware"; +import { createUploadMiddleware } from '../middlewares/multer.middleware'; import { checkRole } from '../middlewares/user.middleware'; const uploadMiddleware = createUploadMiddleware(); const imexportRouter = express.Router(); imexportRouter.post( - '/admin/import/:category/:item', checkRole("Admin", []), - uploadMiddleware.multerUpload.single("file"), + '/admin/import/:category/:item', + checkRole('Admin', []), + uploadMiddleware.multerUpload.single('file'), uploadMiddleware.verifyAndSave, - imexportController.updateFoodMenu); + imexportController.updateFoodMenu, +); -imexportRouter.post('/admin/exportgsheet', checkRole("Admin", []), imexportController.exportAllDataToSheets); -imexportRouter.get('/admin/exportbus', checkRole("Admin", []), imexportController.exportUsersCSV); -imexportRouter.get('/admin/document/:category/:item', checkRole("Admin", []), imexportController.getUploadedDocumentStatus); -imexportRouter.delete('/admin/document/:category/:item', checkRole("Admin", []), imexportController.deleteDocument); +imexportRouter.post('/admin/exportgsheet', checkRole('Admin', []), imexportController.exportAllDataToSheets); +imexportRouter.get('/admin/exportbus', checkRole('Admin', []), imexportController.exportUsersCSV); +imexportRouter.get('/admin/exportteammembers', checkRole('Admin', []), imexportController.exportTeamMembersCSV); +imexportRouter.get( + '/admin/document/:category/:item', + checkRole('Admin', []), + imexportController.getUploadedDocumentStatus, +); +imexportRouter.delete('/admin/document/:category/:item', checkRole('Admin', []), imexportController.deleteDocument); export default imexportRouter; diff --git a/backend/src/services/im_export.service.ts b/backend/src/services/im_export.service.ts index ba7b756..8f46705 100644 --- a/backend/src/services/im_export.service.ts +++ b/backend/src/services/im_export.service.ts @@ -1,6 +1,6 @@ -import { existsSync, mkdirSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { google } from 'googleapis'; -import { parse } from "json2csv"; +import { parse } from 'json2csv'; import * as user_service from './user.service'; import path from 'path'; @@ -14,11 +14,7 @@ const jwtClient = new google.auth.JWT({ }); // Fonction pour écrire dans Google Sheets -export const writeToGoogleSheet = async ( - spreadsheetId: string, - range: string, - values: any[][] -) => { +export const writeToGoogleSheet = async (spreadsheetId: string, range: string, values: any[][]) => { try { // Crée un client Sheets en utilisant JWT pour l'authentification const sheets = google.sheets({ version: 'v4', auth: jwtClient }); @@ -35,7 +31,7 @@ export const writeToGoogleSheet = async ( console.log(`Données envoyées à Google Sheets dans la plage ${range}`); } catch (error) { - console.error('Erreur lors de l\'écriture dans Google Sheets:', error); + console.error("Erreur lors de l'écriture dans Google Sheets:", error); throw error; } }; @@ -43,18 +39,18 @@ export const writeToGoogleSheet = async ( export const exportUsersToCSV = async (): Promise => { const users = await user_service.getUsersAll(); - const formattedUsers = users.map(u => { + const formattedUsers = users.map((u) => { const isOrga = u.roles && u.roles.length > 0; - const isCE = u.permission === "Student" && u.teamId !== null; - const isBenevole = u.roles.some(role => role.roleName === "Bénévole"); + const isCE = u.permission === 'Student' && u.teamId !== null; + const isBenevole = u.roles.some((role) => role.roleName === 'Bénévole'); return { id: u.id, - prenom: u.first_name ?? "", - nom: u.last_name ?? "", - mail: u.email ?? "", - telephone: u.contact ?? "", - nouveau: u.permission === "Nouveau", + prenom: u.first_name ?? '', + nom: u.last_name ?? '', + mail: u.email ?? '', + telephone: u.contact ?? '', + nouveau: u.permission === 'Nouveau', ce: isCE, num_equipe: u.teamId ?? null, benevole: isBenevole, @@ -65,13 +61,23 @@ export const exportUsersToCSV = async (): Promise => { const csv = parse(formattedUsers, { fields: [ - "id", "prenom", "nom", "mail", "telephone", - "nouveau", "ce", "num_equipe", "benevole", "orga", "majeur", "bus_manual" - ] + 'id', + 'prenom', + 'nom', + 'mail', + 'telephone', + 'nouveau', + 'ce', + 'num_equipe', + 'benevole', + 'orga', + 'majeur', + 'bus_manual', + ], }); - const exportDir = path.join(__dirname, "../../exports/bus"); - const filePath = path.join(exportDir, "bus.csv"); + const exportDir = path.join(__dirname, '../../exports/bus'); + const filePath = path.join(exportDir, 'bus.csv'); if (!existsSync(exportDir)) { mkdirSync(exportDir, { recursive: true }); @@ -80,5 +86,34 @@ export const exportUsersToCSV = async (): Promise => { writeFileSync(filePath, csv); return filePath; +}; + +export const exportTeamMembersToCSV = async (): Promise => { + const users = await user_service.getUsersAll(); + + const formattedUsers = users.map((u) => { + const isCE = u.permission === 'Student' && u.teamId !== null; + return { + prenom: u.first_name ?? '', + nom: u.last_name ?? '', + ce: isCE, + equipe: u.teamName, + }; + }); + const csv = parse(formattedUsers, { + fields: ['prenom', 'nom', 'ce', 'equipe'], + }); + + const exportDir = path.join(__dirname, '../../exports/teammembers'); + const filePath = path.join(exportDir, 'teammembers.csv'); + console.log('Exporting team members to:', filePath); + + if (!existsSync(exportDir)) { + mkdirSync(exportDir, { recursive: true }); + } + + writeFileSync(filePath, csv); + + return filePath; }; diff --git a/frontend/src/components/Admin/adminExportImport.tsx b/frontend/src/components/Admin/adminExportImport.tsx index 33a01e5..c2cd0eb 100644 --- a/frontend/src/components/Admin/adminExportImport.tsx +++ b/frontend/src/components/Admin/adminExportImport.tsx @@ -1,22 +1,26 @@ import { useState } from 'react'; -import { exportBus, exportDb } from '../../services/requests/im_export.service'; +import { exportBus, exportDb, exportTeamMembers } from '../../services/requests/im_export.service'; import { Button } from '../ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; import { AdminFileImport } from './adminFileImport'; export const AdminExportConnect = () => { - const [loading, setLoading] = useState<{ db: boolean; bus: boolean }>({ + const [loading, setLoading] = useState<{ db: boolean; bus: boolean; teamMembers: boolean }>({ db: false, bus: false, + teamMembers: false, }); const [error, setError] = useState(null); const [message, setMessage] = useState(''); const [showBusExport, setShowBusExport] = useState(false); + const [showTeamMembersExport, setShowTeamMembersExport] = useState(false); - const busUrl = 'https://integration.utt.fr/api/exports/bus/bus.csv'; + const busUrl = `${import.meta.env.VITE_API_URL}/exports/bus/bus.csv`; - const handleExport = async (type: 'db' | 'bus', exportFn: () => Promise<{ message: string }>) => { + const teamMembersUrl = `${import.meta.env.VITE_API_URL}/exports/teammembers/teammembers.csv`; + + const handleExport = async (type: 'db' | 'bus' | 'teamMembers', exportFn: () => Promise<{ message: string }>) => { setLoading((prev) => ({ ...prev, [type]: true })); setError(null); setMessage(''); @@ -24,6 +28,7 @@ export const AdminExportConnect = () => { const response = await exportFn(); setMessage(response.message); if (type === 'bus') setShowBusExport(true); + if (type === 'teamMembers') setShowTeamMembersExport(true); } catch (err) { console.error(`Erreur export ${type}`, err); setError( @@ -56,6 +61,11 @@ export const AdminExportConnect = () => { className="w-full sm:w-auto bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 text-white py-2 px-6 rounded-xl shadow-md transition-all duration-200"> {loading.bus ? '⏳ Export en cours...' : 'Exporter les bus'} + {error &&

{error}

} @@ -76,6 +86,20 @@ export const AdminExportConnect = () => { )} + + {showTeamMembersExport && ( + + +

📄 Télécharger le csv des équipes

+ + ⬇️ Télécharger le csv + +
+
+ )} ); diff --git a/frontend/src/services/requests/im_export.service.ts b/frontend/src/services/requests/im_export.service.ts index dd0c4da..5de586b 100644 --- a/frontend/src/services/requests/im_export.service.ts +++ b/frontend/src/services/requests/im_export.service.ts @@ -1,4 +1,4 @@ -import api from "../api"; +import api from '../api'; type ExistingDocumentStatus = { exists: boolean; @@ -16,7 +16,7 @@ export const exportDb = async () => { // Fonction import export const importFile = async (formData: FormData, category: string, item: string) => { const response = await api.post(`/imexport/admin/import/${category}/${item}`, formData, { - headers: { "Content-Type": "multipart/form-data" }, + headers: { 'Content-Type': 'multipart/form-data' }, }); return response.data; }; @@ -26,6 +26,11 @@ export const exportBus = async () => { return response.data; }; +export const exportTeamMembers = async () => { + const response = await api.get('/imexport/admin/exportteammembers'); + return response.data; +}; + export const checkIfExistingDocument = async (category: string, item: string) => { const response = await api.get('/imexport/admin/document/' + category + '/' + item); return response.data.data as ExistingDocumentStatus;