import Link from 'next/link'; import { PageBody, PageHeader } from '@/components/shell/PageHeader'; import { Badge, type Tone } from '@/components/ui/Badge'; import { can } from '@/domain/access/authorize'; import { formatMinutes } from '@/domain/counters/week'; import { monthOf, parseMonthParam } from '@/domain/planning/month'; import { cx } from '@/lib/cx'; import { requireSession } from '@/server/context'; import { getDashboard, type ListKey } from '@/server/hr/queries'; export const metadata = { title: 'Aperçu RH · PlanFlow' }; interface PageProps { searchParams: Promise<{ mois?: string; etablissement?: string; liste?: string; }>; } const LIST_LABELS: Record = { entrees: 'Entrées du mois', sorties: 'Sorties du mois', essais: 'Fins de période d’essai', profils: 'Profils incomplets', titres: 'Titres de séjour', absences: 'Journal des absences', contrats: 'Modifications de contrat', }; const LIST_KEYS = Object.keys(LIST_LABELS) as ListKey[]; export default async function ApercuPage({ searchParams }: PageProps) { const params = await searchParams; const session = await requireSession(); // Un salarié n'a pas accès à l'annuaire : lui servir une erreur // d'autorisation sur la page d'accueil serait absurde. Il voit son propre // point d'entrée. if (!can(session.actor, 'members.view')) { return ; } const month = parseMonthParam(params.mois) ?? monthOf(new Date()); const board = await getDashboard(month, params.etablissement); const selected: ListKey = LIST_KEYS.includes(params.liste as ListKey) ? (params.liste as ListKey) : 'profils'; const href = ( next: Partial<{ mois: string; etablissement: string; liste: string }>, ) => { const query = new URLSearchParams({ mois: next.mois ?? board.monthParam, liste: next.liste ?? selected, }); const location = next.etablissement ?? board.location?.id; if (location) query.set('etablissement', location); return `/?${query.toString()}`; }; return ( ← Mois précédent Mois suivant → } />
{board.locations.map((location) => ( {location.name} ))}
{/* Chaque tuile est un lien : un indicateur qu'on ne peut pas ouvrir ne se corrige pas, il se conteste. */}
    {board.tiles.map((tile) => (
  • {tile.label} {tile.value} {tile.hint}
  • ))}

{LIST_LABELS[selected]}

{board.lists[selected].length} ligne {board.lists[selected].length > 1 ? 's' : ''}
{LIST_KEYS.map((key) => ( {LIST_LABELS[key]} ))}
{board.lists[selected].length === 0 ? (

Rien à signaler sur cette période.

) : (
    {board.lists[selected].map((row) => (
  • {row.name} {row.detail} {row.date ? ( {row.date.split('-').reverse().join('/')} ) : null}
  • ))}
)}
); } function Indicator({ label, value, hint, }: { label: string; value: string; hint: string; }) { return (

{label}

{value}

{hint}

); } /** * Accueil d'un salarié. * * Il n'a ni annuaire ni indicateurs : lui servir une erreur d'autorisation sur * la page d'accueil serait absurde, et lui montrer un tableau de bord vide le * serait tout autant. */ function EmployeeHome() { return (
  • Mon planning Les semaines publiées de votre équipe
  • Mes absences et compteurs Demander un congé, suivre son solde
); }