big refactor

This commit is contained in:
2026-04-29 16:25:11 -05:00
parent 6c411ce2b6
commit 8265fb689a
156 changed files with 15845 additions and 50373 deletions

View File

@@ -1,13 +1,163 @@
<script setup lang="ts">
import type { AlertRecipient, CustomAlertRule } from '~/composables/useAlertConfig'
definePageMeta({ ssr: false })
usePageTitle('Alerts & Notifications · Settings')
const { config, addThreshold, removeThreshold, addPaymentTier, removePaymentTier, addCustomRule, updateCustomRule, removeCustomRule } = useAlertConfig()
const toast = useToast()
// ── Recipient helpers ──────────────────────────────────────────────────────
type AlertRecipient = 'handler' | 'manager' | 'customer' | 'custom'
interface Threshold {
id: string
daysBefore: number
enabled: boolean
}
interface PaymentTier {
id: string
daysOverdue: number
action: string
recipients: AlertRecipient[]
}
interface CustomAlertRule {
id: string
alertName: string
field: string
operator: string
value: number
recipients: AlertRecipient[]
enabled: boolean
}
interface AlertConfig {
emailSender: {
senderEmail: string
senderDisplayName: string
replyToEmail: string
}
renewals: {
enabled: boolean
thresholds: Threshold[]
}
cancellations: {
enabled: boolean
recipients: AlertRecipient[]
}
latePayments: {
enabled: boolean
tiers: PaymentTier[]
}
creditCardExpiry: {
enabled: boolean
autoDebitOnly: boolean
thresholds: Threshold[]
}
customRules: CustomAlertRule[]
}
const STORAGE_KEY = 'policy-ui.alerts'
function loadConfig(): AlertConfig {
if (import.meta.client) {
const stored = localStorage.getItem(STORAGE_KEY)
if (stored) {
try {
return JSON.parse(stored)
} catch {
return defaultConfig()
}
}
}
return defaultConfig()
}
function defaultConfig(): AlertConfig {
return {
emailSender: {
senderEmail: '',
senderDisplayName: '',
replyToEmail: ''
},
renewals: {
enabled: true,
thresholds: [
{ id: '1', daysBefore: 30, enabled: true },
{ id: '2', daysBefore: 7, enabled: true }
]
},
cancellations: {
enabled: true,
recipients: ['handler', 'manager']
},
latePayments: {
enabled: true,
tiers: [
{ id: '1', daysOverdue: 15, action: 'First reminder', recipients: ['handler'] },
{ id: '2', daysOverdue: 30, action: 'Second reminder', recipients: ['handler', 'manager'] },
{ id: '3', daysOverdue: 45, action: 'Escalate to collections', recipients: ['handler', 'manager', 'customer'] }
]
},
creditCardExpiry: {
enabled: true,
autoDebitOnly: true,
thresholds: [
{ id: '1', daysBefore: 30, enabled: true },
{ id: '2', daysBefore: 7, enabled: true }
]
},
customRules: []
}
}
const config = ref<AlertConfig>(loadConfig())
function saveConfig() {
if (import.meta.client) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(config.value))
}
}
function addThreshold(type: 'renewals' | 'creditCardExpiry', days: number) {
const id = String(Date.now())
config.value[type].thresholds.push({ id, daysBefore: days, enabled: true })
saveConfig()
}
function removeThreshold(type: 'renewals' | 'creditCardExpiry', id: string) {
config.value[type].thresholds = config.value[type].thresholds.filter(t => t.id !== id)
saveConfig()
}
function addPaymentTier(days: number, action: string, recipients: AlertRecipient[]) {
const id = String(Date.now())
config.value.latePayments.tiers.push({ id, daysOverdue: days, action, recipients: [...recipients] })
saveConfig()
}
function removePaymentTier(id: string) {
config.value.latePayments.tiers = config.value.latePayments.tiers.filter(t => t.id !== id)
saveConfig()
}
function addCustomRule(rule: Omit<CustomAlertRule, 'id'>) {
const id = String(Date.now())
config.value.customRules.push({ ...rule, id })
saveConfig()
}
function updateCustomRule(id: string, updates: Partial<CustomAlertRule>) {
const idx = config.value.customRules.findIndex(r => r.id === id)
if (idx !== -1) {
config.value.customRules[idx] = { ...config.value.customRules[idx], ...updates }
saveConfig()
}
}
function removeCustomRule(id: string) {
config.value.customRules = config.value.customRules.filter(r => r.id !== id)
saveConfig()
}
const RECIPIENT_OPTIONS: { id: AlertRecipient; label: string }[] = [
{ id: 'handler', label: 'Handler' },
{ id: 'manager', label: 'Manager' },
@@ -19,9 +169,9 @@ function toggleRecipient(list: AlertRecipient[], r: AlertRecipient) {
const idx = list.indexOf(r)
if (idx === -1) list.push(r)
else list.splice(idx, 1)
saveConfig()
}
// ── Threshold add ──────────────────────────────────────────────────────────
const newRenewalDays = ref(7)
const newCcDays = ref(7)
@@ -29,19 +179,18 @@ function addRenewalThreshold() {
addThreshold('renewals', newRenewalDays.value)
newRenewalDays.value = 7
}
function addCcThreshold() {
addThreshold('creditCardExpiry', newCcDays.value)
newCcDays.value = 7
}
// ── Late payment tier add ──────────────────────────────────────────────────
const newLpDays = ref(45)
function addNewPaymentTier() {
addPaymentTier(newLpDays.value, 'Notify assigned handler', ['handler'])
newLpDays.value = 45
}
// ── Custom rule add ────────────────────────────────────────────────────────
const FIELD_OPTIONS = [
{ value: 'premium', label: 'Annual premium' },
{ value: 'policy_count', label: 'Policy count' },
@@ -75,7 +224,6 @@ function submitNewRule() {
showNewRule.value = false
}
// ── Escalation dot color ───────────────────────────────────────────────────
function tierDotClass(idx: number, total: number) {
if (total <= 1) return 'al-dot-green'
if (idx === total - 1) return 'al-dot-red'
@@ -84,6 +232,7 @@ function tierDotClass(idx: number, total: number) {
}
function handleSave() {
saveConfig()
toast.add({ title: 'Alert configuration saved', color: 'green' })
}
</script>