DOC
Accueil/ Projets/ Gestion RH/ 01 - Technologies & architecture

01 - Technologies & architecture

1. Stack technique (réel, vérifié dans le dépôt)

ℹ️ La demande mentionnait « React, Java, déploiement… ». Il n'y a aucun Java dans ce projet, ni backend applicatif. La stack est 100 % JavaScript + Supabase.

Frontend

Techno Version Rôle
React ^18.3.1 UI (composants fonctionnels + hooks)
Vite ^5.3.1 Build & dev server
Tailwind CSS ^3.4.4 Styling utilitaire (tout le style est inline en classes)
lucide-react ^0.383.0 Icônes
@types/react ^18.3.3 Types présents… mais le code est en JSX pur, pas TypeScript
PostCSS + autoprefixer - Pipeline CSS

Backend / plateforme

Techno Rôle
Supabase (@supabase/supabase-js ^2.49.1) Base PostgreSQL, Auth (email/mot de passe), Storage (bucket privé employee-docs)
PostgreSQL + RLS Stockage relationnel, Row Level Security (⚠️ permissive, voir ADR-0003)

Déploiement

Techno Rôle
Netlify Hébergement statique du SPA ; netlify.toml fait le fallback SPA (/* → /index.html)
Variables d'env VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY (injectées au build)

Il n'y a donc pas de couche serveur : le navigateur parle directement à Supabase avec la clé publique anon. Toute la logique métier (paie, droits, périmètres) s'exécute côté client.


2. Schéma de la base de données

2.1 Tables documentées (dans les fichiers supabase-*.sql)

app_users        (id, auth_id→auth.users, email, role[admin|manager], scope_type?, scope_id?, created_at)
entities         (id, name, siret, address, created_at)                      ← sociétés / entités juridiques
locations        (id, entity_id→entities, name, address, manager, created_at) ← établissements / boutiques
employees        (id, first_name, last_name, birth_date, email, phone, address,
                  contract_type[CDI|CDD|Alternance|Stage], start_date, end_date, position,
                  work_time, gross_salary, entity_id, location_id,
                  trial_start/duration/renewal/end, documents(JSONB), status[actif|sorti],
                  nationality, sex, qualification, notes, cp_balance,
                  -- via migrations contrats : alternance_rhythm, cdd_reason, cdd_replaced_name,
                  --   cdd_renewable, cdd_renewal_end, stage_gratification, stage_hours_max, stage_convention
                  -- via migration paie : weekly_hours, sale_bonus_amount, transport_amount, transport_justified)
leave_requests   (id, employee_id, type, start_date, end_date, days,
                  status[en_attente|validé|refusé], note, created_at)
monthly_payroll  (id, employee_id, month, hours_worked, hours_overtime, sunday_hours,
                  sale_count, sale_bonus, transport_reimbursement, notes, UNIQUE(employee_id,month))
schedule_templates (id, name, entity_id, slots(JSONB), ...)                   ← planning type récurrent
weekly_schedules   (id, entity_id, week_start, status[brouillon|publié], slots(JSONB), published_at, UNIQUE(entity_id,week_start))

2.2 ⚠️ Tables/objets utilisés par le code mais ABSENTS des migrations

Le code appelle ces objets, qui n'existent dans aucun fichier supabase-*.sql :

Objet Utilisé où Preuve
transport_receipts dépôt/validation des justificatifs transport App.jsx supabase.from("transport_receipts")
planning_notes notes sur le planning App.jsx supabase.from("planning_notes")
contract_history historique/versions de contrat + documents App.jsx supabase.from("contract_history")
app_users.scope_type / scope_id périmètre manager lu/écrit dans App.jsx, absent de supabase-schema.sql
app_users.role = 'employee' rôle salarié isEmployee, alors que le CHECK SQL n'autorise que `admin
bucket Storage employee-docs documents & justificatifs storage.from("employee-docs")

Conséquence : on ne peut pas recréer une base fonctionnelle à partir du dépôt. C'est une dette bloquante (P0) - voir backlog.

2.3 Modèle de données (relations)

entities (société) 1───* locations (boutique)
    │                         │
    │                         └──* employees.location_id
    └──────────────────────────* employees.entity_id
employees 1───* leave_requests
employees 1───* monthly_payroll        (1 ligne / mois)
employees 1───* transport_receipts     (1 justificatif / mois)
employees 1───* contract_history       (versions successives du contrat)
entities  1───* weekly_schedules / schedule_templates   (planning par société, filtré par ville côté app)
app_users *───1 auth.users             (compte d'authentification)

3. Architecture applicative

3.1 Vue d'ensemble

┌─────────────────────────────────────────────────────────┐
│  Navigateur (SPA React servi par Netlify)               │
│                                                          │
│  App.jsx  ── AuthScreen | HRApp                          │
│    ├─ Pages : Dashboard, Planning, Salariés, Congés,     │
│    │          Paie, Transport, Registre, Paramètres,     │
│    │          Utilisateurs, (+ vues Employé)             │
│    ├─ Logique métier : alertes, calcul paie, périmètres  │
│    └─ UI primitives : Btn, Modal, Badge, Card, Input…    │
│                                                          │
│         │  @supabase/supabase-js (clé anon publique)     │
└─────────┼────────────────────────────────────────────────┘
          ▼
┌─────────────────────────────────────────────────────────┐
│  Supabase                                                │
│    ├─ Auth (email/mot de passe)                          │
│    ├─ PostgreSQL + RLS (⚠️ permissive)                   │
│    └─ Storage : bucket privé employee-docs (URLs signées)│
└─────────────────────────────────────────────────────────┘

3.2 Chargement des données

Toutes les données sont chargées d'un bloc au montage via un Promise.all de 11 requêtes select("*") (loadData, App.jsx:363), puis stockées dans des useState au niveau de HRApp. Il n'y a pas de cache, de pagination, ni de rechargement ciblé : chaque écran travaille sur les tableaux en mémoire, et les mutations mettent à jour l'état local en parallèle de l'écriture Supabase (optimistic-like, mais sans rollback en cas d'échec réseau).

Implications :

  • ✅ Simplicité, réactivité immédiate entre écrans.
  • ⚠️ Passage à l'échelle : select("*") sans filtre ni pagination - se dégrade avec le nombre de salariés/plannings ; charge tout côté client.
  • ⚠️ Sécurité : puisque la RLS est ouverte, select("*") renvoie toutes les lignes, y compris hors périmètre du manager/salarié (le filtrage est purement visuel, visibleEmployees).

3.3 Rôles & périmètres (uniquement applicatifs)

  • admin : accès à tout, gestion des utilisateurs.
  • manager : pages Dashboard, Planning, Congés, Transport ; périmètre limité par scope_type (city | entity | location) + scope_id. Filtrage calculé en mémoire (visibleEmployees, isEmployeeInScope).
  • employee : self-service (Ma fiche, Mon planning, Mon transport).

⚠️ Ce cloisonnement n'existe que dans le JS. La base ne le connaît pas (ADR-0003).

3.4 Sécurité & confidentialité - synthèse

Sujet État Risque
RLS Permissive (tout authentifié = accès total) 🔴 Élevé
Contrôle des rôles Côté client uniquement 🔴 Élevé
Clé Supabase anon publique dans le bundle (normal), mais sans RLS stricte derrière 🔴 Élevé
Stockage documents Bucket privé + URLs signées 120 s 🟢 Correct
Mots de passe Gérés par Supabase Auth 🟢 Correct
Confirmation email Désactivée (procédure README) 🟡 Acceptable en interne
RGPD (données perso, salaires, RIB) Pas de minimisation/cloisonnement DB 🟠 À traiter

Détails et remédiation : ADR-0003 et prompts/05-durcir-securite-rls.


4. Points techniques notables

  • Dates : le code mélange calculs locaux (getMonday, addDays, commentés pour éviter le décalage UTC) et calculs UTC (toISOString().slice(0,10) dans computeHoursFromPlanning, alerts). Risque de décalage d'un jour selon le fuseau/l'heure - à uniformiser (tout en local).
  • Calcul de paie : heures issues du planning publié, heures sup = au-delà des heures contractuelles du mois (weekly_hours × semaines), bonus dimanche (+7 h/dimanche travaillé), prime de vente (sale_count × sale_bonus_amount), transport remboursé à 50 % si justificatif. Logique correcte mais non testée.
  • Gestion d'erreurs : essentiellement console.error + toast. Pas d'Error Boundary React, pas de remontée structurée.
  • Accessibilité / i18n : UI en français en dur, pas d'a11y explicite.
  • Performance build : un seul chunk (pas de code-splitting) - acceptable vu la taille, à surveiller si l'app grandit.

Voir aussi 02 - Conventions & organisation du code.