import { useMemo, useState } from 'react'; import { useArtists, useCreateArtist, useDeleteArtist, useEdition, useUpdateArtist, } from '@/api/hooks'; import type { ArtistRead } from '@/api/schema'; import { ErrorNote, Loading } from '@/components/state'; import { HelpTip, Panel, PanelHeader } from '@/components/ui'; import { t } from '@/i18n'; import { longDayLabel } from '@/lib/time'; function editionDays(start: string, end: string): string[] { const out: string[] = []; for ( let d = Date.parse(`${start}T00:00:00Z`); d <= Date.parse(`${end}T00:00:00Z`); d += 86_400_000 ) { out.push(new Date(d).toISOString().slice(0, 10)); } return out; } /** « HH:MM:SS » -> « HH:MM » (pour un ``). */ const hm = (time: string): string => time.slice(0, 5); // Programmation de l'édition : la liste d'artistes que les bénévoles pourront // choisir (voir onglet « Bénévoles & import »). Constituée jour par jour. // L'heure de passage est prise en compte par le solveur (vœux d'artistes). export function ConfigArtists() { const edition = useEdition(); const artists = useArtists(); const byDate = useMemo(() => { const map = new Map(); for (const a of artists.data ?? []) { const list = map.get(a.performance_date) ?? []; list.push(a); map.set(a.performance_date, list); } for (const list of map.values()) { list.sort((a, b) => a.start_time.localeCompare(b.start_time) || a.name.localeCompare(b.name, 'fr')); } return map; }, [artists.data]); if (edition.isLoading || artists.isLoading) return ; const error = edition.error ?? artists.error; if (error) return ; const days = edition.data ? editionDays(edition.data.start_date, edition.data.end_date) : []; const extraDays = [...byDate.keys()].filter((d) => !days.includes(d)).sort(); const total = artists.data?.length ?? 0; return (
{t('config.artists.title')}
{total}
{t('config.artists.count')}
{days.map((date) => ( ))} {extraDays.map((date) => ( ))}
); } function DayColumn({ date, artists, outside = false, }: { date: string; artists: ArtistRead[]; outside?: boolean; }) { const create = useCreateArtist(); const update = useUpdateArtist(); const remove = useDeleteArtist(); const [draft, setDraft] = useState(''); const [startDraft, setStartDraft] = useState('20:00'); const [endDraft, setEndDraft] = useState('21:00'); const add = () => { const name = draft.trim(); if (!name) return; create.mutate( { name, performance_date: date, start_time: `${startDraft}:00`, end_time: `${endDraft}:00`, }, { onSuccess: () => setDraft('') }, ); }; return ( {longDayLabel(`${date}T12:00`)} {outside ? ` · ${t('config.artists.outside')}` : ''}
{artists.length === 0 ? (
{t('config.artists.empty')}
) : null} {artists.map((a) => (
{ const v = e.target.value.trim(); if (v && v !== a.name) update.mutate({ id: a.id, body: { name: v } }); }} className="min-w-0 flex-1 border-2 border-ink bg-paper px-2 py-1 font-display text-[12px] font-bold" /> e.target.value && update.mutate({ id: a.id, body: { start_time: `${e.target.value}:00` } }) } className="w-[76px] flex-none border-2 border-ink bg-paper px-1 py-1 font-mono text-[10.5px]" /> e.target.value && update.mutate({ id: a.id, body: { end_time: `${e.target.value}:00` } }) } className="w-[76px] flex-none border-2 border-ink bg-paper px-1 py-1 font-mono text-[10.5px]" />
))}
setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); add(); } }} placeholder={t('config.artists.namePlaceholder')} className="min-w-0 flex-1 border-2 border-ink bg-yellow px-2 py-1 font-display text-[12px] font-bold" /> setStartDraft(e.target.value)} className="w-[76px] flex-none border-2 border-ink bg-yellow px-1 py-1 font-mono text-[10.5px]" /> setEndDraft(e.target.value)} className="w-[76px] flex-none border-2 border-ink bg-yellow px-1 py-1 font-mono text-[10.5px]" />
{create.isError ? : null} {update.isError ? : null}
); }