'use client' import { useState, useEffect } from 'react' import { useRouter } from 'next/navigation' interface Drop { id: number item: string size: number fill: number unit: string ppu: number created_at: string } export default function AdminPage() { const router = useRouter() const [drops, setDrops] = useState([]) const [loading, setLoading] = useState(true) const [submitting, setSubmitting] = useState(false) const [uploadingImage, setUploadingImage] = useState(false) const [formData, setFormData] = useState({ item: '', size: '', unit: 'g', ppu: '', imageFile: null as File | null, imagePreview: '', }) useEffect(() => { fetchDrops() }, []) const fetchDrops = async () => { try { const response = await fetch('/api/drops') if (!response.ok) { throw new Error('Failed to fetch drops') } const data = await response.json() // Ensure data is always an array setDrops(Array.isArray(data) ? data : []) } catch (error) { console.error('Error fetching drops:', error) setDrops([]) // Set to empty array on error } finally { setLoading(false) } } const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) { setFormData({ ...formData, imageFile: file, imagePreview: URL.createObjectURL(file), }) } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setSubmitting(true) try { let imageUrl = '' // Upload image if provided if (formData.imageFile) { setUploadingImage(true) const uploadFormData = new FormData() uploadFormData.append('file', formData.imageFile) const uploadResponse = await fetch('/api/upload', { method: 'POST', body: uploadFormData, }) if (!uploadResponse.ok) { const error = await uploadResponse.json() alert(`Image upload failed: ${error.error}`) setUploadingImage(false) setSubmitting(false) return } const uploadData = await uploadResponse.json() imageUrl = uploadData.url setUploadingImage(false) } // Create drop const response = await fetch('/api/drops', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ item: formData.item, size: parseInt(formData.size), unit: formData.unit.trim() || 'g', ppu: parseFloat(formData.ppu), imageUrl: imageUrl, }), }) if (response.ok) { // Reset form setFormData({ item: '', size: '', unit: 'g', ppu: '', imageFile: null, imagePreview: '', }) // Clear file input const fileInput = document.getElementById('imageFile') as HTMLInputElement if (fileInput) fileInput.value = '' // Refresh drops list fetchDrops() alert('Drop created successfully!') } else { const error = await response.json() alert(`Error: ${error.error}`) } } catch (error) { console.error('Error creating drop:', error) alert('Failed to create drop') } finally { setSubmitting(false) setUploadingImage(false) } } const getProgressPercentage = (fill: number, size: number) => { return Math.min((fill / size) * 100, 100).toFixed(1) } const isSoldOut = (fill: number, size: number) => { return fill >= size } return (

Admin Panel

{/* Create Drop Form */}

Create New Drop

setFormData({ ...formData, item: e.target.value }) } required placeholder="e.g. Harlequin – Collective Drop" />
setFormData({ ...formData, size: e.target.value }) } required placeholder="1000" min="1" />
setFormData({ ...formData, unit: e.target.value }) } placeholder="g, kg, ml, etc." required maxLength={12} />
setFormData({ ...formData, ppu: e.target.value }) } required placeholder="2.50" step="0.01" min="0" />
{formData.imagePreview && (
Preview
)}

Max file size: 5MB. Allowed formats: JPEG, PNG, WebP

{/* Drops List */}

All Drops

{loading ? (

Loading...

) : !Array.isArray(drops) || drops.length === 0 ? (

No drops yet

) : (
{drops.map((drop) => (

{drop.item}

ID: {drop.id} · Created:{' '} {new Date(drop.created_at).toLocaleDateString()}

{isSoldOut(drop.fill, drop.size) && ( Sold Out )}
{drop.fill} {drop.unit} / {drop.size} {drop.unit} {getProgressPercentage(drop.fill, drop.size)}%
{drop.ppu.toFixed(2)} CHF / {drop.unit}
))}
)}
) }