Files
cv-maker/README.md
T

11 KiB
Raw Blame History

CV-Maker — Documentation technique

Application web de création de CV en temps réel : saisie Markdown dans un panneau latéral, preview A4 instantanée, sauvegarde locale automatique et export PDF — le tout sans backend, 100 % côté navigateur.

Sommaire

  1. Prérequis
  2. Installation et lancement
  3. Scripts
  4. Stack technique
  5. Structure du projet
  6. Modèle de données
  7. Fonctionnement clé
  8. Conception détaillée
  9. Limitations connues

Prérequis

Outil Version minimale
Node.js 20.9+
pnpm 12.x (corepack enable puis pnpm -v — figé via packageManager)

Installation et lancement

# 1. Installer les dépendances
pnpm install

# 2. Lancer en développement
pnpm dev            # → http://localhost:3000

# 3. Construire + servir en production
pnpm build
pnpm start

Scripts

Script Commande Description
dev pnpm dev Serveur de développement (Turbopack)
dev:sw concurrently … Dev + re-build du service worker en continu (--watch)
build next build && serwist build Build de production + génération du service worker
start pnpm start Lancement du build de production
lint pnpm lint Vérification ESLint (Next.js)
typecheck tsc --noEmit Validation TypeScript strict

Stack technique

Brique Choix Rôle
Framework Next.js (App Router) Routage, composants serveur/client, métadonnées
Langage TypeScript (strict) Typage du modèle de données
Styles Tailwind CSS Design tokens, responsive, print
Markdown marked Conversion Markdown → HTML
Sécurité dompurify Assainissement du HTML avant injection (anti-XSS)
Persistance localStorage Données locales, aucune transmission
PWA @serwist/next + @serwist/cli Service worker (mode configurator), manifest, installabilité
Icônes lucide-react Iconographie
Conteneurisation Docker (multi-stage) Image de production, distribution Docker Hub

Structure du projet

cv-maker/
├── app/
│   ├── layout.tsx                 # Layout racine (lang, meta, manifest)
│   ├── page.tsx                   # Éditeur principal
│   ├── globals.css                # Tokens Tailwind + @media print
│   ├── documentation/
│   │   └── page.tsx               # Page d'aide (statique)
│   ├── mentions-legales/
│   │   └── page.tsx               # Page Mentions légales (statique)
│   └── sw.ts                      # Source du service worker (Serwist)
├── components/
│   ├── ui/                        # Primitives réutilisables
│   │   ├── Button.tsx
│   │   ├── IconButton.tsx
│   │   ├── Textarea.tsx
│   │   ├── Tabs.tsx
│   │   └── Footer.tsx             # Pied de page (lien Mentions légales)
│   └── cv/                        # Composants métier du CV
│       ├── CvWorkspace.tsx        # Assemblage 2 colonnes + toolbar
│       ├── CvToolbar.tsx          # Barre d'app (PDF, JSON, liens)
│       ├── CvInputPanel.tsx       # Panneau gauche (tabs + expériences)
│       ├── CvTabs.tsx             # Onglets 1..6
│       ├── MarkdownTextarea.tsx   # Textarea contrôlé
│       ├── ExperienceManager.tsx  # Formulaire + liste expériences
│       ├── ExperienceItem.tsx     # Ligne « n. titre [Modifier] [×] »
│       ├── CvPreview.tsx          # Feuille A4
│       ├── CvHeader.tsx           # Accroche (zone 1)
│       ├── CvLeftColumn.tsx       # Sections 2→6 (1/3)
│       ├── CvSection.tsx          # Section générique
│       ├── CvRightColumn.tsx      # Expériences pro (2/3)
│       └── CvExperiencePreview.tsx
├── lib/
│   ├── types/cv.ts                # Contracts du domaine
│   ├── markdown.ts                # markdownToHtml (marked + DOMPurify)
│   ├── storage.ts                 # Adapter localStorage
│   └── hooks/
│       ├── useLocalStorage.ts     # Persistance générique
│       └── useCVData.ts           # Façade métier d'édition
├── public/
│   ├── manifest.webmanifest       # Manifest PWA
│   └── icons/                     # icônes 192 / 512
├── Dockerfile                     # Image multi-stage (deps/builder/runner)
├── docker-compose.yml             # Service cv-maker, healthcheck
├── .dockerignore                  # Exclusions de contexte build
├── serwist.config.mjs             # Config service worker (mode configurator)
├── next.config.ts                 # output: "standalone"
├── tsconfig.json                  # strict
├── GOAL.md                        # Cahier des charges source
└── README.md                      # Ce document

Modèle de données

Contenu stocké dans une seule clé localStorage : cv-maker:v1 (JSON).

interface CVCareerData {
  version: 1;
  accroche: string;        // onglet 1 — message d'accroche
  etatCivil: string;       // onglet 2 — état civil + contacts + site + GitHub
  formations: string;      // onglet 3 — formations
  competences: string;     // onglet 4 — compétences techniques
  softSkills: string;      // onglet 5 — soft skills
  centresInteret: string;  // onglet 6 — centres d'intérêt
  experiences: Experience[];
}

interface Experience {
  id: string;             // crypto.randomUUID() — identifiant stable
  titre: string;          // titre affiché (liste + section CV)
  contenu: string;        // corps en Markdown
  createdAt: number;
  updatedAt: number;
}
  • Les 6 zones sont du Markdown brut (le HTML rendu n'est jamais stocké).
  • Les expériences sont affichées dans l'ordre de saisie : la 1ʳᵉ ajoutée en haut de la section « Expériences professionnelles », la suivante en dessous, etc. (la 1ʳᵉ porte le numéro 1).
  • Sauvegarde invalide/corrompue → état par défaut, sans crash.

Fonctionnement clé

Rendu Markdown sécurisé

lib/markdown.ts — toute injection HTML passe par ici :

const rawHtml = marked.parse(source, { async: false }) as string;
return DOMPurify.sanitize(rawHtml);

marked génère le HTML ; DOMPurify neutralise tout script/événement URL dangereuse avant le dangerouslySetInnerHTML. Aucun HTML brut saisi n'est injecté directement.

Persistance locale

useLocalStorage synchronise l'état React avec localStorage à chaque changement (via useEffect) ; l'enregistrement est silencieux (aucun indicateur affiché). useCVData expose la façade d'édition (setText, addExperience, updateExperience, removeExperience, resetCV, importCV), et la barre d'application permet l'export/import JSON (cv-maker.json) pour partager un CV entre appareils.

Gestion des expériences

  • Bouton + : nouvelle expérience (formulaire titre + contenu).
  • Liste numérotée avec Modifier / × (suppression confirmée).
  • Identifiants UUID : jamais d'index comme identifiant (robustesse lors des suppressions).

Impression PDF

  • Bouton « Imprimer / PDF » → window.print().
  • CSS @media print dans app/globals.css : @page { size: A4; margin: 0 }, panneau de saisie et toolbar masqués, seule la feuille A4 est imprimée.

PWA

  • Mode « configurator » (@serwist/next + @serwist/cli, compatible Turbopack) : serwist.config.mjs décrit le service worker, compilé par pnpm build après Next.js (next build && serwist build).
  • public/sw.js pré-cache les routes /, /documentation, /mentions-legales et tous les assets (787 kB, 20 URL).
  • En développement le service worker est désactivé (SerwistProvider disable) ; utiliser pnpm dev:sw pour le reconstruire à chaud.
  • public/manifest.webmanifest + icônes 192/512 → installable, fonctionne hors-ligne (les données du CV restent dans localStorage).

Dockerisation

L'image est construite en 3 étapes (Dockerfile multi-stage) :

Étape Image Contenu
deps node:20-alpine Installation déterministe des dépendances (pnpm install --frozen-lockfile, incl. pnpm-workspace.yaml)
builder node:20-alpine Compilation (pnpm build, sortie standalone + sw.js)
runner node:20-alpine Exécution uniquement (node server.js, image ≈ 60 Mo)

Build et lancement :

docker build -t cv-maker:latest .
docker run --rm -p 3001:3000 cv-maker:latest   # → http://localhost:3001

Si le port 3000 est déjà occupé en local, mapper un autre port hôte (-p 3001:3000).

Avec Docker Compose :

docker compose up -d
docker compose down

Publication sur Docker Hub :

docker login
docker tag cv-maker:latest <mon-user-dockerhub>/cv-maker:latest
docker push <mon-user-dockerhub>/cv-maker:latest

Voir la documentation détaillée Docker pour le contenu complet des fichiers (Dockerfile, docker-compose.yml, .dockerignore) et la recette de validation.

Conception détaillée

Le dossier de conception complet (analyse fonctionnelle, architecture, modèle de données, UI, plan de réalisation pas-à-pas, sécurité/RGPD, plan de tests) se trouve dans :

docs/conception/
├── README.md                       → sommaire et ordre de lecture
├── 01-analyse-fonctionnelle.md
├── 02-architecture-technique.md
├── 03-modele-donnees.md
├── 04-specifications-ui.md
├── 05-etapes-realisation.md
├── 06-securite-rgpd.md
├── 07-plan-tests.md
└── 08-deploiement-docker.md

Limitations connues

  • Desktop first : tablette/mobile = niveau « utilisable », non optimisé.
  • One page A4 à l'impression : feuille de preview à hauteur flexible ; un contenu très long peut générer plusieurs pages PDF.
  • Pas de tests automatisés (v1) : recette manuelle documentée dans docs/conception/07-plan-tests.md ; tests Vitest prévus en v2.
  • Pas de drag & drop de réorganisation des expériences (évolution prévue).