FIX dialog front et perf pagination

This commit is contained in:
Eddi3_As 2025-03-04 19:49:18 -05:00
parent c9d65c0082
commit 0f87bab5ff
4 changed files with 191 additions and 61 deletions

View file

@ -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<React.SetStateAction<boolean>>;
setImageLinks: React.Dispatch<React.SetStateAction<string[]>>;
}
const ImageDialog: React.FC<Props> = ({ galleryOpen, admin, setDialogOpen, setImageLinks }) => {
const [copiedId, setCopiedId] = useState<string | null>(null);
const [deleteId, setDeleteId] = useState<string | null>(null);
/* const [editedFileNames, setEditedFileNames] = useState<{ [key: string]: string }>(
Object.fromEntries(images.map((img) => [img.id, img.file_name]))
);
*/
const [editingId, setEditingId] = useState<string | null>(null);
const [images, setImages] = useState<Images[]>([]);
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 (
<Dialog
open={galleryOpen}
onClose={() => setDialogOpen(false)}
maxWidth="xl" // 'md' stands for medium size
>
<DialogTitle>
Images disponibles
<IconButton
aria-label="close"
color="primary"
onClick={() => setDialogOpen(false)}
style={{ position: "absolute", right: 8, top: 8 }}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<TableContainer component={Paper}>
<Table>
<TableBody>
{images.map((obj: Images) => (
<TableRow key={obj.id}>
<TableCell>
<img
src={`data:${obj.mime_type};base64,${obj.file_content}`}
alt={`Image ${obj.file_name}`}
style={{ width: 350, height: "auto", borderRadius: 8 }}
/>
</TableCell>
<TableCell>
{admin && editingId === obj.id ? (
<TextField
value={obj.file_name}
onChange={(e) => handleFileNameChange(obj.id, e.target.value)}
variant="outlined"
size="small"
style={{ maxWidth: 150 }}
/>
) : (
obj.file_name
)}
</TableCell>
<TableCell style={{ minWidth: 150 }}>
{obj.id}
<IconButton onClick={() => onCopy(obj.id)} size="small">
<ContentCopyIcon fontSize="small" />
</IconButton>
{admin && (
<>
<IconButton onClick={() => handleEditClick(obj.id)} size="small" color="primary">
<EditIcon fontSize="small" />
</IconButton>
<IconButton onClick={() => handleDeleteClick(obj.id)} size="small" color="primary">
<DeleteIcon fontSize="small" />
</IconButton>
</>
)}
{copiedId === obj.id && <span style={{ marginLeft: 8, color: "green" }}>Copié!</span>}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</DialogContent>
<DialogActions style={{ justifyContent: "center", width: "100%" }}>
<Box display="flex" justifyContent="center" width="100%">
<Button onClick={handlePrevPage} disabled={imgPage === 1} color="primary">
Précédent
</Button>
<Button onClick={handleNextPage} disabled={(imgPage * imgLimit) >= totalImg} color="primary">
Suivant
</Button>
</Box>
</DialogActions>
</Dialog>
);
};
export default ImageDialog;

View file

@ -13,11 +13,11 @@ import { QuizType } from '../../../Types/QuizType';
import './editorQuiz.css'; import './editorQuiz.css';
import { Button, TextField, NativeSelect, Divider, Dialog, DialogTitle, DialogActions, DialogContent } from '@mui/material'; import { Button, TextField, NativeSelect, Divider, Dialog, DialogTitle, DialogActions, DialogContent } from '@mui/material';
import ReturnButton from 'src/components/ReturnButton/ReturnButton'; import ReturnButton from 'src/components/ReturnButton/ReturnButton';
import ImageGallery from 'src/components/ImageGallery/ImageGallery';
import ApiService from '../../../services/ApiService'; import ApiService from '../../../services/ApiService';
import { escapeForGIFT } from '../../../utils/giftUtils'; import { escapeForGIFT } from '../../../utils/giftUtils';
import { Upload, ImageSearch } from '@mui/icons-material'; import { Upload, ImageSearch } from '@mui/icons-material';
import { Images } from '../../../Types/Images';
interface EditQuizParams { interface EditQuizParams {
id: string; id: string;
@ -43,10 +43,6 @@ const QuizForm: React.FC = () => {
const [dialogOpen, setDialogOpen] = useState(false); const [dialogOpen, setDialogOpen] = useState(false);
const [galleryOpen, setGalleryOpen] = useState(false); const [galleryOpen, setGalleryOpen] = useState(false);
const [showScrollButton, setShowScrollButton] = useState(false); const [showScrollButton, setShowScrollButton] = useState(false);
const [images, setImages] = useState<Images[]>([]);
const [totalImg, setTotalImg] = useState(0);
const [imgPage, setImgPage] = useState(1);
const [imgLimit] = useState(5);
const scrollToTop = () => { const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' }); 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(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
const userFolders = await ApiService.getUserFolders(); const userFolders = await ApiService.getUserFolders();
fetchImages(1, imgLimit);
setFolders(userFolders as FolderType[]); setFolders(userFolders as FolderType[]);
}; };
@ -221,12 +207,12 @@ const QuizForm: React.FC = () => {
navigator.clipboard.writeText(link); navigator.clipboard.writeText(link);
} }
const handleMoreImages = async () => { const handleCopy = (imgId: string) => {
let page = imgPage; setImageLinks(prevLinks => [...prevLinks, imgId]);
page += 1; console.log(imgId);
setImgPage(page); };
fetchImages(imgPage, imgLimit);
}
return ( return (
<div className='quizEditor'> <div className='quizEditor'>
@ -321,40 +307,14 @@ const QuizForm: React.FC = () => {
Images <ImageSearch /> Images <ImageSearch />
</Button> </Button>
<Dialog <ImageGallery
open={galleryOpen} galleryOpen={galleryOpen}
onClose={() => setDialogOpen(false)} > setDialogOpen={setGalleryOpen}
<DialogTitle>Images disponibles</DialogTitle> admin={false}
<DialogContent> setImageLinks={setImageLinks}
>
</ImageGallery>
<div className="grid grid-cols-3 gap-4 p-4">
{images.map((obj: Images, index) => (
<div key={obj.id}>
<img
key={index}
src={`data:${obj.mime_type};base64,${obj.file_content}`}
alt={`Image ${obj.file_name + 1}`}
className="w-full h-auto rounded-lg shadow-md"
/>
{`lien: ${obj.id}`}
</div>
))}
</div>
</DialogContent>
<DialogActions>
{
totalImg > 10 ?
<Button onClick={() => handleMoreImages()} color="primary">
Plus
</Button>
:
<Button onClick={() => setDialogOpen(false)} color="primary">
OK
</Button>
}
</DialogActions>
</Dialog>
<div> <div>
<div> <div>
<div style={{ display: "inline" }}>(Voir section </div> <div style={{ display: "inline" }}>(Voir section </div>

View file

@ -53,7 +53,7 @@ class ImagesController {
getImages = async (req, res, next) => { getImages = async (req, res, next) => {
try { try {
const page = parseInt(req.query.page) || 1; 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); const imagesBit = await this.images.getImages(page, limit);

View file

@ -48,13 +48,17 @@ class Images {
const imagesCollection = conn.collection('images'); const imagesCollection = conn.collection('images');
const result = await imagesCollection.find({}).sort({created_at: 1}).toArray();
if (!result) return null; const total = await imagesCollection.countDocuments(); // Efficient total count
if (!total || total === 0) return { images: [], total };
const total = result.length; const result = await imagesCollection.find({})
.sort({ created_at: 1 }) // Ensure 'created_at' is indexed
.skip((page - 1) * limit)
.limit(limit)
.toArray();
const objImages = result.slice((page - 1) * limit, page * limit).map(image => ({ const objImages = result.map(image => ({
id: image._id, id: image._id,
user: image.userId, user: image.userId,
file_name: image.file_name, file_name: image.file_name,