- 3 routes publiques /portal (les seules @Public métier), throttlées
(@nestjs/throttler : 10 signalements/min, 60 lectures/min) : résolution
du QR, signalement (retourne un jeton de suivi opaque), suivi par
référence + jeton — pas de jeton, pas de lecture, jamais de liste
- migration r2_portail : Request.publicToken (unique)
- page /q/{réf} fidèle à l'écran 6 validé R0 (mobile d'abord) : équipement
prérempli « détecté par le QR », interrupteur personne bloquée,
suivi sans jargon Reçu → Intervention → Résolu (dérivé de l'OT lié),
rejet affiché « Sans suite : {motif} », photo annoncée (upload R3) ;
signalements gardés sur le téléphone (localStorage, 10 max)
- e2e : LA boucle produit — gardien sans compte → traitement → ✓ Résolu ;
11 tests Playwright verts, 50 tests API, 52 opérations au contrat
- générateur OpenAPI : paramètres non-« id » plus typés uuid
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.7 KiB
SIOP V2 — GMAO ascenseurs (refondation design-first)
Contexte
Projet réel (SPELEV, maintenance d'ascenseurs, Maroc) mené par le Pr. Daaif (ENSET) avec ses étudiants. V2 = refondation de siop-spelev/siop (v1, gelée, référence de lecture seulement) décidée le 15 juillet 2026 : 100 % nouveau code, design en premier, documentation en « playbook » pédagogique. Double objectif : produit complet déployé (web + mobile + IA, Dokploy) et playbook du cycle de vie logiciel (cours ENSET + méthode de la future entreprise du référent).
Langue de travail : français (docs, échanges, commits descriptifs). Code et identifiants en anglais.
Principes non négociables (V2)
- Design-first : aucune ligne de code applicatif avant validation par le référent de la charte graphique, des tokens et des maquettes HD (
docs/02-design/). Chaque release subit une revue « pixel » écrans ↔ maquettes. - Playbook vivant : chaque phase du cycle de vie a son dossier
docs/0X-*/(template réutilisable + artefacts réels). Le journal quotidien vit dansdocs/journal/— jamais dans le livre. - Périmètre fermé par release : R0 Fondations → R1 Référentiel → R2 Exploitation → R3 Gestion → R4 Mobile → R5 IA. On n'ouvre pas Rn+1 avant recette et déploiement de Rn.
- Déployer tôt : chaque release part sur le serveur de production (Dokploy) dès sa recette.
- Le développeur est le premier utilisateur :
DEMO_MODE=trueactive un sélecteur de compte démo (connexion 1 clic sur les comptes seedés, endpointPOST /auth/demo-loginstrictement absent si l'env ne l'active pas). Critère : changer de rôle en < 3 s sans mot de passe.
Stack (ADR-001 — reconduite de v1 car éprouvée, code neuf)
pnpm + Turborepo. apps/api : NestJS, Prisma, PostgreSQL (pgvector + PostGIS), Redis, MinIO derrière l'interface FileStorage (jamais d'import direct du SDK). apps/web : React 19, Vite, TanStack Query/Table, shadcn/ui + Tailwind v4 thématisés par les tokens de docs/02-design/tokens.css, Lucide. apps/mobile : Expo (Expo Go). apps/ai : FastAPI (Python, uv). Règle d'or du contrat : schémas Zod dans packages/shared → OpenAPI committée → clients typés régénérés dans le même commit (CI bloquante).
Conventions
- Journal de bord : toute session se clôt par une entrée en tête de
docs/journal/journal.md(date — auteur — release/tâche ; Actions / Décisions / Prochaine étape). - Mode solo temporaire : commits directs sur
maintolérés ; à l'arrivée des étudiants → PR + CI obligatoires. - Commits conventionnels ; branches courtes
feat/…,fix/…. - Tests écrits avec le code : Jest (api), Vitest (web), jest-expo (mobile), pytest (ai) ; couverture backend ≥ 70 % bloquante ; parcours Playwright par release.
- API fermée par défaut (guard JWT global,
@Public()explicite) ; matrice de permissions en base. - Conformité loi 09-08 : géolocalisation limitée au service, purge des audios, consentements.
- IA : l'IA propose, un humain valide (sauf urgence « personne bloquée ») ; toute réponse RAG cite sa source.
Conventions d'infrastructure
- Docker/Dokploy : tous les services et conteneurs sont préfixés
siop2-(demande du référent, leçon v1 : sur le réseau partagé Dokploy, un service nommépostgres/apicollisionne avec les autres projets).
État d'avancement
- ✅ R0 (1/2) : playbook 00-vision, 01-cadrage, 02-design — charte + tokens + maquettes HD VALIDÉES par le référent le 15/07/2026 (artefact : maquette-web.html ; 7 écrans, bi-thème). 03-architecture rédigé (ADR-001 stack, ADR-002 démo-login, C4, modèle R0). Racine monorepo posée (package.json/pnpm-workspace/turbo/.nvmrc) +
infra/(compose PostgreSQL pgvector+PostGIS, Redis, MinIO — services préfixéssiop2-). - ✅ R0.10
apps/api: NestJS + Prisma (migrationr0_identity), guard JWT global fermé par défaut +@Public(), PermissionsGuard (matrice en base, cache 60 s), démo-login ADR-002 (module conditionnel, 404 sinon, double verrou prod), seed idempotent (7 rôles, 70 lignes de matrice, 7 comptes démo),FileStorage/health ;packages/shared(Zod) +pnpm contract→docs/openapi.jsoncommittée ; 19 tests Jest verts + smoke test build prod. - ✅ R0.11
apps/web: React 19 + Vite + Tailwind v4,tokens.csscopié tel quel + classes extraites de la maquette validée, Manrope auto-hébergée ; connexion + sélecteur démo (masqué si 404 — une seule source de vérité : l'API), coquille sidebar (rail safran, écrans futurs marqués R1-R3) / topbar (thème, chip DÉMO, sélecteur de rôle), page /design ; client typéschema.d.tsgénéré depuisdocs/openapi.jsonet committé ; vérifié en navigateur réel (bascule de rôle 101 ms, bi-thème, 0 erreur console). - ✅ R0.12 — tests + CI (5 jobs verts) :
lint(ESLint 10 flat config racine, règle ADR-001 anti-import MinIO codée et vérifiée),ci-contract(régénération spec+client, diff bloquant),api(PostgreSQL 18 + Redis, migrate deploy, Jest couverture ≥ 70 % bloquante — mesurée 97,5 %),web(typecheck + vitest + build),e2e(Playwright : parcours démo, bascule < 3 s chronométrée, déconnexion). Badge au README. - ✅ R0.13 — Dockerfiles + runbook Dokploy : image api (multi-stage pnpm deploy,
prisma migrate deployau boot, seed optionnelSEED_ON_START, non-root, healthcheck ;binaryTargetsexplicites) ; image web (nginx, proxy/apirésolu à la requête, fallback SPA, cache assets) ;infra/docker-compose.dokploy.yml(5 servicessiop2-, seul le web surdokploy-network) ; runbookdocs/06-production/runbook-dokploy.md. Répétition locale complète validée (migrate+seed au boot, parcours via nginx conteneurisé, double verrou ADR-002 observé). Le déploiement réel attend les accès au serveur du partenaire. - ✅ R0 CLOSE (tag
release/r0) — en production :https://siop2.apps.enset.top(Dokploy ENSET, profil démo, vérifiée en ligne). Restes non bloquants : secretDOKPLOY_WEBHOOK_URL(CD), sauvegardes PostgreSQL Dokploy. Production client SPELEV : attend les accès au serveur du partenaire. - ✅ R1 — maquettes validées (16/07) :
maquette-r1.html, 7 écrans + 4 décisions (statuts d'équipement ≠ statuts OT ; organe sans emplacement par construction ; invitation par lien 7 j ; catégories jamais supprimées si utilisées). - ✅ R1.1 — socle backend : migration
r1_referentiel(Category, Location site→zone + PostGIS générée, Asset, AssetComponent, Team, invitation User), 21 nouvelles opérations au contrat (26 total), 4 modules API + invitations/activation sous@RequirePermission, seed parc maquette (5 sites, 8 appareils, organes, 2 équipes), 36 tests (96 %/85 %), CI surpostgis/postgis:18-3.6. - ✅ R1.2 — écrans web du référentiel : 7 écrans fidèles à maquette-r1.html (Sites + carte Leaflet/OSM, fiche site, ascenseurs, fiche appareil + QR réel, création, étiquette A6 imprimable, personnes & équipes avec lien d'activation à copier, catégories) + page /activation ; navigation et actions pilotées par la matrice (
usePermissions) ; recette R1 rejouée en e2e Playwright (dont activation d'un invité) ; alias Vite@siop/shared→ source TS (leçon CJS/workspace). - 🏁 R1 CLOSE (16/07/2026, tag
release/r1) : recettée par le référent, déployée et vérifiée en ligne (migration + seed au boot, 5 sites / 8 appareils sur l'instance). - ✅ R2 — maquettes validées (16/07) :
maquette-r2.html(demandes/approbation, nouvel OT, préventif, checklist, compteurs) + 4 décisions actées. - ✅ R2.1 — socle backend exploitation : migration
r2_exploitation(10 tables), 15 opérations au contrat (41 total, transitions + champs requis du bilan dans@siop/shared), machine à états stricte avec garde de clôture (bilan 3 champs + checklist), demande→OT 1-1, rejet à motif, scoping « voir autre » (listes + accès directs), seed maquette (31 valeurs de bilan, 8 gabarits, OT/demandes/compteurs), 45 tests (95 %/79 %). - ✅ R2.2 — préventif + compteurs :
underContract+periodKey(unicité[assetId, periodKey]= idempotence EN BASE), génération mensuelle (ancrage mise en service, premier contrôle, gabarits administrables, statut du mois), compteurs strictement croissants ; +7 opérations (48), 50 tests (94,7 %/78 %). Automatisation cron/BullMQ notée pour le durcissement production. - ✅ R2.3 — écrans web exploitation : liste/fiche OT (transitions via
allowedTransitions, garde visible, bilan codé, checklist cliquable, activité), demandes+approbation/rejet motivé, nouvel OT (interrupteur urgence), préventif (tuiles+générer+gabarits), compteurs, tableau de bord réel, urgence traversante (chip topbar, badges, bandeau) ;GET /assets/options(trou Demandeur corrigé) ; retry sur collision de référence dans la génération ; 9 Playwright verts (recette R2 complète), 50 tests API. - ✅ R2.4 — portail public QR :
/q/{réf}sans compte (écran validé R0, mobile d'abord), 3 routes publiques/portalthrottlées, suivi par jeton opaque (Request.publicToken, jamais de liste exposée), étapes Reçu → Intervention → Résolu dérivées de l'OT ; boucle produit complète en e2e (11 Playwright verts, 52 opérations). - 🔄 R2 — reste pour clore : recette R2 avec le référent (revue pixel ↔ maquettes R0+R2, sur
pnpm devou l'instance en ligne après déploiement), tagrelease/r2→ ouverture R3 Gestion (stock, BC, tiers, main-d'œuvre, analytics, bibliothèque — maquettes à produire d'abord). - Détail quotidien :
docs/journal/journal.md. Dépôt :siop-spelev/siop2(privé), jalons R0→R5.