diff --git a/client/src/Types/LabelMap.tsx b/client/src/Types/LabelMap.tsx new file mode 100644 index 0000000..7ceeab3 --- /dev/null +++ b/client/src/Types/LabelMap.tsx @@ -0,0 +1,2 @@ + +export type LabelMap = { [key: string]: string }; \ No newline at end of file diff --git a/client/src/components/AdminTable/AdminTable.tsx b/client/src/components/AdminTable/AdminTable.tsx new file mode 100644 index 0000000..b56abd7 --- /dev/null +++ b/client/src/components/AdminTable/AdminTable.tsx @@ -0,0 +1,169 @@ +import React, { useState } from "react"; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TablePagination, + Paper, + Input, + IconButton, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Button, + InputAdornment, + Box, +} from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import SearchIcon from "@mui/icons-material/Search"; +import { QuizTypeShort } from "../../Types/QuizType"; +import { LabelMap } from "../../Types/LabelMap"; + + +interface AdminTableProps { + data: QuizTypeShort[]; + onDelete: (row: QuizTypeShort) => void; + filterKeys?: string[]; + labelMap?: LabelMap; +} + +const AdminTable: React.FC = ({ + data, + onDelete, + filterKeys = [], + labelMap = {}, +}) => { + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [searchQuery, setSearchQuery] = useState(""); + const [openDialog, setOpenDialog] = useState(false); + const [deleteRow, setDeleteRow] = useState(null); + + const handleChangePage = (_event: unknown, newPage: number) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event: React.ChangeEvent) => { + setRowsPerPage(parseInt(event.target.value, 10)); + setPage(0); + }; + + const handleSearchChange = (event: React.ChangeEvent) => { + setSearchQuery(event.target.value); + setPage(0); + }; + + const handleOpenDialog = (row: QuizTypeShort) => { + setDeleteRow(row); + setOpenDialog(true); + }; + + const handleCloseDialog = () => { + setOpenDialog(false); + setDeleteRow(null); + }; + + const handleConfirmDelete = () => { + if (deleteRow) { + onDelete(deleteRow); + } + handleCloseDialog(); + }; + + const filteredData = data.filter((row) => { + return Object.values(row).some((value) => + value.toString().toLowerCase().includes(searchQuery.toLowerCase()) + ); + }); + + const headers = Object.keys(labelMap).filter((key) => !filterKeys.includes(key)); + + return ( + + + + + + } + sx={{ width: "30%" }} + /> + + + + + + {headers.map((key) => ( + + {labelMap[key] || key} {/* Use custom label from map or fallback to key */} + + ))} + + + + + {filteredData + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((row, index) => ( + + {headers.map((key) => { + const value = row[key as keyof QuizTypeShort]; + let displayValue; + if (value instanceof Date) { + displayValue = value.toLocaleDateString("en-GB"); + } else if (value && typeof value === "string" && !isNaN(Date.parse(value))) { + displayValue = new Date(value).toLocaleDateString("en-GB"); + } else { + displayValue = value; + } + + return {displayValue}; + })} + + handleOpenDialog(row)}> + + + + + ))} + +
+
+ + + + Confirm Deletion + + + Are you sure you want to delete this record? + + + + + + + +
+ ); +}; + +export default AdminTable; diff --git a/client/src/pages/Admin/Stats.tsx b/client/src/pages/Admin/Stats.tsx index 8dab712..290b7f9 100644 --- a/client/src/pages/Admin/Stats.tsx +++ b/client/src/pages/Admin/Stats.tsx @@ -1,12 +1,10 @@ import React, { useState, useEffect } from "react"; -import { - Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, - IconButton, Grid, Typography, CircularProgress, Box, TextField, Accordion, AccordionSummary, AccordionDetails -} from "@mui/material"; -import DeleteIcon from "@mui/icons-material/Delete"; +import { Paper, Grid, Typography, CircularProgress, Box, TextField, Accordion, AccordionSummary, AccordionDetails} from "@mui/material"; import ApiService from '../../services/ApiService'; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { QuizTypeShort } from "../../Types/QuizType"; +import AdminTable from "../../components/AdminTable/AdminTable"; + const Users: React.FC = () => { const [quizzes, setQuizzes] = useState([]); @@ -52,8 +50,8 @@ const Users: React.FC = () => { setFilteredQuizzes(filtered); }, [emailFilter, dateFilter, quizzes]); - const handleDelete = (id: string) => { - setQuizzes(quizzes.filter(quiz => quiz._id !== id)); + const handleQuizDelete = (rowToDelete: QuizTypeShort) => { + setQuizzes((prevData) => prevData.filter((row) => row._id !== rowToDelete._id)); }; const totalQuizzes = quizzes.length; @@ -66,6 +64,14 @@ const Users: React.FC = () => { ); } + const labelMap = { + _id: "ID", + email: "Enseignant", + title: "Titre", + created_at: "Création", + updated_at: "Mise à Jour", + }; + return ( @@ -98,72 +104,12 @@ const Users: React.FC = () => { - - setExpanded(!expanded)}> - } - aria-controls="filter-content" - id="filter-header" - > - Filtres - - - - - setEmailFilter(e.target.value)} - /> - - - setDateFilter(e.target.value)} - /> - - - - - - - {/* Table */} - - - - - - Enseignant - - Titre - Crée - Modifié - - - - {filteredQuizzes.map((quiz) => ( - - {quiz.email} - {quiz.title} - {new Date(quiz.created_at).toLocaleDateString()} - {new Date(quiz.updated_at).toLocaleDateString()} - - handleDelete(quiz._id)} color="error"> - - - - - ))} - -
-
+
); };