feat: enhance DailyCheckbox model and service for type management

- Added `DailyCheckboxType` to define checkbox types ('task' | 'meeting') in TypeScript.
- Updated `DailyCheckbox` model in Prisma schema to include `type` field with a default value of 'task'.
- Modified `DailyService` to handle checkbox type during creation and updates.
- Adjusted API route to accept checkbox type in requests.
- Refactored `DailyPageClient` to support type management in checkbox operations.
This commit is contained in:
Julien Froidefond
2025-09-15 22:16:34 +02:00
parent 08d344652f
commit adfef551ab
11 changed files with 744 additions and 211 deletions

View File

@@ -0,0 +1,183 @@
'use client';
import { useState, useEffect } from 'react';
import { Task } from '@/lib/types';
import { tasksClient } from '@/clients/tasks-client';
import { Button } from '@/components/ui/Button';
interface TaskSelectorProps {
selectedTaskId?: string;
onTaskSelect: (taskId: string | undefined) => void;
disabled?: boolean;
}
export function TaskSelector({ selectedTaskId, onTaskSelect, disabled }: TaskSelectorProps) {
const [tasks, setTasks] = useState<Task[]>([]);
const [loading, setLoading] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [search, setSearch] = useState('');
const selectedTask = tasks.find(task => task.id === selectedTaskId);
useEffect(() => {
if (isOpen && tasks.length === 0) {
loadTasks();
}
}, [isOpen]);
const loadTasks = async () => {
setLoading(true);
try {
const response = await tasksClient.getTasks({
status: ['todo', 'in_progress', 'backlog'],
limit: 100
});
setTasks(response.data);
} catch (error) {
console.error('Erreur lors du chargement des tâches:', error);
} finally {
setLoading(false);
}
};
const filteredTasks = tasks.filter(task =>
task.title.toLowerCase().includes(search.toLowerCase()) ||
task.description?.toLowerCase().includes(search.toLowerCase())
);
const handleTaskSelect = (taskId: string) => {
onTaskSelect(taskId);
setIsOpen(false);
setSearch('');
};
const handleClear = () => {
onTaskSelect(undefined);
setIsOpen(false);
setSearch('');
};
if (!isOpen) {
return (
<div className="flex gap-1">
<Button
type="button"
onClick={() => setIsOpen(true)}
disabled={disabled}
variant="ghost"
size="sm"
className="text-xs px-2 py-1 h-6"
title="Lier à une tâche"
>
{selectedTask ? `#${selectedTask.id.slice(-6)}` : '🔗'}
</Button>
{selectedTask && (
<Button
type="button"
onClick={handleClear}
disabled={disabled}
variant="ghost"
size="sm"
className="text-xs px-1 py-1 h-6 text-[var(--destructive)]"
title="Délier"
>
×
</Button>
)}
</div>
);
}
return (
<div className="relative">
<div className="absolute bottom-full mb-2 right-0 bg-[var(--card)] border border-[var(--border)] rounded-lg shadow-lg z-10 min-w-[300px] max-w-[400px]">
<div className="p-3">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-medium text-[var(--foreground)]">Lier à une tâche</h3>
<Button
type="button"
onClick={() => setIsOpen(false)}
variant="ghost"
size="sm"
className="text-xs px-1 py-1 h-6"
>
×
</Button>
</div>
<input
type="text"
placeholder="Rechercher une tâche..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full mb-2 px-2 py-1 text-xs border border-[var(--border)] rounded bg-[var(--background)] text-[var(--foreground)]"
autoFocus
/>
<div className="max-h-32 overflow-y-auto space-y-1">
{loading ? (
<div className="text-xs text-[var(--muted-foreground)] text-center py-2">
Chargement...
</div>
) : filteredTasks.length === 0 ? (
<div className="text-xs text-[var(--muted-foreground)] text-center py-2">
Aucune tâche trouvée
</div>
) : (
filteredTasks.map((task) => (
<button
key={task.id}
type="button"
onClick={() => handleTaskSelect(task.id)}
className="w-full text-left p-2 rounded text-xs hover:bg-[var(--muted)] transition-colors"
>
<div className="font-medium text-[var(--foreground)] truncate">
{task.title}
</div>
{task.description && (
<div className="text-[var(--muted-foreground)] truncate">
{task.description}
</div>
)}
<div className="flex items-center gap-2 mt-1">
<span className={`px-1 py-0.5 rounded text-xs ${
task.status === 'todo' ? 'bg-blue-100 text-blue-800' :
task.status === 'in_progress' ? 'bg-yellow-100 text-yellow-800' :
'bg-gray-100 text-gray-800'
}`}>
{task.status}
</span>
<span className="text-[var(--muted-foreground)]">
#{task.id.slice(-6)}
</span>
</div>
</button>
))
)}
</div>
<div className="flex gap-2 mt-2 pt-2 border-t border-[var(--border)]">
<Button
type="button"
onClick={handleClear}
variant="ghost"
size="sm"
className="text-xs flex-1"
>
Aucune tâche
</Button>
<Button
type="button"
onClick={() => setIsOpen(false)}
variant="ghost"
size="sm"
className="text-xs flex-1"
>
Annuler
</Button>
</div>
</div>
</div>
</div>
);
}