Files
portfolio/.planning/phases/02-content/02-03-PLAN.md
T

275 lines
13 KiB
Markdown

---
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>