feat(todos): favicon maison + mode édition double-tap/double-clic

- Favicon SVG maison Gruvbox orange sur fond sombre
- TodoForm accepte initialValues et submitLabel pour l'édition
- SwipeableRow détecte le double-tap (< 300ms, sans déplacement)
- TodosPage : double-tap mobile / double-clic laptop ouvre l'édition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 14:54:47 +02:00
co-authored by Claude Sonnet 4.6
parent 6ef64dfe1c
commit 134678a6f1
5 changed files with 83 additions and 20 deletions
+43 -1
View File
@@ -5,6 +5,8 @@ import { fetchTodos, createTodo, updateTodo, deleteTodo, postponeTodo } from '..
import SwipeableRow from '../components/todos/SwipeableRow'
import TodoForm from '../components/todos/TodoForm'
type EditingTodo = Todo | null
const DOMAINS = [
'informatique', 'diy', 'electronique', 'domotique',
'bricolage', 'jardin', 'cuisine', 'voyage', 'animaux',
@@ -31,6 +33,7 @@ export default function TodosPage() {
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [showForm, setShowForm] = useState(false)
const [editingTodo, setEditingTodo] = useState<EditingTodo>(null)
const [filters, setFilters] = useState<TodoFilters>({ status: 'pending' })
const load = useCallback(async () => {
@@ -57,6 +60,16 @@ export default function TodosPage() {
}
}
async function handleUpdate(id: string, data: TodoCreate) {
try {
await updateTodo(id, data)
setEditingTodo(null)
void load()
} catch {
setError('Erreur lors de la mise à jour')
}
}
async function handleDone(id: string) {
try {
await updateTodo(id, { status: 'done' })
@@ -148,6 +161,30 @@ export default function TodosPage() {
</div>
)}
{/* Formulaire d'édition */}
{editingTodo && (
<div className="glass" style={{ padding: 16, borderRadius: 10, marginBottom: 16, borderLeft: '3px solid var(--accent)' }}>
<p style={{ color: 'var(--ink-3)', fontSize: 11, fontFamily: 'var(--font-ui)', marginBottom: 8, textTransform: 'uppercase', letterSpacing: 1 }}>
Modifier la tâche
</p>
<TodoForm
onSubmit={data => handleUpdate(editingTodo.id, data)}
onCancel={() => setEditingTodo(null)}
extended
submitLabel="Enregistrer"
initialValues={{
title: editingTodo.title,
domain: editingTodo.domain ?? undefined,
priority: editingTodo.priority,
due_date: editingTodo.due_date ?? undefined,
body: editingTodo.body ?? undefined,
url: editingTodo.url ?? undefined,
tags: editingTodo.tags,
}}
/>
</div>
)}
{loading && (
<p style={{ color: 'var(--ink-3)', textAlign: 'center', padding: 24 }}>Chargement</p>
)}
@@ -177,6 +214,7 @@ export default function TodosPage() {
<SwipeableRow
key={todo.id}
onSwipeRight={() => void handleDone(todo.id)}
onDoubleTap={() => setEditingTodo(todo)}
rightContent={
<div style={{ display: 'flex', gap: 4, padding: '0 8px' }}>
<button
@@ -265,7 +303,11 @@ export default function TodosPage() {
background: idx % 2 === 0 ? 'transparent' : 'rgba(0,0,0,0.1)',
}}
>
<td style={{ padding: '10px 14px', color: 'var(--ink-1)', maxWidth: 280 }}>
<td
style={{ padding: '10px 14px', color: 'var(--ink-1)', maxWidth: 280, cursor: 'pointer' }}
onDoubleClick={() => setEditingTodo(todo)}
title="Double-clic pour modifier"
>
<div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{todo.title}
</div>