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,162 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
import { DailyCheckbox, DailyCheckboxType } from '@/lib/types';
import { Input } from '@/components/ui/Input';
import { EditCheckboxModal } from './EditCheckboxModal';
interface DailyCheckboxItemProps {
checkbox: DailyCheckbox;
onToggle: (checkboxId: string) => Promise<void>;
onUpdate: (checkboxId: string, text: string, type: DailyCheckboxType, taskId?: string) => Promise<void>;
onDelete: (checkboxId: string) => Promise<void>;
saving?: boolean;
}
export function DailyCheckboxItem({
checkbox,
onToggle,
onUpdate,
onDelete,
saving = false
}: DailyCheckboxItemProps) {
const [inlineEditingId, setInlineEditingId] = useState<string | null>(null);
const [inlineEditingText, setInlineEditingText] = useState('');
const [editingCheckbox, setEditingCheckbox] = useState<DailyCheckbox | null>(null);
// Édition inline simple
const handleStartInlineEdit = () => {
setInlineEditingId(checkbox.id);
setInlineEditingText(checkbox.text);
};
const handleSaveInlineEdit = async () => {
if (!inlineEditingText.trim()) return;
try {
await onUpdate(checkbox.id, inlineEditingText.trim(), checkbox.type, checkbox.taskId);
setInlineEditingId(null);
setInlineEditingText('');
} catch (error) {
console.error('Erreur lors de la modification:', error);
}
};
const handleCancelInlineEdit = () => {
setInlineEditingId(null);
setInlineEditingText('');
};
const handleInlineEditKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
handleSaveInlineEdit();
} else if (e.key === 'Escape') {
e.preventDefault();
handleCancelInlineEdit();
}
};
// Modal d'édition avancée
const handleStartAdvancedEdit = () => {
setEditingCheckbox(checkbox);
};
const handleSaveAdvancedEdit = async (text: string, type: DailyCheckboxType, taskId?: string) => {
await onUpdate(checkbox.id, text, type, taskId);
setEditingCheckbox(null);
};
const handleCloseAdvancedEdit = () => {
setEditingCheckbox(null);
};
return (
<>
<div className={`flex items-center gap-3 p-2 rounded border transition-colors group ${
checkbox.type === 'meeting'
? 'border-l-4 border-l-blue-500 border-t-[var(--border)]/30 border-r-[var(--border)]/30 border-b-[var(--border)]/30 hover:border-t-[var(--border)] hover:border-r-[var(--border)] hover:border-b-[var(--border)]'
: 'border-l-4 border-l-green-500 border-t-[var(--border)]/30 border-r-[var(--border)]/30 border-b-[var(--border)]/30 hover:border-t-[var(--border)] hover:border-r-[var(--border)] hover:border-b-[var(--border)]'
}`}>
{/* Checkbox */}
<input
type="checkbox"
checked={checkbox.isChecked}
onChange={() => onToggle(checkbox.id)}
disabled={saving}
className="w-4 h-4 rounded border border-[var(--border)] text-[var(--primary)] focus:ring-[var(--primary)]/20 focus:ring-2"
/>
{/* Contenu principal */}
{inlineEditingId === checkbox.id ? (
<Input
value={inlineEditingText}
onChange={(e) => setInlineEditingText(e.target.value)}
onKeyDown={handleInlineEditKeyPress}
onBlur={handleSaveInlineEdit}
autoFocus
className="flex-1 h-8 text-sm"
/>
) : (
<div className="flex-1 flex items-center gap-2">
{/* Texte cliquable pour édition inline */}
<span
className={`flex-1 text-sm font-mono transition-all cursor-pointer hover:bg-[var(--muted)]/50 p-1 rounded ${
checkbox.isChecked
? 'line-through text-[var(--muted-foreground)]'
: 'text-[var(--foreground)]'
}`}
onClick={handleStartInlineEdit}
title="Cliquer pour éditer le texte"
>
{checkbox.text}
</span>
{/* Icône d'édition avancée */}
<button
onClick={handleStartAdvancedEdit}
disabled={saving}
className="opacity-0 group-hover:opacity-100 w-6 h-6 rounded-full bg-[var(--muted)]/50 hover:bg-[var(--muted)] border border-[var(--border)]/30 hover:border-[var(--border)] flex items-center justify-center transition-all duration-200 text-[var(--foreground)] text-xs"
title="Éditer les options (type, liaison tâche)"
>
</button>
</div>
)}
{/* Lien vers la tâche si liée */}
{checkbox.task && (
<Link
href={`/?highlight=${checkbox.task.id}`}
className="text-xs text-[var(--primary)] hover:text-[var(--primary)]/80 font-mono"
title={`Tâche: ${checkbox.task.title}`}
>
#{checkbox.task.id.slice(-6)}
</Link>
)}
{/* Bouton de suppression */}
<button
onClick={() => onDelete(checkbox.id)}
disabled={saving}
className="opacity-0 group-hover:opacity-100 w-6 h-6 rounded-full bg-[var(--destructive)]/20 hover:bg-[var(--destructive)]/30 border border-[var(--destructive)]/30 hover:border-[var(--destructive)]/50 flex items-center justify-center transition-all duration-200 text-[var(--destructive)] text-xs"
title="Supprimer"
>
×
</button>
</div>
{/* Modal d'édition avancée */}
{editingCheckbox && (
<EditCheckboxModal
checkbox={editingCheckbox}
isOpen={true}
onClose={handleCloseAdvancedEdit}
onSave={handleSaveAdvancedEdit}
saving={saving}
/>
)}
</>
);
}