2025-03-04 19:49:18 -05:00
import React , { useState , useEffect } from "react" ;
import {
2025-03-20 21:11:46 -04:00
Box ,
CircularProgress ,
Button ,
IconButton ,
Card ,
CardContent ,
2025-03-04 19:49:18 -05:00
Dialog ,
DialogContent ,
DialogActions ,
2025-03-20 21:11:46 -04:00
DialogTitle ,
DialogContentText ,
Tabs ,
Tab ,
TextField , Snackbar ,
Alert
2025-03-04 19:49:18 -05:00
} from "@mui/material" ;
2025-03-20 21:11:46 -04:00
import DeleteIcon from "@mui/icons-material/Delete" ;
2025-03-04 19:49:18 -05:00
import ContentCopyIcon from "@mui/icons-material/ContentCopy" ;
import CloseIcon from "@mui/icons-material/Close" ;
2025-03-20 21:11:46 -04:00
import { ImageType } from "../../Types/ImageType" ;
import ApiService from "../../services/ApiService" ;
import { Upload } from "@mui/icons-material" ;
2025-03-27 19:18:43 -04:00
import { escapeForGIFT } from "src/utils/giftUtils" ;
2025-04-07 22:00:28 -04:00
import { ENV_VARIABLES } from "src/constants" ;
2025-03-20 21:11:46 -04:00
interface ImagesProps {
handleCopy ? : ( id : string ) = > void ;
2025-03-21 15:24:21 -04:00
handleDelete ? : ( id : string ) = > void ;
2025-03-20 21:11:46 -04:00
}
2025-03-04 19:49:18 -05:00
2025-03-21 15:24:21 -04:00
const ImageGallery : React.FC < ImagesProps > = ( { handleCopy , handleDelete } ) = > {
2025-03-20 21:11:46 -04:00
const [ images , setImages ] = useState < ImageType [ ] > ( [ ] ) ;
2025-03-04 19:49:18 -05:00
const [ totalImg , setTotalImg ] = useState ( 0 ) ;
const [ imgPage , setImgPage ] = useState ( 1 ) ;
2025-03-20 21:11:46 -04:00
const [ imgLimit ] = useState ( 6 ) ;
2025-03-13 18:02:26 -04:00
const [ loading , setLoading ] = useState ( false ) ;
2025-03-20 21:11:46 -04:00
const [ selectedImage , setSelectedImage ] = useState < ImageType | null > ( null ) ;
const [ openDeleteDialog , setOpenDeleteDialog ] = useState ( false ) ;
const [ imageToDelete , setImageToDelete ] = useState < ImageType | null > ( null ) ;
const [ tabValue , setTabValue ] = useState ( 0 ) ;
const [ importedImage , setImportedImage ] = useState < File | null > ( null ) ;
const [ preview , setPreview ] = useState < string | null > ( null ) ;
const [ snackbarOpen , setSnackbarOpen ] = useState ( false ) ;
const [ snackbarMessage , setSnackbarMessage ] = useState ( "" ) ;
const [ snackbarSeverity , setSnackbarSeverity ] = useState < "success" | "error" > ( "success" ) ;
2025-03-04 19:49:18 -05:00
2025-03-20 21:11:46 -04:00
const fetchImages = async ( ) = > {
setLoading ( true ) ;
2025-03-20 23:19:54 -04:00
const data = await ApiService . getUserImages ( imgPage , imgLimit ) ;
2025-03-04 19:49:18 -05:00
setImages ( data . images ) ;
setTotalImg ( data . total ) ;
2025-03-20 21:11:46 -04:00
setLoading ( false ) ;
2025-03-04 19:49:18 -05:00
} ;
useEffect ( ( ) = > {
2025-03-20 21:11:46 -04:00
fetchImages ( ) ;
2025-03-13 18:02:26 -04:00
} , [ imgPage ] ) ;
2025-03-04 19:49:18 -05:00
2025-03-21 15:24:21 -04:00
const defaultHandleDelete = async ( id : string ) = > {
2025-03-20 21:11:46 -04:00
if ( imageToDelete ) {
setLoading ( true ) ;
2025-03-21 15:24:21 -04:00
const isDeleted = await ApiService . deleteImage ( id ) ;
2025-03-20 21:11:46 -04:00
setLoading ( false ) ;
2025-03-04 19:49:18 -05:00
2025-03-20 21:11:46 -04:00
if ( isDeleted ) {
2025-03-21 15:24:21 -04:00
setImgPage ( 1 ) ;
fetchImages ( ) ;
2025-03-27 19:18:43 -04:00
setSnackbarMessage ( "Image supprimée avec succès!" ) ;
2025-03-20 21:11:46 -04:00
setSnackbarSeverity ( "success" ) ;
} else {
setSnackbarMessage ( "Erreur lors de la suppression de l'image. Veuillez réessayer." ) ;
setSnackbarSeverity ( "error" ) ;
}
setSnackbarOpen ( true ) ;
setSelectedImage ( null ) ;
setImageToDelete ( null ) ;
setOpenDeleteDialog ( false ) ;
2025-03-13 18:02:26 -04:00
}
} ;
2025-03-20 21:11:46 -04:00
const defaultHandleCopy = ( id : string ) = > {
if ( navigator . clipboard ) {
2025-04-07 22:00:28 -04:00
const link = ` ${ ENV_VARIABLES . BACKEND_URL } /api/image/get/ ${ id } ` ;
2025-04-07 21:10:53 -04:00
const imgTag = ` [markdown]  } "texte de l'infobulle (ne fonctionne pas sur écran tactile généralement)") ` ;
setSnackbarMessage ( "Le lien Markdown de l'image a été copié dans le presse-papiers" ) ;
2025-03-27 19:18:43 -04:00
setSnackbarSeverity ( "success" ) ;
setSnackbarOpen ( true ) ;
navigator . clipboard . writeText ( imgTag ) ;
}
if ( handleCopy ) {
handleCopy ( id ) ;
2025-03-13 18:02:26 -04:00
}
} ;
2025-03-21 15:24:21 -04:00
const handleDeleteFunction = handleDelete || defaultHandleDelete ;
2025-03-20 21:11:46 -04:00
const handleImageUpload = ( event : React.ChangeEvent < HTMLInputElement > ) = > {
const file = event . target . files ? event . target . files [ 0 ] : null ;
setImportedImage ( file ) ;
if ( file ) {
const objectUrl = URL . createObjectURL ( file ) ;
setPreview ( objectUrl ) ;
2025-03-13 18:02:26 -04:00
}
2025-03-04 19:49:18 -05:00
} ;
2025-03-13 18:02:26 -04:00
2025-03-20 21:11:46 -04:00
const handleSaveImage = async ( ) = > {
try {
if ( ! importedImage ) {
2025-03-21 23:29:20 -04:00
setSnackbarMessage ( "Veuillez choisir une image à téléverser." ) ;
2025-03-20 21:11:46 -04:00
setSnackbarSeverity ( "error" ) ;
setSnackbarOpen ( true ) ;
return ;
}
const imageUrl = await ApiService . uploadImage ( importedImage ) ;
if ( imageUrl . includes ( "ERROR" ) ) {
setSnackbarMessage ( "Une erreur est survenue. Veuillez réessayer plus tard." ) ;
setSnackbarSeverity ( "error" ) ;
setSnackbarOpen ( true ) ;
return ;
}
fetchImages ( ) ;
setSnackbarMessage ( "Téléversée avec succès !" ) ;
setSnackbarSeverity ( "success" ) ;
setSnackbarOpen ( true ) ;
setImportedImage ( null ) ;
setPreview ( null ) ;
2025-03-27 19:18:43 -04:00
setTabValue ( 0 ) ;
2025-03-20 21:11:46 -04:00
} catch ( error ) {
setSnackbarMessage ( ` Une erreur est survenue. \ n ${ error } \ nVeuillez réessayer plus tard. ` ) ;
setSnackbarSeverity ( "error" ) ;
setSnackbarOpen ( true ) ;
2025-03-13 18:02:26 -04:00
}
2025-03-04 19:49:18 -05:00
} ;
2025-03-21 23:29:20 -04:00
const handleCloseSnackbar = ( ) = > {
setSnackbarOpen ( false ) ;
} ;
2025-03-27 19:18:43 -04:00
2025-03-04 19:49:18 -05:00
return (
2025-03-20 21:11:46 -04:00
< Box p = { 3 } >
< Tabs value = { tabValue } onChange = { ( _ , newValue ) = > setTabValue ( newValue ) } >
2025-04-07 15:43:41 -04:00
< Tab label = "Galerie" / >
2025-03-20 21:11:46 -04:00
< Tab label = "Import" / >
< / Tabs >
{ tabValue === 0 && (
< >
{ loading ? (
< Box display = "flex" justifyContent = "center" alignItems = "center" height = { 200 } >
< CircularProgress / >
< / Box >
) : (
< >
< Box display = "grid" gridTemplateColumns = "repeat(3, 1fr)" gridTemplateRows = "repeat(2, 1fr)" gap = { 2 } maxWidth = "900px" margin = "auto" >
{ images . map ( ( obj ) = > (
< Card key = { obj . id } sx = { { cursor : "pointer" , display : "flex" , flexDirection : "column" , alignItems : "center" } } onClick = { ( ) = > setSelectedImage ( obj ) } >
< CardContent sx = { { p : 0 } } >
2025-03-13 18:02:26 -04:00
< img
src = { ` data: ${ obj . mime_type } ;base64, ${ obj . file_content } ` }
alt = { ` Image ${ obj . file_name } ` }
2025-03-20 21:11:46 -04:00
style = { { width : "100%" , height : 250 , objectFit : "cover" , borderRadius : 8 } }
2025-03-04 19:49:18 -05:00
/ >
2025-03-20 21:11:46 -04:00
< / CardContent >
< Box display = "flex" justifyContent = "center" mt = { 1 } >
< IconButton onClick = { ( e ) = > {
e . stopPropagation ( ) ;
2025-03-27 19:18:43 -04:00
defaultHandleCopy ( obj . id ) ;
2025-03-20 21:11:46 -04:00
} }
2025-03-20 23:19:54 -04:00
color = "primary"
data - testid = { ` gallery-tab-copy- ${ obj . id } ` } >
2025-03-20 21:11:46 -04:00
< ContentCopyIcon sx = { { fontSize : 18 } } / >
2025-03-13 18:02:26 -04:00
< / IconButton >
2025-03-20 21:11:46 -04:00
< IconButton
onClick = { ( e ) = > {
e . stopPropagation ( ) ;
setImageToDelete ( obj ) ;
setOpenDeleteDialog ( true ) ;
} }
2025-03-20 23:19:54 -04:00
color = "error"
data - testid = { ` gallery-tab-delete- ${ obj . id } ` } >
2025-03-20 21:11:46 -04:00
< DeleteIcon sx = { { fontSize : 18 } } / >
2025-03-13 18:02:26 -04:00
< / IconButton >
2025-03-20 21:11:46 -04:00
< / Box >
< / Card >
2025-03-13 18:02:26 -04:00
) ) }
2025-03-20 21:11:46 -04:00
< / Box >
< Box display = "flex" justifyContent = "center" mt = { 2 } >
< Button onClick = { ( ) = > setImgPage ( ( prev ) = > Math . max ( prev - 1 , 1 ) ) } disabled = { imgPage === 1 } color = "primary" >
Précédent
< / Button >
< Button onClick = { ( ) = > setImgPage ( ( prev ) = > ( prev * imgLimit < totalImg ? prev + 1 : prev ) ) } disabled = { imgPage * imgLimit >= totalImg } color = "primary" >
Suivant
< / Button >
< / Box >
< / >
) }
< / >
) }
{ tabValue === 1 && (
< Box display = "flex" flexDirection = "column" alignItems = "center" width = "100%" mt = { 3 } >
{ /* Image Preview at the top */ }
{ preview && (
< Box
mt = { 2 }
mb = { 2 }
sx = { {
width : "100%" ,
maxWidth : 600 ,
textAlign : "center" ,
display : "flex" ,
justifyContent : "center" ,
alignItems : "center" ,
} }
>
< img
src = { preview }
alt = "Preview"
style = { {
width : "100%" ,
height : "auto" ,
borderRadius : 8 ,
maxHeight : "600px" ,
} }
/ >
< / Box >
) }
< Box display = "flex" flexDirection = "row" alignItems = "center" width = "100%" maxWidth = { 400 } >
< TextField
type = "file"
2025-03-21 23:29:20 -04:00
data - testid = "file-input"
2025-03-20 21:11:46 -04:00
onChange = { handleImageUpload }
slotProps = { {
htmlInput : {
2025-03-21 23:29:20 -04:00
"data-testid" : "file-input" ,
2025-03-20 21:11:46 -04:00
accept : "image/*" ,
} ,
} }
sx = { { flexGrow : 1 } }
/ >
< Button
variant = "outlined"
aria - label = "Téléverser"
onClick = { ( ) = > { handleSaveImage ( ) } }
sx = { { ml : 2 , height : "100%" } }
>
Téléverser < Upload sx = { { ml : 1 } } / >
2025-03-04 19:49:18 -05:00
< / Button >
2025-03-20 21:11:46 -04:00
< / Box >
< / Box >
2025-03-13 18:02:26 -04:00
) }
2025-03-20 21:11:46 -04:00
< Dialog open = { ! ! selectedImage } onClose = { ( ) = > setSelectedImage ( null ) } maxWidth = "md" >
2025-03-21 23:29:20 -04:00
< IconButton color = "primary" onClick = { ( ) = > setSelectedImage ( null ) } sx = { { position : "absolute" , right : 8 , top : 8 , zIndex : 1 } }
data - testid = "close-button" >
2025-03-20 21:11:46 -04:00
< CloseIcon / >
< / IconButton >
< DialogContent >
{ selectedImage && (
< img
src = { ` data: ${ selectedImage . mime_type } ;base64, ${ selectedImage . file_content } ` }
alt = "Enlarged view"
style = { { width : "100%" , height : "auto" , borderRadius : 8 , maxHeight : "500px" } }
/ >
) }
< / DialogContent >
< / Dialog >
{ /* Delete Confirmation Dialog */ }
< Dialog open = { openDeleteDialog } onClose = { ( ) = > setOpenDeleteDialog ( false ) } >
< DialogTitle > Supprimer < / DialogTitle >
< DialogContent >
< DialogContentText > Voulez - vous supprimer cette image ? < / DialogContentText >
< / DialogContent >
< DialogActions >
< Button onClick = { ( ) = > setOpenDeleteDialog ( false ) } color = "primary" >
Annuler
2025-03-13 18:02:26 -04:00
< / Button >
2025-03-21 15:24:21 -04:00
< Button onClick = { ( ) = > imageToDelete && handleDeleteFunction ( imageToDelete . id ) } color = "error" >
2025-03-20 21:11:46 -04:00
Delete
2025-03-13 18:02:26 -04:00
< / Button >
2025-03-20 21:11:46 -04:00
< / DialogActions >
< / Dialog >
2025-03-21 23:29:20 -04:00
< Snackbar
open = { snackbarOpen }
autoHideDuration = { 4000 }
onClose = { handleCloseSnackbar }
>
< Alert
onClose = { handleCloseSnackbar }
severity = { snackbarSeverity }
sx = { { width : "100%" } } >
2025-03-20 21:11:46 -04:00
{ snackbarMessage }
< / Alert >
< / Snackbar >
< / Box >
2025-03-04 19:49:18 -05:00
) ;
} ;
2025-03-20 21:11:46 -04:00
export default ImageGallery ;