Add per-user notification time preferences

Allow users to customize when they receive notification reminders:
- Add task_due_soon_hour, task_overdue_hour, warranty_expiring_hour fields
- Store times in UTC, clients convert to/from local timezone
- Worker runs hourly, queries only users scheduled for that hour
- Early exit optimization when no users need notifications
- Admin UI displays custom notification times

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-07 00:23:57 -06:00
parent af87bd943e
commit dd16019ce2
7 changed files with 267 additions and 113 deletions

View File

@@ -104,21 +104,20 @@ func main() {
// Start scheduler for periodic tasks
scheduler := asynq.NewScheduler(redisOpt, nil)
// Schedule task reminder notifications
reminderCron := formatCron(cfg.Worker.TaskReminderHour)
if _, err := scheduler.Register(reminderCron, asynq.NewTask(jobs.TypeTaskReminder, nil)); err != nil {
// Schedule task reminder notifications (runs every hour to support per-user custom times)
// The job handler filters users based on their preferred notification hour
if _, err := scheduler.Register("0 * * * *", asynq.NewTask(jobs.TypeTaskReminder, nil)); err != nil {
log.Fatal().Err(err).Msg("Failed to register task reminder job")
}
log.Info().Str("cron", reminderCron).Msg("Registered task reminder job")
log.Info().Str("cron", "0 * * * *").Int("default_hour", cfg.Worker.TaskReminderHour).Msg("Registered task reminder job (runs hourly for per-user times)")
// Schedule overdue reminder
overdueCron := formatCron(cfg.Worker.OverdueReminderHour)
if _, err := scheduler.Register(overdueCron, asynq.NewTask(jobs.TypeOverdueReminder, nil)); err != nil {
// Schedule overdue reminder (runs every hour to support per-user custom times)
if _, err := scheduler.Register("0 * * * *", asynq.NewTask(jobs.TypeOverdueReminder, nil)); err != nil {
log.Fatal().Err(err).Msg("Failed to register overdue reminder job")
}
log.Info().Str("cron", overdueCron).Msg("Registered overdue reminder job")
log.Info().Str("cron", "0 * * * *").Int("default_hour", cfg.Worker.OverdueReminderHour).Msg("Registered overdue reminder job (runs hourly for per-user times)")
// Schedule daily digest
// Schedule daily digest (runs at configured hour - no per-user customization yet)
dailyCron := formatCron(cfg.Worker.DailyNotifHour)
if _, err := scheduler.Register(dailyCron, asynq.NewTask(jobs.TypeDailyDigest, nil)); err != nil {
log.Fatal().Err(err).Msg("Failed to register daily digest job")