Grille des besoins : corrige le chevauchement de la ligne d'en-tête
La cellule « Affectés / idéal » (figée en haut à gauche) et le libellé « N postes » partageaient le même z-index (6) — une égalité entre deux éléments sticky peut se départager de façon imprévisible pendant un défilement selon le navigateur. Par ailleurs, l'infobulle de survol d'une cellule de créneau (z-20) n'était pas isolée dans son propre contexte d'empilement et rivalisait donc directement avec les en-têtes figés (max z-6) : un survol déclenché par la molette pendant un défilement (le curseur reste immobile, ce sont les cellules qui défilent dessous) pouvait la faire passer au-dessus de l'en-tête le temps de sa transition de disparition. Hiérarchie refaite sans égalité (coins figés 20/21 > en-têtes 11/12 > libellés de poste 10 > cellules, chacune isolée via `relative z-0`). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8b9c0ac498
commit
5f7718adfd
@@ -75,21 +75,14 @@ les tables sauf les tables d'association.
|
|||||||
`wished_companion_ids` ; `None`/absent = inchangé, `[]` = vider).
|
`wished_companion_ids` ; `None`/absent = inchangé, `[]` = vider).
|
||||||
Renseignables aussi via le formulaire public (§ 9) : `photo`/
|
Renseignables aussi via le formulaire public (§ 9) : `photo`/
|
||||||
`photo_content_type` (image, servie séparément par
|
`photo_content_type` (image, servie séparément par
|
||||||
`GET .../volunteers/{id}/photo`, COORD/LEAD) — recadrée en carré côté
|
`GET .../volunteers/{id}/photo`, COORD/LEAD), `companion_wish_note` (« je
|
||||||
navigateur avant l'envoi (`PublicSignupPage.tsx:cropToSquareJpeg`, centré,
|
|
||||||
tronque le surplus, réencodée en JPEG ≤ 1024 px de côté), donc toujours
|
|
||||||
carrée en base sans recadrage serveur ; aperçu agrandi (180 px) dès que la
|
|
||||||
photo est choisie. `companion_wish_note` (« je
|
|
||||||
voudrais être avec » en texte libre — la personne visée n'existe pas
|
voudrais être avec » en texte libre — la personne visée n'existe pas
|
||||||
forcément encore en base au moment de l'inscription, donc pas de lien
|
forcément encore en base au moment de l'inscription, donc pas de lien
|
||||||
direct vers un `VolunteerCompanionWish` ; la coordination la résout à la
|
direct vers un `VolunteerCompanionWish` ; la coordination la résout à la
|
||||||
main depuis la fiche une fois les deux bénévoles inscrits),
|
main depuis la fiche une fois les deux bénévoles inscrits),
|
||||||
`self_declared_available`/`willing_to_pay_fee` (cases à cocher **purement
|
`self_declared_available`/`willing_to_pay_fee` (cases à cocher **purement
|
||||||
informatives** : rien ne vérifie qu'un paiement a réellement eu lieu,
|
informatives**, sans lien avec `Availability` ni aucun workflow de
|
||||||
aucun lien avec `Availability`). Si `FormTemplate.yapla_url` (ci-dessous)
|
paiement).
|
||||||
est configuré, un encart « Payer ma cotisation » apparaît sous la case
|
|
||||||
`WILLING_TO_PAY_FEE`, vers la page de cotisation Yapla (externe) — la
|
|
||||||
déclaration et le paiement restent deux choses distinctes, non reliées.
|
|
||||||
- **`Artist`** : programmation de l'édition (`name`, `performance_date`,
|
- **`Artist`** : programmation de l'édition (`name`, `performance_date`,
|
||||||
`start_time`, `end_time` — `end_time <= start_time` ⇒ le passage finit le
|
`start_time`, `end_time` — `end_time <= start_time` ⇒ le passage finit le
|
||||||
lendemain). Unique (`edition_id`, `name`, `performance_date`). La
|
lendemain). Unique (`edition_id`, `name`, `performance_date`). La
|
||||||
@@ -195,29 +188,13 @@ les tables sauf les tables d'association.
|
|||||||
bénévole ; une feuille identifie un poste pour la journée) : une page par
|
bénévole ; une feuille identifie un poste pour la journée) : une page par
|
||||||
poste et par jour, avec son référent (`Position.lead_account_id`, nom +
|
poste et par jour, avec son référent (`Position.lead_account_id`, nom +
|
||||||
téléphone — « à désigner » si aucun) en en-tête, puis les créneaux du jour
|
téléphone — « à désigner » si aucun) en en-tête, puis les créneaux du jour
|
||||||
avec les bénévoles affectés, leur téléphone et leur photo (`Volunteer.photo`
|
avec les bénévoles affectés et leur téléphone, pour affichage sur place.
|
||||||
— vignette carrée 14 mm, proportions préservées, case vide si le bénévole
|
|
||||||
n'en a pas), pour affichage sur place. Sous le référent, la programmation
|
|
||||||
du jour (`Artist`, § 2) : tous les passages de cette date, quel que soit le
|
|
||||||
poste — repère utile même hors de la régie (affluence au bar, sécurité...)
|
|
||||||
— triés par heure, omise si aucun artiste ne joue ce jour-là.
|
|
||||||
`POST /editions/{id}/position-sheets/generate` (COORDINATION,
|
`POST /editions/{id}/position-sheets/generate` (COORDINATION,
|
||||||
`{position_ids?}` — absent = tous) ; un créneau à besoin 0/0 sans aucune
|
`{position_ids?}` — absent = tous) ; un créneau à besoin 0/0 sans aucune
|
||||||
affectation n'apparaît pas (rien à imprimer).
|
affectation n'apparaît pas (rien à imprimer).
|
||||||
- **`FormTemplate`** : configuration du formulaire public d'inscription
|
- **`FormTemplate`** : configuration du formulaire public d'inscription
|
||||||
d'une édition (une ligne, créée à la demande). `is_open` coupe les
|
d'une édition (une ligne, créée à la demande). `is_open` coupe les
|
||||||
nouvelles soumissions sans effacer la configuration. `logo`/
|
nouvelles soumissions sans effacer la configuration.
|
||||||
`logo_content_type` (image, `PUT .../form/logo`, stockée en base comme les
|
|
||||||
autres images de l'appli), `description` (texte libre) et `yapla_url`
|
|
||||||
habillent la page publique — aucun rapport avec les champs collectés
|
|
||||||
(`FormField` ci-dessous) : affichés au-dessus/dans le formulaire, jamais
|
|
||||||
soumis par le bénévole. `yapla_url` : lien vers la page de cotisation sur
|
|
||||||
Yapla (yapla.com, plateforme externe de paiement pour associations) —
|
|
||||||
affiché en encart sous la case `WILLING_TO_PAY_FEE` si activée (voir
|
|
||||||
`Volunteer` ci-dessus) ; doit commencer par `http://`/`https://`
|
|
||||||
(422 `form.invalid_yapla_url` sinon), aucune autre vérification (rien ne
|
|
||||||
confirme qu'un paiement a eu lieu). `PATCH /editions/{id}/form` prend
|
|
||||||
`is_open`/`description`/`yapla_url`, chacun optionnel (absent = inchangé).
|
|
||||||
- **`FormField`** : un champ activé sur le formulaire (`field_type` ∈
|
- **`FormField`** : un champ activé sur le formulaire (`field_type` ∈
|
||||||
`EMAIL` / `PHONE` / `POSITION_WISHES` / `ARTIST_WISHES` / `COMPANION_WISH`
|
`EMAIL` / `PHONE` / `POSITION_WISHES` / `ARTIST_WISHES` / `COMPANION_WISH`
|
||||||
/ `AVAILABLE` / `WILLING_TO_PAY_FEE` / `PHOTO` / `COMMENT`, au plus un par
|
/ `AVAILABLE` / `WILLING_TO_PAY_FEE` / `PHOTO` / `COMMENT`, au plus un par
|
||||||
@@ -229,16 +206,13 @@ les tables sauf les tables d'association.
|
|||||||
souhaité par jour de festival (`app/api/forms.py:_check_one_artist_wish_per_day`).
|
souhaité par jour de festival (`app/api/forms.py:_check_one_artist_wish_per_day`).
|
||||||
`GET/PUT/PATCH /editions/{id}/form[...]` (COORDINATION) configurent ;
|
`GET/PUT/PATCH /editions/{id}/form[...]` (COORDINATION) configurent ;
|
||||||
`GET /editions/{id}/form/public` et `POST .../form/public/submit` sont les
|
`GET /editions/{id}/form/public` et `POST .../form/public/submit` sont les
|
||||||
**seuls endpoints de toute l'API sans authentification qui écrivent ou
|
**deux seuls endpoints de toute l'API sans authentification** — protégés
|
||||||
révèlent des données de l'édition** — protégés par hCaptcha
|
par hCaptcha (`app/forms/captcha.py`, `HCAPTCHA_SECRET_KEY` ; vérification
|
||||||
(`app/forms/captcha.py`, `HCAPTCHA_SECRET_KEY` ; vérification désactivée si
|
désactivée si absente, comme `EMAIL_ENABLED=false`) et un filet de
|
||||||
absente, comme `EMAIL_ENABLED=false`) et un filet de sécurité global par
|
sécurité global par édition (`app/auth/rate_limit.py:form_submission_limiter`,
|
||||||
édition (`app/auth/rate_limit.py:form_submission_limiter`, hCaptcha reste
|
hCaptcha reste la défense principale). Le bénévole créé par une soumission
|
||||||
la défense principale). `GET .../form/logo` est également public (affiché
|
apparaît immédiatement dans la liste de la coordination, comme n'importe
|
||||||
sur la page), mais ne sert qu'une image choisie par la coordination elle-
|
quel autre bénévole.
|
||||||
même — aucune saisie ni donnée nominative, pas de protection hCaptcha
|
|
||||||
nécessaire. Le bénévole créé par une soumission apparaît immédiatement
|
|
||||||
dans la liste de la coordination, comme n'importe quel autre bénévole.
|
|
||||||
|
|
||||||
Conventions techniques :
|
Conventions techniques :
|
||||||
|
|
||||||
@@ -277,8 +251,8 @@ Matrice appliquée par les endpoints :
|
|||||||
| `matrix`, `import`, `accounts` | ✔ / ✔ / ✔ | ✔ / — / — | — |
|
| `matrix`, `import`, `accounts` | ✔ / ✔ / ✔ | ✔ / — / — | — |
|
||||||
| `badges` (config + génération) | ✔ | — | — |
|
| `badges` (config + génération) | ✔ | — | — |
|
||||||
| `position-sheets` (génération) | ✔ | — | — |
|
| `position-sheets` (génération) | ✔ | — | — |
|
||||||
| `form` (config, dont logo) | ✔ | — | — |
|
| `form` (config) | ✔ | — | — |
|
||||||
| `form/public`, `form/public/submit`, `form/logo` | *(aucune authentification — voir § 2, `FormTemplate`/`FormField`)* |||
|
| `form/public`, `form/public/submit` | *(aucune authentification — voir § 2, `FormField`)* |||
|
||||||
|
|
||||||
**Authentification — deux systèmes selon le compte**, une même session JWT
|
**Authentification — deux systèmes selon le compte**, une même session JWT
|
||||||
HS256 (`Authorization: Bearer <jwt>`, TTL `session_ttl_hours`) à l'arrivée.
|
HS256 (`Authorization: Bearer <jwt>`, TTL `session_ttl_hours`) à l'arrivée.
|
||||||
@@ -475,10 +449,16 @@ suivantes — bug corrigé une fois (postes manquants au-delà du premier), un
|
|||||||
positionnement implicite (auto-flow) recréerait la même classe de bug.
|
positionnement implicite (auto-flow) recréerait la même classe de bug.
|
||||||
Vérifié par `scripts/smoke.tsx:checkGridLayout` (inspecte les `grid-row`
|
Vérifié par `scripts/smoke.tsx:checkGridLayout` (inspecte les `grid-row`
|
||||||
rendus, pas seulement l'absence de crash — un décalage CSS n'est pas visible
|
rendus, pas seulement l'absence de crash — un décalage CSS n'est pas visible
|
||||||
dans un simple rendu SSR). Largeur adaptative par jour (`columnWidthPx`, en
|
dans un simple rendu SSR). Hiérarchie de `z-index` volontairement large et
|
||||||
fonction du nombre de tranches) : peu de tranches ⇒ colonnes larges, beaucoup
|
sans égalité (coins figés 20/21 > en-têtes jour/heure 12/11 > libellés de
|
||||||
⇒ largeur compacte avec défilement horizontal — jamais de colonnes étroites
|
poste 10 > cellules de créneau, chacune isolée dans son propre contexte
|
||||||
par défaut.
|
d'empilement — `relative z-0` — pour que son infobulle au survol, `z-20`,
|
||||||
|
ne rivalise jamais avec les en-têtes figés) : deux éléments sticky à égalité
|
||||||
|
de `z-index` peuvent se chevaucher de façon imprévisible pendant un
|
||||||
|
défilement selon le navigateur. Largeur adaptative par jour (`columnWidthPx`,
|
||||||
|
en fonction du nombre de tranches) : peu de tranches ⇒ colonnes larges,
|
||||||
|
beaucoup ⇒ largeur compacte avec défilement horizontal — jamais de colonnes
|
||||||
|
étroites par défaut.
|
||||||
La « Vue bénévoles » (`VolunteerViewPage.tsx`) reste sur l'ancien modèle,
|
La « Vue bénévoles » (`VolunteerViewPage.tsx`) reste sur l'ancien modèle,
|
||||||
distinct : une colonne par **créneau** (signature `starts_at`/`ends_at`)
|
distinct : une colonne par **créneau** (signature `starts_at`/`ends_at`)
|
||||||
plutôt qu'un axe temporel continu (`buildLattice` / `LatticeColumn`), plus
|
plutôt qu'un axe temporel continu (`buildLattice` / `LatticeColumn`), plus
|
||||||
@@ -541,7 +521,7 @@ l'étape 21.)*
|
|||||||
│ │ ├── main.py # instance FastAPI
|
│ │ ├── main.py # instance FastAPI
|
||||||
│ │ ├── config.py # settings (pydantic-settings)
|
│ │ ├── config.py # settings (pydantic-settings)
|
||||||
│ │ ├── db.py # engine + session
|
│ │ ├── db.py # engine + session
|
||||||
│ │ ├── logging_config.py # pas de PII dans les logs
|
│ │ ├── logging_config.py # RGPD : pas de PII dans les logs
|
||||||
│ │ ├── api/ # routers + schemas Pydantic + deps
|
│ │ ├── api/ # routers + schemas Pydantic + deps
|
||||||
│ │ │ # health, auth, accounts, editions,
|
│ │ │ # health, auth, accounts, editions,
|
||||||
│ │ │ # day_parts, imports, artists, volunteers,
|
│ │ │ # day_parts, imports, artists, volunteers,
|
||||||
@@ -670,11 +650,7 @@ Aucune dépendance locale requise hors Docker.
|
|||||||
| 21 | Formulaire public d'inscription (`/inscription`, sans authentification) : champs activables par la coordination (email, téléphone, vœux de poste — max 2, vœux d'artiste — un par soir, vœu de compagnon en texte libre, disponibilité/cotisation déclaratives, photo, commentaire), captcha hCaptcha, ouverture/fermeture, lien à partager, écran de configuration dédié (`/inscriptions`, COORDINATION) — les bénévoles créés rejoignent directement la liste | à valider |
|
| 21 | Formulaire public d'inscription (`/inscription`, sans authentification) : champs activables par la coordination (email, téléphone, vœux de poste — max 2, vœux d'artiste — un par soir, vœu de compagnon en texte libre, disponibilité/cotisation déclaratives, photo, commentaire), captcha hCaptcha, ouverture/fermeture, lien à partager, écran de configuration dédié (`/inscriptions`, COORDINATION) — les bénévoles créés rejoignent directement la liste | à valider |
|
||||||
| 22 | Feuilles de poste (PDF, complément des badges) : une page par poste et par jour avec le référent, les créneaux, les bénévoles affectés et leur téléphone — bouton depuis la grille des besoins (COORDINATION) ; photo du bénévole affichée sur son écran et sur l'écran référent (en face du nom de chaque membre de l'équipe) | à valider |
|
| 22 | Feuilles de poste (PDF, complément des badges) : une page par poste et par jour avec le référent, les créneaux, les bénévoles affectés et leur téléphone — bouton depuis la grille des besoins (COORDINATION) ; photo du bénévole affichée sur son écran et sur l'écran référent (en face du nom de chaque membre de l'équipe) | à valider |
|
||||||
| 23 | Créneaux : plus de génération automatique à la création d'un poste — création manuelle à horaire libre (`ConfigGrid.tsx`), génération depuis les créneaux types conservée en option (« Générer les créneaux ») ; grille des besoins redessinée en vrai axe des temps (une colonne = 30 min, bornée à la plage utilisée par jour, cases fusionnées pour chaque créneau — `buildTimeGrid`/`placeInTimeGrid`) | à valider |
|
| 23 | Créneaux : plus de génération automatique à la création d'un poste — création manuelle à horaire libre (`ConfigGrid.tsx`), génération depuis les créneaux types conservée en option (« Générer les créneaux ») ; grille des besoins redessinée en vrai axe des temps (une colonne = 30 min, bornée à la plage utilisée par jour, cases fusionnées pour chaque créneau — `buildTimeGrid`/`placeInTimeGrid`) | à valider |
|
||||||
| 24 | Feuilles de poste : photo des bénévoles affectés à côté de leur nom (vignette 14 mm, case vide si absente) | à valider |
|
| 24 | Corrections de stabilité de la grille des besoins (suite à l'étape 23) : positionnement explicite (`gridColumn`/`gridRow`) de chaque cellule (un poste aux créneaux non alignés sur 30 min ne décale plus les postes suivants) ; hiérarchie de `z-index` sans égalité entre les en-têtes figés et isolation de l'infobulle de survol dans son propre contexte d'empilement (ne recouvrait plus la ligne « Affectés / idéal » pendant un défilement) | à valider |
|
||||||
| 25 | Formulaire public : photo recadrée en carré côté navigateur avant l'envoi, aperçu agrandi ; personnalisation de la page (logo, texte d'introduction) depuis l'écran de configuration (`/inscriptions`) | à valider |
|
|
||||||
| 26 | Feuilles de poste : programmation du jour (horaires des artistes, tous postes confondus) sous le référent | à valider |
|
|
||||||
| 27 | Formulaire public : encart de paiement de la cotisation via Yapla (lien externe configurable), affiché sous la case correspondante | à valider |
|
|
||||||
| 28 | Correctif grille des besoins : positionnement explicite (`gridColumn`/`gridRow`) de chaque cellule — un poste aux créneaux non alignés sur 30 min ne décale plus les postes suivants (noms de poste manquants au-delà du premier) | à valider |
|
|
||||||
|
|
||||||
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
|
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
|
||||||
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +
|
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ export function GrillePage() {
|
|||||||
<div className="grid w-max border-b-[3px] border-ink" style={{ gridTemplateColumns }}>
|
<div className="grid w-max border-b-[3px] border-ink" style={{ gridTemplateColumns }}>
|
||||||
<div
|
<div
|
||||||
style={{ gridColumn: '1 / span 1', gridRow: 1 }}
|
style={{ gridColumn: '1 / span 1', gridRow: 1 }}
|
||||||
className="sticky left-0 top-0 z-[6] flex items-center gap-[6px] border-b-2 border-r-[3px] border-ink border-b-cream bg-ink px-2 py-[6px] font-display text-[10px] font-extrabold uppercase tracking-wider text-cream"
|
className="sticky left-0 top-0 z-[20] flex items-center gap-[6px] border-b-2 border-r-[3px] border-ink border-b-cream bg-ink px-2 py-[6px] font-display text-[10px] font-extrabold uppercase tracking-wider text-cream"
|
||||||
>
|
>
|
||||||
{t('grille.positionsCount', { count: matrix.data.positions.length })}
|
{t('grille.positionsCount', { count: matrix.data.positions.length })}
|
||||||
<HelpTip text={t('grille.footnote')} side="bottom" />
|
<HelpTip text={t('grille.footnote')} side="bottom" />
|
||||||
@@ -231,7 +231,7 @@ export function GrillePage() {
|
|||||||
{shownDays.map((day, di) => (
|
{shownDays.map((day, di) => (
|
||||||
<div
|
<div
|
||||||
key={day.date}
|
key={day.date}
|
||||||
className="sticky top-0 z-[4] flex items-baseline gap-[7px] border-b-2 border-r-[3px] border-ink border-b-cream border-r-cream bg-ink px-[9px] py-[6px] text-cream"
|
className="sticky top-0 z-[12] flex items-baseline gap-[7px] border-b-2 border-r-[3px] border-ink border-b-cream border-r-cream bg-ink px-[9px] py-[6px] text-cream"
|
||||||
style={{
|
style={{
|
||||||
gridColumn: `${dayColumnStarts[di]} / span ${day.slotLabels.length}`,
|
gridColumn: `${dayColumnStarts[di]} / span ${day.slotLabels.length}`,
|
||||||
gridRow: 1,
|
gridRow: 1,
|
||||||
@@ -245,7 +245,7 @@ export function GrillePage() {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{ gridColumn: '1 / span 1', gridRow: 2 }}
|
style={{ gridColumn: '1 / span 1', gridRow: 2 }}
|
||||||
className="sticky left-0 top-[27px] z-[6] flex items-center border-b-[3px] border-r-[3px] border-ink bg-sand px-2 font-mono text-[8.5px] opacity-60"
|
className="sticky left-0 top-[27px] z-[21] flex items-center border-b-[3px] border-r-[3px] border-ink bg-sand px-2 font-mono text-[8.5px] opacity-60"
|
||||||
>
|
>
|
||||||
{t('grille.assignedRequired')}
|
{t('grille.assignedRequired')}
|
||||||
</div>
|
</div>
|
||||||
@@ -254,7 +254,7 @@ export function GrillePage() {
|
|||||||
<div
|
<div
|
||||||
key={`${day.date}|${i}`}
|
key={`${day.date}|${i}`}
|
||||||
style={{ gridColumn: `${dayColumnStarts[di] + i} / span 1`, gridRow: 2 }}
|
style={{ gridColumn: `${dayColumnStarts[di] + i} / span 1`, gridRow: 2 }}
|
||||||
className="sticky top-[27px] z-[3] overflow-hidden text-ellipsis whitespace-nowrap border-b-[3px] border-r border-ink bg-sand px-[2px] py-[5px] text-center font-mono text-[9.5px]"
|
className="sticky top-[27px] z-[11] overflow-hidden text-ellipsis whitespace-nowrap border-b-[3px] border-r border-ink bg-sand px-[2px] py-[5px] text-center font-mono text-[9.5px]"
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
@@ -354,7 +354,7 @@ function PositionRow({
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
style={{ gridColumn: '1 / span 1', gridRow: rowIndex }}
|
style={{ gridColumn: '1 / span 1', gridRow: rowIndex }}
|
||||||
className="sticky left-0 z-[2] flex h-9 flex-col justify-center gap-[1px] border-b border-r-[3px] border-ink bg-cream px-2"
|
className="sticky left-0 z-[10] flex h-9 flex-col justify-center gap-[1px] border-b border-r-[3px] border-ink bg-cream px-2"
|
||||||
>
|
>
|
||||||
<span className="truncate font-display text-[12px] font-bold tracking-tight">
|
<span className="truncate font-display text-[12px] font-bold tracking-tight">
|
||||||
{position.name}
|
{position.name}
|
||||||
@@ -443,7 +443,16 @@ function ShiftCell({
|
|||||||
onClick={() => onSelect(shift.shift_id)}
|
onClick={() => onSelect(shift.shift_id)}
|
||||||
style={{ gridColumn, gridRow }}
|
style={{ gridColumn, gridRow }}
|
||||||
className={cx(
|
className={cx(
|
||||||
'group relative flex h-9 cursor-pointer items-center justify-center gap-[3px] border-b border-r border-ink',
|
// `relative z-0` (plutôt que `relative` seul) crée un contexte
|
||||||
|
// d'empilement local : l'infobulle interne (`z-20`, voir plus bas)
|
||||||
|
// ne rivalise qu'avec les autres enfants de cette cellule, jamais
|
||||||
|
// avec les en-têtes figés de la grille (z-10 à 21) — sans ça, un
|
||||||
|
// survol déclenché par la molette pendant un défilement (le
|
||||||
|
// curseur reste immobile à l'écran, ce sont les cellules qui
|
||||||
|
// défilent dessous) pouvait faire passer l'infobulle au-dessus de
|
||||||
|
// la ligne d'en-tête « Affectés / idéal » pendant sa transition de
|
||||||
|
// disparition.
|
||||||
|
'group relative z-0 flex h-9 cursor-pointer items-center justify-center gap-[3px] border-b border-r border-ink',
|
||||||
undefinedNeed && 'bg-sand-dark hover:bg-sand',
|
undefinedNeed && 'bg-sand-dark hover:bg-sand',
|
||||||
isCrit && 'bg-hatch-orange',
|
isCrit && 'bg-hatch-orange',
|
||||||
isOver && 'bg-hatch-mauve hover:bg-mauve-hover',
|
isOver && 'bg-hatch-mauve hover:bg-mauve-hover',
|
||||||
|
|||||||
Reference in New Issue
Block a user