diff --git a/packages/backend/app/routers/__pycache__/celebrities.cpython-311.pyc b/packages/backend/app/routers/__pycache__/celebrities.cpython-311.pyc index 8757475..878e1dc 100644 Binary files a/packages/backend/app/routers/__pycache__/celebrities.cpython-311.pyc and b/packages/backend/app/routers/__pycache__/celebrities.cpython-311.pyc differ diff --git a/packages/backend/app/routers/celebrities.py b/packages/backend/app/routers/celebrities.py index 6c41894..d3a4807 100644 --- a/packages/backend/app/routers/celebrities.py +++ b/packages/backend/app/routers/celebrities.py @@ -46,20 +46,39 @@ def delete_celebrity(celebrity_id: int, db: Session = Depends(get_db)): # --- NUOVI ENDPOINT PER LE IMMAGINI --- -@router.post("/{celebrity_id}/images", response_model=schemas.Image, status_code=201) -def upload_celebrity_image(celebrity_id: int, file: UploadFile = File(...), db: Session = Depends(get_db)): +@router.post("/{celebrity_id}/images", response_model=List[schemas.Image], status_code=201) +def upload_celebrity_images(celebrity_id: int, files: List[UploadFile] = File(...), db: Session = Depends(get_db)): db_celebrity = crud.get_celebrity(db, celebrity_id=celebrity_id) if db_celebrity is None: raise HTTPException(status_code=404, detail="Celebrity not found") - file_extension = file.filename.split('.')[-1] - unique_filename = f"{uuid.uuid4()}.{file_extension}" - file_location = f"uploads/{unique_filename}" + created_images = [] + for file in files: + try: + file_extension = file.filename.split('.')[-1] + if not file_extension: # Gestisce file senza estensione + file_extension = "jpg" # Default + + unique_filename = f"{uuid.uuid4()}.{file_extension}" + file_location = f"uploads/{unique_filename}" - with open(file_location, "wb+") as file_object: - shutil.copyfileobj(file.file, file_object) + with open(file_location, "wb+") as file_object: + shutil.copyfileobj(file.file, file_object) + + db_image = crud.create_celebrity_image(db=db, celebrity_id=celebrity_id, file_path=unique_filename) + created_images.append(db_image) + + except Exception as e: + # Se un file fallisce, potremmo voler continuare con gli altri, + # ma per ora lanciamo un'eccezione per l'intero batch. + # In un'app di produzione, si potrebbe restituire una risposta parziale. + print(f"Failed to upload file {file.filename}: {e}") + raise HTTPException(status_code=500, detail=f"Could not upload file: {file.filename}") - return crud.create_celebrity_image(db=db, celebrity_id=celebrity_id, file_path=unique_filename) + if not created_images: + raise HTTPException(status_code=400, detail="No files were successfully uploaded.") + + return created_images @router.post("/{celebrity_id}/images/from-url", response_model=schemas.Image, status_code=201) async def add_image_from_url(celebrity_id: int, request: schemas.ImageUrlRequest, db: Session = Depends(get_db)): diff --git a/packages/frontend/src/components/CelebrityProfile.jsx b/packages/frontend/src/components/CelebrityProfile.jsx index 62eafe6..9c456f9 100644 --- a/packages/frontend/src/components/CelebrityProfile.jsx +++ b/packages/frontend/src/components/CelebrityProfile.jsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; -import { getCelebrityById, updateCelebrity, deleteCelebrity, uploadImage, setProfileImage, deleteImage, addImageFromUrl } from '../services/api'; +import { getCelebrityById, updateCelebrity, deleteCelebrity, uploadImages, setProfileImage, deleteImage, addImageFromUrl } from '../services/api'; import EditableField from './EditableField'; import './CelebrityProfile.css'; @@ -14,7 +14,7 @@ function CelebrityProfile() { const [error, setError] = useState(null); // Stati per la gestione upload - const [selectedFile, setSelectedFile] = useState(null); + const [selectedFiles, setSelectedFiles] = useState(null); // Da file a files const [isUploading, setIsUploading] = useState(false); const [imageUrl, setImageUrl] = useState(''); const [isFetchingFromUrl, setIsFetchingFromUrl] = useState(false); @@ -43,12 +43,13 @@ function CelebrityProfile() { } }, [id, navigate, fetchProfile]); - // Effetto per caricare il file quando `selectedFile` cambia + // Effetto per caricare i file quando `selectedFiles` cambia useEffect(() => { - if (selectedFile) { + if (selectedFiles && selectedFiles.length > 0) { handleUpload(); } - }, [selectedFile]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedFiles]); const handleFieldSave = async (fieldName, newValue) => { let valueToSend = newValue === '' ? null : newValue; @@ -77,22 +78,26 @@ function CelebrityProfile() { } } }; - + // --- Gestori per le Immagini --- - const handleFileChange = (e) => setSelectedFile(e.target.files[0]); + const handleFileChange = (e) => { + if (e.target.files.length > 0) { + setSelectedFiles(Array.from(e.target.files)); + } + }; const handleUpload = async () => { - if (!selectedFile) return; + if (!selectedFiles || selectedFiles.length === 0) return; setIsUploading(true); setError(null); try { - await uploadImage(id, selectedFile); - setSelectedFile(null); - document.querySelector('input[type="file"]').value = ""; // Reset del campo file - fetchProfile(); // Ricarica i dati per vedere la nuova immagine + await uploadImages(id, selectedFiles); // Chiama la nuova funzione plurale + fetchProfile(); } catch (err) { setError(`Upload fallito: ${err.message}`); } finally { + setSelectedFiles(null); + if(fileInputRef.current) fileInputRef.current.value = ""; setIsUploading(false); } }; @@ -142,7 +147,7 @@ function CelebrityProfile() { setIsDraggingOver(false); const files = e.dataTransfer.files; if (files && files.length > 0) { - setSelectedFile(files[0]); + setSelectedFiles(Array.from(files)); } }; @@ -177,7 +182,7 @@ function CelebrityProfile() {
{error &&

Errore: {error}

} - +

Galleria e Upload

@@ -207,8 +212,11 @@ function CelebrityProfile() { onDragLeave={handleDragLeave} aria-busy={isUploading} > - - {isUploading ?

Caricamento...

:

Trascina un file qui, o clicca per selezionare.

} + + {isUploading + ?

Caricamento di {selectedFiles.length} file...

+ :

Trascina i file qui, o clicca per selezionare.

+ }
@@ -251,7 +259,7 @@ function CelebrityProfile() {
- +

Misure Corporee

@@ -277,7 +285,7 @@ function CelebrityProfile() {
)} - +

Biografia

diff --git a/packages/frontend/src/services/api.js b/packages/frontend/src/services/api.js index f64a022..f0ae701 100644 --- a/packages/frontend/src/services/api.js +++ b/packages/frontend/src/services/api.js @@ -60,6 +60,20 @@ export const uploadImage = async (celebrityId, file) => { return handleResponse(response); }; +export const uploadImages = async (celebrityId, files) => { + const formData = new FormData(); + // Aggiunge ogni file allo stesso campo 'files'. Il backend lo interpreterà come una lista. + files.forEach(file => { + formData.append('files', file); + }); + + const response = await fetch(`${API_BASE_URL}/celebrities/${celebrityId}/images`, { + method: 'POST', + body: formData, + }); + return handleResponse(response); +}; + export const setProfileImage = async (celebrityId, imageId) => { const response = await fetch(`${API_BASE_URL}/celebrities/${celebrityId}/profile-image`, { method: 'PUT', diff --git a/uploads/165fa68d-eb00-47f7-a1b2-4d5fa8352728.png b/uploads/165fa68d-eb00-47f7-a1b2-4d5fa8352728.png new file mode 100644 index 0000000..0180853 Binary files /dev/null and b/uploads/165fa68d-eb00-47f7-a1b2-4d5fa8352728.png differ diff --git a/uploads/1d024628-4db7-4333-8b77-6e3e61de5a20.webp b/uploads/1d024628-4db7-4333-8b77-6e3e61de5a20.webp new file mode 100644 index 0000000..5324e25 Binary files /dev/null and b/uploads/1d024628-4db7-4333-8b77-6e3e61de5a20.webp differ diff --git a/uploads/34da28b0-5f05-4996-acf3-fbeec43fe916.png b/uploads/34da28b0-5f05-4996-acf3-fbeec43fe916.png new file mode 100644 index 0000000..32e2d52 Binary files /dev/null and b/uploads/34da28b0-5f05-4996-acf3-fbeec43fe916.png differ diff --git a/uploads/dfdcb6c6-68c4-4dc0-a15e-889df4356e3e.png b/uploads/dfdcb6c6-68c4-4dc0-a15e-889df4356e3e.png new file mode 100644 index 0000000..158d318 Binary files /dev/null and b/uploads/dfdcb6c6-68c4-4dc0-a15e-889df4356e3e.png differ diff --git a/uploads/ecb201fe-6968-4891-99d6-deafe7fca852.png b/uploads/ecb201fe-6968-4891-99d6-deafe7fca852.png new file mode 100644 index 0000000..fb2d6a9 Binary files /dev/null and b/uploads/ecb201fe-6968-4891-99d6-deafe7fca852.png differ