Complete resume page with hero section, skills badges, projects list, contact CTA and adventure link. Uses minimal layout, loads data from API with graceful fallbacks, SEO optimized for recruiters. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
423 lines
18 KiB
Markdown
423 lines
18 KiB
Markdown
# Story 1.7: Page résumé express et mode pressé
|
|
|
|
Status: review
|
|
|
|
## 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
|
|
|
|
- [x] **Task 1: Structure de la page résumé** (AC: #1, #9)
|
|
- [x] Implémenter `frontend/app/pages/resume.vue`
|
|
- [x] Utiliser le layout minimal : `definePageMeta({ layout: 'minimal' })`
|
|
- [x] Structure en sections verticales : Hero â Skills â Projets â Contact
|
|
- [x] Design épuré, scannable en 30 secondes
|
|
|
|
- [x] **Task 2: Section Hero (5s)** (AC: #1)
|
|
- [x] Photo/avatar de Célian (image optimisée via nuxt/image)
|
|
- [x] Nom : "Célian" (ou nom complet)
|
|
- [x] Titre : "Développeur Full-Stack"
|
|
- [x] Accroche courte : 1-2 phrases percutantes traduites
|
|
- [x] Liens sociaux : GitHub, LinkedIn (icĂŽnes cliquables)
|
|
|
|
- [x] **Task 3: Section Compétences (10s)** (AC: #2, #7)
|
|
- [x] Titre de section : "Stack technique"
|
|
- [x] Afficher les compétences principales par catégorie (Frontend, Backend, Tools)
|
|
- [x] Format compact : badges ou liste avec icĂŽnes
|
|
- [x] Charger depuis l'API `/api/skills` (filtrer les principales)
|
|
- [x] Limiter à 8-12 compétences max pour la lisibilité
|
|
|
|
- [x] **Task 4: Section Projets highlights (10s)** (AC: #3, #7)
|
|
- [x] Titre de section : "Projets récents"
|
|
- [x] Afficher 3-4 projets featured
|
|
- [x] Format compact : titre + 1 ligne description + lien
|
|
- [x] Charger depuis l'API `/api/projects?featured=true`
|
|
- [x] Liens vers les détails (ouvre dans nouvel onglet ou garde sur resume)
|
|
|
|
- [x] **Task 5: Section Contact (5s)** (AC: #4)
|
|
- [x] CTA principal : "Me contacter" (lien vers `/contact` ou email direct)
|
|
- [x] Email visible (cliquable mailto:)
|
|
- [x] Optionnel : téléphone si souhaité
|
|
- [x] Style accent pour le CTA principal
|
|
|
|
- [x] **Task 6: Bouton "Voir l'aventure"** (AC: #5)
|
|
- [x] Position discrĂšte mais visible (en bas ou en sidebar)
|
|
- [x] Texte : "Envie d'explorer ? Découvrir l'aventure complÚte"
|
|
- [x] Lien vers `/` (landing page)
|
|
- [x] Style secondaire, pas en compétition avec le CTA contact
|
|
|
|
- [x] **Task 7: Chargement des données API** (AC: #7)
|
|
- [x] Utiliser `useFetch` ou `useAsyncData` pour charger skills et projets
|
|
- [x] Gérer les états loading et error
|
|
- [x] Cache cÎté client pour éviter les appels répétés
|
|
- [x] SSR : données chargées cÎté serveur pour SEO
|
|
|
|
- [x] **Task 8: Traductions bilingue** (AC: #6)
|
|
- [x] Ajouter toutes les traductions dans `i18n/fr.json` et `i18n/en.json`
|
|
- [x] Section titles, accroche, CTA labels
|
|
- [x] Le contenu API est déjà traduit (Story 1.3)
|
|
|
|
- [x] **Task 9: Meta tags SEO optimisés** (AC: #8)
|
|
- [x] Utiliser `useSeo()` avec meta spécifiques
|
|
- [x] Title : "Célian - Développeur Full-Stack | CV Express"
|
|
- [x] Description : optimisée pour les recruteurs
|
|
- [x] Open Graph image : image de preview professionnelle
|
|
- [x] Structured data (JSON-LD) pour Person/Developer (optionnel)
|
|
|
|
- [x] **Task 10: Responsive et accessibilité** (AC: #1)
|
|
- [x] Mobile : sections empilées verticalement
|
|
- [x] Desktop : layout plus aéré, possible 2 colonnes pour skills/projets
|
|
- [x] Contraste suffisant (WCAG AA)
|
|
- [x] Navigation clavier fluide
|
|
- [x] Skip link vers le contenu principal
|
|
|
|
- [x] **Task 11: Validation finale** (AC: tous)
|
|
- [x] Page accessible via `/resume` (FR) et `/en/resume` (EN)
|
|
- [x] Chargement < 2s (données légÚres)
|
|
- [x] Toutes les sections visibles sans scroll excessif sur desktop
|
|
- [x] CTA contact fonctionnel
|
|
- [x] Lien vers aventure fonctionne
|
|
- [x] Layout minimal utilisé (pas de header complet)
|
|
- [x] 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
|
|
|
|
```vue
|
|
<!-- 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
|
|
|
|
```json
|
|
// 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."
|
|
}
|
|
}
|
|
```
|
|
|
|
```json
|
|
// 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
|
|
|
|
Claude Opus 4.5 (claude-opus-4-5-20251101)
|
|
|
|
### Debug Log References
|
|
|
|
- Les traductions i18n lazy-loaded nĂ©cessitent un redĂ©marrage du serveur dev pour ĂȘtre rechargĂ©es.
|
|
- L'API Laravel n'est pas démarrée pendant les tests - les fallback hardcodés s'affichent correctement.
|
|
|
|
### Completion Notes List
|
|
|
|
- Page résumé express complÚte avec layout minimal
|
|
- Section Hero : avatar SVG placeholder, nom, titre, tagline, icĂŽnes sociales (GitHub/LinkedIn)
|
|
- Section compétences : badges par catégorie, chargement API avec fallback hardcodé
|
|
- Section projets : liste avec liens vers détails, chargement API avec fallback
|
|
- Section contact : CTA principal vers /contact, email mailto cliquable
|
|
- Lien discret vers l'aventure complĂšte (landing page)
|
|
- SEO : meta title/description optimisés pour recruteurs
|
|
- Traductions FR/EN complĂštes
|
|
- Responsive : mobile/desktop, layout épuré scannable en ~30s
|
|
- useFetch avec headers X-API-Key et Accept-Language
|
|
|
|
### Change Log
|
|
| Date | Change | Author |
|
|
|------|--------|--------|
|
|
| 2026-02-03 | Story créée avec contexte complet | SM Agent |
|
|
| 2026-02-06 | Tasks 1-11 implémentées et validées | Dev Agent (Claude Opus 4.5) |
|
|
|
|
### File List
|
|
|
|
- `frontend/app/pages/resume.vue` â RĂĂCRIT (page complĂšte avec toutes les sections)
|
|
- `frontend/public/images/avatar.svg` â CRĂĂ (placeholder avatar)
|
|
- `frontend/i18n/fr.json` â MODIFIĂ (ajout resume.*)
|
|
- `frontend/i18n/en.json` â MODIFIĂ (ajout resume.*)
|
|
|