Add onboarding email campaign system with post-verification welcome email
Implements automated onboarding emails to encourage user engagement: - Post-verification welcome email with 5 tips (sent after email verification) - "No Residence" email (2+ days after registration with no property) - "No Tasks" email (5+ days after first residence with no tasks) Key features: - Each onboarding email type sent only once per user (enforced by unique constraint) - Email open tracking via tracking pixel endpoint - Daily scheduled job at 10:00 AM UTC to process eligible users - Admin panel UI for viewing sent emails, stats, and manual sending - Admin can send any email type to users from the user detail Testing section New files: - internal/models/onboarding_email.go - Database model with tracking - internal/services/onboarding_email_service.go - Business logic and eligibility queries - internal/handlers/tracking_handler.go - Email open tracking endpoint - internal/admin/handlers/onboarding_handler.go - Admin API endpoints - admin/src/app/(dashboard)/onboarding-emails/ - Admin UI pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -100,6 +100,7 @@ func main() {
|
||||
mux.HandleFunc(jobs.TypeDailyDigest, jobHandler.HandleDailyDigest)
|
||||
mux.HandleFunc(jobs.TypeSendEmail, jobHandler.HandleSendEmail)
|
||||
mux.HandleFunc(jobs.TypeSendPush, jobHandler.HandleSendPush)
|
||||
mux.HandleFunc(jobs.TypeOnboardingEmails, jobHandler.HandleOnboardingEmails)
|
||||
|
||||
// Start scheduler for periodic tasks
|
||||
scheduler := asynq.NewScheduler(redisOpt, nil)
|
||||
@@ -124,6 +125,13 @@ func main() {
|
||||
}
|
||||
log.Info().Str("cron", "0 * * * *").Int("default_hour", cfg.Worker.DailyNotifHour).Msg("Registered daily digest job (runs hourly for per-user times)")
|
||||
|
||||
// Schedule onboarding emails (runs daily at 10:00 AM UTC)
|
||||
// Sends emails to users who haven't created residences or tasks after registration
|
||||
if _, err := scheduler.Register("0 10 * * *", asynq.NewTask(jobs.TypeOnboardingEmails, nil)); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to register onboarding emails job")
|
||||
}
|
||||
log.Info().Str("cron", "0 10 * * *").Msg("Registered onboarding emails job (runs daily at 10:00 AM UTC)")
|
||||
|
||||
// Handle graceful shutdown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
Reference in New Issue
Block a user