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,157 @@
'use client';
import { useState, useRef } from 'react';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
export interface AddFormOption {
value: string;
label: string;
icon?: string;
color?: string;
}
interface AddFormProps {
onAdd: (text: string, option?: string) => Promise<void>;
disabled?: boolean;
placeholder?: string;
options?: AddFormOption[];
defaultOption?: string;
className?: string;
}
export function DailyAddForm({
onAdd,
disabled = false,
placeholder = "Ajouter un élément...",
options = [],
defaultOption,
className = ''
}: AddFormProps) {
const [newItemText, setNewItemText] = useState('');
const [selectedOption, setSelectedOption] = useState<string>(defaultOption || (options.length > 0 ? options[0].value : ''));
const inputRef = useRef<HTMLInputElement>(null);
const handleAddItem = () => {
if (!newItemText.trim()) return;
const text = newItemText.trim();
// Vider et refocus IMMÉDIATEMENT pour l'UX optimiste
setNewItemText('');
inputRef.current?.focus();
// Lancer l'ajout en arrière-plan (fire and forget)
onAdd(text, selectedOption).catch(error => {
console.error('Erreur lors de l\'ajout:', error);
// En cas d'erreur, on pourrait restaurer le texte
// setNewItemText(text);
});
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
handleAddItem();
}
};
const getPlaceholder = () => {
if (placeholder !== "Ajouter un élément...") return placeholder;
if (options.length > 0) {
const selectedOptionData = options.find(opt => opt.value === selectedOption);
if (selectedOptionData) {
return `Ajouter ${selectedOptionData.label.toLowerCase()}...`;
}
}
return placeholder;
};
const getOptionColor = (option: AddFormOption) => {
if (option.color) return option.color;
// Couleurs par défaut selon le type
switch (option.value) {
case 'task':
return 'green';
case 'meeting':
return 'blue';
default:
return 'gray';
}
};
const getOptionClasses = (option: AddFormOption) => {
const color = getOptionColor(option);
const isSelected = selectedOption === option.value;
if (isSelected) {
switch (color) {
case 'green':
return 'border-l-green-500 bg-green-500/30 text-white font-medium';
case 'blue':
return 'border-l-blue-500 bg-blue-500/30 text-white font-medium';
default:
return 'border-l-gray-500 bg-gray-500/30 text-white font-medium';
}
} else {
switch (color) {
case 'green':
return 'border-l-green-300 hover:border-l-green-400 opacity-70 hover:opacity-90';
case 'blue':
return 'border-l-blue-300 hover:border-l-blue-400 opacity-70 hover:opacity-90';
default:
return 'border-l-gray-300 hover:border-l-gray-400 opacity-70 hover:opacity-90';
}
}
};
return (
<div className={`space-y-2 ${className}`}>
{/* Sélecteur d'options */}
{options.length > 0 && (
<div className="flex gap-2">
{options.map((option) => (
<Button
key={option.value}
type="button"
onClick={() => setSelectedOption(option.value)}
variant="ghost"
size="sm"
className={`flex items-center gap-1 text-xs border-l-4 ${getOptionClasses(option)}`}
disabled={disabled}
>
{option.icon && <span>{option.icon}</span>}
{option.label}
</Button>
))}
</div>
)}
{/* Champ de saisie et bouton d'ajout */}
<div className="flex gap-2">
<Input
ref={inputRef}
type="text"
placeholder={getPlaceholder()}
value={newItemText}
onChange={(e) => setNewItemText(e.target.value)}
onKeyDown={handleKeyPress}
disabled={disabled}
className="flex-1 min-w-[300px]"
/>
<Button
onClick={handleAddItem}
disabled={!newItemText.trim() || disabled}
variant="primary"
size="sm"
className="min-w-[40px]"
>
+
</Button>
</div>
</div>
);
}