mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
fix(r6): recette Gestionnaire — Menu non scrollable, BC sans confirmation
Deux bugs réels trouvés sur iPhone physique par le référent : 1. app/(tabs)/menu.tsx n'était pas scrollable (View, pas ScrollView) — avec les 4 groupes désormais pleinement câblés (12 liens + 4 en-têtes), le contenu dépasse la hauteur de l'écran et le dernier groupe (Pilotage, dont Personnes & équipes) était strictement inaccessible. Présent depuis R6.1, seulement révélé maintenant. Fix : ScrollView autour de la liste des groupes, en-tête fixe au-dessus. 2. app/stock/[id]/commander.tsx naviguait silencieusement en arrière après succès — le référent a créé deux BC (vérifiés corrects côté serveur) sans aucun retour dans l'app. Fix : écran de confirmation explicite (référence, fournisseur, total, statut) avant de revenir. Amélioration sur retour direct : Tiers gagne une fiche détail (app/tiers/[id].tsx — identité, contact, BC en cours, sites rattachés, aucune nouvelle requête) — le lecture seule sans réaction au tap se lisait comme un écran cassé plutôt que délibérément simple. Typecheck propre, 17 tests Jest, lint 5/5 paquets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,5 +77,6 @@ pnpm + Turborepo. `apps/api` : NestJS, Prisma, PostgreSQL (pgvector + PostGIS),
|
|||||||
- ✅ **R6.4 — Pilotage (Statistiques, Personnes)** : `EXPO_PUBLIC_WEB_URL`/`WEB_URL` (lien d'activation vers la page web, pas d'équivalent mobile) ; `api/pilotage.ts` ; écran Statistiques (période 3/6/12, coût du mois, taux préventif, pannes par organe, top équipements — cartes plutôt que graphes, D4) ; écran Personnes & équipes (liste + statut, Inviter, lien d'activation en texte sélectionnable — `expo-clipboard` différée, même raisonnement que `expo-sharing` en R6.3 ; taux horaire/rôles/équipes restent au web). **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 Carte/LigneInfo/EnteteFiche déjà validés ; mérite son propre tour de design-first. Typecheck propre, 17 tests Jest, lint 5/5, contrat non touché.
|
- ✅ **R6.4 — Pilotage (Statistiques, Personnes)** : `EXPO_PUBLIC_WEB_URL`/`WEB_URL` (lien d'activation vers la page web, pas d'équivalent mobile) ; `api/pilotage.ts` ; écran Statistiques (période 3/6/12, coût du mois, taux préventif, pannes par organe, top équipements — cartes plutôt que graphes, D4) ; écran Personnes & équipes (liste + statut, Inviter, lien d'activation en texte sélectionnable — `expo-clipboard` différée, même raisonnement que `expo-sharing` en R6.3 ; taux horaire/rôles/équipes restent au web). **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 Carte/LigneInfo/EnteteFiche déjà validés ; mérite son propre tour de design-first. Typecheck propre, 17 tests Jest, lint 5/5, contrat non touché.
|
||||||
- ✅ **Nav adaptative + correctif déconnexion confirmés par le référent sur iPhone physique** — dernier point ouvert depuis le début de R6, clos.
|
- ✅ **Nav adaptative + correctif déconnexion confirmés par le référent sur iPhone physique** — dernier point ouvert depuis le début de R6, clos.
|
||||||
- ✅ **R6.5 — Assistant mobile (chat sourcé + dictée)** : maquette dédiée validée (3 écrans, D1-D5 — D5 ajoutée en revue : question tapée OU dictée, même pipeline que la dictée déjà livrée en clôture, transcription remplit le champ, jamais d'envoi automatique). `api/assistant.ts` (503 géré comme le web). Écran Assistant : chat un échange à la fois, citations numérotées, sources avec extrait exact (« Voir le document » → métadonnées seules, R6.3 ; « Ouvrir l'OT » → fiche R4), refus honnête chiffré + Reformuler, avertissement permanent. Fiche document (`bibliotheque/[id].tsx`, nouveau) — la liste R6.3 y mène aussi désormais. **Le Menu R6 n'a plus d'entrée « à venir »** dans les 4 groupes (Catégories exceptée, admin, hors périmètre mobile). Typecheck propre, 17 tests Jest, lint 5/5, contrat non touché.
|
- ✅ **R6.5 — Assistant mobile (chat sourcé + dictée)** : maquette dédiée validée (3 écrans, D1-D5 — D5 ajoutée en revue : question tapée OU dictée, même pipeline que la dictée déjà livrée en clôture, transcription remplit le champ, jamais d'envoi automatique). `api/assistant.ts` (503 géré comme le web). Écran Assistant : chat un échange à la fois, citations numérotées, sources avec extrait exact (« Voir le document » → métadonnées seules, R6.3 ; « Ouvrir l'OT » → fiche R4), refus honnête chiffré + Reformuler, avertissement permanent. Fiche document (`bibliotheque/[id].tsx`, nouveau) — la liste R6.3 y mène aussi désormais. **Le Menu R6 n'a plus d'entrée « à venir »** dans les 4 groupes (Catégories exceptée, admin, hors périmètre mobile). Typecheck propre, 17 tests Jest, lint 5/5, contrat non touché.
|
||||||
- 🔄 **Reprise ici — R6 fonctionnellement complet** (Exploitation/Parc/Ressources/Pilotage, Assistant inclus, pour tous les rôles) : reste une recette complète (parcourir tous les écrans sur au moins 2 rôles non-technicien — pour l'instant chaque sous-étape n'a été vérifiée que par typecheck/tests/lint, jamais en usage réel au-delà de la confirmation R6.1) avant de considérer R6 close au même sens que R0→R5 (chacune avait sa recette chiffrée avant tag) ; `expo-sharing` (ouverture de documents) et `expo-clipboard` (copie du lien d'activation) en réserve pour une prochaine étape native. Restes non bloquants inchangés : redéploiement Dokploy de l'instance ENSET (`AI_SERVICE_TOKEN` à créer — runbook §2 — puis « Réindexer tout »), recette Android sur appareil physique, calibrage `AI_SEUIL_*` et qualité darija sur corpus SPELEV réel, secret `DOKPLOY_WEBHOOK_URL`, production client SPELEV (attend les accès serveur du partenaire).
|
- 🔄 **Recette R6 en cours (Gestionnaire, partie 1)** — 2 bugs réels trouvés et corrigés sur iPhone physique : Menu non scrollable (le dernier groupe, Pilotage, était strictement inaccessible une fois les 4 groupes pleinement câblés — présent depuis R6.1, révélé seulement maintenant) ; BC créé sans confirmation visible dans l'app (vérifié côté serveur : les BC étaient bien créés, juste aucun retour affiché). Amélioration sur retour direct : Tiers gagne une fiche détail (identité/contact/BC en cours/sites rattachés) — le lecture-seule sans aucune réaction au tap se lisait comme cassé. Trois signalements vérifiés et écartés (faux positifs) : approbation de demande (a fonctionné), bibliothèque vide (confirmé côté serveur — aucun document sur cette instance, pas un bug mobile), période statistiques (transmise et traitée correctement, les indicateurs affichés ne varient juste pas avec ce jeu de données). Typecheck propre, 17 tests Jest, lint 5/5 après correctifs.
|
||||||
|
- 🔄 **Reprise ici** : suite de la recette Gestionnaire (Sites/Ascenseurs/Fichiers/Assistant+dictée restent à confirmer), puis passage Demandeur (permissions les plus étroites) — avant de considérer R6 close au même sens que R0→R5. `expo-sharing` (ouverture de documents) et `expo-clipboard` (copie du lien d'activation) en réserve pour une prochaine étape native. Restes non bloquants inchangés : redéploiement Dokploy de l'instance ENSET (`AI_SERVICE_TOKEN` à créer — runbook §2 — puis « Réindexer tout »), recette Android sur appareil physique, calibrage `AI_SEUIL_*` et qualité darija sur corpus SPELEV réel, secret `DOKPLOY_WEBHOOK_URL`, production client SPELEV (attend les accès serveur du partenaire).
|
||||||
- Détail quotidien : `docs/journal/journal.md`. Dépôt : `siop-spelev/siop2` (privé), jalons R0→R5 (v1) + R6 en cours.
|
- Détail quotidien : `docs/journal/journal.md`. Dépôt : `siop-spelev/siop2` (privé), jalons R0→R5 (v1) + R6 en cours.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { router } from 'expo-router';
|
import { router } from 'expo-router';
|
||||||
import { Pressable, Text, View } from 'react-native';
|
import { Pressable, ScrollView, Text, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import type { ObjectCategory, PermissionRight } from '@siop/shared';
|
import type { ObjectCategory, PermissionRight } from '@siop/shared';
|
||||||
import { useMe } from '@/auth/session';
|
import { useMe } from '@/auth/session';
|
||||||
@@ -75,9 +75,10 @@ export default function PageMenu() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1, backgroundColor: t.fond }} edges={['top']}>
|
<SafeAreaView style={{ flex: 1, backgroundColor: t.fond }} edges={['top']}>
|
||||||
<View style={{ padding: 14, gap: 14, flex: 1 }}>
|
<View style={{ padding: 14, gap: 10, flex: 1 }}>
|
||||||
<EnteteTabs />
|
<EnteteTabs />
|
||||||
<Text style={{ fontFamily: 'Manrope_800ExtraBold', fontSize: 19, color: t.encre }}>Menu</Text>
|
<Text style={{ fontFamily: 'Manrope_800ExtraBold', fontSize: 19, color: t.encre }}>Menu</Text>
|
||||||
|
<ScrollView contentContainerStyle={{ gap: 14, paddingBottom: 16 }} showsVerticalScrollIndicator={false}>
|
||||||
{groupesVisibles.length === 0 ? (
|
{groupesVisibles.length === 0 ? (
|
||||||
<Text style={{ color: t.encre3, fontFamily: 'Manrope_600SemiBold', textAlign: 'center', padding: 24 }}>
|
<Text style={{ color: t.encre3, fontFamily: 'Manrope_600SemiBold', textAlign: 'center', padding: 24 }}>
|
||||||
Rien d'autre accessible à votre rôle ici.
|
Rien d'autre accessible à votre rôle ici.
|
||||||
@@ -101,6 +102,7 @@ export default function PageMenu() {
|
|||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,13 +2,19 @@ import { router, useLocalSearchParams } from 'expo-router';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ScrollView, Text, TextInput, View } from 'react-native';
|
import { ScrollView, Text, TextInput, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import type { PurchaseOrderDto } from '@siop/shared';
|
||||||
import { useCreatePurchaseOrder, usePart } from '@/api/ressources';
|
import { useCreatePurchaseOrder, usePart } from '@/api/ressources';
|
||||||
import { BoutonTel, Carte, EnteteFiche, LigneInfo } from '@/composants/ui';
|
import { BoutonTel, Carte, EnteteFiche, LigneInfo } from '@/composants/ui';
|
||||||
import { useTokens } from '@/theme/tokens';
|
import { useTokens } from '@/theme/tokens';
|
||||||
|
|
||||||
|
const fmtMAD = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'MAD' });
|
||||||
|
|
||||||
/** Nouveau BC pré-rempli depuis l'alerte stock — maquette écran 6. Une
|
/** Nouveau BC pré-rempli depuis l'alerte stock — maquette écran 6. Une
|
||||||
* seule ligne (cette pièce) : le bon de commande multi-lignes détaillé
|
* seule ligne (cette pièce) : le bon de commande multi-lignes détaillé
|
||||||
* reste au web (D4). */
|
* reste au web (D4). Confirmation explicite après création (trouvé en
|
||||||
|
* recette 02/08 : la navigation instantanée en cas de succès ne donnait
|
||||||
|
* aucun retour visible — deux BC créés côté serveur sans que le référent
|
||||||
|
* s'en aperçoive dans l'app). */
|
||||||
export default function PageCommander() {
|
export default function PageCommander() {
|
||||||
const t = useTokens();
|
const t = useTokens();
|
||||||
const { id } = useLocalSearchParams<{ id: string }>();
|
const { id } = useLocalSearchParams<{ id: string }>();
|
||||||
@@ -17,8 +23,33 @@ export default function PageCommander() {
|
|||||||
const manquant = piece ? Math.max(piece.threshold - piece.stock, 1) : 1;
|
const manquant = piece ? Math.max(piece.threshold - piece.stock, 1) : 1;
|
||||||
const [quantite, setQuantite] = useState(String(manquant));
|
const [quantite, setQuantite] = useState(String(manquant));
|
||||||
const [prix, setPrix] = useState('');
|
const [prix, setPrix] = useState('');
|
||||||
|
const [cree, setCree] = useState<PurchaseOrderDto | null>(null);
|
||||||
|
|
||||||
if (!piece || !piece.supplierId) return null;
|
if (!piece || !piece.supplierId) return null;
|
||||||
|
|
||||||
|
if (cree) {
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={{ flex: 1, backgroundColor: t.fond }} edges={['top']}>
|
||||||
|
<ScrollView contentContainerStyle={{ padding: 14, gap: 12 }}>
|
||||||
|
<EnteteFiche titre="BC créé" />
|
||||||
|
<Carte titre="✓ Bon de commande enregistré">
|
||||||
|
<LigneInfo nom="Référence" valeur={cree.reference} />
|
||||||
|
<LigneInfo nom="Fournisseur" valeur={cree.supplierName} />
|
||||||
|
<LigneInfo nom="Total" valeur={fmtMAD.format(cree.total)} />
|
||||||
|
<LigneInfo nom="Statut" valeur="Brouillon" />
|
||||||
|
</Carte>
|
||||||
|
<Text style={{ color: t.encre3, fontFamily: 'Manrope_400Regular', fontSize: 11.5, textAlign: 'center' }}>
|
||||||
|
L'envoi au fournisseur se fait depuis le web pour l'instant.
|
||||||
|
</Text>
|
||||||
|
<BoutonTel
|
||||||
|
libelle="Terminé"
|
||||||
|
surAppui={() => (router.canGoBack() ? router.back() : router.replace('/stock'))}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const prixDefaut = piece.lastUnitPrice != null ? String(piece.lastUnitPrice) : '';
|
const prixDefaut = piece.lastUnitPrice != null ? String(piece.lastUnitPrice) : '';
|
||||||
const prixSaisi = prix || prixDefaut;
|
const prixSaisi = prix || prixDefaut;
|
||||||
const qte = Number(quantite);
|
const qte = Number(quantite);
|
||||||
@@ -89,7 +120,7 @@ export default function PageCommander() {
|
|||||||
supplierId: piece.supplierId!,
|
supplierId: piece.supplierId!,
|
||||||
lines: [{ partId: piece.id, quantity: qte, unitPrice: pu }],
|
lines: [{ partId: piece.id, quantity: qte, unitPrice: pu }],
|
||||||
},
|
},
|
||||||
{ onSuccess: () => (router.canGoBack() ? router.back() : router.replace('/stock')) },
|
{ onSuccess: (bc) => setCree(bc) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
48
apps/mobile/app/tiers/[id].tsx
Normal file
48
apps/mobile/app/tiers/[id].tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { useLocalSearchParams } from 'expo-router';
|
||||||
|
import { ScrollView, Text } from 'react-native';
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import { usePartners } from '@/api/ressources';
|
||||||
|
import { Carte, EnteteFiche, LigneInfo } from '@/composants/ui';
|
||||||
|
import { useTokens } from '@/theme/tokens';
|
||||||
|
|
||||||
|
/** Fiche tiers — R6.3, ajoutée en recette (02/08). Consultation seule (D4) :
|
||||||
|
* identité, contact, activité (BC en cours pour un fournisseur, sites
|
||||||
|
* rattachés pour un client/syndic) — déjà connu de la liste, pas de
|
||||||
|
* requête supplémentaire. Création/édition restent au web. */
|
||||||
|
export default function PageFicheTiers() {
|
||||||
|
const t = useTokens();
|
||||||
|
const { id } = useLocalSearchParams<{ id: string }>();
|
||||||
|
const { data: partners } = usePartners();
|
||||||
|
const p = (partners ?? []).find((x) => x.id === id);
|
||||||
|
if (!p) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={{ flex: 1, backgroundColor: t.fond }} edges={['top']}>
|
||||||
|
<ScrollView contentContainerStyle={{ padding: 14, gap: 10 }}>
|
||||||
|
<EnteteFiche titre={p.name} />
|
||||||
|
<Carte titre="Identité">
|
||||||
|
<LigneInfo nom="Type" valeur={p.kind === 'SUPPLIER' ? 'Fournisseur' : 'Client'} />
|
||||||
|
<LigneInfo nom="Statut" valeur={p.isActive ? 'Actif' : 'Désactivé'} />
|
||||||
|
<LigneInfo nom="Contact" valeur={p.contactName ?? '—'} />
|
||||||
|
<LigneInfo nom="Téléphone" valeur={p.phone ?? '—'} />
|
||||||
|
<LigneInfo nom="E-mail" valeur={p.email ?? '—'} />
|
||||||
|
<LigneInfo nom="Ville" valeur={p.city ?? '—'} />
|
||||||
|
</Carte>
|
||||||
|
{p.kind === 'SUPPLIER' ? (
|
||||||
|
<Carte titre="Achats">
|
||||||
|
<LigneInfo nom="BC en cours" valeur={String(p.openOrders)} />
|
||||||
|
</Carte>
|
||||||
|
) : null}
|
||||||
|
{p.siteNames.length ? (
|
||||||
|
<Carte titre="Sites rattachés">
|
||||||
|
{p.siteNames.map((s) => (
|
||||||
|
<Text key={s} style={{ fontFamily: 'Manrope_600SemiBold', fontSize: 12.5, color: t.encre }}>
|
||||||
|
{s}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</Carte>
|
||||||
|
) : null}
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import { FlatList, Text, View } from 'react-native';
|
import { router } from 'expo-router';
|
||||||
|
import { FlatList, Pressable, Text, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { usePartners } from '@/api/ressources';
|
import { usePartners } from '@/api/ressources';
|
||||||
import { EnteteFiche } from '@/composants/ui';
|
import { EnteteFiche } from '@/composants/ui';
|
||||||
import { useTokens } from '@/theme/tokens';
|
import { useTokens } from '@/theme/tokens';
|
||||||
|
|
||||||
/** Tiers — R6.3 (Ressources). Consultation seule sur mobile (D4) : la
|
/** Tiers — R6.3 (Ressources). Consultation seule sur mobile (D4) : la
|
||||||
* création/édition de fournisseurs et clients reste au web pour l'instant. */
|
* création/édition de fournisseurs et clients reste au web pour l'instant.
|
||||||
|
* Fiche détail ajoutée en recette (02/08) : le tap sans réaction se lisait
|
||||||
|
* comme un écran cassé plutôt que délibérément simple. */
|
||||||
export default function PageTiers() {
|
export default function PageTiers() {
|
||||||
const t = useTokens();
|
const t = useTokens();
|
||||||
const { data: partners } = usePartners();
|
const { data: partners } = usePartners();
|
||||||
@@ -25,7 +28,9 @@ export default function PageTiers() {
|
|||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
renderItem={({ item: p }) => (
|
renderItem={({ item: p }) => (
|
||||||
<View
|
<Pressable
|
||||||
|
accessibilityRole="button"
|
||||||
|
onPress={() => router.push(`/tiers/${p.id}`)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: t.surface,
|
backgroundColor: t.surface,
|
||||||
borderColor: t.bordure,
|
borderColor: t.bordure,
|
||||||
@@ -58,7 +63,7 @@ export default function PageTiers() {
|
|||||||
{[p.contactName, p.phone, p.city].filter(Boolean).join(' · ') || '—'}
|
{[p.contactName, p.phone, p.city].filter(Boolean).join(' · ') || '—'}
|
||||||
{p.kind === 'SUPPLIER' && p.openOrders ? ` · ${p.openOrders} BC en cours` : ''}
|
{p.kind === 'SUPPLIER' && p.openOrders ? ` · ${p.openOrders} BC en cours` : ''}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</Pressable>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -4,6 +4,52 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-08-02 — Pr. Daaif (+ Claude) — Recette R6 (Gestionnaire, partie 1) — 2 bugs corrigés
|
||||||
|
|
||||||
|
**Actions**
|
||||||
|
|
||||||
|
- Début de la recette complète promise en fin de R6.5 : parcours du référent en Gestionnaire
|
||||||
|
(Nadia Berrada) sur iPhone physique, verrouillé côté serveur après chaque étape utile.
|
||||||
|
- **Bug réel 1 — Menu inaccessible en bas** : `app/(tabs)/menu.tsx` n'était PAS scrollable (un
|
||||||
|
simple `View`, pas de `ScrollView`) — avec les 4 groupes maintenant pleinement câblés (12 liens
|
||||||
|
et 4 en-têtes), le contenu dépasse la hauteur de l'écran et « Personnes & équipes » (dernier
|
||||||
|
groupe) était strictement hors d'atteinte, sans aucun moyen d'y accéder. Présent depuis R6.1,
|
||||||
|
seulement révélé une fois tous les groupes remplis. **Corrigé** : `ScrollView` autour de la
|
||||||
|
liste des groupes, en-tête (EnteteTabs + titre) fixe au-dessus.
|
||||||
|
- **Bug réel 2 — BC créé sans confirmation visible** : `app/stock/[id]/commander.tsx` naviguait
|
||||||
|
silencieusement en arrière après succès — le référent a créé DEUX bons de commande
|
||||||
|
(BC-2026-1009, BC-2026-1010, vérifiés côté serveur, tous deux corrects) sans aucun retour dans
|
||||||
|
l'app, doute légitime sur ce qui s'était vraiment passé. **Corrigé** : écran de confirmation
|
||||||
|
explicite (référence, fournisseur, total, statut) avant de revenir, même patron que
|
||||||
|
`personnes/lien.tsx`.
|
||||||
|
- **Amélioration Tiers** : le référent a signalé qu'on ne pouvait « ni ajouter ni consulter » —
|
||||||
|
l'absence de fiche détail (délibérée, D4 — lecture seule) se lisait comme un écran cassé plutôt
|
||||||
|
que simple. Arbitrage : fiche détail ajoutée (`app/tiers/[id].tsx` — identité, contact, BC en
|
||||||
|
cours pour un fournisseur, sites rattachés pour un client, aucune nouvelle requête, tout déjà
|
||||||
|
dans `usePartners()`), création/édition restent au web.
|
||||||
|
- **Faux positifs écartés, vérifiés côté serveur** :
|
||||||
|
- Approbation de DEM-2026-0110 (« Voyant étage éteint ») : a fonctionné (statut `APPROVED`,
|
||||||
|
OT-2026-1200 créé) — le message du référent citait juste le libellé de la demande.
|
||||||
|
- Fichiers « Aucun document accessible » : confirmé — `GET /documents` renvoie `[]` sur cette
|
||||||
|
instance. Pas un bug mobile : aucun document n'existe sur cette base locale (README/seed à
|
||||||
|
vérifier séparément si un jeu de documents de démo est attendu).
|
||||||
|
- Statistiques « les chiffres ne changent pas » entre 3/6/12 mois : confirmé côté serveur que la
|
||||||
|
période EST bien transmise et traitée (`costsByMonth` varie en nombre de points), mais les
|
||||||
|
indicateurs affichés (coût du mois, taux préventif, OT clôturés, pannes) ne varient pas parce
|
||||||
|
que l'activité codée sur ce jeu de données est concentrée sur une fenêtre récente — réalité
|
||||||
|
des données de démo, pas un bug du sélecteur.
|
||||||
|
- Vérifié après correctifs : typecheck propre, 17 tests Jest, lint 5/5 paquets.
|
||||||
|
|
||||||
|
**Décisions**
|
||||||
|
|
||||||
|
- Tiers gagne une fiche détail en lecture seule (voir ci-dessus) — seul écart au périmètre D4
|
||||||
|
initial de R6.3, sur retour direct de recette.
|
||||||
|
|
||||||
|
**Prochaine étape** : suite de la recette Gestionnaire (Sites/Ascenseurs/Fichiers/Assistant+dictée
|
||||||
|
restent à confirmer), puis passage Demandeur (permissions les plus étroites).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-08-02 — Pr. Daaif (+ Claude) — R6.5 : Assistant mobile (chat sourcé + dictée)
|
## 2026-08-02 — Pr. Daaif (+ Claude) — R6.5 : Assistant mobile (chat sourcé + dictée)
|
||||||
|
|
||||||
**Actions**
|
**Actions**
|
||||||
|
|||||||
Reference in New Issue
Block a user