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

13 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
02-content 03 execute 2
02-01
app/pages/hytale.vue
app/components/sections/hytale/HytaleHeroSection.vue
app/components/sections/hytale/HytalePricingSection.vue
app/components/sections/hytale/HytaleServicesSection.vue
false
CONT-02
CONT-03
CONT-04
truths artifacts key_links
/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
path provides contains
app/pages/hytale.vue Hytale dedicated page with 4 sections useSeoMeta
path provides contains
app/components/sections/hytale/HytaleHeroSection.vue Hero section for /hytale page t('hytale.hero
path provides contains
app/components/sections/hytale/HytalePricingSection.vue Pricing grid with 5 tiers hytalePricing
path provides contains
app/components/sections/hytale/HytaleServicesSection.vue Services/expertise section t('hytale.services
from to via pattern
app/pages/hytale.vue app/components/sections/hytale/HytaleHeroSection.vue component composition HytaleHeroSection
from to via pattern
app/components/sections/hytale/HytalePricingSection.vue app/data/pricing.ts import hytalePricing hytalePricing
from to via pattern
app/components/sections/hytale/HytalePricingSection.vue /contact UButton :to localePath('/contact') localePath.*contact
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/.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_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

export interface PricingTier { id: string priceFixed: string | null priceLabel?: string featured?: boolean }

export const hytalePricing: PricingTier[] // 5 tiers

// Accepts optional featured prop — omit prop to show all 5 testimonials

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

Task 1: Create hytale.vue page and 3 section components app/pages/hytale.vue, app/components/sections/hytale/HytaleHeroSection.vue, app/components/sections/hytale/HytalePricingSection.vue, app/components/sections/hytale/HytaleServicesSection.vue app/pages/index.vue, app/components/sections/HeroSection.vue, app/components/sections/ServicesSection.vue, app/data/pricing.ts 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):

<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 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" <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> 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.
Task 2: Verify /hytale page and homepage changes app/pages/hytale.vue 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. Human visual verification — no automated test User approved homepage hero, /hytale page with pricing and testimonials, and bilingual content Complete Hytale content phase: homepage hero rebranded, /hytale page with pricing grid and testimonials, nav link added 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) Type "approved" or describe issues to fix

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

<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>
After completion, create `.planning/phases/02-content/02-03-SUMMARY.md`