feat: refactor Daily components and enhance UI integration

- Replaced `DailyCalendar` with a new `Calendar` component for improved functionality and consistency.
- Introduced `AlertBanner` to replace `DeadlineReminder`, providing a more flexible way to display urgent tasks.
- Updated `DailyAddForm` to use new options for task types, enhancing user experience when adding tasks.
- Removed unused state and components, streamlining the DailyPageClient for better performance and maintainability.
- Enhanced `DailySection` to utilize new `CheckboxItem` format for better integration with the UI.
- Cleaned up imports and improved overall structure for better readability.
This commit is contained in:
Julien Froidefond
2025-09-29 09:47:13 +02:00
parent 41fdd0c5b5
commit d45a04d347
18 changed files with 1583 additions and 654 deletions

View File

@@ -0,0 +1,224 @@
'use client';
import { useState } from 'react';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
export interface CollapsibleItem {
id: string;
title: string;
subtitle?: string;
metadata?: string;
isChecked?: boolean;
isArchived?: boolean;
icon?: string;
actions?: Array<{
label: string;
icon: string;
onClick: () => void;
variant?: 'primary' | 'secondary' | 'destructive';
disabled?: boolean;
}>;
}
interface CollapsibleSectionProps {
title: string;
items: CollapsibleItem[];
icon?: string;
defaultCollapsed?: boolean;
loading?: boolean;
emptyMessage?: string;
filters?: Array<{
label: string;
value: string;
options: Array<{ value: string; label: string }>;
onChange: (value: string) => void;
}>;
onRefresh?: () => void;
onItemToggle?: (itemId: string) => void;
className?: string;
}
export function CollapsibleSection({
title,
items,
icon = '📋',
defaultCollapsed = false,
loading = false,
emptyMessage = 'Aucun élément',
filters = [],
onRefresh,
onItemToggle,
className = ''
}: CollapsibleSectionProps) {
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
const handleItemToggle = (itemId: string) => {
onItemToggle?.(itemId);
};
const getItemClasses = (item: CollapsibleItem) => {
let classes = 'flex items-center gap-3 p-3 rounded-lg border border-[var(--border)]';
if (item.isArchived) {
classes += ' opacity-60 bg-[var(--muted)]/20';
} else {
classes += ' bg-[var(--card)]';
}
return classes;
};
const getCheckboxClasses = (item: CollapsibleItem) => {
let classes = 'w-5 h-5 rounded border-2 flex items-center justify-center transition-colors';
if (item.isArchived) {
classes += ' border-[var(--muted)] cursor-not-allowed';
} else {
classes += ' border-[var(--border)] hover:border-[var(--primary)]';
}
return classes;
};
const getActionClasses = (action: NonNullable<CollapsibleItem['actions']>[0]) => {
let classes = 'text-xs px-2 py-1';
switch (action.variant) {
case 'destructive':
classes += ' text-[var(--destructive)] hover:text-[var(--destructive)]';
break;
case 'primary':
classes += ' text-[var(--primary)] hover:text-[var(--primary)]';
break;
default:
classes += ' text-[var(--foreground)]';
}
return classes;
};
return (
<Card className={`mt-6 ${className}`}>
<CardHeader>
<div className="flex items-center justify-between">
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="flex items-center gap-2 text-lg font-semibold hover:text-[var(--primary)] transition-colors"
>
<span className={`transform transition-transform ${isCollapsed ? 'rotate-0' : 'rotate-90'}`}>
</span>
{icon} {title}
{items.length > 0 && (
<span className="bg-[var(--warning)] text-[var(--warning-foreground)] px-2 py-1 rounded-full text-xs font-medium">
{items.length}
</span>
)}
</button>
{!isCollapsed && (
<div className="flex items-center gap-2">
{/* Filtres */}
{filters.map((filter, index) => (
<select
key={index}
value={filter.value}
onChange={(e) => filter.onChange(e.target.value)}
className="text-xs px-2 py-1 border border-[var(--border)] rounded bg-[var(--background)]"
>
{filter.options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
))}
{/* Bouton refresh */}
{onRefresh && (
<Button
variant="ghost"
size="sm"
onClick={onRefresh}
disabled={loading}
>
{loading ? '🔄' : '↻'}
</Button>
)}
</div>
)}
</div>
</CardHeader>
{!isCollapsed && (
<CardContent>
{loading ? (
<div className="text-center py-4 text-[var(--muted-foreground)]">
Chargement...
</div>
) : items.length === 0 ? (
<div className="text-center py-4 text-[var(--muted-foreground)]">
🎉 {emptyMessage} ! Excellent travail.
</div>
) : (
<div className="space-y-2">
{items.map((item) => (
<div
key={item.id}
className={getItemClasses(item)}
>
{/* Checkbox */}
{item.isChecked !== undefined && (
<button
onClick={() => handleItemToggle(item.id)}
disabled={item.isArchived}
className={getCheckboxClasses(item)}
>
{item.isChecked && <span className="text-[var(--primary)]"></span>}
</button>
)}
{/* Contenu */}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
{item.icon && <span>{item.icon}</span>}
<span className={`text-sm font-medium ${item.isArchived ? 'line-through' : ''}`}>
{item.title}
</span>
</div>
{(item.subtitle || item.metadata) && (
<div className="flex items-center gap-3 text-xs text-[var(--muted-foreground)]">
{item.subtitle && <span>{item.subtitle}</span>}
{item.metadata && <span>{item.metadata}</span>}
</div>
)}
</div>
{/* Actions */}
{item.actions && (
<div className="flex items-center gap-1">
{item.actions.map((action, index) => (
<Button
key={index}
variant="ghost"
size="sm"
onClick={action.onClick}
disabled={action.disabled}
title={action.label}
className={getActionClasses(action)}
>
{action.icon}
</Button>
))}
</div>
)}
</div>
))}
</div>
)}
</CardContent>
)}
</Card>
);
}