feat(r6): R6.4 — Pilotage sur mobile (Statistiques, Personnes)

EXPO_PUBLIC_WEB_URL/WEB_URL pour le lien d'activation (pointe la page web
/activation?token=…, aucun équivalent mobile). api/pilotage.ts
(useAnalyticsSummary, useUsers, useRoles, useInviteUser).

Écran Statistiques (maquette écran 7) : période 3/6/12 mois, coût du mois,
taux préventif, OT clôturés, pannes par organe, top équipements — cartes
plutôt que graphes denses (D4).

Écran Personnes & équipes : liste + statut, Inviter (nom/e-mail/rôle),
lien d'activation en texte sélectionnable (Text selectable, RN natif) —
expo-clipboard est une dépendance native absente, différée comme
expo-sharing en R6.3 plutôt qu'ajoutée à la légère. Taux horaire/rôles/
équipes restent gérés au web (D4).

Assistant reste "à venir" dans le Menu : un chat sourcé est un nouveau
patron d'écran jamais maquetté sur mobile, contrairement aux autres
familles qui réutilisaient des patrons déjà validés — mérite son propre
tour de design-first plutôt qu'être exécuté par réflexe en fin de release.

R6 quasi close (Exploitation/Parc/Ressources/Pilotage livrés pour tous les
rôles, hors Assistant) — reste la confirmation du référent sur iPhone
(R6.1 + correctif déconnexion) avant clôture réelle.

Typecheck propre, 17 tests Jest, lint 5/5 paquets. Contrat non touché.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-08-02 11:43:57 +01:00
parent f210d4f801
commit 4c1a2d67a0
10 changed files with 463 additions and 4 deletions

View File

@@ -7,6 +7,11 @@ import { lireJeton } from './jeton';
* EXPO_PUBLIC_API_URL pointe l'API (IP LAN pour Expo Go sur téléphone). */
export const API_URL = process.env.EXPO_PUBLIC_API_URL ?? 'http://localhost:3000';
/** Origine du web (R6.4 — lien d'activation « Personnes », même page
* `/activation?token=…` que le web, il n'existe pas d'équivalent mobile).
* À définir par environnement comme EXPO_PUBLIC_API_URL. */
export const WEB_URL = process.env.EXPO_PUBLIC_WEB_URL ?? 'http://localhost:5173';
export const api = createClient<paths>({ baseUrl: API_URL });
api.use({

View File

@@ -0,0 +1,39 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import type { InvitationCreate } from '@siop/shared';
import { api, unwrap } from './client';
/** Hooks R6.4 — Pilotage (Statistiques, Personnes & équipes) : mêmes
* opérations que le web (gestion.ts/referentiel.ts), lecture + une action
* courante (inviter) sur mobile — pas la gestion complète des rôles/
* équipes/taux horaires, réservée au web (D4). */
export function useAnalyticsSummary(months: 3 | 6 | 12 = 12) {
return useQuery({
queryKey: ['analytics', months],
queryFn: async () =>
unwrap(await api.GET('/analytics/summary', { params: { query: { months: String(months) } } })),
});
}
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: async () => (await unwrap(await api.GET('/users'))).users,
});
}
export function useRoles() {
return useQuery({
queryKey: ['roles'],
queryFn: async () => (await unwrap(await api.GET('/roles'))).roles,
});
}
export function useInviteUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (body: InvitationCreate) =>
unwrap(await api.POST('/users/invitations', { body })),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
});
}