Fix reminder alerts not firing and add AM/PM time picker
- Backend: /due endpoint now matches both NULL and empty string for recurrence_rule (form was sending '' not null, excluding all reminders) - Form: sends null instead of '' for empty recurrence_rule - ReminderForm: replaced datetime-local with date + hour/minute/AM-PM selects for 12-hour time format Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5080e23256
commit
b2e336ab4a
@ -50,7 +50,10 @@ async def get_due_reminders(
|
||||
Reminder.remind_at <= now,
|
||||
Reminder.is_dismissed == False,
|
||||
Reminder.is_active == True,
|
||||
or_(
|
||||
Reminder.recurrence_rule.is_(None),
|
||||
Reminder.recurrence_rule == '',
|
||||
),
|
||||
or_(
|
||||
Reminder.snoozed_until.is_(None),
|
||||
Reminder.snoozed_until <= now,
|
||||
|
||||
@ -22,17 +22,48 @@ 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 [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 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 mutation = useMutation({
|
||||
mutationFn: async (data: typeof formData) => {
|
||||
mutationFn: async () => {
|
||||
const data = {
|
||||
title,
|
||||
description: description || null,
|
||||
remind_at: buildRemindAt(date, hour, minute, period) || null,
|
||||
recurrence_rule: recurrenceRule || null,
|
||||
};
|
||||
if (reminder) {
|
||||
const response = await api.put(`/reminders/${reminder.id}`, data);
|
||||
return response.data;
|
||||
@ -55,7 +86,7 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
|
||||
const handleSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
mutation.mutate(formData);
|
||||
mutation.mutate();
|
||||
};
|
||||
|
||||
return (
|
||||
@ -71,8 +102,8 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
<Label htmlFor="title" required>Title</Label>
|
||||
<Input
|
||||
id="title"
|
||||
value={formData.title}
|
||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -81,29 +112,60 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
className="min-h-[80px] flex-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="remind_at">Remind At</Label>
|
||||
<Label htmlFor="remind_date">Remind At</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
id="remind_at"
|
||||
type="datetime-local"
|
||||
value={formData.remind_at}
|
||||
onChange={(e) => setFormData({ ...formData, remind_at: e.target.value })}
|
||||
id="remind_date"
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
className="flex-1"
|
||||
/>
|
||||
<Select
|
||||
value={hour}
|
||||
onChange={(e) => setHour(e.target.value)}
|
||||
className="w-[4.5rem]"
|
||||
>
|
||||
{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>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="recurrence">Recurrence</Label>
|
||||
<Select
|
||||
id="recurrence"
|
||||
value={formData.recurrence_rule}
|
||||
onChange={(e) => setFormData({ ...formData, recurrence_rule: e.target.value })}
|
||||
value={recurrenceRule}
|
||||
onChange={(e) => setRecurrenceRule(e.target.value)}
|
||||
>
|
||||
<option value="">None</option>
|
||||
<option value="daily">Daily</option>
|
||||
@ -112,7 +174,6 @@ export default function ReminderForm({ reminder, onClose }: ReminderFormProps) {
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SheetFooter>
|
||||
<Button type="button" variant="outline" onClick={onClose}>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user