docs(01): create phase 1 foundation plans
This commit is contained in:
+12
-3
@@ -27,7 +27,10 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||||||
2. All static data files exist under `data/` and are importable with TypeScript strict — no `any` types
|
2. All static data files exist under `data/` and are importable with TypeScript strict — no `any` types
|
||||||
3. `useProjects()` composable returns typed project list and supports filtering by category and search
|
3. `useProjects()` composable returns typed project list and supports filtering by category and search
|
||||||
4. `npx nuxi typecheck` and `npx eslint .` exit with 0 errors
|
4. `npx nuxi typecheck` and `npx eslint .` exit with 0 errors
|
||||||
**Plans**: TBD
|
**Plans**: 2 plans
|
||||||
|
Plans:
|
||||||
|
- [ ] 01-01-PLAN.md — Scaffold Nuxt 4, modules, TypeScript strict, interfaces
|
||||||
|
- [ ] 01-02-PLAN.md — Migration donnees statiques + useProjects()
|
||||||
|
|
||||||
### Phase 2: SSR Shell
|
### Phase 2: SSR Shell
|
||||||
**Goal**: Every route renders the correct language, theme, and SEO metadata on the server — confirmed by `curl` with no JavaScript
|
**Goal**: Every route renders the correct language, theme, and SEO metadata on the server — confirmed by `curl` with no JavaScript
|
||||||
@@ -39,7 +42,10 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||||||
3. Toggling dark/light mode in the header persists across page reload with no flash on cold load
|
3. Toggling dark/light mode in the header persists across page reload with no flash on cold load
|
||||||
4. `curl http://localhost:3000` response includes `<title>`, `og:title`, `og:description`, and JSON-LD script tag
|
4. `curl http://localhost:3000` response includes `<title>`, `og:title`, `og:description`, and JSON-LD script tag
|
||||||
5. `http://localhost:3000/sitemap.xml` returns a valid XML sitemap with `hreflang` alternates for FR and EN URLs
|
5. `http://localhost:3000/sitemap.xml` returns a valid XML sitemap with `hreflang` alternates for FR and EN URLs
|
||||||
**Plans**: TBD
|
**Plans**: 2 plans
|
||||||
|
Plans:
|
||||||
|
- [ ] 01-01-PLAN.md — Scaffold Nuxt 4, modules, TypeScript strict, interfaces
|
||||||
|
- [ ] 01-02-PLAN.md — Migration donnees statiques + useProjects()
|
||||||
**UI hint**: yes
|
**UI hint**: yes
|
||||||
|
|
||||||
### Phase 3: Pages & Ship
|
### Phase 3: Pages & Ship
|
||||||
@@ -52,7 +58,10 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||||||
3. Submitting the contact form with valid data shows a success toast; EmailJS delivers the email
|
3. Submitting the contact form with valid data shows a success toast; EmailJS delivers the email
|
||||||
4. `docker build` completes and `docker run` serves the SSR app on port 3000
|
4. `docker build` completes and `docker run` serves the SSR app on port 3000
|
||||||
5. Google Analytics 4 events appear in GA4 DebugView when browsing in production mode
|
5. Google Analytics 4 events appear in GA4 DebugView when browsing in production mode
|
||||||
**Plans**: TBD
|
**Plans**: 2 plans
|
||||||
|
Plans:
|
||||||
|
- [ ] 01-01-PLAN.md — Scaffold Nuxt 4, modules, TypeScript strict, interfaces
|
||||||
|
- [ ] 01-02-PLAN.md — Migration donnees statiques + useProjects()
|
||||||
**UI hint**: yes
|
**UI hint**: yes
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|||||||
@@ -0,0 +1,338 @@
|
|||||||
|
---
|
||||||
|
phase: 01-foundation
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- nuxt.config.ts
|
||||||
|
- package.json
|
||||||
|
- pnpm-lock.yaml
|
||||||
|
- tsconfig.json
|
||||||
|
- app/app.vue
|
||||||
|
- shared/types/index.ts
|
||||||
|
- .gitignore
|
||||||
|
autonomous: true
|
||||||
|
requirements:
|
||||||
|
- SSR-01
|
||||||
|
- SSR-02
|
||||||
|
- SSR-03
|
||||||
|
- INFRA-02
|
||||||
|
- INFRA-03
|
||||||
|
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "nuxt dev demarre sans erreur et sert localhost:3000"
|
||||||
|
- "La structure app/ est utilisee (Nuxt 4 compatibilityVersion 4)"
|
||||||
|
- "Tous les modules sont installes dans nuxt.config.ts"
|
||||||
|
- "TypeScript strict mode est actif"
|
||||||
|
- "ESLint via @nuxt/eslint fonctionne sans erreur"
|
||||||
|
artifacts:
|
||||||
|
- path: "nuxt.config.ts"
|
||||||
|
provides: "Configuration principale Nuxt 4 avec tous les modules"
|
||||||
|
contains: "compatibilityVersion: 4"
|
||||||
|
- path: "app/app.vue"
|
||||||
|
provides: "Composant racine Nuxt"
|
||||||
|
- path: "shared/types/index.ts"
|
||||||
|
provides: "Interfaces TypeScript resserrees"
|
||||||
|
exports: ["Project", "ProjectButton", "Technology", "TechStack", "Testimonial", "FAQ"]
|
||||||
|
- path: "package.json"
|
||||||
|
provides: "Dependances Nuxt 4 + tous modules"
|
||||||
|
key_links:
|
||||||
|
- from: "nuxt.config.ts"
|
||||||
|
to: "app/app.vue"
|
||||||
|
via: "Nuxt srcDir convention"
|
||||||
|
pattern: "compatibilityVersion.*4"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Initialiser le projet Nuxt 4 avec pnpm, installer tous les modules, configurer TypeScript strict et ESLint, et definir les interfaces TypeScript resserrees.
|
||||||
|
|
||||||
|
Purpose: Creer le squelette technique Nuxt 4 sur lequel toute la migration repose.
|
||||||
|
Output: Projet Nuxt 4 fonctionnel avec `pnpm dev` qui demarre, tous modules configures, types definis.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@C:/Users/minit/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@C:/Users/minit/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
@.planning/phases/01-foundation/01-CONTEXT.md
|
||||||
|
@.planning/phases/01-foundation/01-RESEARCH.md
|
||||||
|
|
||||||
|
<interfaces>
|
||||||
|
<!-- Types existants a migrer et resserrer depuis src/types/index.ts -->
|
||||||
|
From src/types/index.ts:
|
||||||
|
```typescript
|
||||||
|
export interface Project {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
image: string
|
||||||
|
description: string
|
||||||
|
longDescription?: string
|
||||||
|
technologies?: string[]
|
||||||
|
category?: string
|
||||||
|
featured?: boolean
|
||||||
|
buttons?: ProjectButton[]
|
||||||
|
date?: string
|
||||||
|
demoUrl?: string
|
||||||
|
githubUrl?: string
|
||||||
|
features?: string[]
|
||||||
|
gallery?: string[]
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectButton {
|
||||||
|
title: string
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Technology {
|
||||||
|
name: string
|
||||||
|
level: 'Beginner' | 'Intermediate' | 'Advanced'
|
||||||
|
image: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TechStack {
|
||||||
|
programming: Technology[]
|
||||||
|
front: Technology[]
|
||||||
|
database: Technology[]
|
||||||
|
devtools: Technology[]
|
||||||
|
operating_systems: Technology[]
|
||||||
|
socials: Technology[]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</interfaces>
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Initialiser le projet Nuxt 4 avec pnpm et tous les modules</name>
|
||||||
|
<files>nuxt.config.ts, package.json, pnpm-lock.yaml, app/app.vue, .gitignore, tsconfig.json</files>
|
||||||
|
<read_first>
|
||||||
|
- src/types/index.ts (types existants pour reference)
|
||||||
|
- package.json (dependances actuelles Vue 3)
|
||||||
|
- .gitignore (regles existantes)
|
||||||
|
</read_first>
|
||||||
|
<action>
|
||||||
|
1. Installer pnpm globalement si absent: `npm install -g pnpm`
|
||||||
|
2. Initialiser le projet Nuxt 4: `pnpm dlx nuxi@latest init . --force` (force car le dossier n'est pas vide). Si nuxi init ne supporte pas --force dans un repo existant, creer dans un sous-dossier temp et copier les fichiers generes.
|
||||||
|
3. Installer tous les modules (per D-08, D-09):
|
||||||
|
```bash
|
||||||
|
pnpm add @nuxt/ui @nuxtjs/i18n @nuxt/eslint @nuxtjs/sitemap nuxt-gtag @nuxt/image
|
||||||
|
```
|
||||||
|
NOTE: Ne PAS installer @nuxtjs/color-mode — deja inclus dans @nuxt/ui.
|
||||||
|
|
||||||
|
4. Configurer nuxt.config.ts avec ce contenu exact:
|
||||||
|
```typescript
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
future: {
|
||||||
|
compatibilityVersion: 4
|
||||||
|
},
|
||||||
|
ssr: true,
|
||||||
|
modules: [
|
||||||
|
'@nuxt/ui',
|
||||||
|
'@nuxtjs/i18n',
|
||||||
|
'@nuxt/eslint',
|
||||||
|
'@nuxtjs/sitemap',
|
||||||
|
'nuxt-gtag',
|
||||||
|
'@nuxt/image'
|
||||||
|
],
|
||||||
|
typescript: {
|
||||||
|
strict: true
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
locales: ['fr', 'en'],
|
||||||
|
defaultLocale: 'fr'
|
||||||
|
},
|
||||||
|
gtag: {
|
||||||
|
id: 'G-CDVVNFY6MV',
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Creer `app/app.vue` minimal:
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<NuxtRouteAnnouncer />
|
||||||
|
<NuxtPage />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Creer `app/pages/index.vue` minimal pour que le serveur demarre sans erreur:
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Portfolio Killian Dalcin</h1>
|
||||||
|
<p>Nuxt 4 Foundation</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
7. Mettre a jour .gitignore pour inclure: `node_modules`, `.nuxt`, `.output`, `dist`, `.env`
|
||||||
|
|
||||||
|
8. Verifier que `pnpm dev` demarre sans erreur sur localhost:3000
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd C:/Users/minit/Desktop/portfolio/portfolio && pnpm dev --port 3000 & sleep 15 && curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | grep -q "200" && echo "PASS" || echo "FAIL"; kill %1 2>/dev/null</automated>
|
||||||
|
</verify>
|
||||||
|
<acceptance_criteria>
|
||||||
|
- nuxt.config.ts contains `compatibilityVersion: 4`
|
||||||
|
- nuxt.config.ts contains `'@nuxt/ui'` in modules array
|
||||||
|
- nuxt.config.ts contains `'@nuxtjs/i18n'` in modules array
|
||||||
|
- nuxt.config.ts contains `'@nuxt/eslint'` in modules array
|
||||||
|
- nuxt.config.ts contains `'@nuxtjs/sitemap'` in modules array
|
||||||
|
- nuxt.config.ts contains `'nuxt-gtag'` in modules array
|
||||||
|
- nuxt.config.ts contains `'@nuxt/image'` in modules array
|
||||||
|
- nuxt.config.ts contains `strict: true`
|
||||||
|
- package.json contains `@nuxt/ui` in dependencies
|
||||||
|
- package.json contains `@nuxtjs/i18n` in dependencies
|
||||||
|
- app/app.vue exists with NuxtPage component
|
||||||
|
- pnpm dev starts and localhost:3000 returns HTTP 200
|
||||||
|
</acceptance_criteria>
|
||||||
|
<done>Projet Nuxt 4 demarre sur localhost:3000 avec tous les modules installes, TypeScript strict actif</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Definir les interfaces TypeScript resserrees et configurer ESLint</name>
|
||||||
|
<files>shared/types/index.ts</files>
|
||||||
|
<read_first>
|
||||||
|
- src/types/index.ts (types existants a resserrer per D-03)
|
||||||
|
- src/data/testimonials.ts (interface Testimonial existante)
|
||||||
|
- src/data/faq.ts (interface FAQ existante)
|
||||||
|
- nuxt.config.ts (verifier @nuxt/eslint present)
|
||||||
|
</read_first>
|
||||||
|
<action>
|
||||||
|
1. Creer `shared/types/index.ts` avec les interfaces resserrees (per D-03 — rendre obligatoires technologies, category, date):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface ProjectButton {
|
||||||
|
title: string
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: string
|
||||||
|
image: string // URL /images/xxx.webp
|
||||||
|
technologies: string[] // OBLIGATOIRE (etait optionnel)
|
||||||
|
category: string // OBLIGATOIRE (etait optionnel)
|
||||||
|
date: string // OBLIGATOIRE (etait optionnel)
|
||||||
|
featured?: boolean
|
||||||
|
buttons?: ProjectButton[]
|
||||||
|
gallery?: string[]
|
||||||
|
demoUrl?: string
|
||||||
|
githubUrl?: string
|
||||||
|
features?: string[]
|
||||||
|
// Pas de title/description/longDescription/status — i18n via cles
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Technology {
|
||||||
|
name: string
|
||||||
|
level: 'Beginner' | 'Intermediate' | 'Advanced'
|
||||||
|
image: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TechStack {
|
||||||
|
programming: Technology[]
|
||||||
|
front: Technology[]
|
||||||
|
database: Technology[]
|
||||||
|
devtools: Technology[]
|
||||||
|
operating_systems: Technology[]
|
||||||
|
socials: Technology[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Testimonial {
|
||||||
|
name: string
|
||||||
|
role: string
|
||||||
|
company: string
|
||||||
|
avatar: string
|
||||||
|
rating: number
|
||||||
|
content: string
|
||||||
|
date: string
|
||||||
|
platform: string
|
||||||
|
featured?: boolean
|
||||||
|
project_type: string
|
||||||
|
results?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestimonialsStats {
|
||||||
|
totalReviews: number
|
||||||
|
averageRating: number
|
||||||
|
projectsCompleted: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FAQ {
|
||||||
|
questionKey: string
|
||||||
|
answerKey: string
|
||||||
|
featuresKey: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: FAQ utilise des cles i18n (per D-02) au lieu de texte direct. L'ancienne interface avait `question: string` (texte), la nouvelle a `questionKey: string` (cle de traduction).
|
||||||
|
|
||||||
|
2. Verifier que `pnpm nuxi typecheck` passe (les types sont auto-importes depuis shared/ en Nuxt 4).
|
||||||
|
|
||||||
|
3. Verifier que `pnpm eslint .` passe sans erreur (ESLint configure via @nuxt/eslint dans les modules).
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd C:/Users/minit/Desktop/portfolio/portfolio && npx nuxi typecheck 2>&1 | tail -5</automated>
|
||||||
|
</verify>
|
||||||
|
<acceptance_criteria>
|
||||||
|
- shared/types/index.ts contains `technologies: string[]` (not optional)
|
||||||
|
- shared/types/index.ts contains `category: string` (not optional)
|
||||||
|
- shared/types/index.ts contains `date: string` (not optional, in Project interface)
|
||||||
|
- shared/types/index.ts contains `export interface Project`
|
||||||
|
- shared/types/index.ts contains `export interface Technology`
|
||||||
|
- shared/types/index.ts contains `export interface TechStack`
|
||||||
|
- shared/types/index.ts contains `export interface Testimonial`
|
||||||
|
- shared/types/index.ts contains `export interface FAQ`
|
||||||
|
- shared/types/index.ts contains `questionKey: string`
|
||||||
|
- npx nuxi typecheck exits with code 0
|
||||||
|
</acceptance_criteria>
|
||||||
|
<done>Toutes les interfaces TypeScript resserrees existent dans shared/types/index.ts, typecheck et eslint passent sans erreur</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<threat_model>
|
||||||
|
## Trust Boundaries
|
||||||
|
|
||||||
|
| Boundary | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| Aucune | Phase 1 est une initialisation technique sans surface d'attaque |
|
||||||
|
|
||||||
|
## STRIDE Threat Register
|
||||||
|
|
||||||
|
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||||
|
|-----------|----------|-----------|-------------|-----------------|
|
||||||
|
| T-01-01 | I (Information Disclosure) | nuxt.config.ts | mitigate | gtag.enabled: false — pas de tracking en dev |
|
||||||
|
| T-01-02 | T (Tampering) | pnpm dependencies | accept | lockfile pnpm-lock.yaml tracke dans git |
|
||||||
|
</threat_model>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
1. `pnpm dev` demarre sans erreur sur localhost:3000
|
||||||
|
2. `npx nuxi typecheck` exit 0
|
||||||
|
3. `pnpm eslint .` exit 0 (si le script existe, sinon `npx eslint .`)
|
||||||
|
4. nuxt.config.ts contient les 6 modules et compatibilityVersion 4
|
||||||
|
5. shared/types/index.ts exporte Project, Technology, TechStack, Testimonial, FAQ
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Le projet Nuxt 4 demarre localement
|
||||||
|
- Tous les modules sont installes et declares
|
||||||
|
- TypeScript strict mode actif
|
||||||
|
- Interfaces resserrees per D-03
|
||||||
|
- ESLint fonctionne via @nuxt/eslint
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md`
|
||||||
|
</output>
|
||||||
@@ -0,0 +1,439 @@
|
|||||||
|
---
|
||||||
|
phase: 01-foundation
|
||||||
|
plan: 02
|
||||||
|
type: execute
|
||||||
|
wave: 2
|
||||||
|
depends_on: ["01-01"]
|
||||||
|
files_modified:
|
||||||
|
- app/data/projects.ts
|
||||||
|
- app/data/testimonials.ts
|
||||||
|
- app/data/faq.ts
|
||||||
|
- app/data/techstack.ts
|
||||||
|
- app/composables/useProjects.ts
|
||||||
|
- public/images/
|
||||||
|
autonomous: true
|
||||||
|
requirements:
|
||||||
|
- DATA-01
|
||||||
|
- DATA-02
|
||||||
|
- DATA-03
|
||||||
|
- DATA-04
|
||||||
|
- DATA-05
|
||||||
|
|
||||||
|
must_haves:
|
||||||
|
truths:
|
||||||
|
- "Les donnees projets sont importables depuis app/data/projects.ts avec le type Project"
|
||||||
|
- "Les donnees testimonials sont importables avec le type Testimonial"
|
||||||
|
- "Les donnees FAQ utilisent des cles i18n et non du texte direct"
|
||||||
|
- "Les donnees techstack sont importables avec le type TechStack"
|
||||||
|
- "useProjects() retourne une liste typee et supporte filterByCategory, search, findById"
|
||||||
|
- "Toutes les images referenceent /images/ et non @/assets/images/"
|
||||||
|
artifacts:
|
||||||
|
- path: "app/data/projects.ts"
|
||||||
|
provides: "Donnees brutes des 7 projets"
|
||||||
|
contains: "export const projects"
|
||||||
|
- path: "app/data/testimonials.ts"
|
||||||
|
provides: "Donnees temoignages"
|
||||||
|
contains: "export const testimonials"
|
||||||
|
- path: "app/data/faq.ts"
|
||||||
|
provides: "Donnees FAQ avec cles i18n"
|
||||||
|
contains: "export const homeFAQs"
|
||||||
|
- path: "app/data/techstack.ts"
|
||||||
|
provides: "Donnees tech stack"
|
||||||
|
contains: "export const techStack"
|
||||||
|
- path: "app/composables/useProjects.ts"
|
||||||
|
provides: "Composable filtrage/recherche projets"
|
||||||
|
exports: ["useProjects"]
|
||||||
|
key_links:
|
||||||
|
- from: "app/composables/useProjects.ts"
|
||||||
|
to: "app/data/projects.ts"
|
||||||
|
via: "import direct"
|
||||||
|
pattern: "import.*from.*data/projects"
|
||||||
|
- from: "app/data/projects.ts"
|
||||||
|
to: "shared/types/index.ts"
|
||||||
|
via: "type import"
|
||||||
|
pattern: "import type.*Project"
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
Migrer toutes les donnees statiques vers app/data/, copier les images vers public/images/, et reecrire useProjects() en style Nuxt natif.
|
||||||
|
|
||||||
|
Purpose: Les donnees du portfolio sont disponibles et typees pour les phases suivantes.
|
||||||
|
Output: 4 fichiers data, 1 composable, images dans public/images/.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<execution_context>
|
||||||
|
@C:/Users/minit/.claude/get-shit-done/workflows/execute-plan.md
|
||||||
|
@C:/Users/minit/.claude/get-shit-done/templates/summary.md
|
||||||
|
</execution_context>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
@.planning/PROJECT.md
|
||||||
|
@.planning/ROADMAP.md
|
||||||
|
@.planning/STATE.md
|
||||||
|
@.planning/phases/01-foundation/01-CONTEXT.md
|
||||||
|
@.planning/phases/01-foundation/01-RESEARCH.md
|
||||||
|
@.planning/phases/01-foundation/01-01-SUMMARY.md
|
||||||
|
|
||||||
|
<interfaces>
|
||||||
|
<!-- Types crees par Plan 01 dans shared/types/index.ts -->
|
||||||
|
```typescript
|
||||||
|
export interface Project {
|
||||||
|
id: string
|
||||||
|
image: string
|
||||||
|
technologies: string[]
|
||||||
|
category: string
|
||||||
|
date: string
|
||||||
|
featured?: boolean
|
||||||
|
buttons?: ProjectButton[]
|
||||||
|
gallery?: string[]
|
||||||
|
demoUrl?: string
|
||||||
|
githubUrl?: string
|
||||||
|
features?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjectButton {
|
||||||
|
title: string
|
||||||
|
link: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Technology {
|
||||||
|
name: string
|
||||||
|
level: 'Beginner' | 'Intermediate' | 'Advanced'
|
||||||
|
image: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TechStack {
|
||||||
|
programming: Technology[]
|
||||||
|
front: Technology[]
|
||||||
|
database: Technology[]
|
||||||
|
devtools: Technology[]
|
||||||
|
operating_systems: Technology[]
|
||||||
|
socials: Technology[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Testimonial {
|
||||||
|
name: string
|
||||||
|
role: string
|
||||||
|
company: string
|
||||||
|
avatar: string
|
||||||
|
rating: number
|
||||||
|
content: string
|
||||||
|
date: string
|
||||||
|
platform: string
|
||||||
|
featured?: boolean
|
||||||
|
project_type: string
|
||||||
|
results?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestimonialsStats {
|
||||||
|
totalReviews: number
|
||||||
|
averageRating: number
|
||||||
|
projectsCompleted: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FAQ {
|
||||||
|
questionKey: string
|
||||||
|
answerKey: string
|
||||||
|
featuresKey: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</interfaces>
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Migrer les donnees statiques et les images</name>
|
||||||
|
<files>app/data/projects.ts, app/data/testimonials.ts, app/data/faq.ts, app/data/techstack.ts, public/images/</files>
|
||||||
|
<read_first>
|
||||||
|
- src/composables/useProjects.ts (donnees projets inline a extraire)
|
||||||
|
- src/data/testimonials.ts (donnees + interface existantes)
|
||||||
|
- src/data/faq.ts (donnees + pattern getHomeFAQs existant)
|
||||||
|
- src/data/techstack.ts (donnees existantes)
|
||||||
|
- shared/types/index.ts (interfaces resserrees de Plan 01)
|
||||||
|
</read_first>
|
||||||
|
<action>
|
||||||
|
1. Copier toutes les images WebP de `src/assets/images/` vers `public/images/` (per D-06, D-07):
|
||||||
|
```bash
|
||||||
|
mkdir -p public/images/flowboard
|
||||||
|
cp src/assets/images/*.webp public/images/
|
||||||
|
cp src/assets/images/flowboard/*.webp public/images/flowboard/
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Creer `app/data/projects.ts` (per D-01, D-02 — donnees separees, cles i18n):
|
||||||
|
```typescript
|
||||||
|
import type { Project } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const projects: Project[] = [
|
||||||
|
{
|
||||||
|
id: 'virtual-tour',
|
||||||
|
image: '/images/virtualtour.webp',
|
||||||
|
technologies: ['Vue.js', 'Three.js', 'WebGL', 'Node.js'],
|
||||||
|
category: 'Web Development',
|
||||||
|
date: '2022',
|
||||||
|
buttons: [
|
||||||
|
{ title: 'Visit', link: 'https://www.lycee-chabanne16.fr/visites/BACSN/index.htm' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'xinko',
|
||||||
|
image: '/images/xinko.webp',
|
||||||
|
technologies: ['Node.js', 'Discord.js', 'MongoDB', 'Express'],
|
||||||
|
category: 'Bot Development',
|
||||||
|
date: '2023',
|
||||||
|
featured: true,
|
||||||
|
buttons: [
|
||||||
|
{ title: 'Invite', link: 'https://discord.com/api/oauth2/authorize?client_id=1035571329866407976&permissions=292288982151&scope=applications.commands%20bot' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'image-manipulation',
|
||||||
|
image: '/images/dig.webp',
|
||||||
|
technologies: ['JavaScript', 'Node.js', 'Canvas', 'npm'],
|
||||||
|
category: 'Open Source',
|
||||||
|
date: '2022',
|
||||||
|
featured: true,
|
||||||
|
buttons: [
|
||||||
|
{ title: 'Repository', link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-image-generation' },
|
||||||
|
{ title: 'NPM Package', link: 'https://www.npmjs.com/package/discord-image-generation' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'primate-web-admin',
|
||||||
|
image: '/images/primate.webp',
|
||||||
|
technologies: ['React', 'TypeScript', 'Node.js', 'Express'],
|
||||||
|
category: 'Enterprise Software',
|
||||||
|
date: '2023'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'instagram-bot',
|
||||||
|
image: '/images/instagram.webp',
|
||||||
|
technologies: ['JavaScript', 'Node.js', 'Instagram API', 'Canvas'],
|
||||||
|
category: 'Social Media Bot',
|
||||||
|
date: '2022',
|
||||||
|
buttons: [
|
||||||
|
{ title: 'Repository', link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/instagram-bot' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'crowdin-status-bot',
|
||||||
|
image: '/images/crowdin.webp',
|
||||||
|
technologies: ['Node.js', 'Discord.js', 'Crowdin API', 'Cron'],
|
||||||
|
category: 'Automation',
|
||||||
|
date: '2023',
|
||||||
|
buttons: [
|
||||||
|
{ title: 'Repository', link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-crowdin-status' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'flowboard',
|
||||||
|
image: '/images/flowboard/flowboard_1.webp',
|
||||||
|
technologies: ['Vue.js', 'Node.js', 'TypeScript', 'MongoDB', 'Express'],
|
||||||
|
category: 'Web Development',
|
||||||
|
date: '2024',
|
||||||
|
featured: true,
|
||||||
|
features: [
|
||||||
|
'Organize your tasks, projects and ideas by creating thematic boards adapted to your needs',
|
||||||
|
'Add cards for each task, assign members, set due dates, and track progress at a glance',
|
||||||
|
'Invite colleagues and teammates to join your boards to work together, share ideas, and coordinate your efforts',
|
||||||
|
'Keep an overview of the progress of your projects thanks to a simple and intuitive interface',
|
||||||
|
'Use labels, lists and tables to prioritize tasks, set priorities and keep the overview clear'
|
||||||
|
],
|
||||||
|
gallery: [
|
||||||
|
'/images/flowboard/flowboard_1.webp',
|
||||||
|
'/images/flowboard/flowboard_2.webp',
|
||||||
|
'/images/flowboard/flowboard_3.webp',
|
||||||
|
'/images/flowboard/flowboard_4.webp'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Creer `app/data/testimonials.ts` — copie directe, juste changer l'import type:
|
||||||
|
```typescript
|
||||||
|
import type { Testimonial, TestimonialsStats } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const testimonials: Testimonial[] = [
|
||||||
|
// ... (copier les 5 temoignages existants tels quels de src/data/testimonials.ts)
|
||||||
|
]
|
||||||
|
|
||||||
|
export const testimonialsStats: TestimonialsStats = {
|
||||||
|
totalReviews: 10,
|
||||||
|
averageRating: 5.0,
|
||||||
|
projectsCompleted: 25
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Creer `app/data/faq.ts` (per D-02 — cles i18n au lieu de texte):
|
||||||
|
```typescript
|
||||||
|
import type { FAQ } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const homeFAQs: FAQ[] = [
|
||||||
|
{
|
||||||
|
questionKey: 'faq.homeFaq.delivery.question',
|
||||||
|
answerKey: 'faq.homeFaq.delivery.answer',
|
||||||
|
featuresKey: 'faq.homeFaq.delivery.features'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionKey: 'faq.homeFaq.maintenance.question',
|
||||||
|
answerKey: 'faq.homeFaq.maintenance.answer',
|
||||||
|
featuresKey: 'faq.homeFaq.maintenance.features'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionKey: 'faq.homeFaq.companies.question',
|
||||||
|
answerKey: 'faq.homeFaq.companies.answer',
|
||||||
|
featuresKey: 'faq.homeFaq.companies.features'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Creer `app/data/techstack.ts` — copie avec chemins images mis a jour:
|
||||||
|
```typescript
|
||||||
|
import type { TechStack } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const techStack: TechStack = {
|
||||||
|
// ... (copier depuis src/data/techstack.ts, remplacer TOUS les `@/assets/images/xxx.webp` par `/images/xxx.webp`)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Remplacement a effectuer: `@/assets/images/` -> `/images/` pour CHAQUE entree (60+ images).
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd C:/Users/minit/Desktop/portfolio/portfolio && npx nuxi typecheck 2>&1 | tail -5</automated>
|
||||||
|
</verify>
|
||||||
|
<acceptance_criteria>
|
||||||
|
- app/data/projects.ts contains `export const projects: Project[]`
|
||||||
|
- app/data/projects.ts contains `/images/virtualtour.webp` (not `@/assets/images/`)
|
||||||
|
- app/data/projects.ts contains 7 project objects (virtual-tour through flowboard)
|
||||||
|
- app/data/testimonials.ts contains `export const testimonials: Testimonial[]`
|
||||||
|
- app/data/testimonials.ts contains `export const testimonialsStats: TestimonialsStats`
|
||||||
|
- app/data/faq.ts contains `export const homeFAQs: FAQ[]`
|
||||||
|
- app/data/faq.ts contains `questionKey:` (i18n keys, not direct text)
|
||||||
|
- app/data/techstack.ts contains `export const techStack: TechStack`
|
||||||
|
- app/data/techstack.ts contains `/images/javascript.webp` (not `@/assets/images/`)
|
||||||
|
- public/images/ directory contains .webp files
|
||||||
|
- No file in app/data/ contains `@/assets/images/`
|
||||||
|
- npx nuxi typecheck exits with code 0
|
||||||
|
</acceptance_criteria>
|
||||||
|
<done>4 fichiers data migres avec types corrects, images dans public/images/, aucune reference a @/assets/images/</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Reecrire useProjects() en style Nuxt natif</name>
|
||||||
|
<files>app/composables/useProjects.ts</files>
|
||||||
|
<read_first>
|
||||||
|
- src/composables/useProjects.ts (composable existant a reecrire)
|
||||||
|
- app/data/projects.ts (donnees separees de Task 1)
|
||||||
|
- shared/types/index.ts (interfaces)
|
||||||
|
</read_first>
|
||||||
|
<action>
|
||||||
|
Creer `app/composables/useProjects.ts` en style Nuxt natif (per D-04, D-05):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { projects as projectsData } from '~/data/projects'
|
||||||
|
|
||||||
|
export function useProjects() {
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const projects = computed(() =>
|
||||||
|
projectsData.map(p => ({
|
||||||
|
...p,
|
||||||
|
title: t(`projects.${p.id}.title`),
|
||||||
|
description: t(`projects.${p.id}.description`),
|
||||||
|
longDescription: t(`projects.${p.id}.longDescription`) || undefined
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const featuredProjects = computed(() =>
|
||||||
|
projects.value.filter(p => p.featured)
|
||||||
|
)
|
||||||
|
|
||||||
|
function filterByCategory(category: string) {
|
||||||
|
return computed(() =>
|
||||||
|
projects.value.filter(p => p.category === category)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(query: Ref<string> | string) {
|
||||||
|
return computed(() => {
|
||||||
|
const q = typeof query === 'string' ? query : query.value
|
||||||
|
if (!q) return projects.value
|
||||||
|
const lower = q.toLowerCase()
|
||||||
|
return projects.value.filter(p =>
|
||||||
|
p.title.toLowerCase().includes(lower) ||
|
||||||
|
p.description.toLowerCase().includes(lower) ||
|
||||||
|
p.technologies.some(tech => tech.toLowerCase().includes(lower))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function findById(id: string) {
|
||||||
|
return computed(() => projects.value.find(p => p.id === id))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
projects,
|
||||||
|
featuredProjects,
|
||||||
|
filterByCategory,
|
||||||
|
search,
|
||||||
|
findById
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Points cles per D-04:
|
||||||
|
- Pas d'import `computed`, `useI18n` — auto-importes par Nuxt
|
||||||
|
- Import des donnees depuis `~/data/projects` (pas `@/`)
|
||||||
|
- Pas de wrapper useI18n custom — utilise directement l'auto-import @nuxtjs/i18n
|
||||||
|
- Les cles i18n suivent le pattern `projects.${id}.title` (per D-02)
|
||||||
|
</action>
|
||||||
|
<verify>
|
||||||
|
<automated>cd C:/Users/minit/Desktop/portfolio/portfolio && npx nuxi typecheck 2>&1 | tail -5</automated>
|
||||||
|
</verify>
|
||||||
|
<acceptance_criteria>
|
||||||
|
- app/composables/useProjects.ts contains `export function useProjects()`
|
||||||
|
- app/composables/useProjects.ts contains `import { projects as projectsData } from '~/data/projects'`
|
||||||
|
- app/composables/useProjects.ts contains `const { t } = useI18n()`
|
||||||
|
- app/composables/useProjects.ts contains `filterByCategory`
|
||||||
|
- app/composables/useProjects.ts contains `search`
|
||||||
|
- app/composables/useProjects.ts contains `findById`
|
||||||
|
- app/composables/useProjects.ts contains `featuredProjects`
|
||||||
|
- app/composables/useProjects.ts does NOT contain `import { computed }` (auto-imported)
|
||||||
|
- app/composables/useProjects.ts does NOT contain `from '@/composables/useI18n'`
|
||||||
|
- npx nuxi typecheck exits with code 0
|
||||||
|
</acceptance_criteria>
|
||||||
|
<done>useProjects() retourne projects, featuredProjects, filterByCategory, search, findById — tout type-safe et style Nuxt natif</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<threat_model>
|
||||||
|
## Trust Boundaries
|
||||||
|
|
||||||
|
| Boundary | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| Aucune | Donnees statiques, pas d'input utilisateur |
|
||||||
|
|
||||||
|
## STRIDE Threat Register
|
||||||
|
|
||||||
|
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||||
|
|-----------|----------|-----------|-------------|-----------------|
|
||||||
|
| T-01-03 | I (Information Disclosure) | testimonials avatars | accept | URLs ui-avatars.com publiques, pas de PII |
|
||||||
|
</threat_model>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
1. `npx nuxi typecheck` exit 0
|
||||||
|
2. Aucun fichier dans app/data/ ne contient `@/assets/images/`
|
||||||
|
3. app/composables/useProjects.ts exporte useProjects avec 5 fonctions/proprietes
|
||||||
|
4. public/images/ contient les fichiers WebP
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Les 4 fichiers data existent et sont type-safe
|
||||||
|
- useProjects() compile sans erreur
|
||||||
|
- Images disponibles dans public/images/
|
||||||
|
- Aucune reference aux anciens chemins @/assets/images/
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
|
||||||
|
</output>
|
||||||
Reference in New Issue
Block a user