Commit Graph

5 Commits

Author SHA1 Message Date
Arnaud dcf99f011a feat(admin): création de catégorie via dashboard
Nouvelle page /admin/categories/new, jumeau de l'écran d'édition :
  - Form complet : name, slug (auto), description, color_hex, icon,
    dimension, sort_order
  - Preview live identique à l'éditeur (top border + folder tinté + badge)
  - Slug auto-généré depuis le nom (normalisation NFD + lowercase + dashes)
    tant que l'utilisateur n'a pas touché manuellement au champ. Flag
    slugDirty mis à true sur premier @input du slug -> on arrête l'auto.
  - Validation color_hex /^#RRGGBB$/ côté client + normalisation upper-case
  - Body construit en omettant les champs vides -> l'API applique ses
    défauts (color_hex='#888888', dimension='transversal', sort_order=0)
  - Sur 201 : redirige vers /admin/categories/:slug/edit pour que l'admin
    voie tout de suite sa nouvelle catégorie et puisse ajuster
  - 422/409/401 gérés comme sur les autres écrans admin

UI
  - AdminCategoriesPage : CTA "Nouvelle catégorie" en haut à droite
  - Router : route nommée admin-category-new avec guard admin

Backend
  - Aucune nouvelle route : POST /api/categories existait déjà
  - Le contrôleur valide name (required, max 128), dimension enum, color
    hex format, slug auto-unique

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 13:45:25 +02:00
Arnaud dd7c3910f3 feat(admin): édition de catégorie via dashboard
Nouvelle page /admin/categories/:slug/edit, même pattern que members :
  - Preview live de la carte catégorie (top border colorée + folder icon
    tinté + badge "X projets" + label dimension)
  - Form complet : name, slug, description, color_hex, icon, dimension, sort_order
  - Color picker natif <input type="color"> + champ HEX texte synchronisés,
    validation /^#[0-9A-Fa-f]{6}$/ côté client
  - Diff PATCH (champs vides -> null), upper-case normalisé pour color_hex
  - Si le slug change -> router.replace vers la nouvelle URL
  - Bouton Supprimer en header désactivé si projects_count > 0 (FK RESTRICT)
  - 422 -> fieldErrors, 409 -> slug collision, 401 -> session expirée
  - projects_count récupéré direct depuis GET /api/categories/:slug
    (la réponse contient ce champ pour la route détail, cf. controller PHP)

UI
  - AdminCategoriesPage : nouveau lien "Éditer" par ligne du tableau
  - Router : route nommée admin-category-edit avec guard admin

Backend
  - Aucune nouvelle route : PATCH /api/categories/:slug existait déjà
  - Validation côté serveur : in dimension enum, colorHex format,
    integer sort_order >= 0, slug auto-unique avec suffixe -2/-3...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 13:43:03 +02:00
Arnaud cb37b461c5 feat(admin): édition de membre via dashboard
Nouvelle page /admin/members/:slug/edit avec :
  - Preview avatar live (initiales/URL mis à jour à la saisie)
  - Form complet : display_name, slug, initials, kind, bio, email, website, avatar
  - Envoie un diff au PATCH (champs vides -> null, pas chaîne vide)
  - Si le slug change, redirige automatiquement vers la nouvelle URL
  - Encart "Projets attachés" en lecture (avec lien vers chaque fiche)
    pour rappeler l'impact d'une suppression ou d'un changement de slug
  - Bouton Supprimer en header avec confirm qui mentionne le nb de projets
  - Banner vert "Modifications enregistrées" sur succès
  - Gestion erreurs : 422 -> fieldErrors, 401, 409 (slug collision), réseau

Backend
  - Aucune nouvelle route : PATCH /api/members/:slug existait déjà
  - Le contrôleur gère déjà slug auto-unique, initials, kind enum, etc.

UI
  - AdminMembersPage : nouveau lien "Éditer" par ligne, à côté de Supprimer
  - Router : route nommée admin-member-edit avec guard admin

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 13:39:33 +02:00
Arnaud adb2bd3fc1 feat: galerie d'images projet (upload disque) + dashboard /admin
==== Galerie d'images ====

Backend
  - Migration 003 : table project_images (FK CASCADE sur projects)
  - ImagesController : CRUD complet, deux modes de création
      * URL externe via JSON body
      * Upload disque via multipart/form-data avec :
          - validation MIME RÉELLE via finfo (jpeg/png/webp/gif)
          - taille max 5 Mo, vérifiée explicitement (au-delà du php.ini)
          - vérification UPLOAD_ERR_* avec messages clairs
          - stockage dans public/uploads/projects/:slug/<hash>.<ext>
          - dimensions image extraites via getimagesize (best-effort)
          - row DB avec mime_type / size_bytes / width / height
  - Request : ajout isMultipart(), formField(), file()
  - public/index.php : 3 routes (index public, store/update/destroy admin)

Proxy
  - caddy.conf : /uploads/* ajouté au reverse_proxy comme /api/*
  - vite.config.ts : pareil pour le dev local

Frontend
  - Type ProjectImage (api.ts) avec url + storage_path + dimensions
  - uploadFile() helper dans client.ts (FormData + Bearer)
  - ImageGallery.vue : grille thumbnails + lightbox plein-écran avec
    nav clavier (← → Esc), bouton supprimer par image en mode admin
  - ImageUploader.vue : drag-drop + file picker, multi-fichiers en série,
    validation taille côté client, légende commune optionnelle
  - ProjectDetailPage : nouvel onglet Images entre Roadmap et Discussions

==== Dashboard /admin ====

5 nouvelles pages sous /admin :
  - /admin                           : KPI + répartition catégorie + flux activité
  - /admin/projects                  : table CRUD (recherche + filtres status/categorie),
                                       actions Éditer / Archiver / Supprimer par ligne
  - /admin/projects/:slug/edit       : form complet d'édition (envoie un diff au PATCH)
  - /admin/members                   : annuaire avec recherche + Supprimer
  - /admin/categories                : table avec compte de projets ;
                                       Supprimer désactivé si la catégorie est utilisée
  - AdminSubNav.vue                  : 4 onglets en haut de chaque page admin

Garde d'auth :
  - beforeEnter sur les routes /admin/* (sauf /admin/login)
  - Redirige vers /admin/login?next=<url> si pas de token
  - Après login, retour automatique vers next= (sinon /admin)

Sidebar :
  - Nouvelle section "Administration" visible UNIQUEMENT quand le token
    est en localStorage, avec un lien vers le dashboard

Vérifié localement
  - Build vue-tsc : 91 modules, OK
  - Upload disque : fichier PNG stocké dans public/uploads/, accessible via /uploads/
  - GET /api/projects/nos-ecoles/images : 2 entrées renvoyées (1 URL + 1 disque)
  - Toutes les routes /admin/* répondent 200 via le proxy Vite

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:07:59 +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