- 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.
158 lines
4.3 KiB
TypeScript
158 lines
4.3 KiB
TypeScript
'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>
|
|
);
|
|
}
|