docs(02): create phase 2 content plans

This commit is contained in:
2026-04-11 03:58:21 +02:00
parent 710692f0ae
commit 215fba6342
4 changed files with 756 additions and 2 deletions
+268
View File
@@ -0,0 +1,268 @@
---
phase: 02-content
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- shared/types/index.ts
- app/data/site.ts
- app/data/testimonials.ts
- app/data/pricing.ts
- i18n/locales/fr.json
- i18n/locales/en.json
autonomous: true
requirements: [CONT-01, CONT-02, CONT-03, CONT-04, SEO-05]
must_haves:
truths:
- "PricingTier and jobTitle types exist in shared/types/index.ts"
- "site.ts has jobTitle 'Hytale Plugin Developer' and title updated per D-20"
- "testimonials.ts has totalReviews 5 and 3 featured testimonials"
- "pricing.ts has 5 tiers with correct price data"
- "fr.json and en.json have all hytale.*, home.*, nav.hytale keys"
artifacts:
- path: "shared/types/index.ts"
provides: "PricingTier interface, jobTitle on SiteConfig"
contains: "PricingTier"
- path: "app/data/pricing.ts"
provides: "5 pricing tiers for Hytale page"
contains: "hytalePricing"
- path: "app/data/site.ts"
provides: "Updated title and jobTitle"
contains: "Hytale Plugin Developer"
- path: "app/data/testimonials.ts"
provides: "Corrected stats and featured flags"
contains: "totalReviews: 5"
- path: "i18n/locales/fr.json"
provides: "All French i18n keys for phase 2"
contains: "hytale"
- path: "i18n/locales/en.json"
provides: "All English i18n keys for phase 2"
contains: "hytale"
key_links:
- from: "app/data/pricing.ts"
to: "shared/types/index.ts"
via: "import PricingTier"
pattern: "import type.*PricingTier"
---
<objective>
Data layer foundation for phase 2 content: types, data files, site config, and all i18n keys.
Purpose: Every subsequent plan (hero refonte, hytale page) depends on these types, data, and i18n keys existing first. By completing data layer in wave 1, plans 02 and 03 can execute in parallel in wave 2.
Output: Updated types, site config, pricing data, testimonials fixes, complete FR+EN i18n keys.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-content/02-CONTEXT.md
@.planning/phases/02-content/02-RESEARCH.md
@.planning/phases/02-content/02-UI-SPEC.md
@shared/types/index.ts
@app/data/site.ts
@app/data/testimonials.ts
@i18n/locales/fr.json
@i18n/locales/en.json
</context>
<tasks>
<task type="auto">
<name>Task 1: Add types, update site.ts, create pricing.ts, fix testimonials.ts</name>
<files>shared/types/index.ts, app/data/site.ts, app/data/pricing.ts, app/data/testimonials.ts</files>
<read_first>shared/types/index.ts, app/data/site.ts, app/data/testimonials.ts</read_first>
<action>
1. **shared/types/index.ts** — Add `PricingTier` interface and `jobTitle` to `SiteConfig`:
```typescript
export interface PricingTier {
id: string
priceFixed: string | null
priceLabel?: string
featured?: boolean
}
```
Add `jobTitle?: string` field to `SiteConfig` interface (after `description`).
2. **app/data/site.ts** — Per D-20, D-21, D-16:
- Change `title` to: `"Killian' DAL-CIN - Hytale Plugin Developer | Freelance"`
- Add `jobTitle: 'Hytale Plugin Developer'` after `description`
- Change `seo.organization.name` to: `"Killian' DAL-CIN - Hytale Plugin Developer"`
- Change `seo.organization.aggregateRating.reviewCount` from `'10'` to `'5'`
3. **app/data/pricing.ts** — Create new file per D-09, D-10. Export `hytalePricing: PricingTier[]` with 5 tiers:
```typescript
import type { PricingTier } from '~~/shared/types'
export const hytalePricing: PricingTier[] = [
{ id: 'simple', priceFixed: '50€', featured: false },
{ id: 'complex', priceFixed: null, priceLabel: 'Sur devis', featured: true },
{ id: 'custom', priceFixed: null, priceLabel: 'Sur devis', featured: false },
{ id: 'maintenance', priceFixed: '30€/mois', featured: false },
{ id: 'web', priceFixed: null, priceLabel: 'Sur devis', featured: false },
]
```
Note: Use "A partir de 50€" / "A partir de 30€/mois" as priceFixed values matching UI-SPEC copywriting contract. Actually, price display text goes through i18n — priceFixed stores the raw value. The i18n keys will hold display strings like "A partir de 50€".
4. **app/data/testimonials.ts** — Per D-16:
- Change `totalReviews: 10` to `totalReviews: 5`
- Add `featured: true` to `colo263` and `cobra2` testimonials (per D-15, need 3 featured for homepage)
</action>
<verify>
<automated>grep -q "PricingTier" shared/types/index.ts && grep -q "jobTitle" shared/types/index.ts && grep -q "Hytale Plugin Developer" app/data/site.ts && grep -q "reviewCount: '5'" app/data/site.ts && grep -q "totalReviews: 5" app/data/testimonials.ts && grep -q "hytalePricing" app/data/pricing.ts && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- `grep "PricingTier" shared/types/index.ts` returns the interface definition
- `grep "jobTitle" shared/types/index.ts` shows field in SiteConfig
- `grep "jobTitle: 'Hytale Plugin Developer'" app/data/site.ts` matches
- `grep "reviewCount: '5'" app/data/site.ts` matches
- `grep "totalReviews: 5" app/data/testimonials.ts` matches
- `grep "hytalePricing" app/data/pricing.ts` returns the exported array
- 3 testimonials have `featured: true`
</acceptance_criteria>
<done>Types extended, site.ts repositioned to Hytale, pricing data created with 5 tiers, testimonials stats corrected to 5 with 3 featured</done>
</task>
<task type="auto">
<name>Task 2: Add all i18n keys for phase 2 in fr.json and en.json</name>
<files>i18n/locales/fr.json, i18n/locales/en.json</files>
<read_first>i18n/locales/fr.json, i18n/locales/en.json</read_first>
<action>
Add ALL the following i18n keys to BOTH fr.json and en.json. This is the complete set for phase 2 — hero, hytale page, pricing, testimonials, nav. Every key added in FR must also exist in EN.
**nav section** — add `"hytale": "Hytale"` (same in both languages)
**home section** — update/add these keys (per D-01, D-02, D-03, D-04, UI-SPEC copywriting contract):
- `home.title`: FR "Hytale Plugin Developer" / EN "Hytale Plugin Developer"
- `home.subtitle`: FR "Des plugins performants et sur-mesure pour votre serveur Hytale" / EN "High-performance, custom plugins for your Hytale server"
- `home.badge.available`: FR "Disponible pour vos projets" / EN "Available for projects"
- `home.cta.discord`: FR "Rejoindre sur Discord" / EN "Join on Discord"
- `home.cta.contact`: FR "Me contacter" / EN "Contact me"
- `home.stats.projects`: FR "50+ projets" / EN "50+ projects"
- `home.stats.rating`: FR "Note 5.0" / EN "5.0 rating"
- `home.terminal.role`: FR "Hytale Plugin Developer" / EN "Hytale Plugin Developer"
**hytale section** — create entirely (per D-08, CONT-02, UI-SPEC):
- `hytale.hero.label`: FR "// hytale" / EN "// hytale"
- `hytale.hero.title`: FR "Plugins Hytale sur-mesure" / EN "Custom Hytale Plugins"
- `hytale.hero.subtitle`: FR "Developpement de plugins performants pour votre serveur Hytale, de la conception a la livraison." / EN "High-performance plugin development for your Hytale server, from design to delivery."
- `hytale.services.label`: FR "// services" / EN "// services"
- `hytale.services.title`: FR "Expertise Hytale" / EN "Hytale Expertise"
- `hytale.services.subtitle`: FR "Des solutions adaptees a chaque besoin" / EN "Solutions tailored to every need"
- `hytale.pricing.label`: FR "// tarifs" / EN "// pricing"
- `hytale.pricing.title`: FR "Tarifs" / EN "Pricing"
- `hytale.pricing.subtitle`: FR "Des offres transparentes pour chaque projet" / EN "Transparent pricing for every project"
- `hytale.pricing.cta`: FR "Demander un devis" / EN "Request a quote"
- `hytale.pricing.popular`: FR "Populaire" / EN "Popular"
- `hytale.pricing.from`: FR "A partir de" / EN "From"
- `hytale.pricing.perMonth`: FR "/mois" / EN "/month"
- `hytale.pricing.onQuote`: FR "Sur devis" / EN "Custom quote"
Per tier (simple, complex, custom, maintenance, web) — UI-SPEC copywriting contract:
- `hytale.pricing.simple.name`: FR "Plugin Simple" / EN "Simple Plugin"
- `hytale.pricing.simple.description`: FR "Un plugin basique avec des fonctionnalites simples" / EN "A basic plugin with simple features"
- `hytale.pricing.simple.features.0` through `.3`: FR features list / EN features list
- "Fonctionnalites de base" / "Basic features"
- "Configuration simple" / "Simple configuration"
- "Documentation incluse" / "Documentation included"
- "Support 30 jours" / "30-day support"
- `hytale.pricing.complex.name`: FR "Plugin Complexe" / EN "Complex Plugin"
- `hytale.pricing.complex.description`: FR "Un plugin avance avec des systemes complexes" / EN "An advanced plugin with complex systems"
- `hytale.pricing.complex.features.0` through `.3`:
- "Systemes avances" / "Advanced systems"
- "Integration API" / "API integration"
- "Tests complets" / "Comprehensive testing"
- "Support 60 jours" / "60-day support"
- `hytale.pricing.custom.name`: FR "Developpement Sur-Mesure" / EN "Custom Development"
- `hytale.pricing.custom.description`: FR "Un projet entierement personnalise" / EN "A fully customized project"
- `hytale.pricing.custom.features.0` through `.3`:
- "Architecture sur-mesure" / "Custom architecture"
- "Fonctionnalites illimitees" / "Unlimited features"
- "Support prioritaire" / "Priority support"
- "Maintenance incluse" / "Maintenance included"
- `hytale.pricing.maintenance.name`: FR "Maintenance & Support" / EN "Maintenance & Support"
- `hytale.pricing.maintenance.description`: FR "Support continu pour vos plugins" / EN "Ongoing support for your plugins"
- `hytale.pricing.maintenance.features.0` through `.3`:
- "Mises a jour regulieres" / "Regular updates"
- "Corrections de bugs" / "Bug fixes"
- "Support technique" / "Technical support"
- "Monitoring" / "Monitoring"
- `hytale.pricing.web.name`: FR "Developpement Web" / EN "Web Development"
- `hytale.pricing.web.description`: FR "Sites web et applications pour votre communaute" / EN "Websites and apps for your community"
- `hytale.pricing.web.features.0` through `.3`:
- "Site responsive" / "Responsive website"
- "SEO optimise" / "SEO optimized"
- "Dashboard admin" / "Admin dashboard"
- "Integration Discord" / "Discord integration"
**seo section** — add hytale page SEO keys (per I18N-04):
- `seo.hytale.title`: FR "Plugins Hytale sur-mesure | Killian' DAL-CIN" / EN "Custom Hytale Plugins | Killian' DAL-CIN"
- `seo.hytale.description`: FR "Developpement de plugins Hytale performants et sur-mesure. Du plugin simple au projet complexe, des solutions adaptees a votre serveur." / EN "High-performance custom Hytale plugin development. From simple plugins to complex projects, solutions tailored to your server."
**testimonials section** — add/update keys:
- `testimonials.label`: FR "// temoignages" / EN "// testimonials"
- `testimonials.title`: FR "Ce que disent nos clients" / EN "What our clients say"
- `testimonials.stats.reviews`: FR "avis clients" / EN "client reviews"
- `testimonials.stats.rating`: FR "note moyenne" / EN "average rating"
- `testimonials.stats.projects`: FR "projets livres" / EN "projects delivered"
- `testimonials.empty`: FR "Aucun temoignage disponible pour l'instant." / EN "No testimonials available yet."
IMPORTANT: Preserve ALL existing keys in both JSON files. Only ADD new keys and UPDATE the specific home.title, home.subtitle, home.cta keys. Do NOT remove any existing keys.
</action>
<verify>
<automated>grep -q "hytale.hero.title" i18n/locales/fr.json && grep -q "hytale.hero.title" i18n/locales/en.json && grep -q "hytale.pricing.simple.name" i18n/locales/fr.json && grep -q "nav.*hytale" i18n/locales/fr.json && grep -q "seo.hytale" i18n/locales/en.json && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- `grep "hytale.hero.title" i18n/locales/fr.json` finds the key (nested or flat)
- `grep "hytale.pricing.simple.name" i18n/locales/en.json` finds the key
- `grep "hytale" i18n/locales/fr.json | wc -l` returns 30+ matches
- `grep "nav" i18n/locales/fr.json` includes "hytale"
- `grep "seo" i18n/locales/en.json` includes hytale title and description
- All existing keys are preserved (no regression)
</acceptance_criteria>
<done>Both fr.json and en.json contain all i18n keys for hero, hytale page, pricing tiers, testimonials, nav, and SEO — complete bilingual coverage for phase 2</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Static data files | All data is hardcoded in TypeScript files, no user input |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-02-01 | I (Information Disclosure) | pricing.ts | accept | Pricing is intentionally public — displayed on website |
| T-02-02 | T (Tampering) | i18n JSON files | accept | Static files served via SSR, no runtime modification possible |
</threat_model>
<verification>
- `pnpm typecheck` passes (no TypeScript errors from new types/data)
- All 5 pricing tiers exist in pricing.ts
- site.ts contains "Hytale Plugin Developer" in title and jobTitle
- reviewCount and totalReviews both show 5
- fr.json and en.json have matching key sets for all hytale.* keys
</verification>
<success_criteria>
- Types compile cleanly with strict mode
- Data layer is complete — plans 02 and 03 can reference all types, data, and i18n keys
- No hardcoded strings remain in the data layer
</success_criteria>
<output>
After completion, create `.planning/phases/02-content/02-01-SUMMARY.md`
</output>
+208
View File
@@ -0,0 +1,208 @@
---
phase: 02-content
plan: 02
type: execute
wave: 2
depends_on: [02-01]
files_modified:
- app/components/sections/HeroSection.vue
- app/components/sections/TestimonialsSection.vue
- app/components/layout/AppHeader.vue
- app/pages/index.vue
autonomous: true
requirements: [CONT-01, CONT-04]
must_haves:
truths:
- "Homepage H1 contains 'Hytale' via i18n key"
- "Hero CTAs are Discord + Contact per D-02"
- "Badge uses i18n key, not hardcoded string"
- "TestimonialsSection accepts featured prop and filters accordingly"
- "Homepage shows 2-3 featured testimonials only"
- "Nav includes /hytale link"
artifacts:
- path: "app/components/sections/HeroSection.vue"
provides: "Hytale-branded hero with i18n"
contains: "t('home.title')"
- path: "app/components/sections/TestimonialsSection.vue"
provides: "Filterable testimonials with featured prop"
contains: "featured"
- path: "app/components/layout/AppHeader.vue"
provides: "Nav with /hytale link"
contains: "hytale"
key_links:
- from: "app/components/sections/HeroSection.vue"
to: "i18n/locales/fr.json"
via: "t('home.title')"
pattern: "t\\('home\\."
- from: "app/components/sections/TestimonialsSection.vue"
to: "app/data/testimonials.ts"
via: "import testimonials + filter on featured"
pattern: "featured"
---
<objective>
Refonte hero homepage for Hytale branding, testimonials featured filtering, and nav update.
Purpose: The homepage must immediately communicate that Killian is a Hytale developer (CONT-01), show featured client testimonials (CONT-04), and provide navigation to the new /hytale page.
Output: Updated HeroSection, TestimonialsSection with featured prop, AppHeader with /hytale nav link.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/02-content/02-CONTEXT.md
@.planning/phases/02-content/02-RESEARCH.md
@.planning/phases/02-content/02-UI-SPEC.md
@.planning/phases/02-content/02-01-SUMMARY.md
@app/components/sections/HeroSection.vue
@app/components/sections/TestimonialsSection.vue
@app/components/layout/AppHeader.vue
@app/pages/index.vue
@i18n/locales/fr.json
@i18n/locales/en.json
<interfaces>
<!-- From shared/types/index.ts (created in plan 01): -->
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[];
}
<!-- From app/data/testimonials.ts (updated in plan 01): -->
export const testimonials: Testimonial[] // 5 items, 3 with featured: true
export const testimonialsStats: TestimonialsStats // totalReviews: 5
<!-- i18n keys available from plan 01: -->
home.title, home.subtitle, home.badge.available, home.cta.discord, home.cta.contact,
home.stats.projects, home.stats.rating, home.terminal.role, nav.hytale
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Refonte HeroSection.vue for Hytale branding + i18n</name>
<files>app/components/sections/HeroSection.vue</files>
<read_first>app/components/sections/HeroSection.vue, i18n/locales/fr.json</read_first>
<action>
Modify HeroSection.vue to rebrand for Hytale per D-01 through D-07 and UI-SPEC:
1. **H1 text** — Replace current title with `{{ t('home.title') }}` (renders "Hytale Plugin Developer"). Keep the existing gradient text styling from UI-SPEC: `bg-gradient-to-r from-brand-500 via-brand-400 to-emerald-400 bg-clip-text text-transparent`.
2. **Subtitle** — Replace with `{{ t('home.subtitle') }}` (per D-04).
3. **Badge "Available for projects"** — The hardcoded string (around line 31) must become `{{ t('home.badge.available') }}` (per D-03). Keep the animated ping dot and existing styling.
4. **CTAs** — Replace existing CTA buttons with exactly 2 buttons per D-02:
- Primary: Discord link — `UButton` with `color="primary"`, icon `i-simple-icons-discord`, text `{{ t('home.cta.discord') }}`, links to Discord URL from siteConfig.social (find Discord entry). Add `target="_blank" rel="noopener"`.
- Secondary: Contact — `UButton` with `variant="outline"`, text `{{ t('home.cta.contact') }}`, links to `localePath('/contact')`.
5. **Floating stats cards** — Replace hardcoded "50+ projects" and "5.0 rating" strings (around lines 148-153) with `{{ t('home.stats.projects') }}` and `{{ t('home.stats.rating') }}`.
6. **Terminal role** — If the hero has a terminal/code block showing a role string (like 'Full Stack Dev'), replace with `{{ t('home.terminal.role') }}` or the literal 'Hytale Plugin Developer'.
7. **Right column** — Per D-05 and D-06, keep the existing 2-column grid layout. Keep whatever is on the right side (placeholder/illustration) as-is. Do NOT add an image.
8. Import `siteConfig` from `~/data/site` to get the Discord URL: `siteConfig.social.find(s => s.name === 'Discord')?.url`.
</action>
<verify>
<automated>grep -q "t('home.title')" app/components/sections/HeroSection.vue && grep -q "t('home.badge.available')" app/components/sections/HeroSection.vue && grep -q "t('home.cta.discord')" app/components/sections/HeroSection.vue && grep -q "t('home.cta.contact')" app/components/sections/HeroSection.vue && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- `grep "Available for projects" app/components/sections/HeroSection.vue` returns NO matches (hardcoded string removed)
- `grep "t('home.title')" app/components/sections/HeroSection.vue` matches
- `grep "t('home.badge.available')" app/components/sections/HeroSection.vue` matches
- `grep "discord" app/components/sections/HeroSection.vue` shows Discord CTA
- `grep "t('home.cta.contact')" app/components/sections/HeroSection.vue` matches
- No hardcoded English/French strings remain in visible text areas
</acceptance_criteria>
<done>HeroSection displays "Hytale Plugin Developer" H1, Hytale subtitle, i18n badge, Discord+Contact CTAs, i18n stats — all via t() keys</done>
</task>
<task type="auto">
<name>Task 2: TestimonialsSection featured prop + AppHeader nav + index.vue wiring</name>
<files>app/components/sections/TestimonialsSection.vue, app/components/layout/AppHeader.vue, app/pages/index.vue</files>
<read_first>app/components/sections/TestimonialsSection.vue, app/components/layout/AppHeader.vue, app/pages/index.vue</read_first>
<action>
1. **TestimonialsSection.vue** — Add `featured` prop for homepage filtering (per D-15, D-17):
```typescript
const props = defineProps<{
featured?: boolean
}>()
```
Use a computed to filter: `const displayed = computed(() => props.featured ? testimonials.filter(t => t.featured) : testimonials)`.
Replace direct `testimonials` usage in the template with `displayed`. Keep existing carousel/scroll pattern (`overflow-x-auto snap-x snap-mandatory`). Keep all existing styling and structure.
Also ensure any hardcoded testimonial-related strings use i18n keys from plan 01:
- Section label should use `t('testimonials.label')`
- Section title should use `t('testimonials.title')`
- Stats labels: `t('testimonials.stats.reviews')`, `t('testimonials.stats.rating')`, `t('testimonials.stats.projects')`
2. **AppHeader.vue** — Add /hytale nav link (per D-19). Insert after 'home' in navLinks:
```typescript
{ key: 'hytale', path: '/hytale' },
```
This single line addition makes it appear in both desktop nav and mobile drawer (same navLinks array drives both).
3. **index.vue** — Pass `featured` prop to TestimonialsSection:
```html
<TestimonialsSection featured />
```
This makes the homepage show only 2-3 featured testimonials instead of all 5.
</action>
<verify>
<automated>grep -q "featured" app/components/sections/TestimonialsSection.vue && grep -q "hytale" app/components/layout/AppHeader.vue && grep -q "featured" app/pages/index.vue && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- `grep "defineProps" app/components/sections/TestimonialsSection.vue` shows featured prop
- `grep "hytale" app/components/layout/AppHeader.vue` shows nav entry
- `grep "TestimonialsSection" app/pages/index.vue` shows featured prop passed
- Existing carousel scroll pattern preserved in TestimonialsSection
</acceptance_criteria>
<done>TestimonialsSection filters by featured prop on homepage (3 shown), all 5 shown by default. Nav includes /hytale. index.vue passes featured prop.</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| External Discord link | Hero CTA opens external Discord URL |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-02-03 | S (Spoofing) | Discord link in HeroSection | mitigate | Use `rel="noopener"` on external link, URL from siteConfig (not user input) |
| T-02-04 | T (Tampering) | i18n keys | accept | Static JSON files, no runtime modification |
</threat_model>
<verification>
- Homepage H1 renders "Hytale Plugin Developer" (check via `curl localhost:3000 | grep -i hytale`)
- Homepage shows exactly 3 featured testimonials, not all 5
- /hytale link visible in nav (desktop and mobile)
- No hardcoded English/French strings in HeroSection or TestimonialsSection
- Badge shows i18n text, not "Available for projects" literal
</verification>
<success_criteria>
- Hero communicates Hytale positioning immediately
- Testimonials section is reusable with featured filter
- Navigation includes /hytale link
- All visible text uses i18n t() function
</success_criteria>
<output>
After completion, create `.planning/phases/02-content/02-02-SUMMARY.md`
</output>
+274
View File
@@ -0,0 +1,274 @@
---
phase: 02-content
plan: 03
type: execute
wave: 2
depends_on: [02-01]
files_modified:
- app/pages/hytale.vue
- app/components/sections/hytale/HytaleHeroSection.vue
- app/components/sections/hytale/HytalePricingSection.vue
- app/components/sections/hytale/HytaleServicesSection.vue
autonomous: false
requirements: [CONT-02, CONT-03, CONT-04]
must_haves:
truths:
- "/hytale route exists and renders SSR HTML"
- "Hytale page has 4 sections: hero, services, pricing, testimonials"
- "Pricing grid shows 5 tiers with correct prices and CTAs"
- "Each pricing CTA links to /contact"
- "All text uses i18n t() — no hardcoded strings"
- "Testimonials section shows all 5 on /hytale page"
- "Page has useSeoMeta with hytale-specific title/description"
artifacts:
- path: "app/pages/hytale.vue"
provides: "Hytale dedicated page with 4 sections"
contains: "useSeoMeta"
- path: "app/components/sections/hytale/HytaleHeroSection.vue"
provides: "Hero section for /hytale page"
contains: "t('hytale.hero"
- path: "app/components/sections/hytale/HytalePricingSection.vue"
provides: "Pricing grid with 5 tiers"
contains: "hytalePricing"
- path: "app/components/sections/hytale/HytaleServicesSection.vue"
provides: "Services/expertise section"
contains: "t('hytale.services"
key_links:
- from: "app/pages/hytale.vue"
to: "app/components/sections/hytale/HytaleHeroSection.vue"
via: "component composition"
pattern: "HytaleHeroSection"
- from: "app/components/sections/hytale/HytalePricingSection.vue"
to: "app/data/pricing.ts"
via: "import hytalePricing"
pattern: "hytalePricing"
- from: "app/components/sections/hytale/HytalePricingSection.vue"
to: "/contact"
via: "UButton :to localePath('/contact')"
pattern: "localePath.*contact"
---
<objective>
Create the dedicated /hytale page with hero, services, pricing grid, and testimonials sections.
Purpose: A visitor landing on /hytale sees Killian's Hytale plugin development services, transparent pricing tiers with CTAs to contact, and client testimonials proving track record (CONT-02, CONT-03, CONT-04).
Output: New hytale.vue page and 3 new section components in app/components/sections/hytale/.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/02-content/02-CONTEXT.md
@.planning/phases/02-content/02-RESEARCH.md
@.planning/phases/02-content/02-UI-SPEC.md
@.planning/phases/02-content/02-01-SUMMARY.md
@app/pages/index.vue
@app/components/sections/HeroSection.vue
@app/components/sections/TestimonialsSection.vue
@app/data/pricing.ts
@i18n/locales/fr.json
<interfaces>
<!-- From shared/types/index.ts (plan 01): -->
export interface PricingTier {
id: string
priceFixed: string | null
priceLabel?: string
featured?: boolean
}
<!-- From app/data/pricing.ts (plan 01): -->
export const hytalePricing: PricingTier[] // 5 tiers
<!-- From TestimonialsSection.vue (plan 02): -->
// Accepts optional `featured` prop — omit prop to show all 5 testimonials
<!-- i18n keys available (plan 01): -->
hytale.hero.label, hytale.hero.title, hytale.hero.subtitle,
hytale.services.label, hytale.services.title, hytale.services.subtitle,
hytale.pricing.label, hytale.pricing.title, hytale.pricing.subtitle,
hytale.pricing.cta, hytale.pricing.popular, hytale.pricing.from,
hytale.pricing.perMonth, hytale.pricing.onQuote,
hytale.pricing.{tierId}.name, hytale.pricing.{tierId}.description,
hytale.pricing.{tierId}.features.{0-3},
seo.hytale.title, seo.hytale.description
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Create hytale.vue page and 3 section components</name>
<files>app/pages/hytale.vue, app/components/sections/hytale/HytaleHeroSection.vue, app/components/sections/hytale/HytalePricingSection.vue, app/components/sections/hytale/HytaleServicesSection.vue</files>
<read_first>app/pages/index.vue, app/components/sections/HeroSection.vue, app/components/sections/ServicesSection.vue, app/data/pricing.ts</read_first>
<action>
Create 4 new files following existing project patterns (index.vue structure, section component patterns).
**1. app/pages/hytale.vue** — Follow index.vue pattern exactly (per D-08):
```vue
<script setup lang="ts">
const { t } = useI18n()
const localePath = useLocalePath()
useSeoMeta({
title: () => t('seo.hytale.title'),
description: () => t('seo.hytale.description'),
ogTitle: () => t('seo.hytale.title'),
ogDescription: () => t('seo.hytale.description'),
ogImage: 'https://killiandalcin.fr/og-image.png',
ogType: 'website',
})
useHead({
script: [{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Service',
name: 'Hytale Plugin Development',
provider: {
'@type': 'Person',
name: "Killian' DAL-CIN",
jobTitle: 'Hytale Plugin Developer',
},
}),
}],
})
</script>
<template>
<div>
<HytaleHeroSection />
<HytaleServicesSection />
<HytalePricingSection />
<TestimonialsSection />
</div>
</template>
```
Note: TestimonialsSection without `featured` prop shows all 5 testimonials (per D-15).
**2. app/components/sections/hytale/HytaleHeroSection.vue** — Simpler hero than homepage. Follow UI-SPEC:
- Mono label: `{{ t('hytale.hero.label') }}` with `font-mono text-sm text-brand-500`
- H1: `{{ t('hytale.hero.title') }}` with gradient text styling (same as homepage H1)
- Subtitle: `{{ t('hytale.hero.subtitle') }}` with `text-lg text-gray-500 dark:text-gray-400`
- Section padding: `py-16 md:py-24` (matching existing hero pattern)
- Center-aligned, max-width container: `max-w-4xl mx-auto text-center`
- Use `<script setup lang="ts">` with `const { t } = useI18n()`
**3. app/components/sections/hytale/HytaleServicesSection.vue** — Services/expertise:
- Mono label: `{{ t('hytale.services.label') }}`
- H2 title: `{{ t('hytale.services.title') }}`
- Subtitle: `{{ t('hytale.services.subtitle') }}`
- Display 3-4 service cards using `UCard` in a responsive grid `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6`
- Each card shows an icon (`UIcon` with lucide icons like `i-lucide-puzzle`, `i-lucide-settings`, `i-lucide-shield`), title, and short description
- Service content via i18n keys. Create simple service items inline (not from data file — services are page-specific):
- Plugin Development (i-lucide-puzzle)
- Server Configuration (i-lucide-settings)
- Maintenance & Support (i-lucide-shield-check)
- Add i18n keys for service items: `hytale.services.items.{id}.title` and `hytale.services.items.{id}.description` — these keys MUST also be added to fr.json and en.json (executor: add them to both locale files).
- Section padding: `py-16 md:py-24`, max-w-7xl container
**4. app/components/sections/hytale/HytalePricingSection.vue** — Pricing grid (per D-09, D-10, D-11, CONT-03, UI-SPEC):
- Mono label: `{{ t('hytale.pricing.label') }}`
- H2 title: `{{ t('hytale.pricing.title') }}`
- Subtitle: `{{ t('hytale.pricing.subtitle') }}`
- Import `hytalePricing` from `~/data/pricing`
- Grid: `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6` (5 tiers wrap to 3+2 or 2+3)
- Each tier is a `UCard`:
- If `tier.featured`, add `ring-2 ring-brand-500` class and `UBadge` with `{{ t('hytale.pricing.popular') }}`
- Header: `{{ t('hytale.pricing.${tier.id}.name') }}` as h3
- Price display: If `tier.priceFixed`, show `{{ t('hytale.pricing.from') }} {{ tier.priceFixed }}`. If not, show `{{ t('hytale.pricing.onQuote') }}`.
- Description: `{{ t('hytale.pricing.${tier.id}.description') }}`
- Features list: Loop 0-3, `{{ t('hytale.pricing.${tier.id}.features.${i}') }}` with `UIcon name="i-lucide-check"` prefix
- Footer CTA: `UButton` with `{{ t('hytale.pricing.cta') }}`, `:to="localePath('/contact')"`, `block` prop, `color="primary"` for featured tier / `variant="outline"` for others
- Hover effect on cards: `hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1 transition-all duration-200`
- All text via i18n — zero hardcoded strings
</action>
<verify>
<automated>test -f app/pages/hytale.vue && test -f app/components/sections/hytale/HytaleHeroSection.vue && test -f app/components/sections/hytale/HytalePricingSection.vue && test -f app/components/sections/hytale/HytaleServicesSection.vue && grep -q "useSeoMeta" app/pages/hytale.vue && grep -q "hytalePricing" app/components/sections/hytale/HytalePricingSection.vue && grep -q "localePath.*contact" app/components/sections/hytale/HytalePricingSection.vue && echo "PASS" || echo "FAIL"</automated>
</verify>
<acceptance_criteria>
- `test -f app/pages/hytale.vue` succeeds (page exists)
- `test -f app/components/sections/hytale/HytalePricingSection.vue` succeeds
- `grep "useSeoMeta" app/pages/hytale.vue` shows SEO meta setup
- `grep "hytalePricing" app/components/sections/hytale/HytalePricingSection.vue` shows data import
- `grep "localePath" app/components/sections/hytale/HytalePricingSection.vue` shows CTA links to /contact
- `grep "UCard" app/components/sections/hytale/HytalePricingSection.vue` shows Nuxt UI cards
- `grep "t('hytale" app/components/sections/hytale/HytaleHeroSection.vue` shows i18n usage
- No hardcoded French or English strings in any of the 4 files (except schema.org JSON-LD values which are language-neutral)
</acceptance_criteria>
<done>4 files created: hytale.vue page with SEO + JSON-LD, 3 section components. /hytale renders hero, services, pricing (5 tiers with CTAs to /contact), and all 5 testimonials.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 2: Verify /hytale page and homepage changes</name>
<files>app/pages/hytale.vue</files>
<action>Human visual verification of the complete phase 2 content delivery. Start dev server with `pnpm dev` if not running. Verify homepage hero, /hytale page, and navigation across FR and EN locales.</action>
<verify>Human visual verification — no automated test</verify>
<done>User approved homepage hero, /hytale page with pricing and testimonials, and bilingual content</done>
<what-built>Complete Hytale content phase: homepage hero rebranded, /hytale page with pricing grid and testimonials, nav link added</what-built>
<how-to-verify>
1. Run `pnpm dev` if not already running
2. Visit http://localhost:3000 — verify:
- H1 says "Hytale Plugin Developer" (gradient text)
- Badge says "Disponible pour vos projets" (FR) or "Available for projects" (EN)
- Two CTAs: Discord + Contact
- 3 featured testimonials shown (not all 5)
3. Visit http://localhost:3000/hytale — verify:
- Page loads with 4 sections: hero, services, pricing, testimonials
- Pricing grid shows 5 tiers with prices and "Demander un devis" buttons
- One tier has "Populaire" badge
- All CTA buttons link to /contact
- All 5 testimonials shown at bottom
4. Visit http://localhost:3000/en/hytale — verify English content (no raw i18n keys visible)
5. Check nav bar — /hytale link present in both desktop and mobile menu
6. View page source (Ctrl+U) on /hytale — confirm SSR renders HTML content (not empty div)
</how-to-verify>
<resume-signal>Type "approved" or describe issues to fix</resume-signal>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| JSON-LD output | Schema.org structured data rendered in page head |
| CTA links | Pricing buttons redirect to /contact |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-02-05 | I (Information Disclosure) | JSON-LD in hytale.vue | accept | Intentionally public structured data for SEO |
| T-02-06 | T (Tampering) | Pricing data | accept | Static TypeScript data, no user input, SSR-rendered |
</threat_model>
<verification>
- `curl localhost:3000/hytale` returns HTML with pricing tier content
- `curl localhost:3000/hytale | grep -i "Hytale"` returns multiple matches
- `curl localhost:3000/en/hytale` returns English content
- `pnpm typecheck` passes
- View source shows SSR-rendered content (not client-only)
</verification>
<success_criteria>
- /hytale page exists with 4 sections, all content bilingual
- Pricing grid shows 5 tiers with working CTAs to /contact
- All 5 testimonials visible on /hytale, only 3 featured on homepage
- SSR renders full HTML (no JS required to see content)
- Human verification confirms visual quality
</success_criteria>
<output>
After completion, create `.planning/phases/02-content/02-03-SUMMARY.md`
</output>