148 lines
4.3 KiB
TypeScript
148 lines
4.3 KiB
TypeScript
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>
|
|
)
|
|
}
|