From 0f87bab5fff9863bb771402adf503acc54633dbe Mon Sep 17 00:00:00 2001 From: Eddi3_As Date: Tue, 4 Mar 2025 19:49:18 -0500 Subject: [PATCH] FIX dialog front et perf pagination --- .../components/ImageGallery/ImageGallery.tsx | 166 ++++++++++++++++++ .../pages/Teacher/EditorQuiz/EditorQuiz.tsx | 70 ++------ server/controllers/images.js | 2 +- server/models/images.js | 14 +- 4 files changed, 191 insertions(+), 61 deletions(-) create mode 100644 client/src/components/ImageGallery/ImageGallery.tsx diff --git a/client/src/components/ImageGallery/ImageGallery.tsx b/client/src/components/ImageGallery/ImageGallery.tsx new file mode 100644 index 0000000..be4a67a --- /dev/null +++ b/client/src/components/ImageGallery/ImageGallery.tsx @@ -0,0 +1,166 @@ +import React, { useState, useEffect } from "react"; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, + IconButton, + Paper, + TextField, + Box +} from "@mui/material"; +import ContentCopyIcon from "@mui/icons-material/ContentCopy"; +import CloseIcon from "@mui/icons-material/Close"; +import DeleteIcon from "@mui/icons-material/Delete"; +import EditIcon from "@mui/icons-material/Edit"; +import { Images } from "../../Types/Images"; +import ApiService from '../../services/ApiService'; + +type Props = { + galleryOpen: boolean; + admin: boolean; + setDialogOpen: React.Dispatch>; + setImageLinks: React.Dispatch>; +} + +const ImageDialog: React.FC = ({ galleryOpen, admin, setDialogOpen, setImageLinks }) => { + + const [copiedId, setCopiedId] = useState(null); + const [deleteId, setDeleteId] = useState(null); + /* const [editedFileNames, setEditedFileNames] = useState<{ [key: string]: string }>( + Object.fromEntries(images.map((img) => [img.id, img.file_name])) + ); + */ + const [editingId, setEditingId] = useState(null); + const [images, setImages] = useState([]); + const [totalImg, setTotalImg] = useState(0); + const [imgPage, setImgPage] = useState(1); + const [imgLimit] = useState(3); + + const fetchImages = async (page: number, limit: number) => { + const data = await ApiService.getImages(page, limit); + console.log(data); + setImages(data.images); + setTotalImg(data.total); + }; + + useEffect(() => { + fetchImages(imgPage, imgLimit); + }, [imgPage]); // Re-fetch images when page changes + + const handleDeleteClick = (id: string) => { + setDeleteId(id); + }; + + const handleEditClick = (id: string) => { + setEditingId(id === editingId ? null : id); + }; + + const handleFileNameChange = (id: string, newFileName: string) => { + //setEditedFileNames((prev) => ({ ...prev, [id]: newFileName })); + }; + + const onCopy = (id: string) => { + setCopiedId(id); + setImageLinks(prevLinks => [...prevLinks, id]); + }; + + const handleNextPage = () => { + if ((imgPage * imgLimit) < totalImg) { + setImgPage(prev => prev + 1); + } + }; + + const handlePrevPage = () => { + if (imgPage > 1) { + setImgPage(prev => prev - 1); + } + }; + + return ( + setDialogOpen(false)} + maxWidth="xl" // 'md' stands for medium size + > + + Images disponibles + setDialogOpen(false)} + style={{ position: "absolute", right: 8, top: 8 }} + > + + + + + + + + {images.map((obj: Images) => ( + + + {`Image + + + {admin && editingId === obj.id ? ( + handleFileNameChange(obj.id, e.target.value)} + variant="outlined" + size="small" + style={{ maxWidth: 150 }} + /> + ) : ( + obj.file_name + )} + + + {obj.id} + onCopy(obj.id)} size="small"> + + + {admin && ( + <> + handleEditClick(obj.id)} size="small" color="primary"> + + + handleDeleteClick(obj.id)} size="small" color="primary"> + + + + )} + {copiedId === obj.id && Copié!} + + + ))} + +
+
+
+ + + + + + +
+ ); +}; + +export default ImageDialog; diff --git a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx index 51ad681..bafb1d9 100644 --- a/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx +++ b/client/src/pages/Teacher/EditorQuiz/EditorQuiz.tsx @@ -13,11 +13,11 @@ import { QuizType } from '../../../Types/QuizType'; import './editorQuiz.css'; import { Button, TextField, NativeSelect, Divider, Dialog, DialogTitle, DialogActions, DialogContent } from '@mui/material'; import ReturnButton from 'src/components/ReturnButton/ReturnButton'; +import ImageGallery from 'src/components/ImageGallery/ImageGallery'; import ApiService from '../../../services/ApiService'; import { escapeForGIFT } from '../../../utils/giftUtils'; import { Upload, ImageSearch } from '@mui/icons-material'; -import { Images } from '../../../Types/Images'; interface EditQuizParams { id: string; @@ -43,10 +43,6 @@ const QuizForm: React.FC = () => { const [dialogOpen, setDialogOpen] = useState(false); const [galleryOpen, setGalleryOpen] = useState(false); const [showScrollButton, setShowScrollButton] = useState(false); - const [images, setImages] = useState([]); - const [totalImg, setTotalImg] = useState(0); - const [imgPage, setImgPage] = useState(1); - const [imgLimit] = useState(5); const scrollToTop = () => { window.scrollTo({ top: 0, behavior: 'smooth' }); @@ -75,19 +71,9 @@ const QuizForm: React.FC = () => { } }; - const fetchImages = async (page: number , limit: number) => { - const data = await ApiService.getImages(page, limit); - const imgs = data.images; - const total = data.total; - - setImages(imgs as Images[]); - setTotalImg(total); - } - useEffect(() => { const fetchData = async () => { const userFolders = await ApiService.getUserFolders(); - fetchImages(1, imgLimit); setFolders(userFolders as FolderType[]); }; @@ -220,13 +206,13 @@ const QuizForm: React.FC = () => { const handleCopyToClipboard = async (link: string) => { navigator.clipboard.writeText(link); } + + const handleCopy = (imgId: string) => { + setImageLinks(prevLinks => [...prevLinks, imgId]); + console.log(imgId); + }; - const handleMoreImages = async () => { - let page = imgPage; - page += 1; - setImgPage(page); - fetchImages(imgPage, imgLimit); - } + return (
@@ -321,40 +307,14 @@ const QuizForm: React.FC = () => { Images - setDialogOpen(false)} > - Images disponibles - - -
- {images.map((obj: Images, index) => ( -
- {`Image - {`lien: ${obj.id}`} -
- ))} -
-
- - { - totalImg > 10 ? - - : - - - } - -
+ + +
(Voir section
diff --git a/server/controllers/images.js b/server/controllers/images.js index 877eb7d..c5675d4 100644 --- a/server/controllers/images.js +++ b/server/controllers/images.js @@ -53,7 +53,7 @@ class ImagesController { getImages = async (req, res, next) => { try { const page = parseInt(req.query.page) || 1; - const limit = parseInt(req.query.limit) || 10; + const limit = parseInt(req.query.limit) || 5; const imagesBit = await this.images.getImages(page, limit); diff --git a/server/models/images.js b/server/models/images.js index b631e0e..13d6ad0 100644 --- a/server/models/images.js +++ b/server/models/images.js @@ -48,13 +48,17 @@ class Images { const imagesCollection = conn.collection('images'); - const result = await imagesCollection.find({}).sort({created_at: 1}).toArray(); + + const total = await imagesCollection.countDocuments(); // Efficient total count + if (!total || total === 0) return { images: [], total }; - if (!result) return null; + const result = await imagesCollection.find({}) + .sort({ created_at: 1 }) // Ensure 'created_at' is indexed + .skip((page - 1) * limit) + .limit(limit) + .toArray(); - const total = result.length; - - const objImages = result.slice((page - 1) * limit, page * limit).map(image => ({ + const objImages = result.map(image => ({ id: image._id, user: image.userId, file_name: image.file_name,