feat: ajouter la section Compétences (6e bloc)
This commit is contained in:
1 parent
a0d312a884
commit
92aaafb4aa
13 files changed
+75
-47
No files matched your search
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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} />
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 |
|
||||
| --- | --- |
|
||||
|
||||
@@ -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 | ☐ |
|
||||
|
||||
|
||||
Reference in new issue
Block a user