cognitive-load: organize secondary info in tabs on dashboard
- Added Tabs component to organize Activity Feed content - Chart and Activity List now in separate tabs (Graphique, Activité) - Reduces cognitive load by showing one view at a time - Default tab is 'Graphique' (Chart) - Tabs are inside the collapsible Activity Feed section - Action 10.1.1.3 complete
This commit is contained in:
parent
e7bec689b0
commit
8bc570cfda
2 changed files with 109 additions and 95 deletions
|
|
@ -3467,12 +3467,19 @@ Critical path dependencies:
|
||||||
- **Status**: Component is fully functional and ready to use
|
- **Status**: Component is fully functional and ready to use
|
||||||
- **Rollback**: Delete component
|
- **Rollback**: Delete component
|
||||||
|
|
||||||
- [ ] **Action 10.1.1.3**: Use tabs or accordions for secondary info
|
- [x] **Action 10.1.1.3**: Use tabs or accordions for secondary info
|
||||||
- **Scope**: `apps/web/src/pages/DashboardPage.tsx` - Group secondary info in tabs
|
- **Scope**: `apps/web/src/pages/DashboardPage.tsx` - Group secondary info in tabs
|
||||||
- **Dependencies**: Action 10.1.1.2 complete
|
- **Dependencies**: Action 10.1.1.2 complete ✅
|
||||||
- **Risk**: MEDIUM (layout change)
|
- **Risk**: MEDIUM (layout change)
|
||||||
- **Validation**: Secondary info in tabs
|
- **Validation**: ✅ Secondary info organized in tabs:
|
||||||
- **Rollback**: Restore single view
|
- **Tabs component**: Added Tabs, TabsList, TabsTrigger, TabsContent imports
|
||||||
|
- **Tab structure**: Two tabs ("Graphique" and "Activité") organize Activity Feed content
|
||||||
|
- **Chart tab**: Contains the activity chart with time period buttons (7J, 30J, MAX)
|
||||||
|
- **Activity tab**: Contains the recent activity list with activity items
|
||||||
|
- **Default tab**: "Graphique" is the default active tab
|
||||||
|
- **Layout**: Tabs are inside the Collapsible Activity Feed section
|
||||||
|
- **Result**: Secondary information (chart and activity list) is now organized in tabs, reducing cognitive load by showing one view at a time
|
||||||
|
- **Rollback**: Remove Tabs wrapper, restore space-y-6 div with both Cards visible
|
||||||
|
|
||||||
- [x] **Action 10.1.1.4**: Create Accordion component (if doesn't exist)
|
- [x] **Action 10.1.1.4**: Create Accordion component (if doesn't exist)
|
||||||
- **Scope**: `apps/web/src/components/ui/accordion.tsx` (create) - Reusable accordion component ✅
|
- **Scope**: `apps/web/src/components/ui/accordion.tsx` (create) - Reusable accordion component ✅
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { formatDistanceToNow } from 'date-fns';
|
||||||
import { fr } from 'date-fns/locale';
|
import { fr } from 'date-fns/locale';
|
||||||
import { KodoEmptyState } from '@/components/ui/KodoEmptyState';
|
import { KodoEmptyState } from '@/components/ui/KodoEmptyState';
|
||||||
import { Collapsible } from '@/components/ui/collapsible';
|
import { Collapsible } from '@/components/ui/collapsible';
|
||||||
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||||
import { FAB } from '@/components/ui/FAB';
|
import { FAB } from '@/components/ui/FAB';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
|
@ -256,101 +257,107 @@ export function DashboardPage() {
|
||||||
triggerClassName="p-0 hover:bg-transparent"
|
triggerClassName="p-0 hover:bg-transparent"
|
||||||
contentClassName="pt-0"
|
contentClassName="pt-0"
|
||||||
>
|
>
|
||||||
<div className="space-y-6">
|
{/* Action 10.1.1.3: Use tabs to organize secondary info */}
|
||||||
{/* Chart Card */}
|
<Tabs defaultValue="chart" className="mt-6">
|
||||||
<Card>
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
<CardHeader>
|
<TabsTrigger value="chart">Graphique</TabsTrigger>
|
||||||
<div className="flex items-center justify-between">
|
<TabsTrigger value="activity">Activité</TabsTrigger>
|
||||||
<CardTitle className="flex items-center gap-2">
|
</TabsList>
|
||||||
<TrendingUp className="w-5 h-5 text-kodo-cyan" />
|
<TabsContent value="chart" className="mt-4">
|
||||||
Activité récente
|
<Card>
|
||||||
</CardTitle>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center justify-between">
|
||||||
<Button variant="ghost" size="sm" className="text-xs">
|
<CardTitle className="flex items-center gap-2">
|
||||||
7J
|
<TrendingUp className="w-5 h-5 text-kodo-cyan" />
|
||||||
</Button>
|
Activité récente
|
||||||
<Button variant="outline" size="sm" className="text-xs text-kodo-cyan bg-kodo-cyan/10 border-kodo-cyan/20">
|
</CardTitle>
|
||||||
30J
|
<div className="flex items-center gap-2">
|
||||||
</Button>
|
<Button variant="ghost" size="sm" className="text-xs">
|
||||||
<Button variant="ghost" size="sm" className="text-xs">
|
7J
|
||||||
MAX
|
</Button>
|
||||||
</Button>
|
<Button variant="outline" size="sm" className="text-xs text-kodo-cyan bg-kodo-cyan/10 border-kodo-cyan/20">
|
||||||
|
30J
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="sm" className="text-xs">
|
||||||
|
MAX
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</CardHeader>
|
||||||
</CardHeader>
|
<CardContent>
|
||||||
<CardContent>
|
<div className="h-64 flex items-end gap-2">
|
||||||
<div className="h-64 flex items-end gap-2">
|
{[40, 65, 35, 90, 55, 75, 45, 85, 60, 70, 50, 95].map(
|
||||||
{[40, 65, 35, 90, 55, 75, 45, 85, 60, 70, 50, 95].map(
|
(h, i) => (
|
||||||
(h, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className="flex-1 bg-gradient-to-t from-kodo-cyan/40 to-kodo-cyan/20 rounded-t-lg transition-all duration-300 hover:from-kodo-cyan/60 hover:to-kodo-cyan/40 cursor-pointer group relative"
|
|
||||||
style={{ height: `${h}%` }}
|
|
||||||
>
|
|
||||||
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-kodo-ink border border-kodo-cyan/30 text-kodo-cyan text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap shadow-lg">
|
|
||||||
{h}%
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Recent Activity List */}
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="flex items-center gap-2">
|
|
||||||
<Activity className="w-5 h-5 text-kodo-cyan" />
|
|
||||||
Dernières activités
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{isLoadingDashboard ? (
|
|
||||||
Array(3)
|
|
||||||
.fill(0)
|
|
||||||
.map((_, i) => (
|
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="h-16 bg-white/5 rounded-xl animate-pulse"
|
className="flex-1 bg-gradient-to-t from-kodo-cyan/40 to-kodo-cyan/20 rounded-t-lg transition-all duration-300 hover:from-kodo-cyan/60 hover:to-kodo-cyan/40 cursor-pointer group relative"
|
||||||
/>
|
style={{ height: `${h}%` }}
|
||||||
|
>
|
||||||
|
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-kodo-ink border border-kodo-cyan/30 text-kodo-cyan text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap shadow-lg">
|
||||||
|
{h}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="activity" className="mt-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Activity className="w-5 h-5 text-kodo-cyan" />
|
||||||
|
Dernières activités
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{isLoadingDashboard ? (
|
||||||
|
Array(3)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="h-16 bg-white/5 rounded-xl animate-pulse"
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : recentActivity.length > 0 ? (
|
||||||
|
recentActivity.slice(0, 5).map((act, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="flex items-center justify-between p-4 rounded-xl bg-white/5 border border-white/5 hover:bg-white/10 hover:border-kodo-cyan/30 transition-all duration-200 group cursor-pointer"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-kodo-cyan/20 to-kodo-cyan/10 border border-kodo-cyan/20 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||||
|
<Clock className="w-5 h-5 text-kodo-cyan" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-white group-hover:text-kodo-cyan transition-colors">
|
||||||
|
{act.title}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-kodo-secondary mt-0.5">
|
||||||
|
{act.description || 'Activité système'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-kodo-secondary font-mono">
|
||||||
|
{formatTimestamp(act.timestamp)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))
|
))
|
||||||
) : recentActivity.length > 0 ? (
|
) : (
|
||||||
recentActivity.slice(0, 5).map((act, i) => (
|
<KodoEmptyState
|
||||||
<div
|
icon={Activity}
|
||||||
key={i}
|
title="Aucune activité récente"
|
||||||
className="flex items-center justify-between p-4 rounded-xl bg-white/5 border border-white/5 hover:bg-white/10 hover:border-kodo-cyan/30 transition-all duration-200 group cursor-pointer"
|
description="Vos activités apparaîtront ici"
|
||||||
>
|
/>
|
||||||
<div className="flex items-center gap-4">
|
)}
|
||||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-kodo-cyan/20 to-kodo-cyan/10 border border-kodo-cyan/20 flex items-center justify-center group-hover:scale-110 transition-transform">
|
</div>
|
||||||
<Clock className="w-5 h-5 text-kodo-cyan" />
|
</CardContent>
|
||||||
</div>
|
</Card>
|
||||||
<div>
|
</TabsContent>
|
||||||
<p className="text-sm font-semibold text-white group-hover:text-kodo-cyan transition-colors">
|
</Tabs>
|
||||||
{act.title}
|
|
||||||
</p>
|
|
||||||
<p className="text-xs text-kodo-secondary mt-0.5">
|
|
||||||
{act.description || 'Activité système'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-kodo-secondary font-mono">
|
|
||||||
{formatTimestamp(act.timestamp)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<KodoEmptyState
|
|
||||||
icon={Activity}
|
|
||||||
title="Aucune activité récente"
|
|
||||||
description="Vos activités apparaîtront ici"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue