Checkpoint: Menu latéral moderne avec couleurs pastel (version douce et élégante) + correction du bug 'Cannot convert undefined or null to object' sur la page émargement formateur
This commit is contained in:
@@ -27,16 +27,41 @@ import { useLocation } from "wouter";
|
|||||||
import { DashboardLayoutSkeleton } from './DashboardLayoutSkeleton';
|
import { DashboardLayoutSkeleton } from './DashboardLayoutSkeleton';
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
|
|
||||||
// Couleurs par catégorie de menu
|
// Couleurs et styles par catégorie de menu
|
||||||
const getCategoryColor = (sectionTitle: string) => {
|
const getCategoryColor = (sectionTitle: string) => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
"Tableau de bord": "text-blue-600",
|
"Tableau de bord": "text-blue-500",
|
||||||
"Gestion": "text-green-600",
|
"Gestion": "text-green-500",
|
||||||
"Configuration": "text-orange-600",
|
"Configuration": "text-orange-500",
|
||||||
"Traçabilité": "text-purple-600",
|
"Traçabilité": "text-purple-500",
|
||||||
"Analyses": "text-pink-600",
|
"Analyses": "text-pink-500",
|
||||||
|
"Gestion technique": "text-red-500",
|
||||||
};
|
};
|
||||||
return colors[sectionTitle] || "";
|
return colors[sectionTitle] || "text-gray-500";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCategoryGradient = (sectionTitle: string) => {
|
||||||
|
const gradients: Record<string, string> = {
|
||||||
|
"Tableau de bord": "from-blue-300 to-blue-400",
|
||||||
|
"Gestion": "from-green-300 to-emerald-400",
|
||||||
|
"Configuration": "from-orange-300 to-orange-400",
|
||||||
|
"Traçabilité": "from-purple-300 to-purple-400",
|
||||||
|
"Analyses": "from-pink-300 to-pink-400",
|
||||||
|
"Gestion technique": "from-red-300 to-red-400",
|
||||||
|
};
|
||||||
|
return gradients[sectionTitle] || "from-gray-300 to-gray-400";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCategoryBgColor = (sectionTitle: string) => {
|
||||||
|
const bgColors: Record<string, string> = {
|
||||||
|
"Tableau de bord": "bg-blue-50/80 hover:bg-blue-100/90",
|
||||||
|
"Gestion": "bg-green-50/80 hover:bg-green-100/90",
|
||||||
|
"Configuration": "bg-orange-50/80 hover:bg-orange-100/90",
|
||||||
|
"Traçabilité": "bg-purple-50/80 hover:bg-purple-100/90",
|
||||||
|
"Analyses": "bg-pink-50/80 hover:bg-pink-100/90",
|
||||||
|
"Gestion technique": "bg-red-50/80 hover:bg-red-100/90",
|
||||||
|
};
|
||||||
|
return bgColors[sectionTitle] || "bg-gray-50/80 hover:bg-gray-100/90";
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuSections = [
|
const menuSections = [
|
||||||
@@ -369,19 +394,29 @@ function DashboardLayoutContent({
|
|||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleSection(section.title)}
|
onClick={() => toggleSection(section.title)}
|
||||||
className="w-full px-4 py-2 flex items-center justify-between hover:bg-accent/50 transition-colors rounded-md mx-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
className={`w-full px-3 py-3 flex items-center justify-between ${getCategoryBgColor(section.title)} transition-all duration-300 rounded-lg mx-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring border-l-4 ${isOpen ? 'border-l-' + section.title.toLowerCase().replace(/ /g, '-') : 'border-l-transparent'} shadow-sm hover:shadow-md hover:translate-x-1`}
|
||||||
|
style={{
|
||||||
|
borderLeftColor: isOpen ? `var(--${section.title.toLowerCase().replace(/ /g, '-')}-color, currentColor)` : 'transparent'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<h3 className={`text-xs font-semibold uppercase tracking-wider ${getCategoryColor(section.title)}`}>
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={`p-1.5 rounded-lg bg-gradient-to-br ${getCategoryGradient(section.title)} shadow-md transition-transform duration-300 ${isOpen ? 'scale-110 rotate-6' : ''}`}>
|
||||||
|
<div className="w-3 h-3 bg-white/30 rounded-sm" />
|
||||||
|
</div>
|
||||||
|
<h3 className={`text-xs font-bold uppercase tracking-wider ${getCategoryColor(section.title)}`}>
|
||||||
{section.title}
|
{section.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
</div>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
className={`h-3 w-3 transition-transform duration-200 ${getCategoryColor(section.title)} ${
|
className={`h-4 w-4 transition-all duration-300 ${getCategoryColor(section.title)} ${
|
||||||
isOpen ? 'rotate-180' : ''
|
isOpen ? 'rotate-180 scale-110' : ''
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{isOpen && (
|
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${
|
||||||
<SidebarMenu className="px-2 py-1">
|
isOpen ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0'
|
||||||
|
}`}>
|
||||||
|
<SidebarMenu className="px-2 py-1 animate-in slide-in-from-top-2">
|
||||||
{section.items.map(item => {
|
{section.items.map(item => {
|
||||||
const isActive = location === item.path;
|
const isActive = location === item.path;
|
||||||
const hasSubItems = 'subItems' in item && item.subItems && Array.isArray(item.subItems) && item.subItems.length > 0;
|
const hasSubItems = 'subItems' in item && item.subItems && Array.isArray(item.subItems) && item.subItems.length > 0;
|
||||||
@@ -393,11 +428,21 @@ function DashboardLayoutContent({
|
|||||||
isActive={isActive || Boolean(isSubItemActive)}
|
isActive={isActive || Boolean(isSubItemActive)}
|
||||||
onClick={() => setLocation(item.path)}
|
onClick={() => setLocation(item.path)}
|
||||||
tooltip={item.label}
|
tooltip={item.label}
|
||||||
className={`h-10 transition-all font-normal`}
|
className={`h-11 transition-all duration-300 font-normal hover:translate-x-2 hover:shadow-md ${
|
||||||
|
isActive || isSubItemActive
|
||||||
|
? `bg-gradient-to-r ${getCategoryGradient(section.title)} text-white shadow-lg scale-105 border-l-4 border-white/30`
|
||||||
|
: 'hover:bg-accent/70'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
|
<div className={`p-1.5 rounded-md transition-all duration-300 ${
|
||||||
|
isActive || isSubItemActive
|
||||||
|
? 'bg-white/20 scale-110'
|
||||||
|
: `bg-gradient-to-br ${getCategoryGradient(section.title)}`
|
||||||
|
}`}>
|
||||||
<item.icon
|
<item.icon
|
||||||
className={`h-4 w-4 ${isActive || isSubItemActive ? "text-primary" : getCategoryColor(section.title)}`}
|
className={`h-4 w-4 ${isActive || isSubItemActive ? "text-white" : "text-white"}`}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<span>{item.label}</span>
|
<span>{item.label}</span>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
@@ -424,7 +469,7 @@ function DashboardLayoutContent({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -221,7 +221,8 @@ export default function FormateurEmargement() {
|
|||||||
|
|
||||||
{/* Dates de formation */}
|
{/* Dates de formation */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{inscrit.dates?.map((date: any) => {
|
{inscrit.dates && Array.isArray(inscrit.dates) && inscrit.dates.length > 0 ? inscrit.dates.map((date: any) => {
|
||||||
|
if (!date || !date.id) return null;
|
||||||
const present = isPresent(inscrit.inscription.id, date.id);
|
const present = isPresent(inscrit.inscription.id, date.id);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -264,7 +265,11 @@ export default function FormateurEmargement() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
}) : (
|
||||||
|
<p className="text-sm text-muted-foreground py-2">
|
||||||
|
Aucune date de formation disponible
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
17
todo.md
17
todo.md
@@ -1003,4 +1003,21 @@
|
|||||||
- [x] Implémenter animations au survol et au chargement
|
- [x] Implémenter animations au survol et au chargement
|
||||||
- [x] Améliorer la palette de couleurs
|
- [x] Améliorer la palette de couleurs
|
||||||
- [x] Tester sur sandbox
|
- [x] Tester sur sandbox
|
||||||
|
- [x] Déployer après validation utilisateur
|
||||||
|
|
||||||
|
## Refonte ergonomie du menu latéral accordéon
|
||||||
|
|
||||||
|
- [x] Analyser le menu latéral actuel (DashboardLayout.tsx)
|
||||||
|
- [x] Créer nouvelle version avec icônes colorées et animations
|
||||||
|
- [x] Ajouter effets hover et transitions fluides
|
||||||
|
- [x] Implémenter indicateur de page active
|
||||||
|
- [x] Améliorer les couleurs et gradients
|
||||||
|
- [x] Tester sur sandbox
|
||||||
- [ ] Déployer après validation utilisateur
|
- [ ] Déployer après validation utilisateur
|
||||||
|
|
||||||
|
## Correction bug page émargement formateur
|
||||||
|
|
||||||
|
- [x] Analyser l'erreur "Cannot convert undefined or null to object"
|
||||||
|
- [x] Identifier la requête tRPC qui échoue
|
||||||
|
- [x] Corriger le bug
|
||||||
|
- [x] Tester la correction
|
||||||
|
|||||||
Reference in New Issue
Block a user