6b828aff67
- Corrected the name in various files including CLAUDE.md, README.md, and configuration files to reflect the updated branding. - Ensured consistency in the use of the new name throughout the project, enhancing brand identity.
213 lines
8.0 KiB
Markdown
213 lines
8.0 KiB
Markdown
---
|
|
phase: 02-ssr-shell
|
|
plan: 03
|
|
type: execute
|
|
wave: 2
|
|
depends_on: [02-01]
|
|
files_modified:
|
|
- app/pages/index.vue
|
|
- app/pages/projects.vue
|
|
- app/pages/about.vue
|
|
- app/pages/contact.vue
|
|
- app/pages/fiverr.vue
|
|
- app/pages/formation.vue
|
|
autonomous: true
|
|
requirements: [SEO-01, SEO-02, SEO-04]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Every route has unique title, description, og:title, og:description in SSR HTML"
|
|
- "Homepage includes JSON-LD Person + ProfessionalService schema"
|
|
- "Every route has og:image with absolute URL"
|
|
- "curl output for each route contains <title> and og:description meta tag"
|
|
artifacts:
|
|
- path: "app/pages/index.vue"
|
|
provides: "Homepage with SEO metadata and JSON-LD"
|
|
contains: "useSeoMeta"
|
|
- path: "app/pages/projects.vue"
|
|
provides: "Projects stub page with SEO metadata"
|
|
contains: "useSeoMeta"
|
|
key_links:
|
|
- from: "app/pages/index.vue"
|
|
to: "app/locales/fr.json"
|
|
via: "t('seo.home.title') for localized SEO"
|
|
pattern: "seo\\.home\\.title"
|
|
- from: "app/pages/index.vue"
|
|
to: "JSON-LD"
|
|
via: "useHead script tag"
|
|
pattern: "application/ld\\+json"
|
|
---
|
|
|
|
<objective>
|
|
Add per-route SEO metadata (useSeoMeta) and JSON-LD structured data to all page stubs.
|
|
|
|
Purpose: Every route returns correct, unique, localized SEO tags in server-rendered HTML — verifiable by curl.
|
|
Output: 6 page files with useSeoMeta(), homepage with JSON-LD, all with og:image.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@~/.claude/get-shit-done/workflows/execute-plan.md
|
|
@~/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/phases/02-ssr-shell/02-CONTEXT.md
|
|
@.planning/phases/02-ssr-shell/02-RESEARCH.md
|
|
@.planning/phases/02-ssr-shell/02-UI-SPEC.md
|
|
@.planning/phases/02-ssr-shell/02-01-SUMMARY.md
|
|
|
|
<interfaces>
|
|
<!-- From app/locales/fr.json (Plan 01): seo.home.title, seo.home.description, seo.projects.title, etc. -->
|
|
<!-- From nuxt.config.ts (Plan 01): site.url = 'https://killiandalcin.fr' -->
|
|
<!-- From public/og-image.png (Plan 01): static og:image file -->
|
|
<!-- Nuxt built-in: useSeoMeta(), useHead() — auto-imported -->
|
|
<!-- @nuxtjs/i18n: useI18n() for t() function -->
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Per-route SEO metadata on all page stubs</name>
|
|
<files>app/pages/index.vue, app/pages/projects.vue, app/pages/about.vue, app/pages/contact.vue, app/pages/fiverr.vue, app/pages/formation.vue</files>
|
|
<read_first>
|
|
- app/pages/index.vue (current stub — will be enhanced)
|
|
- app/locales/fr.json (verify seo.* keys exist)
|
|
- .planning/phases/02-ssr-shell/02-RESEARCH.md (Pattern 3: useSeoMeta per route; Pattern 4: JSON-LD)
|
|
- .planning/phases/02-ssr-shell/02-UI-SPEC.md (SEO Contract table)
|
|
- src/config/site.ts (siteConfig.seo.organization for JSON-LD schema data; url: https://killiandalcin.fr)
|
|
</read_first>
|
|
<action>
|
|
Update each page stub to include `useSeoMeta()` with localized metadata. Pages remain stubs (minimal template content) — Phase 3 fills real content.
|
|
|
|
**Pattern for every page** (example: projects.vue):
|
|
```vue
|
|
<script setup lang="ts">
|
|
const { t } = useI18n()
|
|
|
|
useSeoMeta({
|
|
title: () => t('seo.projects.title'),
|
|
description: () => t('seo.projects.description'),
|
|
ogTitle: () => t('seo.projects.title'),
|
|
ogDescription: () => t('seo.projects.description'),
|
|
ogImage: 'https://killiandalcin.fr/og-image.png',
|
|
ogImageWidth: 1200,
|
|
ogImageHeight: 630,
|
|
ogType: 'website',
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
<h1 class="text-2xl font-bold">{{ t('nav.projects') }}</h1>
|
|
<p class="text-gray-600 dark:text-gray-400 mt-4">Phase 3 content placeholder</p>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
Apply this pattern to all 6 pages using their respective seo.{page}.title and seo.{page}.description keys:
|
|
- index.vue → seo.home.*
|
|
- projects.vue → seo.projects.*
|
|
- about.vue → seo.about.*
|
|
- contact.vue → seo.contact.*
|
|
- fiverr.vue → seo.fiverr.*
|
|
- formation.vue → seo.formation.*
|
|
|
|
All pages use `ogImage: 'https://killiandalcin.fr/og-image.png'` (per user decision: static image, no nuxt-og-image).
|
|
|
|
**Homepage (index.vue) ADDITIONALLY gets JSON-LD** (per D-11, SEO-02):
|
|
```typescript
|
|
useHead({
|
|
script: [
|
|
{
|
|
type: 'application/ld+json',
|
|
innerHTML: JSON.stringify({
|
|
'@context': 'https://schema.org',
|
|
'@graph': [
|
|
{
|
|
'@type': 'Person',
|
|
name: 'Killian' DAL-CIN',
|
|
url: 'https://killiandalcin.fr',
|
|
jobTitle: 'Developpeur Full Stack Freelance',
|
|
email: 'contact@killiandalcin.fr',
|
|
sameAs: [
|
|
'https://linkedin.com/in/killian-dal-cin',
|
|
'https://www.fiverr.com/users/mr_kayjaydee',
|
|
'https://gitea.kamisama.ovh/kayjaydee',
|
|
],
|
|
},
|
|
{
|
|
'@type': 'ProfessionalService',
|
|
name: 'Killian' DAL-CIN - Developpeur Full Stack',
|
|
url: 'https://killiandalcin.fr',
|
|
logo: 'https://killiandalcin.fr/images/logo.webp',
|
|
priceRange: '$$$',
|
|
areaServed: 'Worldwide',
|
|
},
|
|
],
|
|
}),
|
|
},
|
|
],
|
|
})
|
|
```
|
|
|
|
Create pages that do not yet exist (projects.vue, about.vue, contact.vue, fiverr.vue, formation.vue) as new files. Update existing index.vue.
|
|
|
|
Each stub page template should have:
|
|
- `<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">` wrapper (per D-16)
|
|
- An `<h1>` using the nav translation key
|
|
- A placeholder paragraph
|
|
</action>
|
|
<verify>
|
|
<automated>grep -q "useSeoMeta" app/pages/index.vue && grep -q "application/ld+json" app/pages/index.vue && grep -q "useSeoMeta" app/pages/projects.vue && grep -q "useSeoMeta" app/pages/about.vue && grep -q "useSeoMeta" app/pages/contact.vue && grep -q "useSeoMeta" app/pages/fiverr.vue && grep -q "useSeoMeta" app/pages/formation.vue && grep -q "og-image.png" app/pages/index.vue && echo "PASS" || echo "FAIL"</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- All 6 page files exist under app/pages/
|
|
- Every page contains `useSeoMeta` with title, description, ogTitle, ogDescription, ogImage
|
|
- ogImage value is `https://killiandalcin.fr/og-image.png` on every page
|
|
- index.vue contains `application/ld+json` with `Person` and `ProfessionalService`
|
|
- index.vue JSON-LD contains `sameAs` array with LinkedIn, Fiverr, Gitea URLs
|
|
- Each page uses localized seo keys: `t('seo.home.title')`, `t('seo.projects.title')`, etc.
|
|
- Each page template has `max-w-7xl mx-auto` wrapper
|
|
- `npx nuxi typecheck` passes
|
|
</acceptance_criteria>
|
|
<done>All 6 routes have unique, localized SEO metadata via useSeoMeta(). Homepage includes JSON-LD with Person + ProfessionalService schema. Every page has og:image with absolute URL.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| SEO meta tags | Server-rendered meta tags include user-controlled translation values |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|-------------|-----------------|
|
|
| T-02-06 | Injection | JSON-LD innerHTML | mitigate | JSON.stringify() escapes special characters; no user input in JSON-LD — all values are hardcoded constants |
|
|
| T-02-07 | Information Disclosure | og:image URL | accept | Public URL pointing to public image — no sensitive data |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
- `pnpm dev` then `curl http://localhost:3000` returns HTML containing `<title>`, `og:title`, `og:description` meta tags, and JSON-LD script
|
|
- `curl http://localhost:3000/en/` returns English title/description
|
|
- `curl http://localhost:3000/projects` returns projects-specific title
|
|
- Each page curl output contains `og-image.png` in a meta tag
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- All 6 routes have unique, localized SEO metadata in server-rendered HTML
|
|
- Homepage JSON-LD contains Person + ProfessionalService
|
|
- og:image present on every route with absolute URL
|
|
- `npx nuxi typecheck` passes
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/02-ssr-shell/02-03-SUMMARY.md`
|
|
</output>
|