Revert ReminderForm to single datetime-local input
Matches EventForm pattern used across UMBRA. Remind At and Recurrence now sit side-by-side in a 2-column grid. Empty optional fields send null instead of empty string. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
89b4bd4870
commit
42e0fff40c
@ -22,53 +22,28 @@ interface ReminderFormProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function parseRemindAt(remindAt?: string) {
|
||||
if (!remindAt) return { date: '', hour: '12', minute: '00', period: 'PM' as const };
|
||||
const d = new Date(remindAt);
|
||||
const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
let hours = d.getHours();
|
||||
const period = hours >= 12 ? 'PM' as const : 'AM' as const;
|
||||
hours = hours % 12 || 12;
|
||||
return {
|
||||
date,
|
||||
hour: String(hours),
|
||||
minute: String(d.getMinutes()).padStart(2, '0'),
|
||||
period,
|
||||
};
|
||||
}
|
||||
|
||||
function buildRemindAt(date: string, hour: string, minute: string, period: 'AM' | 'PM'): string {
|
||||
if (!date) return '';
|
||||
let h = parseInt(hour, 10);
|
||||
if (period === 'AM' && h === 12) h = 0;
|
||||
else if (period === 'PM' && h !== 12) h += 12;
|
||||
return `${date}T${String(h).padStart(2, '0')}:${minute}`;
|
||||
}
|
||||
|
||||
export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const parsed = parseRemindAt(reminder?.remind_at);
|
||||
const [title, setTitle] = useState(reminder?.title || '');
|
||||
const [description, setDescription] = useState(reminder?.description || '');
|
||||
const [date, setDate] = useState(parsed.date);
|
||||
const [hour, setHour] = useState(parsed.hour);
|
||||
const [minute, setMinute] = useState(parsed.minute);
|
||||
const [period, setPeriod] = useState<'AM' | 'PM'>(parsed.period);
|
||||
const [recurrenceRule, setRecurrenceRule] = useState(reminder?.recurrence_rule || '');
|
||||
const [formData, setFormData] = useState({
|
||||
title: reminder?.title || '',
|
||||
description: reminder?.description || '',
|
||||
remind_at: reminder?.remind_at ? reminder.remind_at.slice(0, 16) : '',
|
||||
recurrence_rule: reminder?.recurrence_rule || '',
|
||||
});
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const data = {
|
||||
title,
|
||||
description: description || null,
|
||||
remind_at: buildRemindAt(date, hour, minute, period) || null,
|
||||
recurrence_rule: recurrenceRule || null,
|
||||
mutationFn: async (data: typeof formData) => {
|
||||
const payload = {
|
||||
...data,
|
||||
description: data.description || null,
|
||||
remind_at: data.remind_at || null,
|
||||
recurrence_rule: data.recurrence_rule || null,
|
||||
};
|
||||
if (reminder) {
|
||||
const response = await api.put(`/reminders/${reminder.id}`, data);
|
||||
const response = await api.put(`/reminders/${reminder.id}`, payload);
|
||||
return response.data;
|
||||
} else {
|
||||
const response = await api.post('/reminders', data);
|
||||
const response = await api.post('/reminders', payload);
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
@ -86,7 +61,7 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
|
||||
const handleSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
mutation.mutate();
|
||||
mutation.mutate(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -102,8 +77,8 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
<Label htmlFor="title" required>Title</Label>
|
||||
<Input
|
||||
id="title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
value={formData.title}
|
||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -112,67 +87,37 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
className="min-h-[80px] flex-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="remind_date">Remind At</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="remind_at">Remind At</Label>
|
||||
<Input
|
||||
id="remind_date"
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
className="flex-1"
|
||||
id="remind_at"
|
||||
type="datetime-local"
|
||||
value={formData.remind_at}
|
||||
onChange={(e) => setFormData({ ...formData, remind_at: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="recurrence">Recurrence</Label>
|
||||
<Select
|
||||
value={hour}
|
||||
onChange={(e) => setHour(e.target.value)}
|
||||
className="w-[4.5rem]"
|
||||
id="recurrence"
|
||||
value={formData.recurrence_rule}
|
||||
onChange={(e) => setFormData({ ...formData, recurrence_rule: e.target.value })}
|
||||
>
|
||||
{Array.from({ length: 12 }, (_, i) => i + 1).map((h) => (
|
||||
<option key={h} value={String(h)}>{h}</option>
|
||||
))}
|
||||
</Select>
|
||||
<span className="text-muted-foreground">:</span>
|
||||
<Select
|
||||
value={minute}
|
||||
onChange={(e) => setMinute(e.target.value)}
|
||||
className="w-[4.5rem]"
|
||||
>
|
||||
{Array.from({ length: 12 }, (_, i) => i * 5).map((m) => (
|
||||
<option key={m} value={String(m).padStart(2, '0')}>
|
||||
{String(m).padStart(2, '0')}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
value={period}
|
||||
onChange={(e) => setPeriod(e.target.value as 'AM' | 'PM')}
|
||||
className="w-[4.5rem]"
|
||||
>
|
||||
<option value="AM">AM</option>
|
||||
<option value="PM">PM</option>
|
||||
<option value="">None</option>
|
||||
<option value="daily">Daily</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
<option value="monthly">Monthly</option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="recurrence">Recurrence</Label>
|
||||
<Select
|
||||
id="recurrence"
|
||||
value={recurrenceRule}
|
||||
onChange={(e) => setRecurrenceRule(e.target.value)}
|
||||
>
|
||||
<option value="">None</option>
|
||||
<option value="daily">Daily</option>
|
||||
<option value="weekly">Weekly</option>
|
||||
<option value="monthly">Monthly</option>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SheetFooter>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user