mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
FIX admin delete image route
This commit is contained in:
parent
f006dfd195
commit
3124d23df3
10 changed files with 139 additions and 91 deletions
26
client/package-lock.json
generated
26
client/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -17,3 +17,8 @@ export interface QuizTypeShort {
|
|||
created_at: Date;
|
||||
updated_at: Date;
|
||||
}
|
||||
|
||||
export interface QuizResponse {
|
||||
quizzes: QuizTypeShort[];
|
||||
total: number;
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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<QuizTypeShort[]>([]);
|
||||
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 (
|
||||
<Box display="flex" justifyContent="center" alignItems="center" height="100vh">
|
||||
<CircularProgress size={80} thickness={5} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper} className="p-4">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Enseignant</TableCell>
|
||||
<TableCell>Titre</TableCell>
|
||||
<TableCell>Crée</TableCell>
|
||||
<TableCell>Modifié</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{quizzes.map((quiz) => (
|
||||
<TableRow key={quiz._id}>
|
||||
<TableCell>{quiz.userId}</TableCell>
|
||||
<TableCell>{quiz.title}</TableCell>
|
||||
<TableCell>{new Date(quiz.created_at).toLocaleDateString()}</TableCell>
|
||||
<TableCell>{new Date(quiz.updated_at).toLocaleDateString()}</TableCell>
|
||||
<TableCell>
|
||||
<IconButton onClick={() => handleDelete(quiz._id)} color="error">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
<Paper className="p-4" sx={{ boxShadow: 'none' }}>
|
||||
<Grid container spacing={8} justifyContent="center">
|
||||
<Grid item>
|
||||
<Typography variant="h6" align="center">Quiz du Mois</Typography>
|
||||
<Box position="relative" display="inline-flex">
|
||||
<CircularProgress variant="determinate" value={monthlyQuizzes === 0 ? 0 : monthlyQuizzes} size={80} thickness={5} />
|
||||
<Box position="absolute" top={0} left={0} bottom={0} right={0} display="flex" alignItems="center" justifyContent="center">
|
||||
<Typography variant="h6">{monthlyQuizzes}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="h6" align="center">Quiz total</Typography>
|
||||
<Box position="relative" display="inline-flex">
|
||||
<CircularProgress variant="determinate" value={totalQuizzes} size={80} thickness={5} />
|
||||
<Box position="absolute" top={0} left={0} bottom={0} right={0} display="flex" alignItems="center" justifyContent="center">
|
||||
<Typography variant="h6">{totalQuizzes}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="h6" align="center">Enseignants</Typography>
|
||||
<Box position="relative" display="inline-flex">
|
||||
<CircularProgress variant="determinate" value={totalUsers} size={80} thickness={5} />
|
||||
<Box position="absolute" top={0} left={0} bottom={0} right={0} display="flex" alignItems="center" justifyContent="center">
|
||||
<Typography variant="h6">{totalUsers}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Table */}
|
||||
<TableContainer component={Paper} className="mt-4">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Enseignant</TableCell>
|
||||
<TableCell>Titre</TableCell>
|
||||
<TableCell>Crée</TableCell>
|
||||
<TableCell>Modifié</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{quizzes.map((quiz) => (
|
||||
<TableRow key={quiz._id}>
|
||||
<TableCell>{quiz.userId}</TableCell>
|
||||
<TableCell>{quiz.title}</TableCell>
|
||||
<TableCell>{new Date(quiz.created_at).toLocaleDateString()}</TableCell>
|
||||
<TableCell>{new Date(quiz.updated_at).toLocaleDateString()}</TableCell>
|
||||
<TableCell>
|
||||
<IconButton onClick={() => handleDelete(quiz._id)} color="error">
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<any> {
|
|||
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<any> {
|
|||
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<any> {
|
|||
}
|
||||
}
|
||||
|
||||
public async getQuizzes(): Promise<QuizTypeShort[]> {
|
||||
public async getStats(): Promise<QuizResponse> {
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) });
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ class Quiz {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteQuizzesByFolderId(folderId) {
|
||||
await this.db.connect();
|
||||
const conn = this.db.getConnection();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue