Pour le développeur qui reprend le projet. Tout ce qui suit vient du code du dépôt au 13/10/2026.
| Couche | Outil | Où le voir |
|---|---|---|
| Application | Expo SDK 57, React Native 0.86, React 19.2, TypeScript 6 | package.json |
| Navigation | Expo Router (un fichier = une page) | src/app/ |
| Web | React Native Web, export en une page ("output": "single") |
app.json |
| État | zustand 5, persistance par AsyncStorage | src/data/store.ts, src/data/prefs.ts |
| Serveur | Supabase : Postgres avec RLS, Storage, Auth, fonctions Edge (Deno) | supabase/ |
| IA | API Anthropic, appelée seulement depuis la fonction copilot |
supabase/functions/copilot |
| Paiement | Stripe (Checkout, portail client, webhook), depuis la fonction billing |
supabase/functions/billing |
| E-mails | SMTP (Brevo conseillé) ou Resend, module commun | supabase/functions/_shared/mail.ts |
| Notifications téléphone | expo-notifications et Expo Push |
src/lib/push.ts, fonction location |
| Compilation et mises à jour | EAS Build, EAS Update (canaux preview et production), EAS Hosting pour le web |
eas.json, app.json |
| Tests | vitest (unitaires), Playwright (parcours) | tests/, e2e/ |
Options notables de app.json : runtimeVersion.policy = "appVersion", mises à jour vérifiées au lancement (checkAutomatically: "ON_LOAD"), React Compiler activé (experiments.reactCompiler), identifiant fr.rentoo.app, schéma de liens rentoo.
Variables de l’application (fichier .env, noms seulement) : EXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_KEY (clé publishable, publique par nature), EXPO_PUBLIC_WEB_URL, et en option EXPO_PUBLIC_SUPPORT_EMAIL, EXPO_PUBLIC_APP_STORE_URL, EXPO_PUBLIC_PLAY_STORE_URL. Sans les deux premières, src/lib/supabase.ts arrête l’application avec un message.
src/
app/ Écrans (Expo Router). Un dossier par rubrique, avec _layout.tsx, index.tsx, [id].tsx…
index.tsx Accueil du bailleur
biens/ locataires/ finances/ location/ travaux/ assurances/ documents/ calendrier/ fiscalite/
copilote/ Page de Roo
plus/ Profil et réglages (abonnement, notifications, données, journal de Roo…)
components/
ui/ Briques de base : AppText, Button, Card, ListRow, Segmented, TextField, DatePicker…
sheet/ Grammaire commune : InkStrip, TodoBlock et blocs de fiche
public/ Pages sans compte : annonce, candidat, signature, devis, « Mon logement », espace locataire
tenant/ Application du locataire connecté (TenantApp.tsx)
roo/ Écran et cartes de Roo
finances/ fiscalite/ calendar/ home/ charges/ travaux/ Composants propres à une rubrique
AppShell.tsx Cadre : barre du bas (téléphone) ou panneau flottant (ordinateur), bulle de Roo, recherche, bouton « + »
nav.ts Liste des rubriques
data/
store.ts Store principal : état, chargement depuis Supabase, actions d'écriture
prefs.ts Préférences propres à l'appareil
mappers.ts Conversions lignes Supabase <-> objets de l'application
writeQueue.ts File des écritures en arrière-plan
types.ts Types du domaine
ui.ts État d'interface (messages éphémères, panneaux ouverts)
rooChat.ts Conversation avec Roo (flux, documents joints)
seed.ts Données d'exemple (identité fictive)
hooks/ Calculs reliés au store (usePortfolio, useTodo, usePlan, usePropertyScope…)
lib/ Logique pure, testée : finances, fiscalité, baux, IRL, zones, dates, alertes, Roo…
theme/ Couleurs, espacements, typographie, clair et sombre (tokens.ts, useTheme.ts)
supabase/
migrations/ Scripts SQL numérotés 001 à 047
functions/ Fonctions Edge : billing, copilot, location, send-receipt, veille, et _shared/
templates/ Modèles d'e-mails d'authentification (code à 6 chiffres)
tests/ Tests unitaires vitest
e2e/ Tests de parcours, de fonctions et de sécurité (Playwright, Supabase local)
scripts/ deployer.ps1, zones-data.py, tax-tests.ts
public/ Fichiers copiés tels quels dans la version web (site vitrine, pages légales `confidentialite.html` et `mentions-legales.html`, icônes, manifeste)
assets/ Images, icônes, polices
Les écrans restent minces quand c’est possible : un calcul pur va dans src/lib/ avec son test dans tests/. Exemples : listingFlow.ts, insuranceCoverage.ts, documentsOverview.ts, calendarStrip.ts, taxStrip.ts, tenantList.ts, propertyScope.ts.
src/data/store.ts)useStore) contient les données du compte : biens, baux, locataires, paiements, documents, dépenses, etc.loadAll() charge tout depuis Supabase. Les règles RLS limitent ce qui revient.remote().logError), un bandeau s’affiche (syncError), puis loadAll() recharge les données du serveur.rentoo-v2) : seulement hasSeenIntro et remindersEnabled. Les données ne sont pas gardées hors ligne.locazen-v2, locazen-v1).useProperty(id), useTenant(id).src/data/prefs.ts)rentoo-prefs.hiddenProperties), dépenses générales masquées (hideGeneral), dernière vue du calendrier.src/data/mappers.ts)xxxFromRow (ligne Supabase en snake_case vers objet camelCase) et xxxToRow (sens inverse).src/data/writeQueue.ts)Le store crée une file unique : export const writes = createWriteQueue().
writes.run(clé, opération). Avec une clé (l’identifiant de la fiche), les écritures d’une même fiche partent dans l’ordre d’envoi : création, puis modification, puis rangement d’un document. Des fiches différentes partent en parallèle.Failed to fetch, timeout…). Un refus de la base (droits, contrainte, formule) n’est jamais réessayé.23505 (doublon), remote() le considère comme réussi.writes.settled(...ids) : attend que les écritures en cours sur ces fiches soient parties. Utilisé avant une demande de devis à un artisan créé à l’instant.WriteError : erreur qui porte le statut HTTP, pour décider d’un nouvel essai.src/data/outbox.ts) : après les nouveaux essais, une écriture qui n’a pas pu partir faute de réseau est gardée, dans l’ordre, au lieu d’être abandonnée. La saisie reste à l’écran avec « En attente d’envoi » ; un bandeau sombre compte les écritures en attente (SyncBanner). Renvoi au retour du réseau (web), au retour dans l’appli (téléphone), toutes les 20 s et sur « Réessayer ». Une écriture de la même fiche arrivée entre-temps passe derrière (QueuedBehind). Un refus de la base au renvoi est signalé (bandeau rouge) puis les données sont rechargées. loadAll() envoie d’abord les écritures en attente et ne recharge pas tant qu’il en reste.tests/fi-file-ecritures.test.ts.006_partage.sql : l’accès à une donnée découle de l’accès au bien.security definer) : property_role(pid) renvoie proprietaire, gestion ou lecture ; puis can_read_property, can_write_property, is_property_owner, can_read_tenant, can_write_tenant…x-public-token (src/lib/supabase.ts).plan_has, my_plan_has, script 022).donnees-et-securite.md.Toutes se déploient avec --no-verify-jwt : chacune vérifie elle-même son appelant (session de l’utilisateur, signature Stripe, jeton public ou secret). Aucune n’utilise la clé service_role. Le dossier _shared/ est envoyé avec chaque fonction.
| Dossier | Rôle | Actions principales |
|---|---|---|
billing |
Abonnement Stripe | checkout, portal, sync ; webhook Stripe (checkout.session.completed, customer.subscription.*) qui écrit par la fonction SQL billing_apply |
copilot |
Roo | status, lecture (document), diagnostic (avarie), quote_message (demande de devis), week_summary, analyse (patrimoine), question (avec flux stream), letter (courrier) |
location |
Mise en location, espace locataire, e-mails, notifications | candidate_mail, send, signature_invite, seal, portal_* (message, déclaration, document, préavis, signalement…), tenant_invite, invite_member, quote_send, quote_reply, quote_award, recap_send, weekly_send, push_dispatch, export, test_mail ; en GET, flux iCal (?ics=) |
send-receipt |
Quittances et courriers | quittance ou reçu partiel (par défaut), reminder (relance), letter, mail_batch, status |
veille |
Données de référence | irl (indices Insee), zones (fichier « Zonage TLV » de data.gouv.fr) ; appelée par pg_cron avec l’en-tête x-veille-secret |
_shared |
Modules communs | mail.ts, sender.ts, landlord.ts, appFigures.ts, bankDetails.ts, rooDocs.ts, rooMemory.ts, rooFeedback.ts, rooTone.ts, rooSources.ts, veille.ts |
Les noms des secrets sont listés dans deploiement.md.
supabase/migrations/, numérotés 001_init.sql à 048_empreinte_documents.sql. On ajoute toujours un nouveau numéro ; on ne modifie pas un script déjà passé.create table if not exists, create or replace, on conflict do nothing). Les en-têtes le disent, sauf celui de 001_init.sql (archives/INSTALLATION-supabase.md le dit rejouable).016 (ACCESS_SECRET) et 018 (BILLING_SECRET). 027 demande des secrets du coffre créés avant lui.db push) une fois npm run deployer -- -Migrations fait, sinon par copier-coller guidé. Voir deploiement.md.src/lib/errors.ts)logError(erreur, contexte) envoie l’erreur à la fonction SQL log_error, qui écrit dans la table app_errors (script 013) : message, pile, écran, plateforme, version, contexte.noted('ce qui a échoué') s’utilise en .catch(noted('e-mail au candidat')) pour une action secondaire : l’échec est noté sans bloquer l’écran.installErrorHandlers() capte les erreurs non rattrapées (web et téléphone).openFailed (src/lib/openFile.ts) affiche « Ouverture du document impossible… » quand un document ne s’ouvre pas.exploitation.md.src/lib/dates.ts)AAAA-MM-JJ ; les mois, des chaînes AAAA-MM.todayISO() donne le jour local de l’appareil. Ne pas utiliser new Date().toISOString().slice(0, 10) pour « aujourd’hui » : entre minuit et 2 h à Paris, la date universelle est encore la veille.addDaysISO(iso, n) ajoute des jours sur le calendrier, sans effet de fuseau.currentMonth, addMonths, monthRange, dueDateOf, parseFrenchDate, daysUntil.copilot calcule « aujourd’hui » à l’heure de Paris (timeZone: 'Europe/Paris') ; location aussi pour le bail en cours.toISOString().slice(0, 10) dans src/. L’audit du 12/10 les juge justes (calculs sur des dates de calendrier) ; à revoir au cas par cas si l’une sert de « jour même ».tests/fi-dates-paris.test.ts (à 0 h 30 à Paris), tests/t-date-locale.test.ts. La vérification GitHub tourne avec TZ=Europe/Paris.src/lib/ comme fonction pure, avec son test dans tests/. npm test, npm run typecheck et npm run lint doivent passer avant toute mise en ligne (le déployeur l’impose).fix(A06): registre des versements, Lot 10 : …).e2e/config.js).docs/glossaire.md, src/lib/glossary.ts).npm ci, jamais npm install (consigne reprise de l’ancien guide de déploiement).@/ pointe vers src/ (tsconfig.json, vitest.config.ts).| Fichier | Lignes | Piste |
|---|---|---|
src/data/store.ts |
2 838 | Découper par domaine (biens, loyers, location, documents, Roo), une fois la vérification GitHub en place. La logique pure en a déjà été sortie (chantier T08). |
src/components/property/PropertyDetail.tsx |
1 550 | Fiche du bien (UX5, routes src/app/biens/[id]/). Sortir chaque rubrique dans son propre fichier. |
Autres fichiers volumineux : src/components/public/TenantPortalPage.tsx (1 192 lignes), src/data/mappers.ts (1 106), src/app/locataires/[id].tsx (972), src/app/finances/facture.tsx (947). Côté serveur, supabase/functions/location/index.ts (1 309 lignes) et copilot/index.ts (922).