mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
Serveur : updatedAt exposé au détail OT ; baseUpdatedAt optionnel sur transition/coche/bilan → 409 « Conflit de version » contextualisé (qui, quand) ; toute écriture secondaire (coche, bilan, commentaire, conso, MO) fait avancer la version — sans version fournie, le web est inchangé. ADR-003 : sécurité & protocole de routage mobile (qui vit où sur l'appareil, purge complète à la déconnexion — correctif réel : la file et le cache persisté survivaient au logout). Mobile : file persistée AsyncStorage rejouée dans l'ordre — succès → propagation de la version fraîche aux saisies restantes du même OT (nos écritures ne se conflictent pas entre elles, un écart étranger reste détecté) ; coupure → tout attend ; refus → CONFLIT, la file s'arrête, l'humain tranche (voir l'OT / rejouer sur version à jour / abandonner). Transitions, coches, bilan et photos (D5, compressées ~1600 px) passent par la file avec patch optimiste du cache ; écran Synchro (badge tabbar ambre/rouge) ; préchargement parc + référentiels (le bilan hors-ligne a ses vocabulaires). Recette « mode avion » 13/13 en Expo web piloté : gestes hors-ligne → 3 en file → modification concurrente de Salma → conflit tranché → serveur Terminé avec bilan. 17 tests jest-expo, 74 tests API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import type { ChecklistState, ReportUpsert, WorkOrderStatus } from '@siop/shared';
|
|
import { api, unwrap } from './client';
|
|
|
|
/** Hooks R4.2 — mêmes opérations que le web (contrat unique). Les écritures
|
|
* restent EN LIGNE dans cette release ; la mise en file arrive en R4.3 (D1). */
|
|
|
|
export function useWorkOrders() {
|
|
return useQuery({
|
|
queryKey: ['work-orders'],
|
|
queryFn: async () => (await unwrap(await api.GET('/work-orders'))).workOrders,
|
|
});
|
|
}
|
|
|
|
export function useWorkOrder(id: string) {
|
|
return useQuery({
|
|
queryKey: ['work-orders', id],
|
|
queryFn: async () =>
|
|
unwrap(await api.GET('/work-orders/{id}', { params: { path: { id } } })),
|
|
});
|
|
}
|
|
|
|
export function useAssets() {
|
|
return useQuery({
|
|
queryKey: ['assets'],
|
|
queryFn: async () => (await unwrap(await api.GET('/assets'))).assets,
|
|
});
|
|
}
|
|
|
|
export function useAsset(id: string) {
|
|
return useQuery({
|
|
queryKey: ['assets', id],
|
|
queryFn: async () => unwrap(await api.GET('/assets/{id}', { params: { path: { id } } })),
|
|
});
|
|
}
|
|
|
|
export function useDocumentsOT(workOrderId: string) {
|
|
return useQuery({
|
|
queryKey: ['documents', workOrderId],
|
|
queryFn: async () =>
|
|
(
|
|
await unwrap(
|
|
await api.GET('/documents', { params: { query: { workOrderId } } }),
|
|
)
|
|
).documents,
|
|
});
|
|
}
|
|
|
|
export function useReferenceValues() {
|
|
return useQuery({
|
|
queryKey: ['reference-values'],
|
|
staleTime: 3600_000, // référentiels administrables : stables en journée
|
|
queryFn: async () => (await unwrap(await api.GET('/reference-values'))).referenceValues,
|
|
});
|
|
}
|
|
|
|
function useInvalideOT(id: string) {
|
|
const queryClient = useQueryClient();
|
|
return () =>
|
|
Promise.all([
|
|
queryClient.invalidateQueries({ queryKey: ['work-orders', id] }),
|
|
queryClient.invalidateQueries({ queryKey: ['work-orders'] }),
|
|
]);
|
|
}
|
|
|
|
export function useTransition(id: string) {
|
|
const invalide = useInvalideOT(id);
|
|
return useMutation({
|
|
mutationFn: async (input: { to: WorkOrderStatus; comment?: string }) =>
|
|
unwrap(
|
|
await api.POST('/work-orders/{id}/transition', {
|
|
params: { path: { id } },
|
|
body: input,
|
|
}),
|
|
),
|
|
onSuccess: invalide,
|
|
});
|
|
}
|
|
|
|
export function useCocheChecklist(otId: string) {
|
|
const invalide = useInvalideOT(otId);
|
|
return useMutation({
|
|
mutationFn: async (input: { itemId: string; state: ChecklistState }) =>
|
|
unwrap(
|
|
await api.PATCH('/work-orders/{id}/checklist/{itemId}', {
|
|
params: { path: { id: otId, itemId: input.itemId } },
|
|
body: { state: input.state },
|
|
}),
|
|
),
|
|
onSuccess: invalide,
|
|
});
|
|
}
|
|
|
|
export function useBilan(otId: string) {
|
|
const invalide = useInvalideOT(otId);
|
|
return useMutation({
|
|
mutationFn: async (body: ReportUpsert) =>
|
|
unwrap(
|
|
await api.PUT('/work-orders/{id}/report', { params: { path: { id: otId } }, body }),
|
|
),
|
|
onSuccess: invalide,
|
|
});
|
|
}
|