import React, { useState, useRef, useEffect } from 'react'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { getPresetBanners, PresetBanner } from '../../utils/bannersService'; import { getApiPath } from '../../config/paths'; interface BannerEditModalProps { isOpen: boolean; onClose: () => void; onSave: (imageUrl: string) => Promise; currentImageUrl?: string; } const BannerEditModal: React.FC = ({ isOpen, onClose, onSave, currentImageUrl = '', }) => { const { t } = useTranslation(); const [imageFile, setImageFile] = useState(null); const [imagePreview, setImagePreview] = useState(currentImageUrl); const [isUploading, setIsUploading] = useState(false); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(null); const [presetBanners] = useState(getPresetBanners()); const fileInputRef = useRef(null); const modalRef = useRef(null); const [isClosing, setIsClosing] = useState(false); useEffect(() => { setImagePreview(currentImageUrl); }, [currentImageUrl, isOpen]); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { handleClose(); } }; if (isOpen) { document.addEventListener('keydown', handleKeyDown); document.body.style.overflow = 'hidden'; } return () => { document.removeEventListener('keydown', handleKeyDown); document.body.style.overflow = 'unset'; }; }, [isOpen]); const handlePresetBannerSelect = (banner: PresetBanner) => { setImageFile(null); setImagePreview(banner.url); setError(null); }; const handleImageSelect = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const maxSizeBytes = 10 * 1024 * 1024; if (file.size > maxSizeBytes) { setError( t( 'errors.projectImageTooLarge', 'Image is too large. Please choose a file under 10MB.' ) ); if (fileInputRef.current) { fileInputRef.current.value = ''; } return; } setImageFile(file); const reader = new FileReader(); reader.onload = (ev) => { setImagePreview(ev.target?.result as string); }; reader.readAsDataURL(file); setError(null); }; const handleImageUpload = async (): Promise => { if (!imageFile) return null; setIsUploading(true); try { const formData = new FormData(); formData.append('image', imageFile); const response = await fetch(getApiPath('upload/project-image'), { method: 'POST', credentials: 'include', body: formData, }); if (!response.ok) { let serverMessage = 'Failed to upload image'; try { const errData = await response.json(); if (errData?.error) serverMessage = errData.error; } catch { // ignore parse errors } throw new Error(serverMessage); } const result = await response.json(); if (result?.imageUrl) { return result.imageUrl; } throw new Error('Image URL missing from upload response'); } catch (error) { console.error('Error uploading image:', error); setError( t( 'errors.projectImageUpload', 'Failed to upload image. Please try a smaller file or a different format.' ) ); return null; } finally { setIsUploading(false); } }; const handleRemoveImage = () => { setImageFile(null); setImagePreview(''); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleSubmit = async () => { setIsSaving(true); setError(null); try { let imageUrl = imagePreview; // Upload image if a new one was selected if (imageFile) { const uploadedImageUrl = await handleImageUpload(); if (uploadedImageUrl) { imageUrl = uploadedImageUrl; } else { setIsSaving(false); return; } } await onSave(imageUrl); handleClose(); } catch (error) { console.error('Error saving banner:', error); setError(t('errors.bannerSaveFailed', 'Failed to save banner')); } finally { setIsSaving(false); } }; const handleClose = () => { setIsClosing(true); setTimeout(() => { onClose(); setIsClosing(false); setImageFile(null); setError(null); }, 300); }; if (!isOpen) return null; return createPortal(
{ if (e.target === e.currentTarget) { handleClose(); } }} >

{t( 'project.editBanner', 'Edit Project Banner' )}

{error && (

{error}

)} {imagePreview && (

{t( 'project.currentBanner', 'Current Banner' )}

Banner preview
)}

{t( 'project.choosePreset', 'Choose a preset banner:' )}

{presetBanners.map((banner) => ( ))}

{t( 'project.orUploadOwn', 'Or upload your own:' )}

{t( 'project.uploadImageHint', 'Upload an image for your project (max 10MB)' )}

, document.body ); }; export default BannerEditModal;