feat(todos): composant TodoForm (rapide mobile / étendu laptop)

This commit is contained in:
2026-05-24 12:10:31 +02:00
parent 9e6e0902ab
commit 580aab822a
+147
View File
@@ -0,0 +1,147 @@
import { useState } from 'react'
import type { TodoCreate } from '../../api/todos'
const DOMAINS = [
'informatique', 'diy', 'electronique', 'domotique',
'bricolage', 'jardin', 'cuisine', 'voyage', 'animaux',
]
interface TodoFormProps {
onSubmit: (data: TodoCreate) => Promise<void>
onCancel: () => void
extended?: boolean // true = tous les champs (vue laptop)
}
const inputStyle: React.CSSProperties = {
width: '100%',
background: 'var(--bg-4)',
border: '1px solid var(--bg-5)',
borderRadius: 8,
padding: '10px 12px',
color: 'var(--ink-1)',
fontFamily: 'var(--font-ui)',
fontSize: 14,
boxSizing: 'border-box',
}
export default function TodoForm({ onSubmit, onCancel, extended = false }: TodoFormProps) {
const [title, setTitle] = useState('')
const [domain, setDomain] = useState('')
const [priority, setPriority] = useState('medium')
const [dueDate, setDueDate] = useState('')
const [body, setBody] = useState('')
const [url, setUrl] = useState('')
const [tags, setTags] = useState('')
const [loading, setLoading] = useState(false)
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
if (!title.trim()) return
setLoading(true)
try {
await onSubmit({
title: title.trim(),
domain: domain || undefined,
priority,
due_date: dueDate ? new Date(dueDate).toISOString() : undefined,
body: body.trim() || undefined,
url: url.trim() || undefined,
tags: tags ? tags.split(',').map(t => t.trim()).filter(Boolean) : [],
})
} finally {
setLoading(false)
}
}
return (
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<input
style={inputStyle}
placeholder="Titre de la tâche *"
value={title}
onChange={e => setTitle(e.target.value)}
autoFocus
required
/>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
<select style={inputStyle} value={domain} onChange={e => setDomain(e.target.value)}>
<option value="">Domaine</option>
{DOMAINS.map(d => <option key={d} value={d}>{d}</option>)}
</select>
<select style={inputStyle} value={priority} onChange={e => setPriority(e.target.value)}>
<option value="low">Priorité basse</option>
<option value="medium">Priorité moyenne</option>
<option value="high">Priorité haute</option>
</select>
</div>
<input
style={inputStyle}
type="date"
value={dueDate}
onChange={e => setDueDate(e.target.value)}
placeholder="Date objectif"
/>
{extended && (
<>
<textarea
style={{ ...inputStyle, minHeight: 80, resize: 'vertical' }}
placeholder="Description"
value={body}
onChange={e => setBody(e.target.value)}
/>
<input
style={inputStyle}
placeholder="URL (lien externe optionnel)"
value={url}
onChange={e => setUrl(e.target.value)}
/>
<input
style={inputStyle}
placeholder="Tags (séparés par virgule)"
value={tags}
onChange={e => setTags(e.target.value)}
/>
</>
)}
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
<button
type="button"
onClick={onCancel}
style={{
padding: '10px 16px',
borderRadius: 8,
border: '1px solid var(--bg-5)',
background: 'transparent',
color: 'var(--ink-2)',
cursor: 'pointer',
fontFamily: 'var(--font-ui)',
minHeight: 48,
}}
>
Annuler
</button>
<button
type="submit"
disabled={loading}
style={{
padding: '10px 20px',
borderRadius: 8,
border: 'none',
background: 'var(--accent)',
color: '#1d2021',
cursor: 'pointer',
fontFamily: 'var(--font-ui)',
fontWeight: 600,
minHeight: 48,
}}
>
{loading ? '…' : 'Créer'}
</button>
</div>
</form>
)
}