import { router } from 'expo-router'; import { useState } from 'react'; import { FlatList, Text, TextInput, View } from 'react-native'; import type { RequestSummary } from '@siop/shared'; import { useApproveRequest, useRejectRequest, useRequests, } from '@/api/exploitation'; import { useUsers } from '@/api/pilotage'; import { usePermissions } from '@/auth/use-permissions'; import { useTokens, type Tokens } from '@/theme/tokens'; import { BoutonTel, ChoixTel } from './ui'; /** Panneau Demandes — UN SEUL composant pour tous les rôles (maquette * « mobile ouvert à tous les rôles », écran 5) : le Demandeur y crée et * suit SES demandes ; Gestionnaire/Dispatcher/Administrateur y approuvent * ou rejettent (motif obligatoire, comme au web) ; Vue seule consulte sans * bouton. L'API renvoie déjà la liste correctement scopée (ADR-003) — ce * composant affiche ce qu'on lui donne et ne propose que les actions * permises par `can(...)`. */ export function PanneauDemandes() { const t = useTokens(); const { can } = usePermissions(); const { data: requests } = useRequests(); const peutTraiter = can('REQUESTS', 'edit'); const peutCreer = can('REQUESTS', 'create'); const toutes = requests ?? []; const aTraiter = toutes.filter((r) => r.status === 'RECEIVED').length; return ( Demandes {aTraiter} à traiter · {toutes.length} au total {peutCreer ? ( router.push('/demandes/nouvelle')} /> ) : null} r.id} contentContainerStyle={{ gap: 8, paddingBottom: 12 }} ListEmptyComponent={ Aucune demande pour l'instant. } renderItem={({ item: r }) => } /> ); } const STYLE_STATUT: Record [string, string]> = { RECEIVED: (t) => [t.stAttente, t.stAttenteFond], APPROVED: (t) => [t.stTermine, t.stTermineFond], REJECTED: (t) => [t.stAnnule, t.stAnnuleFond], }; const LABEL_STATUT: Record = { RECEIVED: 'Reçue', APPROVED: 'Approuvée', REJECTED: 'Rejetée', }; function CarteDemande({ demande: r, peutTraiter }: { demande: RequestSummary; peutTraiter: boolean }) { const t = useTokens(); const { data: users } = useUsers(); const approbation = useApproveRequest(); const rejet = useRejectRequest(); // Un seul panneau ouvert à la fois : approbation (avec assignation — ce // n'est pas au Gestionnaire d'agir comme un technicien, il délègue) ou // rejet (motif obligatoire). null = fermé, les deux boutons côte à côte. const [panneau, setPanneau] = useState<'approuver' | 'rejeter' | null>(null); const [motif, setMotif] = useState(''); const [assigneId, setAssigneId] = useState(null); const techniciens = (users ?? []).filter( (u) => u.status === 'active' && u.role.name.startsWith('Technicien'), ); const technicien = techniciens.find((tt) => tt.id === assigneId) ?? null; const [enc, fond] = STYLE_STATUT[r.status](t); const enCours = r.status === 'RECEIVED'; return ( {r.reference} {LABEL_STATUT[r.status]} {r.isPersonTrapped ? '⚠ ' : ''} {r.description} Asc. {r.assetReference} — {r.siteName} · {r.requesterLabel} {r.rejectionReason ? ( Motif du rejet : {r.rejectionReason} ) : null} {peutTraiter && enCours ? ( panneau === null ? ( setPanneau('approuver')} /> setPanneau('rejeter')} /> ) : panneau === 'approuver' ? ( ({ id: tt.id, label: tt.displayName }))} surChoix={setAssigneId} /> { setPanneau(null); setAssigneId(null); }} /> approbation.mutate( { id: r.id, priority: r.isPersonTrapped ? 'PERSON_TRAPPED' : 'HIGH', assigneeIds: assigneId ? [assigneId] : undefined, }, { onSuccess: () => setPanneau(null) }, ) } /> ) : ( { setPanneau(null); setMotif(''); }} /> rejet.mutate( { id: r.id, reason: motif.trim() }, { onSuccess: () => setPanneau(null) }, ) } /> ) ) : null} ); }