diff --git a/.planning/STATE.md b/.planning/STATE.md index 1ed941c..82f46ec 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,12 +2,12 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone -status: Ready to plan -last_updated: "2026-04-10T17:20:26.905Z" +status: Ready to execute +last_updated: "2026-04-11T01:59:47.507Z" progress: total_phases: 4 completed_phases: 0 - total_plans: 2 + total_plans: 5 completed_plans: 0 percent: 0 --- diff --git a/.planning/config.json b/.planning/config.json index c3650de..f0c8206 100644 --- a/.planning/config.json +++ b/.planning/config.json @@ -27,7 +27,8 @@ "discuss_mode": "discuss", "skip_discuss": false, "code_review": true, - "code_review_depth": "standard" + "code_review_depth": "standard", + "_auto_chain_active": false }, "hooks": { "context_warnings": true diff --git a/.planning/phases/02-content/02-RESEARCH.md b/.planning/phases/02-content/02-RESEARCH.md new file mode 100644 index 0000000..63561d7 --- /dev/null +++ b/.planning/phases/02-content/02-RESEARCH.md @@ -0,0 +1,489 @@ +# Phase 2: Content - Research + +**Researched:** 2026-04-10 +**Domain:** Nuxt 4 page authoring, i18n content, Nuxt UI v3 pricing grids, testimonials carousel +**Confidence:** HIGH + +--- + + +## User Constraints (from CONTEXT.md) + +### Locked Decisions + +- **D-01:** H1 = "Hytale Plugin Developer" — positionnement niche direct, pas de titre generique +- **D-02:** CTAs = Discord (profil personnel pour l'instant) + Contact +- **D-03:** Badge "Available for projects" → passer en i18n (FR: "Disponible pour vos projets") +- **D-04:** Sous-titre angle benefice client : "Des plugins performants et sur-mesure pour votre serveur Hytale" +- **D-05:** Layout hero = garder le grid 2 colonnes (texte gauche, placeholder/illustration droite) +- **D-06:** Pas d'image pour l'instant cote droit — placeholder en attendant des assets Hytale +- **D-07:** Lien Discord = profil personnel existant dans site.ts (provisoire) +- **D-08:** Page /hytale avec 4 sections : hero dedie Hytale, services/expertise, grille tarifaire, temoignages +- **D-09:** 4-5 tiers de pricing : plugin simple / complexe / sur-mesure / maintenance / web +- **D-10:** Prix en mode mix : prix fixes pour simple/maintenance, sur devis pour complexe/sur-mesure +- **D-11:** CTA de chaque tier = "Demander un devis" → redirige vers /contact +- **D-12:** Pas de demos — Hytale est sorti (janvier 2026), mais pas d'assets a montrer +- **D-13:** Garder les 5 temoignages Fiverr existants tels quels +- **D-14:** Pas de nouveaux temoignages Hytale a ajouter pour l'instant +- **D-15:** Homepage : 2-3 featured en carousel. Page /hytale : tous les 5 en carousel avec plus de details +- **D-16:** Corriger totalReviews : le vrai nombre est 5, pas 10 ni 50 +- **D-17:** Format d'affichage = carousel/slider sur les deux pages +- **D-18:** Positionnement Hytale-first, web secondaire +- **D-19:** Toutes les pages existantes restent (about, projects, fiverr, contact), on ajoute /hytale +- **D-20:** siteConfig.title = "Killian' DAL-CIN - Hytale Plugin Developer | Freelance" +- **D-21:** jobTitle dans site.ts = "Hytale Plugin Developer" (SEO-05) + +### Claude's Discretion +- Stats/chiffres cles dans le hero : Claude decide si pertinent pour la conversion + +### Deferred Ideas (OUT OF SCOPE) +None — discussion stayed within phase scope + + +--- + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|------------------| +| CONT-01 | Refonte Hero accueil — "Hytale Plugin Developer" en H1, CTA Discord/contact, bilingue | HeroSection.vue existant a adapter; i18n keys home.title, home.subtitle, home.cta.* a remplacer | +| CONT-02 | Page Hytale dediee `/hytale` — services plugin dev, tiers pricing, demos placeholders, maintenance, bilingue | Nouvelle page `app/pages/hytale.vue` + sections composees; pattern identique a index.vue | +| CONT-03 | Grille tarifaire — plugin simple/complexe/sur-mesure/maintenance/web avec prix visibles | UCard Nuxt UI v3 en grid; data statique dans app/data/; i18n keys hytale.pricing.* | +| CONT-04 | Temoignages — section featured + stats sur homepage et page Hytale (5 avis Fiverr existants) | TestimonialsSection.vue reutilisable; prop `featured` deja presente sur Testimonial type; corriger totalReviews: 5 | +| SEO-05 | jobTitle corrige — "Hytale Plugin Developer" dans site.ts et JSON-LD | site.ts: title + ajouter jobTitle field; index.vue JSON-LD Person.jobTitle a mettre a jour | + + +--- + +## Summary + +Phase 2 est une phase de contenu pur dans un codebase Nuxt 4 SSR deja fonctionnel. L'infrastructure (routing, i18n, Nuxt UI v3, layouts) existe et fonctionne. Le travail consiste a adapter des composants existants et creer une nouvelle page `/hytale` en suivant des patterns deja etablis dans le projet. + +Le principal risque est la coherence i18n : chaque string visible doit avoir une cle dans `fr.json` ET `en.json`. Le codebase a deja des strings hardcodees (badge "Available for projects" dans HeroSection.vue ligne 31, floating cards "50+ projects" et "5.0 rating" lignes 148-153) qui doivent passer en i18n dans cette phase. Les donnees incoherentes (`totalReviews: 10`, `reviewCount: '10'` dans site.ts) doivent etre corrigees a 5. + +**Primary recommendation:** Adapter HeroSection existant (pas recreer), creer hytale.vue en composant sections reutilisables, tout le nouveau contenu passe par i18n avant d'atterrir dans le template. + +--- + +## Standard Stack + +### Core (deja installe dans le projet) +| Library | Version | Purpose | Why Standard | +|---------|---------|---------|--------------| +| Nuxt 4 | installed | SSR framework, auto-routing `app/pages/` | Stack decide en phase 1 | +| Nuxt UI v3 | installed | UCard, UButton, UBadge pour pricing grid | Constraint CLAUDE.md: Nuxt UI v3 en priorite | +| @nuxtjs/i18n | installed | useI18n(), useLocalePath(), fr/en JSON | Pattern etabli dans tout le projet | +| Tailwind v4 | installed | Classes utilitaires CSS | Stack decide | + +### Patterns etablis dans le projet [VERIFIED: codebase] +- Pages: `app/pages/nom.vue` → route `/nom` automatique (Nuxt auto-routing) +- Sections: `app/components/sections/NomSection.vue` composees dans les pages +- i18n: `useI18n()` dans ` + + +``` + +### Pattern 2: Pricing grid avec Nuxt UI v3 UCard +**What:** Grid de cards avec UCard pour chaque tier de pricing +**When to use:** Section HytalePricingSection.vue + +```typescript +// Source: [ASSUMED] Nuxt UI v3 UCard pattern — a verifier contre docs officielles si besoin +// Pattern repris du design Fiverr existant (app/pages/fiverr.vue) + +``` + +### Pattern 3: TestimonialsSection avec prop featured +**What:** La section temoignages existante supporte deja `featured?: boolean` sur le type `Testimonial`. Pour la homepage, filtrer sur `featured: true` (2-3 temoignages). Pour /hytale, afficher tous les 5. +**When to use:** Homepage = `testimonials.filter(t => t.featured)`, /hytale = `testimonials` complet + +```typescript +// Source: app/components/sections/TestimonialsSection.vue (existant) +// app/data/testimonials.ts: seul unqlf_ a featured: true actuellement +// → marquer 2-3 comme featured pour la homepage + +// Prop a ajouter a TestimonialsSection.vue: +const props = defineProps<{ + featured?: boolean +}>() +const displayed = computed(() => + props.featured ? testimonials.filter(t => t.featured) : testimonials +) +``` + +### Pattern 4: i18n — procedure d'ajout de cles +**What:** Toute string visible doit etre dans les deux fichiers JSON +**When to use:** A chaque nouveau texte ajoute + +``` +Procedure: +1. Definir la cle dans fr.json avec la valeur FR +2. Ajouter la meme cle dans en.json avec la valeur EN +3. Utiliser t('cle') dans le template — jamais de string literale visible +``` + +### Anti-Patterns to Avoid +- **String hardcodee dans le template:** `HeroSection.vue` ligne 31 a "Available for projects" en dur → doit devenir `{{ t('home.badge.available') }}` +- **Floating cards hardcodees:** Lignes 148-153 de HeroSection.vue ont "50+ projects" et "5.0 rating" en dur → i18n ou supprimer +- **Copier-coller de sections entières:** Adapter `TestimonialsSection` via prop plutot que dupliquer +- **Donnees incoherentes:** `totalReviews: 10` dans testimonials.ts ET `reviewCount: '10'` dans site.ts → les deux a corriger a 5 + +--- + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| Pricing cards | Custom card HTML | `UCard` Nuxt UI v3 | Deja installe, coherent avec le design system | +| Boutons CTA | `` custom | `UButton` avec `:to` | Routing i18n automatique via `localePath()` | +| Navigation vers /hytale | Modifier le HTML du header | Ajouter entree dans `navLinks` computed de AppHeader.vue | Pattern etabli, une seule ligne a ajouter | +| Carousel/slider JS custom | Implementer swipe events | `overflow-x-auto snap-x` CSS (deja dans TestimonialsSection) | Le composant existant utilise deja ce pattern CSS | + +**Key insight:** 80% de cette phase est de la configuration de donnees et d'i18n, pas du nouveau code UI. + +--- + +## Common Pitfalls + +### Pitfall 1: Oublier la cle EN quand on ajoute une cle FR +**What goes wrong:** La page `/en/hytale` affiche la cle brute (ex: "hytale.pricing.cta") au lieu du texte +**Why it happens:** fr.json mis a jour, en.json oublie +**How to avoid:** Toujours editer les deux fichiers en meme temps +**Warning signs:** `curl localhost:3000/en/hytale | grep "hytale\."` retourne des resultats + +### Pitfall 2: jobTitle manquant dans SiteConfig type +**What goes wrong:** TypeScript strict mode refuse `siteConfig.jobTitle = '...'` si le champ n'est pas dans l'interface +**Why it happens:** `SiteConfig` dans `shared/types/index.ts` n'a pas de champ `jobTitle` actuellement +**How to avoid:** Ajouter `jobTitle?: string` a l'interface avant de l'utiliser dans site.ts +**Warning signs:** Erreur `vue-tsc` au build + +### Pitfall 3: Navigation /hytale absente du header mobile +**What goes wrong:** Le lien /hytale est visible sur desktop mais pas dans le menu mobile +**Why it happens:** `navLinks` dans AppHeader.vue alimente a la fois le nav desktop et le drawer mobile — une seule entree suffit si le composant est bien structure +**How to avoid:** Verifier que le drawer mobile utilise bien la meme `navLinks` computed (c'est le cas dans le pattern actuel) + +### Pitfall 4: TestimonialsSection importe directement les donnees (pas de prop) +**What goes wrong:** Pour filtrer les featured sur la homepage, il faut refactorer le composant +**Why it happens:** `TestimonialsSection.vue` importe `testimonials` directement (ligne 2 du composant) +**How to avoid:** Ajouter une prop optionnelle `featured?: boolean` et filtrer le tableau en interne — ca ne casse pas l'usage existant sur fiverr.vue et contact.vue s'ils l'utilisent + +### Pitfall 5: reviewCount incoherent entre site.ts et testimonials.ts +**What goes wrong:** `aggregateRating.reviewCount: '10'` dans site.ts et `totalReviews: 10` dans testimonials.ts alors que la decision D-16 dit 5 +**Why it happens:** FIX-04 du REQUIREMENTS.md cible cette incohererence +**How to avoid:** Les deux doivent etre corriges a 5 dans la meme tache + +--- + +## Code Examples + +### Mise a jour HeroSection.vue — badge i18n +```typescript +// AVANT (hardcode, ligne 31): +Available for projects + +// APRES (i18n): +{{ t('home.badge.available') }} + +// fr.json: "home": { "badge": { "available": "Disponible pour vos projets" } } +// en.json: "home": { "badge": { "available": "Available for projects" } } +``` + +### Mise a jour HeroSection.vue — H1 Hytale +```typescript +// i18n keys a remplacer dans fr.json: +// "home": { +// "title": "Hytale Plugin Developer", ← H1 (D-01) +// "subtitle": "Des plugins performants et sur-mesure pour votre serveur Hytale", ← D-04 +// "cta": { +// "viewProjects": "Voir mes projets", +// "discord": "Rejoindre sur Discord", ← D-02 +// "contactMe": "Devis Gratuit Sous 24h" +// } +// } +``` + +### Structure data pricing.ts a creer +```typescript +// app/data/pricing.ts +import type { PricingTier } from '~~/shared/types' + +export const hytalepricing: PricingTier[] = [ + { id: 'simple', priceFixed: '150€', featured: false }, + { id: 'complex', priceFixed: null, priceLabel: 'Sur devis', featured: true }, + { id: 'custom', priceFixed: null, priceLabel: 'Sur devis', featured: false }, + { id: 'maintenance', priceFixed: '50€/mo', featured: false }, + { id: 'web', priceFixed: '300€', featured: false }, +] + +// Interface a ajouter dans shared/types/index.ts: +export interface PricingTier { + id: string + priceFixed: string | null + priceLabel?: string + featured?: boolean +} +``` + +### Correction site.ts +```typescript +// MODIFIER dans app/data/site.ts: +export const siteConfig: SiteConfig = { + title: "Killian' DAL-CIN - Hytale Plugin Developer | Freelance", // D-20 + jobTitle: 'Hytale Plugin Developer', // D-21 / SEO-05 + // ... + seo: { + organization: { + aggregateRating: { + ratingValue: '5', + reviewCount: '5', // D-16: correction de 10 → 5 + }, + }, + }, +} +``` + +### Correction testimonials.ts +```typescript +// MODIFIER totalReviews: +export const testimonialsStats: TestimonialsStats = { + totalReviews: 5, // D-16: correction de 10 → 5 + averageRating: 5.0, + projectsCompleted: 25, // conserver (plausible) +} + +// Marquer 2-3 comme featured pour la homepage: +{ name: 'unqlf_', featured: true, ... }, // deja featured +{ name: 'colo263', featured: true, ... }, // a ajouter +{ name: 'cobra2', featured: true, ... }, // a ajouter (3 max) +``` + +### Ajout lien /hytale dans AppHeader.vue +```typescript +// MODIFIER navLinks dans app/components/layout/AppHeader.vue: +const navLinks = computed(() => [ + { key: 'home', path: '/' }, + { key: 'hytale', path: '/hytale' }, // ← AJOUTER + { key: 'projects', path: '/projects' }, + { key: 'about', path: '/about' }, + { key: 'contact', path: '/contact' }, + { key: 'fiverr', path: '/fiverr' }, +]) + +// fr.json: "nav": { "hytale": "Hytale" } +// en.json: "nav": { "hytale": "Hytale" } +``` + +--- + +## State of the Art + +| Old Approach | Current Approach | When Changed | Impact | +|--------------|------------------|--------------|--------| +| Strings hardcodees dans HeroSection | i18n via t() | Phase 2 | Obligation pour bilingue et success criteria CONT-01 | +| `totalReviews: 10` | `totalReviews: 5` | Phase 2 | Correction donnees incoherentes (FIX-04) | +| `jobTitle: 'Full Stack...'` dans JSON-LD | `jobTitle: 'Hytale Plugin Developer'` | Phase 2 | SEO-05 | + +**Deprecated/outdated dans le contexte de cette phase:** +- CTAs "view projects" / "fiverr" dans le hero → remplacer par Discord + Contact (D-02) +- titre site.ts "Full Stack Developer | Vue.js, React, Node.js Expert" → D-20 + +--- + +## Open Questions + +1. **Prix concrets pour les tiers de pricing** + - What we know: D-09/D-10 definissent les tiers et le mode (fixe vs devis) + - What's unclear: Les montants exacts (ex: "150€" pour plugin simple est une estimation du researcher) + - Recommendation: Claude utilise des prix plausibles basés sur le marche freelance FR; Killian peut les ajuster apres livraison + +2. **Stats hero — chiffres pertinents ?** + - What we know: D-Discretion laisse ca a Claude + - What's unclear: "7 ans d'experience" et "5 avis Fiverr" sont des chiffres modestes pour un hero + - Recommendation: Garder les floating cards (50+ projects, 5.0 rating) mais les passer en i18n; ne pas afficher de chiffres trompeurs + +3. **CTA Discord dans le hero — 2 ou 3 boutons ?** + - What we know: D-02 = Discord + Contact. Le hero actuel a 3 boutons (projects, fiverr, contact) + - What's unclear: Garder "voir mes projets" en 3e bouton ou seulement Discord + Contact ? + - Recommendation: 3 boutons: Discord (primaire), Contact (secondaire), Hytale (tertiaire) — maximize conversion + +--- + +## Environment Availability + +Step 2.6: SKIPPED — phase purement contenu/code, pas de dependances externes nouvelles. Le stack est deja installe depuis Phase 1. + +--- + +## Validation Architecture + +> `workflow.nyquist_validation` non trouve dans `.planning/config.json` → traite comme enabled. + +### Test Framework +| Property | Value | +|----------|-------| +| Framework | Pas de framework de test detecte dans le projet | +| Config file | none | +| Quick run command | `curl localhost:3000 \| grep -i hytale` (smoke test manuel) | +| Full suite command | `pnpm build && pnpm preview` + verification manuelle | + +### Phase Requirements → Test Map +| Req ID | Behavior | Test Type | Automated Command | File Exists? | +|--------|----------|-----------|-------------------|-------------| +| CONT-01 | H1 contient "Hytale" sur homepage | smoke | `curl localhost:3000 \| grep -i ' Aucune nouvelle surface d'attaque introduite dans cette phase. Contenu statique SSR + i18n + data files. Pas de nouveaux endpoints API. Pas d'input utilisateur ajoutee. + +Applicable ASVS: V5 Input Validation — N/A (pas de nouveau formulaire). Toutes les donnees sont statiques et typees TypeScript. + +--- + +## Assumptions Log + +| # | Claim | Section | Risk if Wrong | +|---|-------|---------|---------------| +| A1 | Prix "150€" pour plugin simple Hytale | Code Examples / pricing.ts | Killian doit ajuster — donnees affichees aux visiteurs | +| A2 | Ajouter `featured: true` a colo263 et cobra2 pour la homepage | Code Examples | Killian peut choisir d'autres temoignages featured | +| A3 | `projectsCompleted: 25` conserve dans testimonialsStats | Code Examples | Killian confirme si le chiffre est exact | + +--- + +## Sources + +### Primary (HIGH confidence) +- `app/components/sections/HeroSection.vue` — strings hardcodees identifiees (lignes 31, 148, 152) +- `app/data/testimonials.ts` — totalReviews: 10, 1 temoignage featured +- `app/data/site.ts` — title et jobTitle actuels, reviewCount: '10' +- `app/components/layout/AppHeader.vue` — pattern navLinks, navigation structure +- `app/pages/index.vue` — pattern useSeoMeta + useHead + sections composees +- `i18n/locales/fr.json` — structure des cles existantes, home.title actuel +- `shared/types/index.ts` — interfaces TypeScript existantes (SiteConfig sans jobTitle) + +### Secondary (MEDIUM confidence) +- [ASSUMED] Pattern UCard Nuxt UI v3 pour pricing — a verifier contre docs si comportement inattendu + +--- + +## Metadata + +**Confidence breakdown:** +- Fichiers a modifier: HIGH — codebase lu directement +- Patterns a suivre: HIGH — indexes dans des fichiers existants fonctionnels +- Contenu prix/temoignages: LOW — hypotheses sur les montants, Killian valide +- i18n keys a creer: HIGH — structure claire, pattern etabli + +**Research date:** 2026-04-10 +**Valid until:** 2026-05-10 (stack stable, contenu peut changer) diff --git a/app/components/layout/AppHeader.vue b/app/components/layout/AppHeader.vue index b13c4bf..82325fc 100644 --- a/app/components/layout/AppHeader.vue +++ b/app/components/layout/AppHeader.vue @@ -7,6 +7,7 @@ const mobileOpen = ref(false) const navLinks = computed(() => [ { key: 'home', path: '/' }, + { key: 'hytale', path: '/hytale' }, { key: 'projects', path: '/projects' }, { key: 'about', path: '/about' }, { key: 'contact', path: '/contact' }, diff --git a/app/components/sections/HeroSection.vue b/app/components/sections/HeroSection.vue index 565b173..f35de5a 100644 --- a/app/components/sections/HeroSection.vue +++ b/app/components/sections/HeroSection.vue @@ -1,6 +1,10 @@