Front : heure de passage des artistes (config + mock du solveur)
- ArtistRead/Create/Update gagnent start_time/end_time. - Onglet Programmation : chaque artiste a désormais deux champs heure (début/fin de passage), éditables comme le nom. - Mock du solveur : porte le même malus de conflit d'artiste que le back-end (un créneau qui chevauche un passage souhaité est pénalisé), pour un comportement de démo cohérent. - Jeu de démo : 2 artistes par jour avec des horaires distincts (18h-19h / 20h30-22h) au lieu d'un horaire par défaut implicite. typecheck / lint / build / smoke au vert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
fea9d37019
commit
304b3c1d9e
@@ -25,8 +25,12 @@ function editionDays(start: string, end: string): string[] {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** « HH:MM:SS » -> « HH:MM » (pour un `<input type="time">`). */
|
||||
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();
|
||||
@@ -39,7 +43,7 @@ export function ConfigArtists() {
|
||||
map.set(a.performance_date, list);
|
||||
}
|
||||
for (const list of map.values()) {
|
||||
list.sort((a, b) => a.name.localeCompare(b.name, 'fr'));
|
||||
list.sort((a, b) => a.start_time.localeCompare(b.start_time) || a.name.localeCompare(b.name, 'fr'));
|
||||
}
|
||||
return map;
|
||||
}, [artists.data]);
|
||||
@@ -71,7 +75,7 @@ export function ConfigArtists() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid items-start gap-4 [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]">
|
||||
<div className="grid items-start gap-4 [grid-template-columns:repeat(auto-fill,minmax(320px,1fr))]">
|
||||
{days.map((date) => (
|
||||
<DayColumn key={date} date={date} artists={byDate.get(date) ?? []} />
|
||||
))}
|
||||
@@ -96,12 +100,19 @@ function DayColumn({
|
||||
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 },
|
||||
{
|
||||
name,
|
||||
performance_date: date,
|
||||
start_time: `${startDraft}:00`,
|
||||
end_time: `${endDraft}:00`,
|
||||
},
|
||||
{ onSuccess: () => setDraft('') },
|
||||
);
|
||||
};
|
||||
@@ -117,7 +128,7 @@ function DayColumn({
|
||||
<div className="font-mono text-[10px] opacity-55">{t('config.artists.empty')}</div>
|
||||
) : null}
|
||||
{artists.map((a) => (
|
||||
<div key={a.id} className="flex items-center gap-1">
|
||||
<div key={a.id} className="flex flex-wrap items-center gap-1">
|
||||
<input
|
||||
defaultValue={a.name}
|
||||
onBlur={(e) => {
|
||||
@@ -126,6 +137,26 @@ function DayColumn({
|
||||
}}
|
||||
className="min-w-0 flex-1 border-2 border-ink bg-paper px-2 py-1 font-display text-[12px] font-bold"
|
||||
/>
|
||||
<input
|
||||
type="time"
|
||||
aria-label={t('config.artists.startTime')}
|
||||
defaultValue={hm(a.start_time)}
|
||||
onBlur={(e) =>
|
||||
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]"
|
||||
/>
|
||||
<input
|
||||
type="time"
|
||||
aria-label={t('config.artists.endTime')}
|
||||
defaultValue={hm(a.end_time)}
|
||||
onBlur={(e) =>
|
||||
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]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => remove.mutate(a.id)}
|
||||
@@ -137,7 +168,7 @@ function DayColumn({
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="mt-1 flex items-center gap-1 border-t-2 border-ink pt-2">
|
||||
<div className="mt-1 flex flex-wrap items-center gap-1 border-t-2 border-ink pt-2">
|
||||
<input
|
||||
value={draft}
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
@@ -150,6 +181,20 @@ function DayColumn({
|
||||
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"
|
||||
/>
|
||||
<input
|
||||
type="time"
|
||||
aria-label={t('config.artists.startTime')}
|
||||
value={startDraft}
|
||||
onChange={(e) => setStartDraft(e.target.value)}
|
||||
className="w-[76px] flex-none border-2 border-ink bg-yellow px-1 py-1 font-mono text-[10.5px]"
|
||||
/>
|
||||
<input
|
||||
type="time"
|
||||
aria-label={t('config.artists.endTime')}
|
||||
value={endDraft}
|
||||
onChange={(e) => setEndDraft(e.target.value)}
|
||||
className="w-[76px] flex-none border-2 border-ink bg-yellow px-1 py-1 font-mono text-[10.5px]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!draft.trim() || create.isPending}
|
||||
|
||||
Reference in New Issue
Block a user