Le formulaire d'embauche, posé au bas de la liste, occupait plus de place que l'effectif qu'on vient consulter — une quinzaine de champs déployés en permanence pour un geste occasionnel. Il s'ouvre désormais en modale, depuis le bouton d'en-tête. `<dialog>` natif plutôt qu'un panneau maison : le piège de focus, la fermeture par Échap et le fond inerte viennent avec, et une `<div>` doit les réimplémenter sans jamais les tenir tout à fait. Le tableau perd sa carte et ses fonds : une liste de personnes se lit mieux sans cadre autour. Les colonnes suivent l'annuaire de référence — collaborateur, rôle, email, mobile, rattachement, invitation. Le contrat quitte le tableau, où il doublait le filtre qui le cherche déjà ; son absence reste signalée là où elle compte, dans le rattachement. Le décompte ne s'affiche plus que filtré : « 87 salariés » au-dessus de 87 lignes n'apprend rien. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef, useState } from 'react';
|
|
|
|
import { AddEmployeeForm } from '@/app/(app)/equipe/AddEmployeeForm';
|
|
import { Button } from '@/components/ui/Button';
|
|
import type { ContractLocation } from '@/server/employees/queries';
|
|
|
|
/**
|
|
* Ajout d'un collaborateur, en modale.
|
|
*
|
|
* Le formulaire d'embauche compte une quinzaine de champs : posé au bas de la
|
|
* liste, il occupait plus de place que l'effectif qu'on vient consulter. Il ne
|
|
* s'ouvre donc qu'à la demande.
|
|
*
|
|
* `<dialog>` natif plutôt qu'un panneau maison : il apporte le piège de focus,
|
|
* la fermeture par Échap et le fond inerte, trois choses qu'une `<div>` doit
|
|
* réimplémenter et rate presque toujours.
|
|
*/
|
|
export function AddEmployeeDialog({
|
|
locations,
|
|
}: {
|
|
locations: ContractLocation[];
|
|
}) {
|
|
const dialog = useRef<HTMLDialogElement>(null);
|
|
const [open, setOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const element = dialog.current;
|
|
if (!element) return;
|
|
if (open && !element.open) element.showModal();
|
|
if (!open && element.open) element.close();
|
|
}, [open]);
|
|
|
|
return (
|
|
<>
|
|
<Button variant="primary" onClick={() => setOpen(true)}>
|
|
Ajouter un collaborateur
|
|
</Button>
|
|
|
|
<dialog
|
|
ref={dialog}
|
|
aria-labelledby="ajout-collaborateur"
|
|
onClose={() => setOpen(false)}
|
|
className="m-auto w-[min(46rem,calc(100vw-2rem))] rounded-3 border border-line-1 bg-surface p-0 text-ink-1 backdrop:bg-[rgb(0_0_0/0.4)]"
|
|
>
|
|
<header className="flex items-center gap-3 border-b border-line-1 px-5 py-4">
|
|
<h2 id="ajout-collaborateur" className="text-md font-semibold">
|
|
Ajouter un collaborateur
|
|
</h2>
|
|
<span className="flex-1" />
|
|
<Button variant="ghost" onClick={() => setOpen(false)}>
|
|
Fermer
|
|
</Button>
|
|
</header>
|
|
|
|
<div className="max-h-[70vh] overflow-y-auto p-5">
|
|
{open ? (
|
|
// Monté à l'ouverture : un formulaire démonté entre deux usages
|
|
// repart vide, sans traîner la saisie abandonnée la fois d'avant.
|
|
<AddEmployeeForm
|
|
locations={locations}
|
|
onSaved={() => setOpen(false)}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
</dialog>
|
|
</>
|
|
);
|
|
}
|