- 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.
163 lines
5.7 KiB
TypeScript
163 lines
5.7 KiB
TypeScript
'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}
|
||
/>
|
||
)}
|
||
</>
|
||
);
|
||
}
|