Responsive mobile / tablette / desktop
En-tête (onglets sur une seconde ligne en tablette, menu déroulant en mobile), panneau d'affectation et fiche bénévole en tiroir par-dessus sous lg: (plein écran en mobile), colonne des postes de la grille rétrécie, liste des bénévoles réduite en mobile, écran « Postes & créneaux » empilé, aperçu de l'éditeur de badges mis à l'échelle (glisser-déposer tactile), ligne des types de paiement repliée, marges réduites — desktop inchangé. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0f904f5e3d
commit
85e841ae4a
@@ -75,7 +75,7 @@ export function ConfigArtists() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid items-start gap-4 [grid-template-columns:repeat(auto-fill,minmax(320px,1fr))]">
|
||||
<div className="grid items-start gap-4 [grid-template-columns:repeat(auto-fill,minmax(min(320px,100%),1fr))]">
|
||||
{days.map((date) => (
|
||||
<DayColumn key={date} date={date} artists={byDate.get(date) ?? []} />
|
||||
))}
|
||||
|
||||
@@ -109,8 +109,9 @@ export function ConfigGrid() {
|
||||
].sort();
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 items-stretch">
|
||||
<div className="max-h-[calc(100vh-118px)] w-[250px] flex-none overflow-auto border-r-[3px] border-ink">
|
||||
<div className="flex min-h-0 flex-1 flex-col items-stretch md:flex-row">
|
||||
{/* Mobile : liste des postes au-dessus du détail, hauteur bornée. */}
|
||||
<div className="max-h-[260px] w-full flex-none overflow-auto border-b-[3px] border-ink md:max-h-[calc(100vh-118px)] md:w-[250px] md:border-b-0 md:border-r-[3px]">
|
||||
<div className="sticky top-0 z-[2] flex items-center gap-[6px] bg-ink px-[10px] py-[7px] font-display text-[10px] font-extrabold uppercase tracking-wider text-cream">
|
||||
{t('config.grid.positionsHeader', { count: totals.count })}
|
||||
<HelpTip text={t('config.grid.note')} side="bottom" />
|
||||
@@ -148,7 +149,7 @@ export function ConfigGrid() {
|
||||
{createPosition.isError ? <ErrorNote error={createPosition.error} /> : null}
|
||||
</div>
|
||||
|
||||
<div className="max-h-[calc(100vh-118px)] min-w-0 flex-1 overflow-auto p-4">
|
||||
<div className="min-w-0 flex-1 p-3 sm:p-4 md:max-h-[calc(100vh-118px)] md:overflow-auto">
|
||||
<PositionHeader
|
||||
key={position.id}
|
||||
position={position}
|
||||
@@ -158,7 +159,7 @@ export function ConfigGrid() {
|
||||
{positionShifts.isLoading ? <Loading /> : null}
|
||||
{updateShift.isError ? <ErrorNote error={updateShift.error} /> : null}
|
||||
|
||||
<div className="grid gap-[14px] [grid-template-columns:repeat(auto-fit,minmax(300px,1fr))]">
|
||||
<div className="grid gap-[14px] [grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr))]">
|
||||
{days.map((day) => (
|
||||
<DayCard
|
||||
key={day}
|
||||
@@ -197,15 +198,15 @@ function PositionHeader({
|
||||
|
||||
return (
|
||||
<div className="mb-[14px] flex flex-wrap items-end gap-[14px]">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex w-full min-w-0 flex-col gap-1 sm:w-auto">
|
||||
<input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
onBlur={commitName}
|
||||
onKeyDown={(e) => e.key === 'Enter' && e.currentTarget.blur()}
|
||||
className="border-2 border-ink bg-yellow px-2 py-1 font-display text-[22px] font-black tracking-tight"
|
||||
className="w-full min-w-0 border-2 border-ink bg-yellow px-2 py-1 font-display text-[20px] font-black tracking-tight sm:text-[22px]"
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<select
|
||||
value={position.category}
|
||||
onChange={(e) =>
|
||||
@@ -250,7 +251,7 @@ function PositionHeader({
|
||||
{updatePosition.isError ? <ErrorNote error={updatePosition.error} /> : null}
|
||||
{generate.isError ? <ErrorNote error={generate.error} /> : null}
|
||||
</div>
|
||||
<div className="ml-auto flex gap-2">
|
||||
<div className="flex gap-2 sm:ml-auto">
|
||||
<MiniStat value={`${totals.min} h`} label={t('config.grid.minTotal')} />
|
||||
<MiniStat value={`${totals.ideal} h`} label={t('config.grid.idealTotal')} accent />
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function ConfigMail() {
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="grid items-start gap-4 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
|
||||
<div className="grid items-start gap-4 [grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr))]">
|
||||
<Panel shadow="lg">
|
||||
<PanelHeader>
|
||||
{t('config.mail.templateTitle')} <HelpTip text={t('config.mail.smtpBanner')} side="bottom" />
|
||||
|
||||
@@ -28,7 +28,7 @@ export function ConfigRules() {
|
||||
const shifts = useShifts();
|
||||
|
||||
return (
|
||||
<div className="grid items-start gap-4 p-4 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
|
||||
<div className="grid items-start gap-4 p-3 sm:p-4 [grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr))]">
|
||||
<Panel shadow="lg">
|
||||
<PanelHeader>{t('config.rules.editionTitle')}</PanelHeader>
|
||||
<div className="flex flex-col gap-2 p-[13px]">
|
||||
|
||||
@@ -571,8 +571,10 @@ function issueParams(issue: ImportIssue): Record<string, string | number> {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Mobile : nom + cotisation seulement (téléphone, email et vœux restent sur
|
||||
// la fiche) ; toutes les colonnes à partir de `md:`.
|
||||
const LIST_COLUMNS =
|
||||
'minmax(0,2fr) minmax(0,1.4fr) minmax(0,2fr) minmax(0,1.2fr) minmax(0,1.2fr)';
|
||||
'grid-cols-[minmax(0,1fr)_auto] md:grid-cols-[minmax(0,2fr)_minmax(0,1.4fr)_minmax(0,2fr)_minmax(0,1.2fr)_minmax(0,1.2fr)]';
|
||||
|
||||
/** Résumé d'une ligne de la liste : mode de paiement (et mécénat), statut
|
||||
* d'un paiement en ligne non abouti. */
|
||||
@@ -677,7 +679,7 @@ export function ConfigVolunteers() {
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 items-stretch">
|
||||
<div className="max-h-[calc(100vh-118px)] min-w-0 flex-1 overflow-auto p-4">
|
||||
<div className="min-w-0 flex-1 p-3 sm:p-4 lg:max-h-[calc(100vh-118px)] lg:overflow-auto">
|
||||
<div className="mb-[14px] flex flex-wrap items-stretch gap-3">
|
||||
<div className="flex flex-1 flex-col justify-center gap-[10px] border-[3px] border-ink bg-paper p-[14px] shadow-brutal-lg">
|
||||
<div>
|
||||
@@ -757,13 +759,15 @@ export function ConfigVolunteers() {
|
||||
) : (
|
||||
<div className="border-[3px] border-ink bg-paper shadow-brutal-lg">
|
||||
<div
|
||||
className="grid gap-[10px] bg-ink px-[10px] py-[7px] font-display text-[9.5px] font-extrabold uppercase tracking-wide text-cream"
|
||||
style={{ gridTemplateColumns: LIST_COLUMNS }}
|
||||
className={cx(
|
||||
'grid gap-[10px] bg-ink px-[10px] py-[7px] font-display text-[9.5px] font-extrabold uppercase tracking-wide text-cream',
|
||||
LIST_COLUMNS,
|
||||
)}
|
||||
>
|
||||
<span>{t('config.vol.colName')}</span>
|
||||
<span>{t('config.vol.colPhone')}</span>
|
||||
<span>{t('config.vol.colEmail')}</span>
|
||||
<span>{t('config.vol.colWishes')}</span>
|
||||
<span className="hidden md:block">{t('config.vol.colPhone')}</span>
|
||||
<span className="hidden md:block">{t('config.vol.colEmail')}</span>
|
||||
<span className="hidden md:block">{t('config.vol.colWishes')}</span>
|
||||
<span>{t('config.vol.colFee')}</span>
|
||||
</div>
|
||||
{rows.map((volunteer) => {
|
||||
@@ -774,10 +778,10 @@ export function ConfigVolunteers() {
|
||||
type="button"
|
||||
onClick={() => setSelectedId(volunteer.id)}
|
||||
className={cx(
|
||||
'grid w-full items-center gap-[10px] border-b border-sand-line px-[10px] py-[7px] text-left',
|
||||
'grid w-full items-center gap-[10px] border-b border-sand-line px-[10px] py-[9px] text-left md:py-[7px]',
|
||||
LIST_COLUMNS,
|
||||
active ? 'bg-yellow' : 'hover:bg-cream',
|
||||
)}
|
||||
style={{ gridTemplateColumns: LIST_COLUMNS }}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-[7px]">
|
||||
<span className="flex h-6 w-6 flex-none items-center justify-center border-2 border-ink bg-paper font-display text-[9.5px] font-extrabold">
|
||||
@@ -788,13 +792,13 @@ export function ConfigVolunteers() {
|
||||
{volunteer.first_name} {volunteer.last_name}
|
||||
</span>
|
||||
</span>
|
||||
<span className="truncate font-mono text-[11px] opacity-80">
|
||||
<span className="hidden truncate font-mono text-[11px] opacity-80 md:block">
|
||||
{volunteer.phone ?? '—'}
|
||||
</span>
|
||||
<span className="truncate font-mono text-[10.5px] opacity-70">
|
||||
<span className="hidden truncate font-mono text-[10.5px] opacity-70 md:block">
|
||||
{volunteer.email ?? '—'}
|
||||
</span>
|
||||
<span className="truncate font-mono text-[10px] opacity-70">
|
||||
<span className="hidden truncate font-mono text-[10px] opacity-70 md:block">
|
||||
{t('config.vol.wishesCount', {
|
||||
count:
|
||||
volunteer.artist_ids.length +
|
||||
@@ -815,8 +819,17 @@ export function ConfigVolunteers() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Fiche : à côté de la liste sur grand écran, par-dessus (plein écran
|
||||
sur mobile, tiroir à droite sur tablette) en dessous de `lg:`. */}
|
||||
{selected ? (
|
||||
<aside className="max-h-[calc(100vh-118px)] w-[360px] flex-none overflow-auto border-l-[3px] border-ink bg-paper">
|
||||
<div
|
||||
aria-hidden
|
||||
onClick={() => setSelectedId(null)}
|
||||
className="fixed inset-0 z-40 bg-ink/40 lg:hidden"
|
||||
/>
|
||||
) : null}
|
||||
{selected ? (
|
||||
<aside className="fixed inset-y-0 right-0 z-50 w-full overflow-auto bg-paper sm:w-[400px] sm:border-l-[3px] sm:border-ink lg:static lg:z-auto lg:max-h-[calc(100vh-118px)] lg:w-[360px] lg:flex-none lg:border-l-[3px]">
|
||||
<div className="bg-ink px-[13px] py-[11px] text-cream">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-[6px] font-mono text-[10px] text-yellow">
|
||||
@@ -826,7 +839,8 @@ export function ConfigVolunteers() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedId(null)}
|
||||
className="cursor-pointer font-mono text-[11px] text-cream/70 hover:text-cream"
|
||||
aria-label={t('action.close')}
|
||||
className="h-8 w-8 cursor-pointer border-2 border-cream/40 font-mono text-[12px] text-cream/80 hover:text-cream lg:h-auto lg:w-auto lg:border-0"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user