This commit is contained in:
Trey t
2026-02-18 10:54:18 -06:00
parent a5245955af
commit 215e7c895d
11 changed files with 638 additions and 79 deletions
@@ -0,0 +1,511 @@
'use client';
import { Clock, Mail, Bell, Calendar, Info, Timer, Repeat } from 'lucide-react';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
export default function AutomationReferencePage() {
return (
<div className="p-6 space-y-6">
<div>
<h1 className="text-2xl font-bold">Automation Reference</h1>
<p className="text-muted-foreground">
Quick reference for all automated emails, notifications, and scheduled jobs
</p>
</div>
{/* Scheduled Jobs */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Clock className="h-5 w-5" />
Scheduled Jobs (Cron)
</CardTitle>
<CardDescription>
Background jobs that run on a schedule. All times are in UTC.
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Job</TableHead>
<TableHead>Schedule</TableHead>
<TableHead>Default Hour</TableHead>
<TableHead>Description</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Task Reminders (Due Soon)</TableCell>
<TableCell>
<Badge variant="outline" className="font-mono">0 20 * * *</Badge>
<span className="text-xs text-muted-foreground ml-2">Daily 8 PM UTC</span>
</TableCell>
<TableCell>
<span className="font-mono">20:00</span> (8 PM)
</TableCell>
<TableCell className="max-w-md">
<p className="text-sm">
Sends frequency-aware &quot;due soon&quot; task reminders. Reminder schedule varies by task frequency
(weekly: day-of only, monthly: 7 days + day-of, annual: 30/14/7 days + day-of).
</p>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Overdue Reminders</TableCell>
<TableCell>
<Badge variant="outline" className="font-mono">0 9 * * *</Badge>
<span className="text-xs text-muted-foreground ml-2">Daily 9 AM UTC</span>
</TableCell>
<TableCell>
<span className="font-mono">09:00</span> (9 AM)
</TableCell>
<TableCell className="max-w-md">
<p className="text-sm">
Sends overdue task reminders with tapering: daily for days 1-3, every 3 days for days 4-14,
then stops to avoid notification fatigue.
</p>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Daily Digest</TableCell>
<TableCell>
<Badge variant="outline" className="font-mono">0 11 * * *</Badge>
<span className="text-xs text-muted-foreground ml-2">Daily 11 AM UTC</span>
</TableCell>
<TableCell>
<span className="font-mono">11:00</span> (11 AM)
</TableCell>
<TableCell className="max-w-md">
<p className="text-sm">
Summary push notification with overdue count and tasks due this week.
Uses user&apos;s stored timezone for accurate overdue calculation.
</p>
</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Onboarding Emails</TableCell>
<TableCell>
<Badge variant="outline" className="font-mono text-yellow-600">Not scheduled</Badge>
<span className="text-xs text-muted-foreground ml-2">Manual only</span>
</TableCell>
<TableCell>
<span className="text-muted-foreground">-</span>
</TableCell>
<TableCell className="max-w-md">
<p className="text-sm">
Handler exists but not registered in scheduler. Can be triggered manually from admin.
Sends emails to users who haven&apos;t created residences (2+ days) or tasks (5+ days).
</p>
</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
{/* Push Notification Types */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Bell className="h-5 w-5" />
Push Notification Types
</CardTitle>
<CardDescription>
Types of push notifications and when they are sent
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Type</TableHead>
<TableHead>Trigger</TableHead>
<TableHead>User Preference</TableHead>
<TableHead>Custom Time Field</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">
<Badge>task_due_soon</Badge>
</TableCell>
<TableCell>Smart Reminder job (tasks due within 2 days)</TableCell>
<TableCell><code>task_due_soon</code></TableCell>
<TableCell><code>task_due_soon_hour</code></TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge variant="destructive">task_overdue</Badge>
</TableCell>
<TableCell>Smart Reminder job (past due date)</TableCell>
<TableCell><code>task_overdue</code></TableCell>
<TableCell><code>task_overdue_hour</code></TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge variant="secondary">task_completed</Badge>
</TableCell>
<TableCell>When another user completes a task</TableCell>
<TableCell><code>task_completed</code></TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge variant="secondary">task_assigned</Badge>
</TableCell>
<TableCell>When assigned to a task</TableCell>
<TableCell><code>task_assigned</code></TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge variant="secondary">residence_shared</Badge>
</TableCell>
<TableCell>When a residence is shared with user</TableCell>
<TableCell><code>residence_shared</code></TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge variant="outline">warranty_expiring</Badge>
</TableCell>
<TableCell>Document warranty expiring (not yet implemented)</TableCell>
<TableCell><code>warranty_expiring</code></TableCell>
<TableCell><code>warranty_expiring_hour</code></TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge className="bg-blue-500">daily_digest</Badge>
</TableCell>
<TableCell>Daily Digest job (summary notification)</TableCell>
<TableCell><code>daily_digest</code></TableCell>
<TableCell><code>daily_digest_hour</code></TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
{/* Email Templates */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Mail className="h-5 w-5" />
Email Templates
</CardTitle>
<CardDescription>
All email templates and when they are sent
</CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="transactional" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="transactional" className="flex items-center gap-2">
<Timer className="h-4 w-4" />
Transactional Emails
</TabsTrigger>
<TabsTrigger value="onboarding" className="flex items-center gap-2">
<Repeat className="h-4 w-4" />
Onboarding Emails
</TabsTrigger>
</TabsList>
<TabsContent value="transactional" className="mt-4">
<Table>
<TableHeader>
<TableRow>
<TableHead>Email</TableHead>
<TableHead>Subject</TableHead>
<TableHead>Trigger</TableHead>
<TableHead>Expiry</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">Welcome Email</TableCell>
<TableCell>Welcome to Casera - Verify Your Email</TableCell>
<TableCell>User registration (email/password)</TableCell>
<TableCell>24 hours (verification code)</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Apple Welcome</TableCell>
<TableCell>Welcome to Casera!</TableCell>
<TableCell>Apple Sign In registration</TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Google Welcome</TableCell>
<TableCell>Welcome to Casera!</TableCell>
<TableCell>Google Sign In registration</TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Post-Verification</TableCell>
<TableCell>You&apos;re All Set! Getting Started with Casera</TableCell>
<TableCell>After email verification</TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Verification Email</TableCell>
<TableCell>Casera - Verify Your Email</TableCell>
<TableCell>Resend verification code</TableCell>
<TableCell>24 hours</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Password Reset</TableCell>
<TableCell>Casera - Password Reset Request</TableCell>
<TableCell>Password reset request</TableCell>
<TableCell>15 minutes</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Password Changed</TableCell>
<TableCell>Casera - Your Password Has Been Changed</TableCell>
<TableCell>After password change</TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Task Completed</TableCell>
<TableCell>Casera - Task Completed: [Task Title]</TableCell>
<TableCell>When task is completed (if email_task_completed = true)</TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">Tasks Report</TableCell>
<TableCell>Casera - Tasks Report for [Residence]</TableCell>
<TableCell>User requests PDF export</TableCell>
<TableCell>-</TableCell>
</TableRow>
</TableBody>
</Table>
</TabsContent>
<TabsContent value="onboarding" className="mt-4">
<p className="text-sm text-muted-foreground mb-4">
Cron-triggered daily at 10:00 AM UTC. Each email type sent only once per user.
</p>
<Table>
<TableHeader>
<TableRow>
<TableHead>Email Type</TableHead>
<TableHead>Subject</TableHead>
<TableHead>Criteria</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">
<Badge variant="outline">no_residence</Badge>
</TableCell>
<TableCell>Get started with Casera - Add your first property</TableCell>
<TableCell>2+ days since registration, no residence created</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">
<Badge variant="outline">no_tasks</Badge>
</TableCell>
<TableCell>Stay on top of home maintenance with Casera</TableCell>
<TableCell>5+ days since first residence, no tasks created</TableCell>
</TableRow>
</TableBody>
</Table>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* Default Configuration */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Calendar className="h-5 w-5" />
Scheduled Job Times
</CardTitle>
<CardDescription>
Hardcoded cron schedules in internal/worker/scheduler.go (all times UTC)
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Cron Expression</TableHead>
<TableHead>Hour (UTC)</TableHead>
<TableHead>Description</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-mono">Cron: 0 20 * * *</TableCell>
<TableCell>
<Badge variant="secondary">20</Badge>
<span className="text-xs text-muted-foreground ml-2">(8:00 PM UTC)</span>
</TableCell>
<TableCell>Task reminders (due soon) - hardcoded in scheduler.go</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-mono">Cron: 0 9 * * *</TableCell>
<TableCell>
<Badge variant="secondary">9</Badge>
<span className="text-xs text-muted-foreground ml-2">(9:00 AM UTC)</span>
</TableCell>
<TableCell>Overdue reminders - hardcoded in scheduler.go</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-mono">Cron: 0 11 * * *</TableCell>
<TableCell>
<Badge variant="secondary">11</Badge>
<span className="text-xs text-muted-foreground ml-2">(11:00 AM UTC)</span>
</TableCell>
<TableCell>Daily digest - hardcoded in scheduler.go</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
{/* Smart Reminder Logic */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Info className="h-5 w-5" />
Smart Reminder Logic
</CardTitle>
<CardDescription>
How the frequency-aware reminder system works
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="rounded-lg border p-4 space-y-3">
<h4 className="font-medium">Reminder Stages by Frequency</h4>
<p className="text-xs text-muted-foreground mb-2">
Source: internal/notifications/reminder_config.go
</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 text-sm">
<div>
<p className="font-medium text-muted-foreground">Once / Daily / Weekly:</p>
<ul className="list-disc list-inside mt-1 space-y-1">
<li>Day-of only</li>
</ul>
</div>
<div>
<p className="font-medium text-muted-foreground">Bi-Weekly (14d):</p>
<ul className="list-disc list-inside mt-1 space-y-1">
<li>1 day before</li>
<li>Day-of</li>
</ul>
</div>
<div>
<p className="font-medium text-muted-foreground">Monthly (30d):</p>
<ul className="list-disc list-inside mt-1 space-y-1">
<li>3 days before</li>
<li>1 day before</li>
<li>Day-of</li>
</ul>
</div>
<div>
<p className="font-medium text-muted-foreground">Quarterly (90d):</p>
<ul className="list-disc list-inside mt-1 space-y-1">
<li>7 days before</li>
<li>3 days before</li>
<li>1 day before</li>
<li>Day-of</li>
</ul>
</div>
<div>
<p className="font-medium text-muted-foreground">Semi-Annually (180d):</p>
<ul className="list-disc list-inside mt-1 space-y-1">
<li>14 days before</li>
<li>7 days before</li>
<li>3 days before</li>
<li>1 day before</li>
<li>Day-of</li>
</ul>
</div>
<div>
<p className="font-medium text-muted-foreground">Annually (365d):</p>
<ul className="list-disc list-inside mt-1 space-y-1">
<li>30 days before</li>
<li>14 days before</li>
<li>7 days before</li>
<li>3 days before</li>
<li>1 day before</li>
<li>Day-of</li>
</ul>
</div>
</div>
</div>
<div className="rounded-lg border p-4 space-y-3">
<h4 className="font-medium">Overdue Reminder Tapering</h4>
<p className="text-sm text-muted-foreground">
Overdue reminders taper off to avoid notification fatigue (configured in reminder_config.go):
</p>
<ul className="list-disc list-inside text-sm space-y-1">
<li><strong>Days 1-3:</strong> Daily reminders (DailyReminderDays: 3)</li>
<li><strong>Days 4, 7, 10, 13:</strong> Every 3 days (TaperIntervalDays: 3)</li>
<li><strong>After 14 days:</strong> No more reminders (MaxOverdueDays: 14)</li>
</ul>
</div>
<div className="rounded-lg border p-4 space-y-3">
<h4 className="font-medium">Duplicate Prevention</h4>
<p className="text-sm text-muted-foreground">
Each reminder is tracked in the <code className="bg-muted px-1 rounded">task_reminder_logs</code> table
with task ID, user ID, due date, and stage. This prevents sending the same reminder twice.
Logs older than 90 days are automatically cleaned up.
</p>
</div>
</CardContent>
</Card>
{/* Timezone Handling */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Clock className="h-5 w-5" />
Timezone Handling
</CardTitle>
<CardDescription>
How user timezones are captured and used
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="rounded-lg border p-4 space-y-3">
<h4 className="font-medium">Auto-Capture</h4>
<p className="text-sm text-muted-foreground">
The mobile app sends an <code className="bg-muted px-1 rounded">X-Timezone</code> header
on every API request (IANA format, e.g., &quot;America/Los_Angeles&quot;).
The server captures this when the user fetches their tasks (happens on every app launch)
and stores it in the <code className="bg-muted px-1 rounded">timezone</code> column of
<code className="bg-muted px-1 rounded">notifications_notificationpreference</code>.
</p>
</div>
<div className="rounded-lg border p-4 space-y-3">
<h4 className="font-medium">Usage in Daily Digest</h4>
<p className="text-sm text-muted-foreground">
The Daily Digest job uses the stored timezone to calculate &quot;start of day&quot; in the user&apos;s
local time. This ensures the overdue task count matches what the user sees in the app&apos;s Kanban view.
If no timezone is stored, UTC is used as fallback.
</p>
</div>
</CardContent>
</Card>
</div>
);
}
+2
View File
@@ -30,6 +30,7 @@ import {
Apple,
Image,
ImagePlus,
Cog,
} from 'lucide-react';
import { useRouter, usePathname } from 'next/navigation';
import { useAuthStore } from '@/store/auth';
@@ -77,6 +78,7 @@ const limitationsItems = [
const settingsItems = [
{ title: 'Monitoring', url: '/admin/monitoring', icon: Activity },
{ title: 'Automation Reference', url: '/admin/automation-reference', icon: Cog },
{ title: 'Lookup Tables', url: '/admin/lookups', icon: BookOpen },
{ title: 'Task Templates', url: '/admin/task-templates', icon: LayoutTemplate },
{ title: 'Admin Users', url: '/admin/admin-users', icon: UserCog },