Initial commit: Family Planner application
Complete family planning application with: - React frontend with TypeScript - Node.js/Express backend with TypeScript - Python ingestion service for document processing - Planning ingestion service with LLM integration - Shared UI components and type definitions - OAuth integration for calendar synchronization - Comprehensive documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
446
docs/archive/OPTIMISATION_AFFICHAGE_CONGES.md
Normal file
446
docs/archive/OPTIMISATION_AFFICHAGE_CONGES.md
Normal file
@@ -0,0 +1,446 @@
|
||||
# Optimisation de l'affichage des congés
|
||||
|
||||
## Résumé des modifications
|
||||
|
||||
Les congés et activités ont été optimisés pour prendre moins de place et être plus visuels dans le **Calendrier mensuel** et le **Dashboard**.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Objectifs
|
||||
|
||||
### 1. **Calendrier mensuel** (`MonthlyCalendarScreen`)
|
||||
- **Avant** : Chaque personne en congé prenait une ligne complète
|
||||
- **Après** : Les congés du même type sont regroupés sur une seule ligne avec des pastilles de couleur
|
||||
|
||||
### 2. **Dashboard** (`DashboardScreen` + `TimeGridMulti`)
|
||||
- **Avant** : Pas d'indication de congé visible
|
||||
- **Après** : Badge "Congé" ou "Vacances" affiché à côté du nom de la personne dans la colonne
|
||||
|
||||
---
|
||||
|
||||
## ✅ Modifications effectuées
|
||||
|
||||
### 1. Calendrier mensuel - Regroupement des congés
|
||||
|
||||
#### Fichier modifié : `frontend/src/screens/MonthlyCalendarScreen.js`
|
||||
|
||||
**Ajout de nouveaux composants styled :**
|
||||
```javascript
|
||||
const GroupedHolidayMarker = styled.div `
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.75rem;
|
||||
color: #b8c0ff;
|
||||
padding: 4px 6px;
|
||||
background: rgba(125, 108, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
font-style: italic;
|
||||
`;
|
||||
|
||||
const HolidayColorDots = styled.div `
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const HolidayDot = styled.div `
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: ${({ $color }) => $color};
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 0 4px ${({ $color }) => `${$color}66`};
|
||||
`;
|
||||
```
|
||||
|
||||
**Nouvelle logique d'affichage (lignes 489-507) :**
|
||||
```javascript
|
||||
// Regrouper les congés par type
|
||||
const publicHolidays = dateHolidays.filter(h => h.type === "public");
|
||||
const schoolHolidays = dateHolidays.filter(h => h.type === "school");
|
||||
const personalLeaves = dateHolidays.filter(h => h.type === "personal");
|
||||
|
||||
// Jours fériés publics (affichés normalement)
|
||||
publicHolidays.map((holiday, idx) => (
|
||||
<HolidayMarker $color="#ffa726">
|
||||
🎉 {holiday.label}
|
||||
</HolidayMarker>
|
||||
))
|
||||
|
||||
// Vacances scolaires (regroupées)
|
||||
schoolHolidays.length > 0 && (
|
||||
<GroupedHolidayMarker>
|
||||
Vacances
|
||||
<HolidayColorDots>
|
||||
{schoolHolidays.map((holiday, idx) => (
|
||||
<HolidayDot
|
||||
$color={getProfileColor(holiday.profileId)}
|
||||
title={holiday.childName}
|
||||
/>
|
||||
))}
|
||||
</HolidayColorDots>
|
||||
</GroupedHolidayMarker>
|
||||
)
|
||||
|
||||
// Congés personnels (regroupés)
|
||||
personalLeaves.length > 0 && (
|
||||
<GroupedHolidayMarker>
|
||||
Congé
|
||||
<HolidayColorDots>
|
||||
{personalLeaves.map((holiday, idx) => (
|
||||
<HolidayDot
|
||||
$color={getProfileColor(holiday.profileId)}
|
||||
title={holiday.childName}
|
||||
/>
|
||||
))}
|
||||
</HolidayColorDots>
|
||||
</GroupedHolidayMarker>
|
||||
)
|
||||
```
|
||||
|
||||
**Résultat visuel :**
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ Mar 15/01 │
|
||||
├─────────────────────────────────┤
|
||||
│ 🎉 Nouvel An │ ← Jour férié (normal)
|
||||
│ Vacances ● ● ● │ ← 3 enfants en vacances (1 ligne)
|
||||
│ Congé ● ● │ ← 2 parents en congé (1 ligne)
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Dashboard - Badge de congé dans les colonnes
|
||||
|
||||
#### Fichier modifié : `frontend/src/screens/DashboardScreen.js`
|
||||
|
||||
**Ajout des imports (ligne 8) :**
|
||||
```javascript
|
||||
import {
|
||||
apiClient,
|
||||
listParents,
|
||||
listGrandParents,
|
||||
getHolidays,
|
||||
getPublicHolidays,
|
||||
getPersonalLeaves
|
||||
} from "../services/api-client";
|
||||
```
|
||||
|
||||
**Ajout des états (lignes 142-143) :**
|
||||
```javascript
|
||||
const [holidays, setHolidays] = useState([]);
|
||||
const [personalLeaves, setPersonalLeaves] = useState([]);
|
||||
```
|
||||
|
||||
**Chargement des congés (lignes 264-286) :**
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
const loadHolidaysAndLeaves = async () => {
|
||||
try {
|
||||
// Charger les vacances scolaires
|
||||
const regions = [...new Set(children.map(c => c.schoolRegion).filter(Boolean))];
|
||||
const holidayPromises = regions.map(region =>
|
||||
getHolidays(region, today.getFullYear()).catch(() => ({ holidays: [] }))
|
||||
);
|
||||
const holidayResults = await Promise.all(holidayPromises);
|
||||
const allHolidays = holidayResults.flatMap(r => r.holidays);
|
||||
|
||||
// Charger les jours fériés
|
||||
const publicHolidaysResult = await getPublicHolidays(today.getFullYear())
|
||||
.catch(() => ({ holidays: [] }));
|
||||
|
||||
// Combiner et dédupliquer
|
||||
const combinedHolidays = [...allHolidays, ...publicHolidaysResult.holidays];
|
||||
const uniqueHolidays = Array.from(
|
||||
new Map(combinedHolidays.map(h => [h.id, h])).values()
|
||||
);
|
||||
setHolidays(uniqueHolidays);
|
||||
|
||||
// Charger les congés personnels
|
||||
const leavePromises = selectedProfiles.map(profileId =>
|
||||
getPersonalLeaves(profileId).catch(() => ({ leaves: [] }))
|
||||
);
|
||||
const leaveResults = await Promise.all(leavePromises);
|
||||
const allLeaves = leaveResults.flatMap(r => r.leaves);
|
||||
setPersonalLeaves(allLeaves);
|
||||
}
|
||||
catch (error) {
|
||||
console.warn("Erreur lors du chargement des congés", error);
|
||||
}
|
||||
};
|
||||
void loadHolidaysAndLeaves();
|
||||
}, [children, selectedProfiles, today]);
|
||||
```
|
||||
|
||||
**Modification de profileColumns pour inclure vacationStatus (lignes 430-497) :**
|
||||
```javascript
|
||||
const profileColumns = useMemo(() => {
|
||||
const selected = new Set(selectedProfiles);
|
||||
const todayISO = today.toISOString().slice(0, 10);
|
||||
const todayDate = new Date(todayISO);
|
||||
|
||||
return allProfiles
|
||||
.filter((profile) => selected.has(profile.id))
|
||||
.map((profile) => {
|
||||
// ... code existant ...
|
||||
|
||||
// Vérifier si le profil est en congé aujourd'hui
|
||||
let vacationStatus = null;
|
||||
|
||||
// Vérifier les vacances scolaires pour les enfants
|
||||
if (profile.kind === "child") {
|
||||
const child = children.find(c => c.id === profile.id);
|
||||
if (child?.schoolRegion) {
|
||||
const schoolHoliday = holidays.find(h => {
|
||||
if (h.type !== "school") return false;
|
||||
if (!h.zones || !h.zones.includes(child.schoolRegion)) return false;
|
||||
const start = new Date(h.startDate);
|
||||
const end = new Date(h.endDate);
|
||||
return todayDate >= start && todayDate <= end;
|
||||
});
|
||||
if (schoolHoliday) {
|
||||
vacationStatus = "Vacances";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier les congés personnels pour tous les profils
|
||||
const personalLeave = personalLeaves.find(leave => {
|
||||
if (leave.profileId !== profile.id) return false;
|
||||
const start = new Date(leave.startDate);
|
||||
const end = new Date(leave.endDate);
|
||||
return todayDate >= start && todayDate <= end;
|
||||
});
|
||||
if (personalLeave) {
|
||||
vacationStatus = "Congé";
|
||||
}
|
||||
|
||||
return {
|
||||
// ... autres propriétés ...
|
||||
vacationStatus // ← Nouvelle propriété
|
||||
};
|
||||
});
|
||||
}, [allProfiles, selectedProfiles, dayEventsMap, children, holidays, personalLeaves, today]);
|
||||
```
|
||||
|
||||
#### Fichier modifié : `frontend/src/components/TimeGridMulti.js`
|
||||
|
||||
**Ajout du composant styled VacationBadge (lignes 42-51) :**
|
||||
```javascript
|
||||
const VacationBadge = styled.span `
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: ${({ $color }) => `${$color}33`};
|
||||
border: 1px solid ${({ $color }) => `${$color}66`};
|
||||
color: #e9ebff;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
margin-left: auto;
|
||||
`;
|
||||
```
|
||||
|
||||
**Modification du ColHeader (lignes 32-40) :**
|
||||
```javascript
|
||||
const ColHeader = styled.div `
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid rgba(126, 136, 180, 0.22);
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap; // ← Ajouté pour permettre le retour à la ligne
|
||||
`;
|
||||
```
|
||||
|
||||
**Affichage du badge dans le header (ligne 136) :**
|
||||
```javascript
|
||||
<ColHeader>
|
||||
{/* Avatar et nom */}
|
||||
{col.avatarUrl ? <MiniAvatar ... /> : ...}
|
||||
{col.link ? <Link ...>{col.title}</Link> : <span>{col.title}</span>}
|
||||
|
||||
{/* Badge de congé */}
|
||||
{col.vacationStatus ? (
|
||||
<VacationBadge $color={col.color}>
|
||||
{col.vacationStatus}
|
||||
</VacationBadge>
|
||||
) : null}
|
||||
</ColHeader>
|
||||
```
|
||||
|
||||
**Résultat visuel :**
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ 👤 Robert Hérault [Congé] │ ← Badge à droite du nom
|
||||
├──────────────────────────────────────┤
|
||||
│ 08:00 ────────────────────────── │
|
||||
│ 09:00 ────────────────────────── │
|
||||
│ ... │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Améliorations visuelles
|
||||
|
||||
### Calendrier mensuel
|
||||
|
||||
| Avant | Après |
|
||||
|-------|-------|
|
||||
| 4 lignes pour 4 personnes en congé | 1 ligne avec 4 pastilles de couleur |
|
||||
| Beaucoup d'espace perdu | Plus de place pour les activités |
|
||||
| Difficile de voir d'un coup d'œil | Vue d'ensemble immédiate |
|
||||
|
||||
**Exemple concret :**
|
||||
|
||||
**AVANT** (4 lignes) :
|
||||
```
|
||||
Timéo Hérault - Vacances
|
||||
Gabriel Hérault - Vacances
|
||||
Robert Hérault - Congé
|
||||
Martine Hérault - Congé
|
||||
```
|
||||
|
||||
**APRÈS** (2 lignes) :
|
||||
```
|
||||
Vacances ●🔵 ●🟢 (2 pastilles)
|
||||
Congé ●🔴 ●🟡 (2 pastilles)
|
||||
```
|
||||
|
||||
### Dashboard
|
||||
|
||||
**AVANT** :
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ Robert Hérault │ ← Aucune indication
|
||||
├─────────────────────┤
|
||||
```
|
||||
|
||||
**APRÈS** :
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
│ Robert Hérault [Congé] │ ← Badge visible
|
||||
├──────────────────────────┤
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Avantages de l'optimisation
|
||||
|
||||
### 1. **Gain d'espace**
|
||||
- Calendrier : **Réduction de 75%** de l'espace utilisé pour les congés multiples
|
||||
- Dashboard : Information compacte sur une ligne
|
||||
|
||||
### 2. **Meilleure lisibilité**
|
||||
- Vue d'ensemble immédiate avec les couleurs
|
||||
- Identification rapide des personnes en congé
|
||||
- Plus d'espace pour les vraies activités
|
||||
|
||||
### 3. **Ergonomie améliorée**
|
||||
- Moins de scroll nécessaire
|
||||
- Information contextuelle sans surcharger
|
||||
- Design plus moderne et épuré
|
||||
|
||||
### 4. **Cohérence visuelle**
|
||||
- Les couleurs des profils sont utilisées partout
|
||||
- Style uniforme entre calendrier et dashboard
|
||||
- Pastilles visibles au survol (title attribute)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Détails techniques
|
||||
|
||||
### Types de congés gérés
|
||||
|
||||
1. **Jours fériés publics** (`type: "public"`)
|
||||
- Affichés normalement avec 🎉
|
||||
- Couleur : `#ffa726` (orange)
|
||||
- Non regroupés (un seul par jour généralement)
|
||||
|
||||
2. **Vacances scolaires** (`type: "school"`)
|
||||
- Regroupées sur une ligne "Vacances"
|
||||
- Pastilles avec couleur de chaque enfant
|
||||
- Filtré par zone scolaire
|
||||
|
||||
3. **Congés personnels** (`type: "personal"`)
|
||||
- Regroupés sur une ligne "Congé"
|
||||
- Pastilles avec couleur de chaque profil (parent/grand-parent)
|
||||
- Stockés dans le champ `vacations` du profil
|
||||
|
||||
### Logique de détection
|
||||
|
||||
```javascript
|
||||
// Pour une date donnée
|
||||
const todayDate = new Date(dateISO);
|
||||
|
||||
// Vérifier si dans la plage
|
||||
const start = new Date(holiday.startDate);
|
||||
const end = new Date(holiday.endDate);
|
||||
const isInRange = todayDate >= start && todayDate <= end;
|
||||
```
|
||||
|
||||
### Performances
|
||||
|
||||
- Calcul uniquement pour les profils sélectionnés
|
||||
- Mise en cache via `useMemo` pour éviter les recalculs
|
||||
- Chargement asynchrone des données de congés
|
||||
|
||||
---
|
||||
|
||||
## 📝 Fichiers modifiés
|
||||
|
||||
### Créés
|
||||
- ✅ `OPTIMISATION_AFFICHAGE_CONGES.md` (ce document)
|
||||
|
||||
### Modifiés
|
||||
1. ✅ `frontend/src/screens/MonthlyCalendarScreen.js`
|
||||
- Lignes 194-219 : Nouveaux composants styled
|
||||
- Lignes 489-507 : Logique de regroupement
|
||||
|
||||
2. ✅ `frontend/src/screens/DashboardScreen.js`
|
||||
- Ligne 8 : Imports des APIs de congés
|
||||
- Lignes 142-143 : États holidays et personalLeaves
|
||||
- Lignes 264-286 : useEffect pour charger les congés
|
||||
- Lignes 430-497 : Ajout de vacationStatus dans profileColumns
|
||||
|
||||
3. ✅ `frontend/src/components/TimeGridMulti.js`
|
||||
- Lignes 32-40 : Modification de ColHeader (flex-wrap)
|
||||
- Lignes 42-51 : Nouveau composant VacationBadge
|
||||
- Ligne 136 : Affichage conditionnel du badge
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Pour tester
|
||||
|
||||
1. **Ajouter des congés** :
|
||||
- Aller sur le profil d'un parent ou grand-parent
|
||||
- Section "Congés" → Ajouter une période incluant aujourd'hui
|
||||
|
||||
2. **Configurer les zones scolaires** :
|
||||
- Aller sur le profil d'un enfant
|
||||
- Section "Congés scolaires" → Sélectionner une zone
|
||||
|
||||
3. **Vérifier le calendrier mensuel** :
|
||||
- Naviguer vers "Calendrier mensuel"
|
||||
- Vérifier que plusieurs congés le même jour sont regroupés
|
||||
- Survoler les pastilles pour voir les noms
|
||||
|
||||
4. **Vérifier le dashboard** :
|
||||
- Aller sur "Agenda familial"
|
||||
- Vue "Vue générale"
|
||||
- Vérifier les badges "Congé" ou "Vacances" à côté des noms
|
||||
|
||||
---
|
||||
|
||||
## ✨ Résultat final
|
||||
|
||||
Les congés sont maintenant affichés de manière **compacte, visuelle et ergonomique** :
|
||||
|
||||
- **Calendrier** : 1 ligne "Vacances" + 1 ligne "Congé" au lieu de X lignes individuelles
|
||||
- **Dashboard** : Badge discret mais visible à côté du nom
|
||||
- **Couleurs** : Identification immédiate grâce aux pastilles colorées
|
||||
- **Espace** : Plus de place pour les vraies activités et événements
|
||||
|
||||
🎉 **Objectif atteint : Optimisation réussie !**
|
||||
Reference in New Issue
Block a user