"use client"; import React, { useEffect, useMemo, useState } from 'react'; import PageLayout from '../../../components/PageLayout'; import useCoffeeManagement from '../hooks/useCoffeeManagement'; import { PhotoIcon } from '@heroicons/react/24/solid'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import ImageCropModal from '../components/ImageCropModal'; import { useTranslation } from '../../../i18n/useTranslation'; export default function CreateSubscriptionPage() { const { t } = useTranslation(); const { createProduct } = useCoffeeManagement(); const router = useRouter(); const [error, setError] = useState(null); // form state const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [price, setPrice] = useState('0.00'); const [state, setState] = useState<'available'|'unavailable'>('available'); const [pictureFile, setPictureFile] = useState(undefined); const [previewUrl, setPreviewUrl] = useState(null); const [originalImageSrc, setOriginalImageSrc] = useState(null); const [showCropModal, setShowCropModal] = useState(false); const [currency, setCurrency] = useState('EUR'); const [isFeatured, setIsFeatured] = useState(false); // Gallery images (multi-upload, no crop) const [galleryFiles, setGalleryFiles] = useState([]); const [galleryPreviews, setGalleryPreviews] = useState([]); // Fixed billing defaults (locked: month / 1) const billingInterval: 'month' = 'month'; const intervalCount: number = 1; const onCreate = async (e: React.FormEvent) => { e.preventDefault(); setError(null); try { await createProduct({ title, description, price: parseFloat(price), currency, is_featured: isFeatured, state: state === 'available', pictureFile, pictureFiles: galleryFiles.length ? galleryFiles : undefined, }); router.push('/admin/subscriptions'); } catch (e: any) { setError(e.message || 'Failed to create'); } }; // Cleanup object URLs useEffect(() => { return () => { if (previewUrl) URL.revokeObjectURL(previewUrl); if (originalImageSrc) URL.revokeObjectURL(originalImageSrc); galleryPreviews.forEach(u => URL.revokeObjectURL(u)); }; }, [previewUrl, originalImageSrc, galleryPreviews]); function handleSelectFile(file?: File) { if (!file) return; const allowed = ['image/jpeg','image/png','image/webp']; if (!allowed.includes(file.type)) { setError('Invalid image type. Allowed: JPG, PNG, WebP'); return; } if (file.size > 10 * 1024 * 1024) { // 10MB setError('Image exceeds 10MB limit'); return; } setError(null); if (originalImageSrc) URL.revokeObjectURL(originalImageSrc); // Create object URL for cropping const url = URL.createObjectURL(file); setOriginalImageSrc(url); setShowCropModal(true); } function handleCropComplete(croppedBlob: Blob) { // Convert blob to file const croppedFile = new File([croppedBlob], 'cropped-image.jpg', { type: 'image/jpeg' }); setPictureFile(croppedFile); // Create preview URL if (previewUrl) URL.revokeObjectURL(previewUrl); const url = URL.createObjectURL(croppedBlob); setPreviewUrl(url); } function handleAddGalleryFiles(files: FileList | null) { if (!files || files.length === 0) return; const allowed = ['image/jpeg', 'image/png', 'image/webp']; const newFiles: File[] = []; const newPreviews: string[] = []; for (const file of Array.from(files)) { if (!allowed.includes(file.type)) { setError(`"${file.name}" is not a valid image type (JPG, PNG, WebP only).`); continue; } if (file.size > 10 * 1024 * 1024) { setError(`"${file.name}" exceeds the 10MB limit.`); continue; } newFiles.push(file); newPreviews.push(URL.createObjectURL(file)); } setGalleryFiles(prev => [...prev, ...newFiles]); setGalleryPreviews(prev => [...prev, ...newPreviews]); } function handleRemoveGalleryImage(index: number) { setGalleryPreviews(prev => { URL.revokeObjectURL(prev[index]); return prev.filter((_, i) => i !== index); }); setGalleryFiles(prev => prev.filter((_, i) => i !== index)); } function handleSetThumbnailFromGallery(index: number) { const source = galleryFiles[index]; if (!source) return; setError(null); const thumbFile = new File([source], source.name, { type: source.type }); setPictureFile(thumbFile); if (previewUrl) URL.revokeObjectURL(previewUrl); const thumbPreview = URL.createObjectURL(source); setPreviewUrl(thumbPreview); } return (
{/* Header card */}

{t('autofix.kaa30f0cd')}

{t('autofix.kf72d41db')}

{t('autofix.kd8a5ad17')}
{/* Form card */}
{/* Thumbnail */}

Single image used as the card thumbnail. You can crop it after selecting (16:9, sent as picture).

document.getElementById('file-upload')?.click()} onDragOver={e => e.preventDefault()} onDrop={e => { e.preventDefault(); if (e.dataTransfer.files?.[0]) handleSelectFile(e.dataTransfer.files[0]); }} > {!previewUrl && (
)} {previewUrl && (
Preview
)} handleSelectFile(e.target.files?.[0])} />
{/* Gallery Images */}

Upload additional product images (JPG, PNG, WebP ยท max 10 MB each). These are sent as pictures{t('autofix.k2992fa62')}pictureUrls.

{/* Gallery grid */} {galleryPreviews.length > 0 && (
{galleryPreviews.map((url, i) => (
{`Gallery #{i + 1}
))}
)} {/* Add images button */}
{/* Title */}
setTitle(e.target.value)} />
{/* Description */}