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:
157
src/components/ui/DailyAddForm.tsx
Normal file
157
src/components/ui/DailyAddForm.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user