feat: ajouter la section Compétences (6e bloc)

This commit is contained in:
devcodetools committed 2026-09-17 07:01:05 +02:00
1 parent a0d312a884
commit 92aaafb4aa
13 files changed
+75 -47

No files matched your search

+11 -7
View File
@@ -39,7 +39,7 @@ CV-Maker est une application web **sans backend** permettant à un utilisateur d
│ Panneau GAUCHE │ Panneau DROIT │
│ 1/3 de la largeur │ 2/3 de la largeur │
│ │ │
│ [Tabs 1 ▸ 2 ▸ 3 ▸ 4 ▸ 5] │ ┌────────────────────────┐ │
│ [Tabs 1 ▸ 2 ▸ 3 ▸ 4 ▸ 5 ▸ 6] │ ┌────────────────────────┐ │
│ │ │ Feuille A4 │ │
│ [MarkdownTextarea : │ │ ┌────────────────────┐ │ │
│ zone active] │ │ │ Header accroche │ │ │
@@ -50,6 +50,8 @@ CV-Maker est une application web **sans backend** permettant à un utilisateur d
│ • 1. Titre exp [Modif] [×] │ │ │ civil │ profession.│ │ │
│ • 2. Titre exp [Modif] [×] │ │ │ Forma-│ ... │ │ │
│ │ │ │ tions │ │ │ │
│ │ │ │ Compé- │ │ │ │
│ │ │ │ tences │ │ │ │
│ │ │ │ Soft │ │ │ │
│ │ │ │ skills│ │ │ │
│ │ │ │ Centr.│ │ │ │
@@ -63,15 +65,16 @@ CV-Maker est une application web **sans backend** permettant à un utilisateur d
#### Structure
- **Zones 1 à 5** gérées par un **système d'onglets (tabs)** numérotés de 1 à 5. Un seul onglet actif à la fois ; chaque onglet affiche une `textarea` Markdown dédiée.
- **Zones 1 à 6** gérées par un **système d'onglets (tabs)** numérotés de 1 à 6. Un seul onglet actif à la fois ; chaque onglet affiche une `textarea` Markdown dédiée.
| N° onglet | Zone | Champ(s) du CV associé(s) | Préfixe de navigation |
| --- | --- | --- | --- |
| 1 | Message d'accroche | En-tête du CV (sous le nom/poste) | |
| 2 | État civil + contacts | Nom, prénom, adresse, e-mail, téléphone, **site web**, **lien GitHub** | |
| 3 | Formations | Liste des formations / diplômes | |
| 4 | Soft skills | Compétences comportementales | |
| 5 | Centres d'intérêt | Loisirs, activités | |
| 4 | Compétences | Compétences techniques (langages, frameworks, outils) | |
| 5 | Soft skills | Compétences comportementales | |
| 6 | Centres d'intérêt | Loisirs, activités | |
- **Zone « Expériences »** : fonctionne **hors du système d'onglets**. Elle est toujours visible sous la zone d'onglets. Elle sert à saisir **toutes les expériences professionnelles**, une par une.
@@ -88,11 +91,12 @@ CV-Maker est une application web **sans backend** permettant à un utilisateur d
- Une seule feuille blanche au **format A4** (210 × 297 mm), avec une marge définie, représentant 1 page de CV.
- **Header** (toute la largeur de la feuille) : rendu de l'**onglet 1 — message d'accroche**.
- **Colonne gauche (1/3 de la largeur utile)** : rendus des sections correspondant aux **onglets 2 à 5** :
- **Colonne gauche (1/3 de la largeur utile)** : rendus des sections correspondant aux **onglets 2 à 6** :
- État civil (zone 2) : contacts, site web, GitHub ;
- Formations (zone 3) ;
- Soft skills (zone 4) ;
- Centres d'intérêt (zone 5).
- Compétences (zone 4) : compétences techniques ;
- Soft skills (zone 5) ;
- Centres d'intérêt (zone 6).
- **Colonne droite (2/3 de la largeur utile)** : rendu des **expériences professionnelles**, sous le titre fixe **« Expériences professionnelles »**.
- Le rendu est **temps réel** : toute frappe dans une zone de saisie met à jour la zone cible correspondante instantanément.
+2 -2
View File
@@ -108,13 +108,13 @@ components/
├── CvWorkspace.tsx → layout 2 colonnes + barre d'app
├── CvToolbar.tsx → barre d'application (print, export/import JSON, liens)
├── CvInputPanel.tsx → panneau gauche
│ ├── CvTabs.tsx → tabs 1..5 (labels + numéros)
│ ├── CvTabs.tsx → tabs 1..6 (labels + numéros)
│ ├── MarkdownTextarea.tsx → {label, value, onChange} (textarea md)
│ └── ExperienceManager.tsx
│ └── ExperienceItem.tsx → ligne « n. titre [Modifier] [×] »
└── CvPreview.tsx → panneau droit (feuille A4)
├── CvHeader.tsx → accroche (zone 1)
├── CvLeftColumn.tsx → 1/3 (zones 2-5)
├── CvLeftColumn.tsx → 1/3 (zones 2-6)
│ └── CvSection.tsx → section générique {titre, html}
└── CvRightColumn.tsx → 2/3 (Expériences professionnelles)
└── CvExperiencePreview.tsx
+14 -9
View File
@@ -5,26 +5,28 @@
- **Aucun serveur** : le « modèle de données » = structure de l'état applicatif + son image persistée dans **localStorage**.
- **Une seule clé de stockage** versionnée : `cv-maker:v1`.
- **Toutes les zones sont du Markdown brut** (chaînes de caractères). Le rendu HTML est calculé à la volée, jamais stocké.
- Les expériences sont une **liste d'objets** (avec identifiant stable), contrairement aux 5 zones simples.
- Les expériences sont une **liste d'objets** (avec identifiant stable), contrairement aux 6 zones simples.
## 2. Contracts TypeScript
`lib/types/cv.ts` :
```ts
/** Identifiant des 5 zones de saisie éditées par onglets. */
/** Identifiant des 6 zones de saisie éditées par onglets. */
export type CVTabId =
| "accroche"
| "etatCivil"
| "formations"
| "competences"
| "softSkills"
| "centresInteret";
/** Ordre d'affichage des onglets (1 → 5). */
/** Ordre d'affichage des onglets (1 → 6). */
export const CV_TAB_ORDER: CVTabId[] = [
"accroche",
"etatCivil",
"formations",
"competences",
"softSkills",
"centresInteret",
];
@@ -32,7 +34,7 @@ export const CV_TAB_ORDER: CVTabId[] = [
/** Métadonnées d'affichage d'un onglet. */
export interface CVTabMeta {
id: CVTabId;
numero: 1 | 2 | 3 | 4 | 5; // numéro affiché sur l'onglet
numero: 1 | 2 | 3 | 4 | 5 | 6; // numéro affiché sur l'onglet
label: string; // libellé court de l'onglet
}
@@ -40,8 +42,9 @@ export const CV_TABS: Record<CVTabId, CVTabMeta> = {
accroche: { id: "accroche", numero: 1, label: "Accroche" },
etatCivil: { id: "etatCivil", numero: 2, label: "État civil" },
formations: { id: "formations", numero: 3, label: "Formations" },
softSkills: { id: "softSkills", numero: 4, label: "Soft skills" },
centresInteret: { id: "centresInteret", numero: 5, label: "Centres d'intérêt" },
competences: { id: "competences", numero: 4, label: "Compétences" },
softSkills: { id: "softSkills", numero: 5, label: "Soft skills" },
centresInteret: { id: "centresInteret", numero: 6, label: "Centres d'intérêt" },
};
/** Une expérience professionnelle. */
@@ -59,8 +62,9 @@ export interface CVCareerData {
accroche: string; // zone 1
etatCivil: string; // zone 2
formations: string; // zone 3
softSkills: string; // zone 4
centresInteret: string; // zone 5
competences: string; // zone 4
softSkills: string; // zone 5
centresInteret: string; // zone 6
experiences: Experience[]; // liste, ordre = ordre d'affichage dans le CV
}
@@ -81,6 +85,7 @@ export function createDefaultCVData(): CVCareerData {
accroche: "",
etatCivil: "",
formations: "",
competences: "",
softSkills: "",
centresInteret: "",
experiences: [],
@@ -141,7 +146,7 @@ Regroupe toute la logique d'édition du CV, façade unique pour les composants :
export interface UseCVDataReturn {
data: CVCareerData;
setText(tab: CVTabId, value: string): void; // édition d'une zone 1..5
setText(tab: CVTabId, value: string): void; // édition d'une zone 1..6
addExperience(titre: string, contenu: string): void; // ajoute en fin de liste
updateExperience(id: string, titre: string, contenu: string): void;
removeExperience(id: string): void; // suppression + confirmation côté UI
+5 -4
View File
@@ -44,10 +44,10 @@ Rendre les liens actifs en `font-semibold` et inactifs en `text-muted` (page cou
## 3. Panneau latéral gauche
### 3.1 `CvTabs` — onglets 1 à 5
### 3.1 `CvTabs` — onglets 1 à 6
- Basé sur la primitive `ui/Tabs` : `role="tablist"`, boutons `role="tab"`, `aria-selected`, `role="tabpanel"`.
- Affichage : barre de 5 boutons horizontaux, libellé = `numéro + label` (`1 · Accroche`, `2 · État civil`, …).
- Affichage : barre de 6 boutons horizontaux, libellé = `numéro + label` (`1 · Accroche`, `2 · État civil`, …).
- Onglet actif : fond `accent`, texte blanc ; inactifs : bordure `border` + texte `muted`.
- **Contenu piloté par le parent** (`activeTab` dans `CvInputPanel`) : un seul `MarkdownTextarea` monté pour l'onglet actif. Le texte de l'onglet actif est le `value` contrôlé.
@@ -64,7 +64,7 @@ Rendre les liens actifs en `font-semibold` et inactifs en `text-muted` (page cou
Composition :
- **En-tête** : titre « Expériences » + bouton **+** (`ui/Button`, variante `primary`) → prépare une nouvelle expérience (zones titre/contenu vidées, focus du formulaire).
- **Formulaire** : deux champs — `titre` (input texte) + `contenu` (textarea Markdown, même composant que les zones 1-5). Boutons : **Enregistrer** et **Annuler** (annulation quand modification).
- **Formulaire** : deux champs — `titre` (input texte) + `contenu` (textarea Markdown, même composant que les zones 1-6). Boutons : **Enregistrer** et **Annuler** (annulation quand modification).
- **Liste numérotée** : sous le formulaire.
États internes suivants :
@@ -118,13 +118,14 @@ Ratio 1/3–2/3 : `.cv-sheet-body { display:grid; grid-template-columns: 1fr 2fr
- Rendu Markdown de `data.accroche` (contenu typiquement : nom + poste + accroche).
- `border-bottom` de séparation avec le corps de feuille.
### 4.3 `CvLeftColumn` (sections 2 à 5, 1/3)
### 4.3 `CvLeftColumn` (sections 2 à 6, 1/3)
Section générique `CvSection` :
```tsx
<CvSection title="État civil" html={htmlEtatCivil} />
<CvSection title="Formations" html={htmlFormations} />
<CvSection title="Compétences" html={htmlCompetences} />
<CvSection title="Soft skills" html={htmlSoftSkills} />
<CvSection title="Centres d'intérêt" html={htmlCentresInteret} />
```
+2 -2
View File
@@ -148,9 +148,9 @@ Contrôle visuel : saisir du texte dans chaque onglet, ajouter/modifier/supprime
Créer dans `components/cv/` :
1. `CvPreview.tsx` — wrapper feuille (dimensions `210mm`/`297mm`, ombre, classe d'impression) ; appelle `markdownToHtml` depuis `lib/markdown` pour les 5 zones + chaque expérience ; compose header/colonnes.
1. `CvPreview.tsx` — wrapper feuille (dimensions `210mm`/`297mm`, ombre, classe d'impression) ; appelle `markdownToHtml` depuis `lib/markdown` pour les 6 zones + chaque expérience ; compose header/colonnes.
2. `CvHeader.tsx` — accroche (zone 1).
3. `CvLeftColumn.tsx` — 4 × `CvSection` (État civil, Formations, Soft skills, Centres d'intérêt).
3. `CvLeftColumn.tsx` — 5 × `CvSection` (État civil, Formations, Compétences, Soft skills, Centres d'intérêt).
4. `CvSection.tsx` — titre stylé + `dangerouslySetInnerHTML` (HTML assaini) ; masquée si vide.
5. `CvRightColumn.tsx` — titre « Expériences professionnelles » + liste `CvExperiencePreview` + message vide alternatif (`print:hidden`).
6. `CvExperiencePreview.tsx` — titre en gras + HTML assaini du contenu.
+1 -1
View File
@@ -45,7 +45,7 @@ DOM
## 3. Données personnelles : état de l'analyse
Les zones 1 à 5 du CV contiennent des **données à caractère personnel** (nom, e-mail, téléphone, histoire professionnelle).
Les zones 1 à 6 du CV contiennent des **données à caractère personnel** (nom, e-mail, téléphone, histoire professionnelle).
| Propriété | État |
| --- | --- |
+8 -7
View File
@@ -30,18 +30,19 @@
| G2 | Réduire la fenêtre sous 1024 px | Les panneaux s'empilent (gauche au-dessus de la droite) sans casse |
| G3 | Vérifier la barre d'app et le footer | Barre : logo, bouton « Imprimer / PDF », lien Documentation. Footer : lien Mentions légales (masqué à l'impression) |
### 3.2 Onglets 1 à 5 (zones de saisie)
### 3.2 Onglets 1 à 6 (zones de saisie)
| # | Action | Résultat attendu |
| --- | --- | --- |
| T1 | Cliquer sur les onglets 1→5 | L'onglet actif change ; le textarea affiche le contenu **de cet onglet uniquement** |
| T1 | Cliquer sur les onglets 1→6 | L'onglet actif change ; le textarea affiche le contenu **de cet onglet uniquement** |
| T2 | Saisir du Markdown dans l'onglet 1 (`**Jean Dupont**`) | Rendu temps réel dans le **header** de la feuille A4 (gras) |
| T3 | Idem dans l'onglet 2 (nom, e-mail, site, GitHub en listes) | Rendu dans la section **État civil** (colonne 1/3) |
| T4 | Onglet 3 (formation ex. `### Master` + liste) | Rendu dans la section **Formations** |
| T5 | Onglet 4 | Rendu dans la section **Soft skills** |
| T6 | Onglet 5 | Rendu dans la section **Centres d'intérêt** |
| T7 | Basculer entre onglets après saisie | Chaque contenu est conservé, aucun écrasement croisé |
| T8 | Vider complètement une section | La section correspondante disparaît de la preview |
| T5 | Onglet 4 (compétences ex. `- React\n- TypeScript`) | Rendu dans la section **Compétences** |
| T6 | Onglet 5 | Rendu dans la section **Soft skills** |
| T7 | Onglet 6 | Rendu dans la section **Centres d'intérêt** |
| T8 | Basculer entre onglets après saisie | Chaque contenu est conservé, aucun écrasement croisé |
| T9 | Vider complètement une section | La section correspondante disparaît de la preview |
### 3.3 Expériences professionnelles
@@ -128,7 +129,7 @@ Dès leur mise en place, ajouter à la recette :
| Persistance localStorage | P1–P5 | ☐ |
| Impression PDF | M1–M3 | ☐ |
| Expériences (+, liste, modif, ×) | X1–X9 | ☐ |
| Onglets 1-5 | T1–T8 | ☐ |
| Onglets 1-6 | T1–T9 | ☐ |
| Documentation + Mentions légales | D1–D3 | ☐ |
| PWA | W1–W5 | ☐ |