Commit Graph

8 Commits

Author SHA1 Message Date
Arnaud 986241dc85 feat(donut): texte en arc de cercle + 12 dimensions du plancher social
Limites planétaires + dimensions sociales : labels qui épousent vraiment la
courbure de l'anneau au lieu d'être posés en ligne droite tangente. Implémenté
avec SVG <textPath> + un <path> caché par label.

Petit piège : pour qu'un label en bas du donut reste lisible (pas écrit à
l'envers ou de droite à gauche), il faut INVERSER la direction du path selon
la moitié.

  Helper arcLabelPaths() :
    - Moitié haute (angles ~0°)   : path va de θ-δ vers θ+δ, sweep=1
    - Moitié basse (angles ~180°) : path va de θ+δ vers θ-δ, sweep=0
  → la direction du chemin au point central du label est toujours "horizontale
    à gauche-droite" du point de vue d'un lecteur fixe.

Ajouts :
  - 12 dimensions du plancher social Raworth (Alimentation, Santé, Éducation,
    Revenu & travail, Eau, Énergie, Liens sociaux, Logement, Égalité de genre,
    Équité sociale, Voix politique, Paix & justice) placées en arc entre le
    centre et l'anneau intérieur (rayon 67)
  - R_CENTER passé de 56 à 50 pour libérer 6 unités au profit des labels
    sociaux sans toucher au "LE DONUT / X projets" au centre
  - Suppression des textes centrés "PLAFOND ÉCOLOGIQUE" et "PLANCHER SOCIAL"
    sur les anneaux (redondants avec les labels qui les détaillent)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:26:00 +02:00
Arnaud 362b3c5712 fix(sidebar): liens de nav ne naviguaient pas (RouterLink v-slot sans custom)
Le pattern <RouterLink v-slot="..."><a @click.prevent>...</a></RouterLink>
sans le prop `custom` rend DEUX <a> imbriqués : celui de RouterLink (qui gère
la navigation) + celui du slot. Notre @click.prevent sur le <a> interne
bloquait le clic avant qu'il remonte au <a> externe. Résultat : clic mort.

Fix : ajout du prop `custom` (RouterLink ne rend plus son <a>) +
@click="navigate" (fonction fournie par le v-slot qui fait le router.push
en gérant les modifier keys, le ctrl-clic, etc.).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:14:09 +02:00
Arnaud 8dd9cffaed fix(donut): hover anti-jitter + couleurs catégories + limites planétaires
1. Jitter au survol (bug visible) :
   `transform: scale(1.5)` en SVG prend pour origine le (0,0) du viewBox,
   pas le centre du <circle>. Le scale "déplaçait" donc le cercle vers le centre
   de la viewport → la souris n'était plus dessus → mouseleave → retour →
   mouseenter → re-déplacé. Boucle infinie de tremblement.

   Fix : classe `.dot-circle` avec
       transform-box: fill-box;
       transform-origin: center;
   Le scale prend désormais pour pivot le centre de la forme. Stable.

2. Couleurs des secteurs thématiques (par la maquette d'origine) :
   Réintroduction des wedges colorés dans la zone sûre, opacité 10 % sur
   le fill + 20 % sur le stroke. Reste sobre, garde la lisibilité.

3. Limites planétaires (de Kate Raworth) :
   Les 9 limites planétaires (Changement climatique, Acidification des
   océans, Biodiversité, Eau douce, Cycles N&P, Pollution chimique,
   Pollution de l'air, Couche d'ozone, Usage des sols) sont placées en
   étiquettes tangentes autour de l'anneau extérieur, avec flip 180° en
   moitié basse pour rester lisibles.

Cosmétique :
   - viewBox 560×560 (au lieu de 520) pour caser les labels extérieurs
   - Labels catégorie déplacés AU MILIEU de leur wedge (sur la zone sûre)
     plutôt qu'à l'extérieur (où ils chevauchaient les limites planétaires)
   - Séparateurs radiaux passés en blanc pour mieux contraster avec les
     wedges colorés

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 22:10:37 +02:00
Arnaud 80193e5bc0 feat(viz): refonte du donut + jeu d'essai démo de 20 projets
Donut (DonutPage.vue) — alignement avec la maquette "Capture 2026-05-28 205310" :
  - Suppression des secteurs en couleur vive : 6 secteurs séparés par des
    lignes radiales en pointillé gris léger
  - 2 anneaux gris texturés (pattern diagonal) : "Plafond écologique" (extérieur)
    et "Plancher social" (intérieur), avec leurs labels
  - Espace sûr cream entre les deux où vivent les projets
  - Positionnement des projets :
      angle  : secteur de la catégorie + jitter stable (hash slug)
      rayon  : bande définie par la dimension de la catégorie
               (social -> 108..140, ecologique -> 160..192, transversal -> 142..158)
               puis modulé par progress_percent (plus avancé = plus éloigné du centre)
  - Projets archivés rendus en POINTS CREUX (stroke only, sans fill)
  - Tooltip survol -> RouterLink cliquable vers la fiche projet
  - Légende droite réécrite avec les phrases exactes de la maquette
    ("L'anneau intérieur représente le plancher social, ..." etc.)
  - Liste des catégories par dimension avec compte de projets cliquables

Jeu d'essai (tests/seed-demo.sh) :
  - 10 actifs / 6 réalisés / 4 archivés (= 20 projets), réalistes Marseille
  - 10 membres (4 collectives/orgs, 4 personnes, 2 collectives + AMU)
  - 26 liens project_members, 27 étapes de roadmap (mix done/à faire)
  - 6 discussions, 8 ressources, 8 events d'activité antérieurs
  - Lit DB_* depuis .env, force --default-character-set=utf8mb4
  - TRUNCATE+INSERT idempotent (la table categories est préservée)
  - Hors /migrations/ pour ne PAS être appliqué automatiquement par apply.sh

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:17:47 +02:00
Arnaud 80f96efb56 fix(seed): documenter --default-character-set=utf8mb4 sous Windows
Le client mysql sous Windows + Git Bash utilise cp850 comme
`character_set_client` par défaut. Quand on applique 002_seed_categories.sql
avec un simple `mysql < ...`, le client réinterprète les bytes UTF-8 du fichier
via cp850 AVANT de les insérer : `é` (0xC3 0xA9) devient `├®` (E2949C C2AE)
stocké en clair dans la base, et apparaît tel quel dans l'API + le SPA.

Le `SET NAMES utf8mb4` côté serveur ne corrige pas ça parce que la conversion
est déjà faite côté client. Il faut le flag `--default-character-set=utf8mb4`
sur la ligne de commande mysql.

  - README.md : doc de la commande correcte + commande de vérif HEX
  - 002_seed_categories.sql : commentaire d'alerte + SET NAMES utf8mb4 (no-op
    côté connexion mais documente l'intention)

Côté prod (apply.sh + docker exec mariadb) le client interne utilise utf8mb4
par défaut, donc pas de souci là-bas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:51:13 +02:00
Arnaud 7fb22bcb29 feat(frontend): SPA Vue 3 + Vite + Tailwind v4 consommant l'API
Nouveau dossier /frontend avec un SPA complet qui couvre les écrans de la
maquette (cf. docs/Capture*.png) : public + admin token. Backend inchangé.

Stack
  - Vue 3.5 + Vite 8 + TypeScript 6 + Tailwind v4 (via @tailwindcss/vite)
  - Vue Router 4 en mode history, code-split par page (lazy import)
  - Aucune lib UI, aucun store (pas de Pinia) — état local + fetch direct
  - Bundle initial 50 kB gzip, total dist 240 kB

Pages
  - /                 redirige vers /donut
  - /dashboard        4 KPIs + mini donut + flux activité (via /api/stats + /api/activity)
  - /donut            viz SVG custom : 6 secteurs catégories, projets en points
                      colorés, anneaux plancher social / plafond écologique,
                      tooltip + click vers la fiche projet
  - /projects         grille de cartes paginée + filtres (catégorie, statut, recherche
                      avec debounce, query string comme source de vérité)
  - /projects/new     formulaire complet de création (token admin requis)
  - /projects/:slug   fiche : header méta + tabs Roadmap/Discussions/Ressources
                      + panneau droit Progression + Équipe, modal "Rejoindre"
  - /archives         projets status=archived
  - /categories       6 cartes coloured-top-border + count via /api/stats
  - /admin/login      saisie du token Bearer, validé via POST /api/members (vide)
  - 404               fallback

Structure
  - src/api/client.ts        : wrapper fetch typé + token Bearer auto + ApiError
  - src/composables/useAuth  : token localStorage + reactivité Vue
  - src/types/api.ts         : types miroirs des réponses PHP
  - src/utils/formatDate.ts  : ISO/MySQL/DATE -> JJ mois AAAA, relatif, etc.
  - src/components/          : Avatar, ProjectCard, StatusBadge, CategoryBadge,
                               ProgressBar, LoadingState, ErrorState, JoinProjectModal
  - src/layouts/AppLayout    : sidebar nav + main scrollable
  - src/components/AppSidebar: logo donut SVG, CTA, nav, statut admin en bas
  - src/pages/               : 10 pages ci-dessus

Intégration & déploiement
  - vite.config.ts proxy /api/* vers http://donut-gestprojet.test (Laragon)
  - caddy.conf prod : /api/* -> reverse_proxy PHP, le reste -> static dist/
    avec fallback /index.html (history mode), cache long sur /assets/*
  - DEPLOY.md mis à jour avec build local + rsync dist/ vers
    /srv/donut/web/infolab-front/dist
  - README.md mis à jour avec section "Frontend (SPA Vue 3)"

Build & types
  - npm run build : vue-tsc strict (TS 6 + erasableSyntaxOnly) -> OK
  - paths alias @/* configuré dans tsconfig.app.json

Vérifié localement
  - npm run dev sur :5173, proxy /api -> Apache Laragon -> PHP
  - Toutes les routes répondent 200 (curl http://localhost:5173/...)
  - Routes API consommées correctement (categories, stats, projects, ...)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:40:39 +02:00
Arnaud 3a32e93ab1 feat: initial API skeleton — routes, controllers, migrations, deploy docs
Implements the JSON API for Le Donut Infolab per INSTRUCTIONS.md.

Schema (migrations/001-002):
  - 8 tables: categories, members, projects, project_members, roadmap_steps,
    discussions, resources, activity_log
  - 6 categories seeded with colors / dimensions (social, ecologique, transversal)
  - FK contraintes + soft delete sur projects, CHECK sur progress_percent

Bare PHP framework (src/):
  - Front controller public/index.php + router :params + auth flag par route
  - PSR-4 autoload sans Composer (bootstrap.php)
  - Database PDO singleton + helpers fetchOne/All/insert/update/delete
  - Validator fluent -> 422 {error: validation_failed, fields: {...}}
  - Bearer token admin (hash_equals) sur POST/PATCH/DELETE
  - Slugger avec unicité (suffixe -2, -3...) via intl Transliterator
  - ActivityLogger best-effort sur activity_log
  - Erreurs uniformes : 400/401/404/405/409/422/500 + CORS configurable

10 contrôleurs (Health, Categories, Members, Projects, ProjectMembers,
Roadmap, Discussions, Resources, Activity, Stats). Transitions de statut
loggées (created_project, completed_step, joined, etc.).

Cible prod :
  - Dockerfile (php:8.3-apache + pdo_mysql + intl + opcache)
  - docker-compose.yml sur réseau donut-net, bind 127.0.0.1:8003
  - caddy.conf pour infolab.ledonut-marseille.org
  - migrations/apply.sh idempotent (table schema_migrations)
  - DEPLOY.md avec commandes SSH exactes

Tests :
  - tests/smoke.sh exerce 10 endpoints clés, body envoyé via stdin
    (--data-binary @-) pour gérer l UTF-8 sous Git Bash Windows.

Vérifié localement sous Laragon Apache + MySQL 8.4 : 10/10 OK + cas
négatifs (401, 404, 405, 409, 422) conformes au contrat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 19:55:28 +02:00
Arnaud 6679fa8b30 gitignore 2026-05-26 22:29:57 +02:00