import { useQuery } from '@tanstack/react-query'; import { router } from 'expo-router'; import { FlatList, Pressable, RefreshControl, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { WORK_ORDER_PRIORITY_LABELS, WORK_ORDER_STATUS_LABELS, WORK_ORDER_TYPE_LABELS, type WorkOrderPriority, type WorkOrderStatus, } from '@siop/shared'; import { api, unwrap } from '@/api/client'; import { useHorsLigne, useLogout, useMe } from '@/auth/session'; import { triJournee } from '@/lib/journee'; import { useTokens, type Tokens } from '@/theme/tokens'; /** Écran 1 de la maquette R4 : les OT du technicien, priorité puis * échéance, « personne bloquée » en tête. Hors-ligne : le cache persisté * sert la liste, le bandeau l'assume (D1). */ function useWorkOrders() { return useQuery({ queryKey: ['work-orders'], queryFn: async () => (await unwrap(await api.GET('/work-orders'))).workOrders, }); } const STYLE_STATUT: Record [string, string]> = { OPEN: (t) => [t.stOuvert, t.stOuvertFond], IN_PROGRESS: (t) => [t.stEncours, t.stEncoursFond], ON_HOLD: (t) => [t.stAttente, t.stAttenteFond], DONE: (t) => [t.stTermine, t.stTermineFond], CANCELLED: (t) => [t.stAnnule, t.stAnnuleFond], }; const STRIE_PRIORITE: Record string> = { PERSON_TRAPPED: (t) => t.prioBloque, HIGH: (t) => t.prioHaute, MEDIUM: (t) => t.prioMoyenne, LOW: (t) => t.prioBasse, NONE: () => 'transparent', }; export default function PageJournee() { const t = useTokens(); const horsLigne = useHorsLigne(); const { data: me } = useMe(); const { data: workOrders, refetch, isFetching, dataUpdatedAt } = useWorkOrders(); const logout = useLogout(); const ots = triJournee(workOrders ?? []); const urgences = ots.filter((o) => o.priority === 'PERSON_TRAPPED'); const jour = new Intl.DateTimeFormat('fr-FR', { weekday: 'long', day: 'numeric', month: 'short' }) .format(new Date()); return ( {/* Entête app : marque + pastille synchro + compte */} SIOP Technicien {horsLigne ? 'Hors-ligne' : 'Synchro à jour'} { void logout().then(() => router.replace('/connexion')); }} style={{ width: 26, height: 26, borderRadius: 13, backgroundColor: t.stEncours, alignItems: 'center', justifyContent: 'center', }} > {(me?.displayName ?? '·') .split(' ') .map((m) => m[0]) .join('') .slice(0, 2) .toUpperCase()} Ma journée {jour} {horsLigne ? ( ⚠ Hors-ligne — liste du{' '} {dataUpdatedAt ? new Intl.DateTimeFormat('fr-FR', { hour: '2-digit', minute: '2-digit' }).format(dataUpdatedAt) : '…'} . Vos saisies partiront en file (R4.3). ) : null} {urgences.length ? ( {urgences.length === 1 ? `1 personne bloquée — ${urgences[0]!.assetReference} · ${urgences[0]!.siteName}` : `${urgences.length} personnes bloquées`} ) : null} o.id} refreshControl={ void refetch()} tintColor={t.primaire} /> } contentContainerStyle={{ gap: 8, paddingBottom: 12 }} ListEmptyComponent={ Aucun OT en cours — tirez pour rafraîchir. } renderItem={({ item: o }) => { const [enc, fond] = STYLE_STATUT[o.status](t); return ( {o.reference} · {WORK_ORDER_TYPE_LABELS[o.type]} {WORK_ORDER_STATUS_LABELS[o.status]} {o.title} Asc. {o.assetReference} — {o.siteName} {o.dueDate ? ` · échéance ${new Intl.DateTimeFormat('fr-FR', { day: 'numeric', month: 'short' }).format(new Date(o.dueDate))}` : ''} {o.priority !== 'NONE' && o.priority !== 'PERSON_TRAPPED' ? ` · ${WORK_ORDER_PRIORITY_LABELS[o.priority]}` : ''} ); }} /> ); }