feat(todos): composant TodoForm (rapide mobile / étendu laptop)
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user