feat: add new dashboard components and enhance UI
- Introduced new CSS variables for light theme in `globals.css` to improve visual consistency. - Replaced `Card` component with `StatCard`, `ProgressBar`, and `MetricCard` in `DashboardStats`, `ProductivityAnalytics`, and `RecentTasks` for better modularity and reusability. - Updated `QuickActions` to use `ActionCard` for a more cohesive design. - Enhanced `Badge` and `Button` components with new variants for improved styling options. - Added new UI showcase section in `UIShowcaseClient` to demonstrate the new dashboard components.
This commit is contained in:
@@ -4,9 +4,9 @@ import { Task } from '@/lib/types';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { TagDisplay } from '@/components/ui/TagDisplay';
|
||||
import { formatDateShort } from '@/lib/date-utils';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { TaskCard } from '@/components/ui';
|
||||
import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { getPriorityConfig, getPriorityColorHex, getStatusBadgeClasses, getStatusLabel } from '@/lib/status-config';
|
||||
import { getPriorityConfig, getStatusLabel } from '@/lib/status-config';
|
||||
import { TaskPriority } from '@/lib/types';
|
||||
import Link from 'next/link';
|
||||
|
||||
@@ -22,17 +22,6 @@ export function RecentTasks({ tasks }: RecentTasksProps) {
|
||||
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
|
||||
.slice(0, 5);
|
||||
|
||||
// Fonctions simplifiées utilisant la configuration centralisée
|
||||
|
||||
const getPriorityStyle = (priority: string) => {
|
||||
try {
|
||||
const config = getPriorityConfig(priority as TaskPriority);
|
||||
const hexColor = getPriorityColorHex(config.color);
|
||||
return { color: hexColor };
|
||||
} catch {
|
||||
return { color: '#6b7280' }; // gray-500 par défaut
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="p-6 mt-8">
|
||||
@@ -56,82 +45,46 @@ export function RecentTasks({ tasks }: RecentTasksProps) {
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{recentTasks.map((task) => (
|
||||
<div
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
className="p-3 border border-[var(--border)] rounded-lg hover:bg-[var(--card)]/50 transition-colors"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h4 className="font-medium text-sm truncate">{task.title}</h4>
|
||||
{task.source === 'jira' && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
Jira
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{task.description && (
|
||||
<p className="text-xs text-[var(--muted-foreground)] mb-2 line-clamp-1">
|
||||
{task.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Badge className={`text-xs ${getStatusBadgeClasses(task.status)}`}>
|
||||
{getStatusLabel(task.status)}
|
||||
</Badge>
|
||||
|
||||
{task.priority && (
|
||||
<span
|
||||
className="text-xs font-medium"
|
||||
style={getPriorityStyle(task.priority)}
|
||||
>
|
||||
{(() => {
|
||||
try {
|
||||
return getPriorityConfig(task.priority as TaskPriority).label;
|
||||
} catch {
|
||||
return task.priority;
|
||||
}
|
||||
})()}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{task.tags && task.tags.length > 0 && (
|
||||
<div className="flex gap-1">
|
||||
<TagDisplay
|
||||
tags={task.tags.slice(0, 2)}
|
||||
availableTags={availableTags}
|
||||
size="sm"
|
||||
maxTags={2}
|
||||
showColors={true}
|
||||
/>
|
||||
{task.tags.length > 2 && (
|
||||
<span className="text-xs text-[var(--muted-foreground)]">
|
||||
+{task.tags.length - 2}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-xs text-[var(--muted-foreground)] whitespace-nowrap">
|
||||
{formatDateShort(task.updatedAt)}
|
||||
</div>
|
||||
<Link
|
||||
href={`/kanban?taskId=${task.id}`}
|
||||
className="p-1 rounded hover:bg-[var(--muted)]/50 transition-colors"
|
||||
title="Ouvrir dans le Kanban"
|
||||
>
|
||||
<svg className="w-3 h-3 text-[var(--primary)] hover:text-[var(--primary)]/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
title={task.title}
|
||||
description={task.description}
|
||||
status={getStatusLabel(task.status)}
|
||||
priority={task.priority ? (() => {
|
||||
try {
|
||||
return getPriorityConfig(task.priority as TaskPriority).label;
|
||||
} catch {
|
||||
return task.priority;
|
||||
}
|
||||
})() : undefined}
|
||||
tags={task.tags && task.tags.length > 0 ? [
|
||||
<TagDisplay
|
||||
key="tags"
|
||||
tags={task.tags.slice(0, 2)}
|
||||
availableTags={availableTags}
|
||||
size="sm"
|
||||
maxTags={2}
|
||||
showColors={true}
|
||||
/>,
|
||||
...(task.tags.length > 2 ? [
|
||||
<span key="more" className="text-xs text-[var(--muted-foreground)]">
|
||||
+{task.tags.length - 2}
|
||||
</span>
|
||||
] : [])
|
||||
] : undefined}
|
||||
metadata={formatDateShort(task.updatedAt)}
|
||||
actions={
|
||||
<Link
|
||||
href={`/kanban?taskId=${task.id}`}
|
||||
className="p-1 rounded hover:bg-[var(--muted)]/50 transition-colors"
|
||||
title="Ouvrir dans le Kanban"
|
||||
>
|
||||
<svg className="w-3 h-3 text-[var(--primary)] hover:text-[var(--primary)]/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user