Commit graph

61 commits

Author SHA1 Message Date
Claude Integration
90cc7a94af fix(rental): extract category labels en fichier neutre (importable client)
All checks were successful
CI / test (pull_request) Successful in 2m9s
2026-06-02 03:31:22 +00:00
Claude Integration
e2f3f070fa feat(rental): Sprint A — modèle Prisma + admin CRUD + seed 13 items
Some checks failed
CI / test (pull_request) Failing after 1m52s
2026-06-02 03:26:04 +00:00
Claude Integration
4901bb950e feat(forms): 4 critères opérationnels dans formulaires admin + espace hôte
All checks were successful
CI / test (pull_request) Successful in 2m16s
2026-06-02 02:46:34 +00:00
Claude Integration
dc2b07507f feat: 4 critères opérationnels (route/capacité/électricité/GSM) + presets profils + badges
All checks were successful
CI / test (pull_request) Successful in 2m23s
2026-06-02 02:26:02 +00:00
Claude Integration
d5732917e3 feat(reels): swipe horizontal animé avec suivi du doigt + snap
All checks were successful
CI / test (pull_request) Successful in 2m16s
2026-06-02 02:03:23 +00:00
Claude Integration
bc158ca144 feat(pwa): manifest + icônes 192/512/maskable + Apple touch + viewport theme-color
All checks were successful
CI / test (pull_request) Successful in 2m15s
2026-06-02 01:53:22 +00:00
Claude Integration
4fb7c948ad feat(cron): regenerate-variants task pour batch tous les Media existants
All checks were successful
CI / test (pull_request) Successful in 2m24s
2026-06-02 01:27:20 +00:00
Claude Integration
e2d3b6a686 feat: variantes responsives 320/800/1600 via sharp + srcset partout (Reels, cards, galerie, favoris)
All checks were successful
CI / test (pull_request) Successful in 2m21s
2026-06-02 01:05:25 +00:00
Claude Integration
701a1f02bd feat: Reels plein écran mobile + MediaUploader dans l'admin
All checks were successful
CI / test (pull_request) Successful in 2m19s
2026-06-02 00:52:57 +00:00
Claude Integration
2545a5e1a8 feat: « Au fil de l'eau » — Reels mobile + uploader pro + favoris
All checks were successful
CI / test (pull_request) Successful in 2m18s
2026-06-02 00:27:16 +00:00
Claude Integration
2914e5605a feat: BookingForm bascule sur Stripe Checkout quand STRIPE_SECRET_KEY est posée
All checks were successful
CI / test (pull_request) Successful in 2m10s
2026-06-01 23:35:30 +00:00
Claude Integration
71dd8c1dad feat: carte interactive du catalogue + refonte page À propos (2.2-2.6k caractères)
All checks were successful
CI / test (pull_request) Successful in 2m21s
2026-06-01 23:27:57 +00:00
Claude Integration
a373bd60ad feat(hardening): rate limit (signup/reset/bookings) + tâches cron + backup PostgreSQL nocturne
All checks were successful
CI / test (pull_request) Successful in 2m10s
2026-06-01 20:16:57 +00:00
Claude Integration
55c0244336 fix: rebrancher espace-hote/page.tsx sur le nouveau dashboard (oubli PR#59)
All checks were successful
CI / test (pull_request) Successful in 2m24s
2026-06-01 16:20:06 +00:00
Claude Integration
1e6acf29b9 feat: dashboard espace hôte (KPIs + résa pending + carbets + activité) + lightbox galerie
All checks were successful
CI / test (pull_request) Successful in 2m42s
2026-06-01 16:16:25 +00:00
Claude Integration
a58815ec9c fix: ajout effectif facettes priceMax + amenities dans SearchFilters (oubli PR#57)
All checks were successful
CI / test (pull_request) Successful in 2m10s
2026-06-01 10:21:03 +00:00
Claude Integration
a6df96db7e feat: reset password + page mon-compte (RGPD) + facettes recherche (prix max, équipements)
All checks were successful
CI / test (pull_request) Successful in 2m19s
2026-06-01 10:16:37 +00:00
Claude Integration
31aa7a4865 feat: calendrier visuel mensuel + carte Leaflet sur fiche carbet
All checks were successful
CI / test (pull_request) Successful in 2m0s
2026-06-01 05:27:33 +00:00
Claude Integration
3bc52b2b60 feat: global SiteHeader avec user menu (login/inscription, Mes réservations, Espace hôte, Admin)
All checks were successful
CI / test (pull_request) Successful in 2m9s
2026-06-01 04:29:52 +00:00
Claude Integration
6eed6bffc8 fix(ci): 5 erreurs ESLint Next 16 (Date.now impure, <a> vers /admin, setState dans effect)
All checks were successful
CI / test (pull_request) Successful in 2m0s
2026-06-01 04:18:49 +00:00
Claude Integration
14fd9a5940 feat(p2): vitest + 27 tests + /api/health enrichi + /api/metrics + workflow CI
Some checks failed
CI / test (pull_request) Failing after 2m13s
2026-06-01 02:27:14 +00:00
Claude Integration
b59b8a0af2 feat(p1): calendrier dispo + emails Resend + amount calculé + best-effort welcome/confirmation/refund 2026-06-01 02:20:38 +00:00
Claude Integration
e79b6dd141 feat(p0): prix/nuit + booking form public + /inscription + /reservations/[id] 2026-06-01 01:34:00 +00:00
Claude Integration
a9fcd18022 feat(admin): /admin/home — éditeur des textes de la page d'accueil (FR+EN, override DB) 2026-06-01 01:10:49 +00:00
Claude Integration
1f8dd90979 fix(admin): PATCH content-pages respecte ?lang= (sinon écrasait FR) 2026-06-01 00:51:19 +00:00
Claude Integration
a5ae692cf4 fix(admin): content-pages éditait FR quel que soit le lien cliqué — support multilang complet 2026-06-01 00:49:31 +00:00
Claude Integration
4e6867b365 feat(admin): Sprint 6 — /admin/media gallery + theme write-through 2026-06-01 00:44:39 +00:00
Claude Integration
79ddcd23f5 feat(admin): Sprint 5 — Audit log + Settings (gouvernance) 2026-06-01 00:13:49 +00:00
Claude Integration
99f3bbdc71 feat(admin): Sprint 4 — Organisations CE + Prestataires pirogue (CRUD) 2026-05-31 21:36:22 +00:00
Claude Integration
d9ee072744 feat(admin): Sprint 3 — Réservations, Utilisateurs, Avis 2026-05-31 21:20:46 +00:00
Claude Integration
fea55a7ddb chore(admin): Prisma.CarbetWhereInput type + cleanup options orphelines 2026-05-31 21:08:35 +00:00
Claude Integration
fc01144e0e chore(admin): split options enum dans fichier neutre
Le client component CarbetForm importait des options depuis lib/admin/carbets
qui contient "server-only" → erreur build turbopack. Sortie des options dans
src/lib/admin/carbet-options.ts sans server-only.
2026-05-31 21:06:47 +00:00
Claude Integration
9aa0771001 feat(admin): CRUD complet carbets + gestion médias (Sprint 2)
Server actions (src/app/admin/carbets/actions.ts) avec validation Zod :
- createCarbetAction → INSERT + audit + redirect /admin/carbets/[id]
- updateCarbetAction → UPDATE + revalidate page publique
- updateCarbetStatusAction → DRAFT/PUBLISHED/ARCHIVED
- deleteCarbetAction → soft archive (bookings/reviews FK Restrict)
- addMediaAction(carbetId, fd) → INSERT Media + sortOrder
- removeMediaAction, reorderMediaAction (transactionnel up/down)

Helpers (src/lib/admin/carbets.ts) :
- listCarbetsAdmin avec filtres (q/river/status/accessType)
- listDistinctRivers, listOwners, listPirogueProviders
- getCarbetForEdit (include owner, provider, media, _count bookings/reviews)
- Options enum pour les selects (ACCESS_TYPE, TRANSPORT_MODE, STATUS)

Pages :
- /admin/carbets : liste tableau dense avec recherche/filtres GET, status badge,
  liens vers édition, count médias/résas
- /admin/carbets/new : page création avec CarbetForm
- /admin/carbets/[id] : header titre+badge+actions, MediaManager, CarbetForm
  d'édition. Lien public si PUBLISHED.

Composants admin réutilisables :
- StatusBadge (DRAFT/PUBLISHED/ARCHIVED + statuts Booking)
- FormField + inputCls/selectCls/textareaCls
- CarbetForm (client, 5 sections : identité, localisation, accès, séjour,
  publication) avec useTransition + erreur + succès inline
- MediaManager (client, liste + reorder ↑↓ + suppression + ajout par URL)
- StatusActions (client, publier/dépublier/archiver/réactiver avec confirm)

API :
- GET /api/admin/carbets/[id]/media pour refresh client après mutation

Audit léger en log console (JSON structuré) — Sprint 5 ajoutera la table.
2026-05-31 19:51:33 +00:00
Claude Integration
bcb93c6b29 feat(admin): shell admin + dashboard KPI + recherche ⌘K (Sprint 1)
Layout admin :
- src/app/admin/layout.tsx : route protégée requireRole(ADMIN), sidebar + topbar + breadcrumbs, data-admin sur racine pour theme sobre indépendant du theme public
- Sidebar : 12 sections groupées (Vue d'ensemble, Catalogue, Activité, Membres, Contenu, Système), highlight de la route courante
- TopBar : prompt ⌘K, lien vers site public, email admin
- Breadcrumbs : auto depuis pathname
- CommandPalette : ⌘K / Ctrl K, navigation ↑↓ + Entrée, recherche live debounced 150ms

Dashboard :
- 7 KPI cards avec tone neutral/ok/warn/info (réservations semaine, confirmées 30j, revenus reversés, occupation, nouveaux users, carbets publiés, avis à modérer)
- Section raccourcis fréquents

Theme admin :
- globals.css : [data-admin] override le background+font, neutralise les borders sépia/papier teinté du theme aquarelle, garantit lisibilité permanente

Recherche globale :
- lib/admin/search.ts : query parallèle sur Carbet, User, Booking, ContentPage, PirogueProvider (5 résultats par catégorie, LIKE insensitive)
- api/admin/search?q=… route handler avec requireRole

KPI :
- lib/admin/kpis.ts : 7 métriques live (cache 0), Promise.all, helper formatEur

Pas de dépendance externe ajoutée (cmdk, shadcn) — composants custom Tailwind pour rester léger.
2026-05-31 18:21:50 +00:00
Claude Integration
47258bf1be feat(plugin): image-gallery-aquarelle-seed hook + upload script
Hook onEnable du plugin image-gallery-aquarelle-seed :
- Pour chaque carbet démo, crée une entrée Media qui pointe vers son aquarelle
  hébergée dans MinIO sous karbe-medias/seed/aquarelle/.
- s3Key préfixé seed/aquarelle/ pour faciliter le détachement au disable.
- Idempotent (skip si Media existe déjà).

Hook onDisable :
- Supprime tous les Media avec s3Key startsWith seed/aquarelle/.
- Les fichiers MinIO restent (pas de coût de redéploiement).

Script scripts/upload-aquarelles.sh :
- Upload depuis /tmp/karbe-aquarelles/*.{jpg,png} vers le bucket karbe-medias.
- Applique la policy public-download au bucket pour que media.karbe.cosmolan.fr
  serve les fichiers sans auth.
- À exécuter une fois après génération des illustrations.
2026-05-31 12:20:35 +00:00
Claude Integration
c69c355f90 feat(plugin): theme-aquarelle + hero variant (Phase 2.4 partie 1/2)
Registry : ajoute 2 plugins :
- theme-aquarelle (carnet naturaliste XIXᵉ, mutual exclusion avec theme-guyane)
- image-gallery-aquarelle-seed (14 aquarelles → MinIO + Media carbets démo)

Hooks :
- theme-guyane et theme-aquarelle se désactivent mutuellement au toggle ON
  via disableOtherTheme()

CSS (globals.css) :
- body[data-theme=aquarelle] : background papier teinté #faf5e9 + texture
  grain papier inline SVG + radial gradients ocres/canopy délavés
- Surcharges automatiques des borders zinc/gray vers sépia délavé

Layout :
- PT_Serif (au lieu de Cormorant) en theme aquarelle, plus dense et encrée
- data-theme = aquarelle prioritaire sur guyane si les deux sont enabled
  (défensif — le hook garantit normalement la mutual exclusion)

Hero :
- 2 versions dans le composant : guyane (existant, SVG CarbetRiver) et
  aquarelle (image MinIO 01-hero-fleuve-maroni.jpg en fond, voile crème,
  texte sépia, CTAs carrés sans rounded, hairlines, ornement de planche)
- Branchement via getActiveTheme()
- aquarelleUrl() helper qui construit l'URL MinIO publique

Partie 2/2 (PR ultérieure) : upload des 14 images dans MinIO + hook
image-gallery-aquarelle-seed + variantes aquarelle des autres composants
(CarbetCard, ExperiencesSection, HowItWorksSection, CESection, Footer).
2026-05-31 12:15:07 +00:00
Claude Integration
8196a1a3f9 chore(admin): adapter findUnique/update à la PK composite (slug, lang)
Admin édite la version FR par défaut. Édition multi-langues = future feature.
2026-05-31 11:48:14 +00:00
Claude Integration
87c3e7a581 feat: ContentPage bilingue (PK composite slug+lang) + seed pages EN
Migration : ContentPage.id devient PK composite (slug, lang) au lieu de slug
seul, pour stocker une version FR et une version EN du même slug. Index sur
slug seul pour les lookups.

Schema Prisma : @@id([slug, lang]).

Helpers :
- getContentPage(slug, lang) avec fallback FR si la version dans la langue
  demandée n'existe pas
- listContentPages(category?, lang?) accepte un filtre lang
- upsertContentPage : utilise le composite key

Pages publiques (a-propos, faq, comment-ca-marche, pour-comites-entreprise,
devenir-loueur, cgv, mentions-legales, politique-de-confidentialite) :
ajoutent un appel à getLocale() et le passent à getContentPage.

Seeds :
- src/lib/plugins/seeds/content-pages-en.ts : 8 pages traduites en anglais
- hook onEnable du plugin i18n-fr-en : seed EN pages au toggle on. Désactiver
  i18n n'efface pas les EN pages (elles dorment, fallback FR reprend).

Résultat : quand l'utilisateur switche vers EN, /a-propos, /faq, /cgv, etc.
basculent en anglais. Le contenu hors-DB (composants UI) bascule déjà via les
dictionnaires de la PR i18n-fr-en initiale.
2026-05-31 11:45:47 +00:00
Claude Integration
cf9da94bb5 feat(plugin): i18n FR + EN (Phase 4.2)
Infrastructure i18n légère, sans deps externe :

- lib/i18n/types.ts : LOCALES, DEFAULT_LOCALE, cookie name
- lib/i18n/server.ts : getLocale (cookie > Accept-Language > FR),
  t(key) async server-side, dict(locale)
- lib/i18n/client.tsx : LocaleProvider + useLocale + useT
- messages/fr.json + messages/en.json : ~50 clés pour landing + header + footer
- LocaleSwitcher component (cookie + router.refresh)

Plugin gated :
- Quand i18n-fr-en désactivé, getLocale() force FR. Le switcher ne s'affiche
  pas dans le hero. Pas d'impact sur le rendu existant.
- Quand activé, switcher visible coin haut-droit du hero. Les composants
  landing/header/footer rendent en FR ou EN selon le cookie utilisateur.

Composants i18n-isés :
- HeroSection (eyebrow, titre, CTA)
- ExperiencesSection (route/fleuve vs expédition, tous les bullets)
- HowItWorksSection (3 étapes)
- CESection (KPIs + body + CTA)
- TestimonialsSection (eyebrow + titre, citations restent en VO)
- Footer (taglines, colonnes)
- SeasonBanner (3 saisons + messages)
- AccessTypeBadge (labels + tooltips)

Pour les ContentPage, le champ lang existait déjà. Une suite (PR ultérieure)
ajoutera le filtre lang dans getContentPage + seed pages EN.
2026-05-31 11:38:39 +00:00
Claude Integration
a174f99eba feat(plugin): pirogue-providers (Phase 3.3)
Modèle PirogueProvider (id, name, contacts, fleuves, tarif, description)
+ enum TransportMode (OWNER_PROVIDES, SELF_ARRANGE, PARTNER_PROVIDER) sur Carbet
+ relation Carbet → PirogueProvider (nullable, ondelete:SetNull)

Composants :
- PirogueTransportBlock (server, gated par plugin) sur fiche carbet :
  affiche le mode + provider partenaire avec contacts/tarif/description
- Page publique /partenaires-pirogue : liste des partenaires actifs

Seed onEnable :
- 3 partenaires démo (Pirogues du Maroni, Approuague Aventures, Oyapock Frontière)
  avec tarifs estimatifs et fleuves desservis réels
- Attribution aux 6 carbets démo :
  · Awara (Maroni), Maripa (Approuague), Paripou (Oyapock) → PARTNER_PROVIDER
  · Wapa (Comté), Mahury CE → OWNER_PROVIDES
  · Kourou Couleuvre → SELF_ARRANGE

onDisable désactive les partenaires démo et détache les carbets démo.
2026-05-31 11:29:29 +00:00
Claude Integration
68f37f554f feat(plugins): content-pages + legal-pages (Phase 4.1 + 4.3)
Plugin content-pages :
- Modèle Prisma ContentPage (slug PK, title, body markdown, category, published)
- lib/content-pages.ts : helpers upsert/get/list/unpublish
- lib/markdown.ts : mini-renderer markdown server-side sans deps externe
  (h1-h3, paragraphes, gras/italique, liens, listes ul/ol, hr, blockquote,
  échappement HTML)
- ContentPageRenderer server component, applique le theme Guyane (font-serif)
- 5 pages seedées : /a-propos, /faq, /comment-ca-marche,
  /pour-comites-entreprise, /devenir-loueur
- Routes publiques + force-dynamic + guard requirePluginOr404

Plugin legal-pages :
- Réutilise le même modèle ContentPage, catégorie 'legal'
- 3 pages seedées : /cgv, /mentions-legales, /politique-de-confidentialite
  (contenu de base, à valider par avocat avant prod réelle)

Admin :
- /admin/content-pages : table par catégorie, statut publié/dépublié
- /admin/content-pages/[slug] : éditeur markdown + toggle publié
- PATCH /api/admin/content-pages/[slug]

Hooks plugin :
- onEnable seed + republish toutes les pages
- onDisable dépublie toute la catégorie sans la supprimer (preserve les edits)
2026-05-31 10:12:13 +00:00
Claude Integration
a7761ca323 chore: wire StayConstraints + minStayNights dans carbet-card + search (oubli PR#30) 2026-05-31 08:59:46 +00:00
Claude Integration
be2391998d feat(plugins): seasonality + min-stay (Phase 3.2 + 3.4)
Plugin seasonality :
- Migration : Carbet.seasonalConstraints JSONB nullable
- lib/seasonality.ts : enum Season (DRY|LOW_WATER|WET), currentSeason() helper
  Guyane (juil-sept sèche, oct-nov étiage, déc-juin pluies), parseSeasonalConstraints,
  isCurrentlyOpen, SEASON_META (label/emoji/tone)
- Composant <SeasonBanner /> server, gated par plugin, ajouté dans layout
  au-dessus de tout le contenu — bandeau couleur+emoji+message contextuel

Plugin min-stay :
- Migration : Carbet.minStayNights, maxStayNights, minCapacity nullable
- Composant <StayConstraints /> client, gated par plugin — pill text
  '2 nuits minimum', '2-7 nuits', 'groupe 4+ recommandé'
- Carbet card et fiche enrichies avec les contraintes

Tous deux désactivables : sans le toggle, comportement legacy inchangé.
2026-05-31 08:50:26 +00:00
Claude Integration
5e59202505 feat(plugins): access-type + demo-carbets-seed (Phase 3.1 + 2.5)
Plugin access-type :
- Migration : enum AccessType (ROAD_AND_RIVER, RIVER_ONLY), champ accessType
  sur Carbet avec default ROAD_AND_RIVER, roadAccessNote optionnel,
  pirogueDurationMin rendu nullable + index sur accessType
- Schema Prisma mis à jour
- Composant <AccessTypeBadge> client, gated par le plugin
- Carbet card et fiche enrichies : badge + texte adapté (Pirogue vs Route+pirogue
  vs Route directe), section Accès enrichie avec roadAccessNote
- formatPirogueDuration accepte null

Plugin demo-carbets-seed :
- Hook onEnable : 3 propriétaires demo (Yann/Émilie/CE Hôpital) + 6 carbets
  variés (Maroni, Approuague, Comté, Oyapock, Mahury, Kourou) avec mix
  3 RIVER_ONLY + 3 ROAD_AND_RIVER, GPS plausibles, descriptions naturelles
- Hook onDisable : archive (status=ARCHIVED) les carbets demo via slug prefix
- Toutes les fixtures idempotentes (upsert via slug + email)
2026-05-31 02:56:25 +00:00
Claude Integration
049d0bb423 chore(layout): force-dynamic pour refléter l'état des plugins en live
Sans ça, le layout est rendu statiquement au build et ne re-fetch jamais
l'état des plugins, donc les toggles depuis /admin/plugins ne prennent
jamais effet sur la home jusqu'à un nouveau build.
2026-05-30 23:36:42 +00:00
Claude Integration
b1c2877e43 chore(sitemap): force dynamic + try/catch DB
Évite que le build échoue quand la DB n'est pas joignable au prerender.
2026-05-30 23:32:07 +00:00
Claude Integration
e433ebc439 chore(plugins): cast config en Prisma.InputJsonValue
Le type Record<string,unknown> ne satisfait pas le narrowing JSON Prisma.
Cast explicite pour faire passer le build TS.
2026-05-30 23:30:12 +00:00
Claude Integration
d19701e275 feat(plugins-visuels): theme-guyane + landing-hero + landing-sections
Phase 2 visuals — la page d'accueil prend vie via 3 plugins activables :

- theme-guyane : palette tropicale (vert canopée, eau Maroni, ocre latérite,
  bois karbé, blanc cassé), tokens CSS, typographie display Cormorant Garamond,
  gradient ambient discret. Activé via body[data-theme=guyane].

- landing-hero : section plein écran avec illustration vectorielle SVG (carbet
  sur pilotis au crépuscule + fleuve + jungle), claim 'Le karbé qui dort vous
  attend', double CTA Découvrir / Proposer. Fallback = hero minimaliste actuel.

- landing-sections : 5 sections en cascade — 2 expériences (route+fleuve vs
  expédition fleuve), Comment ça marche (3 étapes), CE (registre coop sans
  commission), Témoignages (3 stubs), Footer riche avec navigation.

Illustrations 100% SVG inline (pas de dépendance image externe). Quand le
plugin image-gallery-seed sera activé (Phase 2.4), les photos remplaceront
progressivement les SVG. Aucune coupure sur le rendu actuel : tous les plugins
visuels sont disabled par défaut, le site garde son look minimaliste tant que
l'admin ne les a pas activés depuis /admin/plugins.
2026-05-30 23:19:24 +00:00
Claude Integration
62cc464738 feat(plugins): foundation système Plugin Karbé
- Modèle Prisma Plugin (key, name, description, category, version, enabled,
  config JSONB, migrationsApplied, timestamps) + migration SQL
- PluginRegistry (src/lib/plugins/registry.ts) avec 12 plugins déclarés :
  visuels (theme-guyane, landing-hero, landing-sections, image-gallery-seed,
  demo-carbets-seed), métier (access-type, seasonality, pirogue-providers,
  min-stay), contenus (content-pages, legal-pages), i18n (i18n-fr-en)
- Server helpers (server.ts) : sync, isEnabled, getEnabledKeys, toggle avec
  hooks onEnable/onDisable, updateConfig, cache 5s
- Client bridge (client.tsx) : PluginProvider + useIsPluginEnabled
- Composant <IfPluginEnabled plugin=... fallback=...>
- Guard requirePluginOr404 pour pages et routes
- Page admin /admin/plugins avec table toggle par catégorie + édition config
- Route PATCH /api/admin/plugins/[key] + GET
- Layout async qui sync registry + passe enabledKeys au PluginProvider

Tous plugins en enabled=false par défaut, activation pilotée depuis l'admin.
2026-05-30 22:17:10 +00:00
c9be24a969 SYS-18: add production deployment stack for karbe.cosmolan.fr (Stripe test)
- enable Next.js standalone output and add Docker/Caddy production stack
- add production env template and deployment runbook
- add healthcheck endpoint for container supervision
- fix existing lint/type blockers discovered during validation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-30 18:01:56 +00:00