mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
fix(mobile): R6.8 — la connexion routait toujours vers "Ma journée"
connexion.tsx faisait router.replace('/(tabs)/journee') en dur — trouvé
en recette : Karim (Demandeur) atterrissait sur l'écran du Technicien
après avoir basculé de compte démo. La redirection par rôle
(ongletAccueil) n'était branchée qu'à l'aiguillage initial (app/index.tsx,
R6.1), pas au retour de connexion — le chemin réellement emprunté à
chaque bascule de compte (pas de sélecteur de rôle en direct sur mobile,
changer de compte = se déconnecter puis se reconnecter).
useLogin()/useDemoLogin() (auth/session.tsx) renvoient maintenant la
réponse complète (contient user.role.name) au lieu de la jeter après en
avoir extrait le jeton. connexion.tsx : entrer(role) route vers
/(tabs)/${ongletAccueil(role)} pour les deux chemins (connexion classique
et sélecteur démo).
Au passage : sous-titre "Technicien" et accroche "sur le terrain — même
sans réseau" (reste de R4, mobile alors réservé au Technicien) devenus
trompeurs pour les autres rôles depuis R6 — généricisés.
Typecheck/tests/lint verts sur les 4 paquets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,5 +82,6 @@ pnpm + Turborepo. `apps/api` : NestJS, Prisma, PostgreSQL (pgvector + PostGIS),
|
||||
- ✅ **R6.6 — Demandeur restreint à son site** : trou trouvé en recette — `GET /assets/options` n'avait aucun filtre (web ET mobile touchés, pas seulement mobile). Corrigé à la racine : relation `User↔Location` (`assignedSites`, migration `r6_demandeur_sites`, vide = aucune restriction pour les autres rôles) ; `AssetsService.allowedLocationIds()` réutilisée par `options()` et par `RequestsService.create` (défense en profondeur, 400 si hors périmètre) ; gestion des sites d'un Demandeur côté web (`personnes.tsx`, invitation + modale dédiée) ; raccourci scan QR côté mobile (`formulaire-demande.tsx`, résout uniquement contre les options déjà filtrées, jamais de repli sur le parc complet). Karim Doukkali (démo) rattaché à Tour Atlas. Bug trouvé en vérification avant tout commit (comparaison id-de-site vs id-d'appareil dans `create()`, aurait rejeté à tort tout signalement d'un Demandeur affecté) et corrigé, méthode renommée `allowedAssetIds` → `allowedLocationIds` pour que le nom dise ce qu'elle retourne. 79/80 tests API (le seul échec est le flake `monthCost` déjà connu, sans rapport) ; typecheck/tests/lint verts sur les 4 paquets.
|
||||
- ✅ **R6.7 — assignation à l'approbation (mobile)** : le Gestionnaire approuvait une demande mobile sans pouvoir assigner de technicien (l'OT partait non assigné) — « ce n'est pas à lui d'agir comme un technicien ». `PanneauDemandes` gagne un panneau d'approbation avec `ChoixTel` « Assigner à » (techniciens actifs, même filtre que le web), avant confirmation. Rien à changer côté API (`assigneeIds` déjà supporté). Vérifié de bout en bout (demande → approuvée avec assigné → OT avec `assignees` correct). Typecheck/tests/lint verts.
|
||||
- ✅ **Périmètre `WORK_ORDERS` du Gestionnaire confirmé acceptable tel quel par le référent** — l'accès complet hérité de R2/R3 (peut techniquement démarrer/clôturer n'importe quel OT) reste en l'état, aucun resserrement demandé.
|
||||
- 🔄 **Reprise ici** : reste de la recette R6 (Sites/Ascenseurs/Fichiers côté Gestionnaire à confirmer explicitement, puis passage Demandeur avec le nouveau périmètre de site et le scan QR) — 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).
|
||||
- ✅ **R6.8 — la connexion routait toujours vers « Ma journée »** : `connexion.tsx` faisait `router.replace('/(tabs)/journee')` en dur — la redirection par rôle (`ongletAccueil`) n'était branchée qu'à l'aiguillage initial (R6.1), pas au retour de connexion, le chemin réellement emprunté à chaque bascule de compte démo (pas de sélecteur de rôle en direct sur mobile, changer de compte = se déconnecter puis se reconnecter). Karim (Demandeur) atterrissait sur l'écran du Technicien. `useLogin()`/`useDemoLogin()` renvoient maintenant la réponse complète (rôle inclus) ; `entrer(role)` route vers le bon onglet. Sous-titre « Technicien »/accroche terrain de l'écran de connexion (reste de R4) généricisés au passage. Typecheck/tests/lint verts.
|
||||
- 🔄 **Reprise ici** : reprendre la recette Demandeur (Accueil, Nouvelle demande filtrée à Tour Atlas, scan QR) une fois ce correctif rechargé sur l'iPhone, puis 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.
|
||||
|
||||
@@ -9,12 +9,18 @@ import {
|
||||
TextInput,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import type { RoleName } from '@siop/shared';
|
||||
import { ongletAccueil } from '@/auth/roles';
|
||||
import { useDemoAccounts, useDemoLogin, useLogin } from '@/auth/session';
|
||||
import { useTokens } from '@/theme/tokens';
|
||||
|
||||
/** Connexion mobile — mêmes règles que le web : formulaire e-mail/mot de
|
||||
* passe, et le sélecteur démo (< 3 s pour changer de rôle, ADR-002)
|
||||
* UNIQUEMENT si l'API l'expose. */
|
||||
* UNIQUEMENT si l'API l'expose. Depuis R6, l'app n'est plus réservée au
|
||||
* Technicien : on route vers l'onglet d'accueil du RÔLE réel (« Ma
|
||||
* journée » pour le terrain, « Accueil » sinon) — jamais un onglet figé
|
||||
* (trouvé en recette : un Demandeur atterrissait sur « Ma journée »,
|
||||
* l'écran du Technicien, faute de lire le rôle retourné par la connexion). */
|
||||
export default function PageConnexion() {
|
||||
const t = useTokens();
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -23,7 +29,7 @@ export default function PageConnexion() {
|
||||
const login = useLogin();
|
||||
const demo = useDemoLogin();
|
||||
|
||||
const entrer = () => router.replace('/(tabs)/journee');
|
||||
const entrer = (role: RoleName) => router.replace(`/(tabs)/${ongletAccueil(role)}`);
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
@@ -36,12 +42,9 @@ export default function PageConnexion() {
|
||||
<Text style={{ fontFamily: 'Manrope_800ExtraBold', fontSize: 26, color: t.encre }}>
|
||||
SIOP
|
||||
</Text>
|
||||
<Text style={{ fontFamily: 'Manrope_600SemiBold', fontSize: 13, color: t.encre3 }}>
|
||||
Technicien
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={{ fontFamily: 'Manrope_400Regular', color: t.encre2, marginBottom: 8 }}>
|
||||
Vos ordres de travail, sur le terrain — même sans réseau.
|
||||
Vos interventions et votre suivi, où que vous soyez.
|
||||
</Text>
|
||||
|
||||
<View style={{ gap: 10 }}>
|
||||
@@ -84,7 +87,10 @@ export default function PageConnexion() {
|
||||
accessibilityRole="button"
|
||||
disabled={!email || !motDePasse || login.isPending}
|
||||
onPress={() =>
|
||||
login.mutate({ email, password: motDePasse }, { onSuccess: entrer })
|
||||
login.mutate(
|
||||
{ email, password: motDePasse },
|
||||
{ onSuccess: (res) => entrer(res.user.role.name) },
|
||||
)
|
||||
}
|
||||
style={{
|
||||
backgroundColor: !email || !motDePasse ? t.bordureForte : t.primaire,
|
||||
@@ -122,7 +128,9 @@ export default function PageConnexion() {
|
||||
key={c.id}
|
||||
accessibilityRole="button"
|
||||
disabled={demo.isPending}
|
||||
onPress={() => demo.mutate(c.id, { onSuccess: entrer })}
|
||||
onPress={() =>
|
||||
demo.mutate(c.id, { onSuccess: (res) => entrer(res.user.role.name) })
|
||||
}
|
||||
style={{
|
||||
backgroundColor: t.surface,
|
||||
borderColor: t.bordure,
|
||||
|
||||
@@ -42,6 +42,7 @@ export function useLogin() {
|
||||
mutationFn: async (input: { email: string; password: string }) => {
|
||||
const res = await unwrap(await api.POST('/auth/login', { body: input }));
|
||||
await apres(res.accessToken);
|
||||
return res; // le rôle sert à router vers le bon onglet d'accueil
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -52,6 +53,7 @@ export function useDemoLogin() {
|
||||
mutationFn: async (userId: string) => {
|
||||
const res = await unwrap(await api.POST('/auth/demo-login', { body: { userId } }));
|
||||
await apres(res.accessToken);
|
||||
return res; // le rôle sert à router vers le bon onglet d'accueil
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,37 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-03 — Pr. Daaif (+ Claude) — R6.8 : connexion routait toujours vers « Ma journée »
|
||||
|
||||
**Contexte** — en recette, le référent a basculé sur Karim Doukkali (Demandeur) et a atterri sur
|
||||
« Ma journée » (l'écran du Technicien, « Aucun OT en cours ») au lieu d'« Accueil ». Cause :
|
||||
`app/connexion.tsx` — la fonction `entrer()` faisait `router.replace('/(tabs)/journee')` **en
|
||||
dur**, quel que soit le rôle du compte choisi. La redirection par rôle (`ongletAccueil`) n'avait
|
||||
été branchée qu'à l'aiguillage initial (`app/index.tsx`, R6.1) — pas au retour de connexion, le
|
||||
chemin réellement emprunté à chaque bascule de compte démo (changer de compte = se déconnecter
|
||||
puis se reconnecter, il n'y a pas de sélecteur de rôle en direct sur mobile).
|
||||
|
||||
**Actions**
|
||||
|
||||
- `useLogin()`/`useDemoLogin()` (`auth/session.tsx`) renvoient maintenant la réponse complète
|
||||
(contient `user.role.name`) au lieu de la jeter après en avoir extrait le jeton.
|
||||
- `connexion.tsx` : `entrer(role)` route vers `/(tabs)/${ongletAccueil(role)}` — les deux
|
||||
boutons (connexion classique et sélecteur démo) passent le rôle réellement retourné par l'API,
|
||||
jamais un onglet figé.
|
||||
- Au passage : le sous-titre « Technicien » et l'accroche « sur le terrain — même sans réseau »
|
||||
de l'écran de connexion étaient un reste de R4 (mobile alors réservé au Technicien) — devenus
|
||||
trompeurs pour les autres rôles depuis R6, généricisés.
|
||||
- Typecheck/tests/lint verts sur les 4 paquets.
|
||||
|
||||
**Décisions**
|
||||
|
||||
- Aucune — correction directe, aucun changement de comportement voulu au-delà du routage.
|
||||
|
||||
**Prochaine étape** : reprendre la recette Demandeur (Accueil, Nouvelle demande filtrée à Tour
|
||||
Atlas, scan QR) une fois ce correctif rechargé sur l'iPhone.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-02 — Pr. Daaif (+ Claude) — R6.7 : assignation à l'approbation (mobile)
|
||||
|
||||
**Contexte** — en recette, le référent a signalé que le Gestionnaire doit pouvoir affecter un OT
|
||||
|
||||
Reference in New Issue
Block a user