Files
Portfolio-Game/docs/implementation-artifacts/1-7-page-resume-express-mode-presse.md
skycel ec1ae92799 🎉 Init monorepo Nuxt 4 + Laravel 12 (Story 1.1)
Setup complet de l'infrastructure projet :
- Frontend Nuxt 4 (SSR, TypeScript, i18n, Pinia, TailwindCSS)
- Backend Laravel 12 API-only avec middleware X-API-Key et CORS
- Design tokens (sky-dark, sky-accent, sky-text) et polices (Merriweather, Inter)
- Documentation planning et implementation artifacts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 02:08:56 +01:00

16 KiB

Story 1.7: Page résumé express et mode pressé

Status: ready-for-dev

Story

As a visiteur pressé ou recruteur, I want une vue condensée de toutes les informations essentielles, so that je peux évaluer le développeur en 30 secondes.

Acceptance Criteria

  1. Given le visiteur accède à /resume (FR) ou /en/resume (EN) directement ou via "Mode express" When la page se charge Then le contenu affiché comprend : nom, titre, photo/avatar, accroche (5s)
  2. And les compétences clés avec stack technique sont visibles (10s)
  3. And 3-4 projets highlights avec liens sont affichés (10s)
  4. And un CTA de contact direct est visible (5s)
  5. And un bouton discret "Voir l'aventure" invite à l'expérience complète
  6. And la page est fonctionnelle en FR et EN
  7. And les données sont chargées depuis l'API (projets, skills)
  8. And les meta tags SEO sont optimisés pour cette page
  9. And le layout minimal.vue est utilisé

Tasks / Subtasks

  • Task 1: Structure de la page résumé (AC: #1, #9)

    • Implémenter frontend/app/pages/resume.vue
    • Utiliser le layout minimal : definePageMeta({ layout: 'minimal' })
    • Structure en sections verticales : Hero → Skills → Projets → Contact
    • Design épuré, scannable en 30 secondes
  • Task 2: Section Hero (5s) (AC: #1)

    • Photo/avatar de Célian (image optimisée via nuxt/image)
    • Nom : "Célian" (ou nom complet)
    • Titre : "Développeur Full-Stack"
    • Accroche courte : 1-2 phrases percutantes traduites
    • Liens sociaux : GitHub, LinkedIn (icônes cliquables)
  • Task 3: Section Compétences (10s) (AC: #2, #7)

    • Titre de section : "Stack technique"
    • Afficher les compétences principales par catégorie (Frontend, Backend, Tools)
    • Format compact : badges ou liste avec icônes
    • Charger depuis l'API /api/skills (filtrer les principales)
    • Limiter à 8-12 compétences max pour la lisibilité
  • Task 4: Section Projets highlights (10s) (AC: #3, #7)

    • Titre de section : "Projets récents"
    • Afficher 3-4 projets featured
    • Format compact : titre + 1 ligne description + lien
    • Charger depuis l'API /api/projects?featured=true
    • Liens vers les détails (ouvre dans nouvel onglet ou garde sur resume)
  • Task 5: Section Contact (5s) (AC: #4)

    • CTA principal : "Me contacter" (lien vers /contact ou email direct)
    • Email visible (cliquable mailto:)
    • Optionnel : téléphone si souhaité
    • Style accent pour le CTA principal
  • Task 6: Bouton "Voir l'aventure" (AC: #5)

    • Position discrète mais visible (en bas ou en sidebar)
    • Texte : "Envie d'explorer ? Découvrir l'aventure complète"
    • Lien vers / (landing page)
    • Style secondaire, pas en compétition avec le CTA contact
  • Task 7: Chargement des données API (AC: #7)

    • Utiliser useFetch ou useAsyncData pour charger skills et projets
    • Gérer les états loading et error
    • Cache côté client pour éviter les appels répétés
    • SSR : données chargées côté serveur pour SEO
  • Task 8: Traductions bilingue (AC: #6)

    • Ajouter toutes les traductions dans i18n/fr.json et i18n/en.json
    • Section titles, accroche, CTA labels
    • Le contenu API est déjà traduit (Story 1.3)
  • Task 9: Meta tags SEO optimisés (AC: #8)

    • Utiliser useSeo() avec meta spécifiques
    • Title : "Célian - Développeur Full-Stack | CV Express"
    • Description : optimisée pour les recruteurs
    • Open Graph image : image de preview professionnelle
    • Structured data (JSON-LD) pour Person/Developer (optionnel)
  • Task 10: Responsive et accessibilité (AC: #1)

    • Mobile : sections empilées verticalement
    • Desktop : layout plus aéré, possible 2 colonnes pour skills/projets
    • Contraste suffisant (WCAG AA)
    • Navigation clavier fluide
    • Skip link vers le contenu principal
  • Task 11: Validation finale (AC: tous)

    • Page accessible via /resume (FR) et /en/resume (EN)
    • Chargement < 2s (données légères)
    • Toutes les sections visibles sans scroll excessif sur desktop
    • CTA contact fonctionnel
    • Lien vers aventure fonctionne
    • Layout minimal utilisé (pas de header complet)
    • SEO : vérifier meta tags dans le code source

Dev Notes

Structure de la page résumé

┌─────────────────────────────────────────────────────────────────┐
│                    PAGE RÉSUMÉ EXPRESS                          │
│                    (Layout minimal)                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────┐                                                    │
│  │  Photo  │  Célian                                            │
│  │         │  Développeur Full-Stack                            │
│  └─────────┘  "Passionné par les expériences web innovantes"   │
│               [GitHub] [LinkedIn]                               │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│  STACK TECHNIQUE                                                │
│  ┌────────────────────────────────────────────────────────────┐│
│  │ Frontend: Vue.js • Nuxt • TypeScript • TailwindCSS        ││
│  │ Backend:  Laravel • PHP • Node.js • MariaDB               ││
│  │ Tools:    Git • Docker • CI/CD                            ││
│  └────────────────────────────────────────────────────────────┘│
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│  PROJETS RÉCENTS                                                │
│  ┌────────────────────────────────────────────────────────────┐│
│  │ • Skycel Portfolio - Portfolio gamifié interactif    [→]  ││
│  │ • Projet E-commerce - Boutique en ligne moderne      [→]  ││
│  │ • Dashboard Analytics - Interface de visualisation   [→]  ││
│  └────────────────────────────────────────────────────────────┘│
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│              ┌──────────────────────────┐                       │
│              │     ME CONTACTER         │                       │
│              └──────────────────────────┘                       │
│                                                                 │
│              contact@skycel.fr                                  │
│                                                                 │
│         "Envie d'explorer ? Voir l'aventure complète →"        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Implémentation de la page

<!-- frontend/app/pages/resume.vue -->
<template>
  <div class="max-w-3xl mx-auto px-4 py-8">
    <!-- Section Hero -->
    <section class="text-center mb-12">
      <NuxtImg
        src="/images/avatar.jpg"
        alt="Célian"
        width="120"
        height="120"
        class="rounded-full mx-auto mb-4"
      />
      <h1 class="text-3xl font-ui font-bold mb-2">Célian</h1>
      <p class="text-xl text-sky-accent mb-3">{{ $t('resume.title') }}</p>
      <p class="text-sky-text/80 font-narrative mb-4">{{ $t('resume.tagline') }}</p>

      <div class="flex justify-center gap-4">
        <a href="https://github.com/celian" target="_blank" rel="noopener" class="text-sky-text/60 hover:text-sky-accent transition-colors">
          <span class="sr-only">GitHub</span>
          <!-- GitHub icon -->
        </a>
        <a href="https://linkedin.com/in/celian" target="_blank" rel="noopener" class="text-sky-text/60 hover:text-sky-accent transition-colors">
          <span class="sr-only">LinkedIn</span>
          <!-- LinkedIn icon -->
        </a>
      </div>
    </section>

    <!-- Section Skills -->
    <section class="mb-12">
      <h2 class="text-xl font-ui font-semibold mb-4 text-sky-accent">
        {{ $t('resume.skills_title') }}
      </h2>

      <div v-if="skillsLoading" class="text-sky-text/50">{{ $t('common.loading') }}</div>

      <div v-else class="space-y-3">
        <div v-for="category in skillsByCategory" :key="category.name">
          <span class="text-sky-text/60 text-sm">{{ category.name }}:</span>
          <span class="ml-2">
            <span
              v-for="(skill, i) in category.skills"
              :key="skill.slug"
              class="text-sky-text"
            >
              {{ skill.name }}<span v-if="i < category.skills.length - 1">  </span>
            </span>
          </span>
        </div>
      </div>
    </section>

    <!-- Section Projets -->
    <section class="mb-12">
      <h2 class="text-xl font-ui font-semibold mb-4 text-sky-accent">
        {{ $t('resume.projects_title') }}
      </h2>

      <div v-if="projectsLoading" class="text-sky-text/50">{{ $t('common.loading') }}</div>

      <ul v-else class="space-y-3">
        <li v-for="project in featuredProjects" :key="project.slug" class="flex items-start gap-2">
          <span class="text-sky-accent"></span>
          <div>
            <NuxtLink
              :to="localePath(`/projets/${project.slug}`)"
              class="font-semibold hover:text-sky-accent transition-colors"
            >
              {{ project.title }}
            </NuxtLink>
            <span class="text-sky-text/60 text-sm ml-2">{{ project.short_description }}</span>
          </div>
        </li>
      </ul>
    </section>

    <!-- Section Contact -->
    <section class="text-center mb-8">
      <NuxtLink
        :to="localePath('/contact')"
        class="inline-block px-8 py-4 bg-sky-accent text-sky-dark font-ui font-bold rounded-lg hover:bg-sky-accent-hover transition-colors"
      >
        {{ $t('resume.cta_contact') }}
      </NuxtLink>

      <p class="mt-4 text-sky-text/60">
        <a href="mailto:contact@skycel.fr" class="hover:text-sky-accent transition-colors">
          contact@skycel.fr
        </a>
      </p>
    </section>

    <!-- Lien vers aventure -->
    <div class="text-center border-t border-sky-text/10 pt-6">
      <NuxtLink
        :to="localePath('/')"
        class="text-sky-text/50 hover:text-sky-accent text-sm transition-colors"
      >
        {{ $t('resume.adventure_link') }} 
      </NuxtLink>
    </div>
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  layout: 'minimal',
})

const { t } = useI18n()
const localePath = useLocalePath()
const { apiFetch } = useApi()
const { setPageMeta } = useSeo()

// SEO
setPageMeta({
  title: t('resume.meta_title'),
  description: t('resume.meta_description'),
})

// Chargement des skills
const { data: skills, pending: skillsLoading } = await useFetch('/api/skills', {
  baseURL: useRuntimeConfig().public.apiUrl,
  headers: {
    'X-API-Key': useRuntimeConfig().public.apiKey,
    'Accept-Language': useI18n().locale.value,
  },
})

// Chargement des projets featured
const { data: projects, pending: projectsLoading } = await useFetch('/api/projects', {
  baseURL: useRuntimeConfig().public.apiUrl,
  headers: {
    'X-API-Key': useRuntimeConfig().public.apiKey,
    'Accept-Language': useI18n().locale.value,
  },
  query: { featured: true },
})

// Grouper les skills par catégorie
const skillsByCategory = computed(() => {
  if (!skills.value?.data) return []

  const categories = ['Frontend', 'Backend', 'Tools']
  return categories.map(cat => ({
    name: cat,
    skills: skills.value.data.filter((s: any) => s.category === cat).slice(0, 4),
  })).filter(c => c.skills.length > 0)
})

// Projets featured (max 4)
const featuredProjects = computed(() => {
  return projects.value?.data?.slice(0, 4) || []
})
</script>

Traductions à ajouter

// frontend/i18n/fr.json
{
  "resume": {
    "title": "Développeur Full-Stack",
    "tagline": "Passionné par les expériences web innovantes et immersives",
    "skills_title": "Stack technique",
    "projects_title": "Projets récents",
    "cta_contact": "Me contacter",
    "adventure_link": "Envie d'explorer ? Découvrir l'aventure complète",
    "meta_title": "Célian - Développeur Full-Stack | CV Express",
    "meta_description": "Développeur Full-Stack spécialisé en Vue.js, Nuxt, Laravel. Découvrez mon profil et mes projets en 30 secondes."
  }
}
// frontend/i18n/en.json
{
  "resume": {
    "title": "Full-Stack Developer",
    "tagline": "Passionate about innovative and immersive web experiences",
    "skills_title": "Tech Stack",
    "projects_title": "Recent Projects",
    "cta_contact": "Contact Me",
    "adventure_link": "Want to explore? Discover the full adventure",
    "meta_title": "Célian - Full-Stack Developer | Quick Resume",
    "meta_description": "Full-Stack Developer specialized in Vue.js, Nuxt, Laravel. Discover my profile and projects in 30 seconds."
  }
}

Dépendances

Cette story DÉPEND de :

  • Story 1.3 : API bilingue, useApi composable
  • Story 1.4 : Layout minimal.vue, useSeo composable
  • Story 1.2 : API projects et skills fonctionnels

Cette story PRÉPARE pour :

  • URL directe pour candidatures (usage recruteurs)
  • Alternative à l'expérience gamifiée

Project Structure Notes

Fichiers à créer/modifier :

frontend/app/
├── pages/
│   └── resume.vue               # CRÉER
├── public/
│   └── images/
│       └── avatar.jpg           # AJOUTER (photo Célian)
└── i18n/
    ├── fr.json                  # MODIFIER (ajouter resume.*)
    └── en.json                  # MODIFIER (ajouter resume.*)

Performance

  • Budget temps : Chargement < 2s
  • Données légères : Skills (8-12 items), Projets (3-4 items)
  • SSR : Données chargées côté serveur pour SEO optimal
  • Images : Avatar optimisé via nuxt/image (WebP, dimensions fixes)

References

  • [Source: docs/planning-artifacts/epics.md#Story-1.7]
  • [Source: docs/planning-artifacts/ux-design-specification.md#Page-Resume]
  • [Source: docs/prd-gamification.md#FR1]

Technical Requirements

Requirement Value Source
Layout minimal.vue Architecture
Temps lecture ~30 secondes UX Design
Projets affichés 3-4 featured UX Design
Skills affichés 8-12 max UX Design
SSR Required NFR5

Dev Agent Record

Agent Model Used

{{agent_model_name_version}}

Debug Log References

Completion Notes List

Change Log

Date Change Author
2026-02-03 Story créée avec contexte complet SM Agent

File List