import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; import { Plus, Trash2, Save } from 'lucide-react'; import { api } from '../../lib/api-client'; import { AppShell, PageHeader, PageBody } from '../../layout/AppShell'; import { Button, Badge, Card, CardContent, Input, Label, Select } from '../../components/ui/primitives'; import { Table, THead, TBody, TR, TH, TD } from '../../components/ui/table'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '../../components/ui/dialog'; interface Release { id: string; slug: string; title: string; brand: string; model: string | null; colorway: string | null; release_date: string; release_time: string | null; price_eur: number | null; style_code: string | null; image_url: string | null; hype_score: number; status: 'upcoming' | 'released' | 'cancelled'; } export function ReleaseCalendar() { const qc = useQueryClient(); const [statusFilter, setStatusFilter] = useState('upcoming'); const [editing, setEditing] = useState | null>(null); const { data } = useQuery({ queryKey: ['releases', statusFilter], queryFn: () => api.get<{ releases: Release[] }>('/releases', statusFilter ? { status: statusFilter } : undefined), }); const saveMut = useMutation({ mutationFn: (body: Partial) => { if (body.id) return api.put(`/releases/${body.id}`, body); return api.post('/releases', body); }, onSuccess: () => { toast.success('Release opgeslagen'); setEditing(null); qc.invalidateQueries({ queryKey: ['releases'] }); }, onError: (e: Error) => toast.error(e.message), }); const deleteMut = useMutation({ mutationFn: (id: string) => api.delete(`/releases/${id}`), onSuccess: () => { toast.success('Release verwijderd'); qc.invalidateQueries({ queryKey: ['releases'] }); }, }); const releases = data?.releases ?? []; return ( setEditing({ slug: '', title: '', brand: '', release_date: new Date().toISOString().slice(0, 10), hype_score: 50, status: 'upcoming', }) } > Nieuwe release } />
{releases.map((r) => ( setEditing(r)}> ))}
Datum Titel Merk Colorway Prijs Hype Status
{new Date(r.release_date).toLocaleDateString('nl-NL', { day: '2-digit', month: 'short', year: 'numeric' })} {r.release_time && {r.release_time.slice(0, 5)}} {r.title} {r.brand} {r.colorway ?? '—'} {r.price_eur ? `€${Number(r.price_eur).toFixed(0)}` : '—'} 70 ? 'text-[#00FF6A]' : r.hype_score > 40 ? 'text-[#FFA500]' : 'text-[#999]'}> {r.hype_score} {r.status} e.stopPropagation()}>
!o && setEditing(null)}> {editing?.id ? 'Release bewerken' : 'Nieuwe release'} {editing && (
setEditing({ ...editing, title: e.target.value })} />
setEditing({ ...editing, slug: e.target.value })} />
setEditing({ ...editing, brand: e.target.value })} />
setEditing({ ...editing, model: e.target.value })} />
setEditing({ ...editing, colorway: e.target.value })} />
setEditing({ ...editing, release_date: e.target.value })} />
setEditing({ ...editing, release_time: e.target.value })} />
setEditing({ ...editing, price_eur: e.target.value ? Number(e.target.value) : null })} />
setEditing({ ...editing, style_code: e.target.value })} />
setEditing({ ...editing, hype_score: Number(e.target.value) })} />
setEditing({ ...editing, image_url: e.target.value })} />
)}
); }