From 3124d23df37640322b1bbc9a391931486a0f2943 Mon Sep 17 00:00:00 2001 From: Eddi3_As Date: Sun, 16 Mar 2025 20:26:55 -0400 Subject: [PATCH] FIX admin delete image route --- client/package-lock.json | 26 +++---- client/package.json | 2 +- client/src/Types/QuizType.tsx | 5 ++ client/src/pages/Admin/Images.tsx | 3 +- client/src/pages/Admin/Stats.tsx | 120 +++++++++++++++++++++-------- client/src/services/ApiService.tsx | 12 ++- server/controllers/admin.js | 37 ++------- server/models/admin.js | 16 +++- server/models/quiz.js | 1 + server/routers/admin.js | 8 +- 10 files changed, 139 insertions(+), 91 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index e2c6890..19cd5cd 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,7 +16,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@mui/icons-material": "^6.4.6", "@mui/lab": "^5.0.0-alpha.153", - "@mui/material": "^6.4.6", + "@mui/material": "^6.4.7", "@types/uuid": "^9.0.7", "axios": "^1.8.1", "dompurify": "^3.2.3", @@ -3397,9 +3397,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.6.tgz", - "integrity": "sha512-rho5Q4IscbrVmK9rCrLTJmjLjfH6m/NcqKr/mchvck0EIXlyYUB9+Z0oVmkt/+Mben43LMRYBH8q/Uzxj/c4Vw==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.7.tgz", + "integrity": "sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==", "license": "MIT", "funding": { "type": "opencollective", @@ -3474,14 +3474,14 @@ } }, "node_modules/@mui/material": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.6.tgz", - "integrity": "sha512-6UyAju+DBOdMogfYmLiT3Nu7RgliorimNBny1pN/acOjc+THNFVE7hlxLyn3RDONoZJNDi/8vO4AQQr6dLAXqA==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.7.tgz", + "integrity": "sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.6", - "@mui/system": "^6.4.6", + "@mui/core-downloads-tracker": "^6.4.7", + "@mui/system": "^6.4.7", "@mui/types": "^7.2.21", "@mui/utils": "^6.4.6", "@popperjs/core": "^2.11.8", @@ -3502,7 +3502,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.6", + "@mui/material-pigment-css": "^6.4.7", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -3584,9 +3584,9 @@ } }, "node_modules/@mui/material/node_modules/@mui/system": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.6.tgz", - "integrity": "sha512-FQjWwPec7pMTtB/jw5f9eyLynKFZ6/Ej9vhm5kGdtmts1z5b7Vyn3Rz6kasfYm1j2TfrfGnSXRvvtwVWxjpz6g==", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.7.tgz", + "integrity": "sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", diff --git a/client/package.json b/client/package.json index b6d62e4..45352e6 100644 --- a/client/package.json +++ b/client/package.json @@ -20,7 +20,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@mui/icons-material": "^6.4.6", "@mui/lab": "^5.0.0-alpha.153", - "@mui/material": "^6.4.6", + "@mui/material": "^6.4.7", "@types/uuid": "^9.0.7", "axios": "^1.8.1", "dompurify": "^3.2.3", diff --git a/client/src/Types/QuizType.tsx b/client/src/Types/QuizType.tsx index 5c2eb0d..f97809f 100644 --- a/client/src/Types/QuizType.tsx +++ b/client/src/Types/QuizType.tsx @@ -16,4 +16,9 @@ export interface QuizTypeShort { title: string; created_at: Date; updated_at: Date; +} + +export interface QuizResponse { + quizzes: QuizTypeShort[]; + total: number; } \ No newline at end of file diff --git a/client/src/pages/Admin/Images.tsx b/client/src/pages/Admin/Images.tsx index ad6326e..a03f70d 100644 --- a/client/src/pages/Admin/Images.tsx +++ b/client/src/pages/Admin/Images.tsx @@ -10,8 +10,7 @@ import { Paper, Box, CircularProgress, - Button, - Typography + Button } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import { ImageType } from "../../Types/ImageType"; diff --git a/client/src/pages/Admin/Stats.tsx b/client/src/pages/Admin/Stats.tsx index 6c42b70..50563e5 100644 --- a/client/src/pages/Admin/Stats.tsx +++ b/client/src/pages/Admin/Stats.tsx @@ -1,56 +1,114 @@ import React, { useState, useEffect } from "react"; -import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton } from "@mui/material"; +import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Grid, Typography, CircularProgress, Box } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import ApiService from '../../services/ApiService'; import { QuizTypeShort } from "../../Types/QuizType"; const Users: React.FC = () => { const [quizzes, setQuizzes] = useState([]); + const [monthlyQuizzes, setMonthlyQuizzes] = useState(0); + const [totalUsers, setTotalUsers] = useState(0); + const [loading, setLoading] = useState(true); useEffect(() => { - const fetchUsers = async () => { + const fetchStats = async () => { try { - const data = await ApiService.getQuizzes(); - setQuizzes(data); + const data = await ApiService.getStats(); + setQuizzes(data.quizzes); + setTotalUsers(data.total); + + const currentMonth = new Date().getMonth(); + const currentYear = new Date().getFullYear(); + const filteredQuizzes = data.quizzes.filter((quiz: QuizTypeShort) => { + const quizDate = new Date(quiz.created_at); + return quizDate.getMonth() === currentMonth && quizDate.getFullYear() === currentYear; + }); + + setMonthlyQuizzes(filteredQuizzes.length === 0 ? 10 : 0); } catch (error) { console.error("Error fetching quizzes:", error); + } finally { + setLoading(false); } }; - fetchUsers(); + fetchStats(); }, []); const handleDelete = (id: string) => { setQuizzes(quizzes.filter(quiz => quiz._id !== id)); }; + const totalQuizzes = quizzes.length; + + if (loading) { + return ( + + + + ); + } + return ( - - - - - Enseignant - Titre - Crée - Modifié - - - - {quizzes.map((quiz) => ( - - {quiz.userId} - {quiz.title} - {new Date(quiz.created_at).toLocaleDateString()} - {new Date(quiz.updated_at).toLocaleDateString()} - - handleDelete(quiz._id)} color="error"> - - - + + + + Quiz du Mois + + + + {monthlyQuizzes} + + + + + Quiz total + + + + {totalQuizzes} + + + + + Enseignants + + + + {totalUsers} + + + + + + {/* Table */} + +
+ + + Enseignant + Titre + Crée + Modifié - ))} - -
-
+ + + {quizzes.map((quiz) => ( + + {quiz.userId} + {quiz.title} + {new Date(quiz.created_at).toLocaleDateString()} + {new Date(quiz.updated_at).toLocaleDateString()} + + handleDelete(quiz._id)} color="error"> + + + + + ))} + + + + ); }; diff --git a/client/src/services/ApiService.tsx b/client/src/services/ApiService.tsx index 0991bc5..209f370 100644 --- a/client/src/services/ApiService.tsx +++ b/client/src/services/ApiService.tsx @@ -3,7 +3,7 @@ import { jwtDecode } from 'jwt-decode'; import { ENV_VARIABLES } from '../constants'; import { FolderType } from 'src/Types/FolderType'; -import { QuizType, QuizTypeShort } from 'src/Types/QuizType'; +import { QuizType, QuizResponse } from 'src/Types/QuizType'; import { RoomType } from 'src/Types/RoomType'; import { UserType } from 'src/Types/UserType'; import { ImagesResponse, ImagesParams } from 'src/Types/ImageType'; @@ -1181,7 +1181,6 @@ public async login(email: string, password: string): Promise { if (result.status !== 200) { throw new Error(`L'obtention des titres des salles a échoué. Status: ${result.status}`); } - console.log(result.data); return result.data.users; } catch (error) { @@ -1209,7 +1208,6 @@ public async login(email: string, password: string): Promise { if (result.status !== 200) { throw new Error(`L'affichage des images a échoué. Status: ${result.status}`); } - console.log(result.data); const images = result.data.data; return images; @@ -1257,18 +1255,18 @@ public async login(email: string, password: string): Promise { } } - public async getQuizzes(): Promise { + public async getStats(): Promise { try { - const url: string = this.constructRequestUrl(`/admin/getQuizzes`); + const url: string = this.constructRequestUrl(`/admin/getStats`); const headers = this.constructRequestHeaders(); const result: AxiosResponse = await axios.get(url, { headers }); if (result.status !== 200) { throw new Error(`L'affichage des images a échoué. Status: ${result.status}`); } - const quiz = result.data.quizzes; + const resp = result.data.data; - return quiz; + return resp; } catch (error) { console.log("Error details: ", error); diff --git a/server/controllers/admin.js b/server/controllers/admin.js index 6487901..37de936 100644 --- a/server/controllers/admin.js +++ b/server/controllers/admin.js @@ -19,11 +19,11 @@ class AdminController { } }; - getQuizzes = async (req, res, next) => { + getStats = async (req, res, next) => { try { - const quizzes = await this.model.getQuizzes(); + const data = await this.model.getStats(); - return res.status(200).json({ quizzes }); + return res.status(200).json({ data }); } catch (error) { return next(error); } @@ -62,41 +62,20 @@ class AdminController { } }; - deleteQuiz = async (req, res, next) => { - try { - const { id } = req.params; - - if (!id) { - throw new AppError(MISSING_REQUIRED_PARAMETER); - } - - const user = await this.model.deleteUser(id); - - if (!user) { - throw new AppError(IMAGE_NOT_FOUND); - } - - return res.status(200).json({ user: user }); - } catch (error) { - return next(error); - } - }; - deleteImage = async (req, res, next) => { try { - const { id } = req.params; - - if (!id) { + const { imgId } = req.query; + if (!imgId) { throw new AppError(MISSING_REQUIRED_PARAMETER); } - const user = await this.model.deleteUser(id); + const deleted = await this.model.deleteImage(imgId); - if (!user) { + if (!deleted) { throw new AppError(IMAGE_NOT_FOUND); } - return res.status(200).json({ user: user }); + return res.status(200).json({ deleted }); } catch (error) { return next(error); } diff --git a/server/models/admin.js b/server/models/admin.js index df4b8f1..d85b217 100644 --- a/server/models/admin.js +++ b/server/models/admin.js @@ -33,17 +33,25 @@ class Admin { return deleted; } - async getQuizzes() { + async getStats() { await this.db.connect() const conn = this.db.getConnection(); + const usrColl = conn.collection('users'); + const total = await usrColl.countDocuments(); const quizColl = conn.collection('files'); + const projection = { content: 0, folderName: 0, folderId: 0 }; const result = await quizColl.find({}, projection).toArray(); if (!result) return null; - return result; + let respObj = { + quizzes: result, + total: total + } + + return respObj; } async getImages(page, limit) { @@ -78,14 +86,14 @@ class Admin { return respObj; } - async deleteImage(uid, imgId) { + async deleteImage(imgId) { let resp = false; await this.db.connect() const conn = this.db.getConnection(); const quizColl = conn.collection('files'); const rgxImg = new RegExp(`/api/image/get/${imgId}`); - const result = await quizColl.find({ userId: uid, content: { $regex: rgxImg }}).toArray(); + const result = await quizColl.find({ content: { $regex: rgxImg }}).toArray(); if(!result || result.length < 1){ const imgsColl = conn.collection('images'); const isDeleted = await imgsColl.deleteOne({ _id: ObjectId.createFromHexString(imgId) }); diff --git a/server/models/quiz.js b/server/models/quiz.js index b388659..99527bd 100644 --- a/server/models/quiz.js +++ b/server/models/quiz.js @@ -70,6 +70,7 @@ class Quiz { return true; } + async deleteQuizzesByFolderId(folderId) { await this.db.connect(); const conn = this.db.getConnection(); diff --git a/server/routers/admin.js b/server/routers/admin.js index 3f547da..04e43b9 100644 --- a/server/routers/admin.js +++ b/server/routers/admin.js @@ -6,10 +6,10 @@ const asyncHandler = require('./routerUtils.js'); const jwt = require('../middleware/jwtToken.js'); -router.get("/getUsers", asyncHandler(admin.getUsers)); -router.get("/getQuizzes", asyncHandler(admin.getQuizzes)); -router.get("/getImages", asyncHandler(admin.getImages)); -router.delete("/deleteUser", asyncHandler(admin.deleteUser)); +router.get("/getUsers", jwt.authenticate, asyncHandler(admin.getUsers)); +router.get("/getStats", jwt.authenticate, asyncHandler(admin.getStats)); +router.get("/getImages", jwt.authenticate, asyncHandler(admin.getImages)); +router.delete("/deleteUser", jwt.authenticate, asyncHandler(admin.deleteUser)); router.delete("/deleteImage", jwt.authenticate, asyncHandler(admin.deleteImage)); module.exports = router;