import 'server-only' import { Resend } from 'resend' import { asAdmin } from '@/lib/db/with-user' import { capaActions, incidents, users } from '@/lib/db/schema' import { and, eq, inArray, isNotNull, lt } from 'drizzle-orm' import { sendWhatsAppMessage } from '@/lib/notifications/whatsapp' import { getApiKey } from '@/lib/settings' const ROUND_LABELS = ['30-day', '60-day', '90-day'] as const const NEXT_INTERVAL_DAYS: (number | null)[] = [60, 90, null] export function getRoundLabel(round: number): string { return ROUND_LABELS[round] ?? '90-day' } export function getNextRecheckDate(verifiedAt: string, currentRound: number): string | null { const days = NEXT_INTERVAL_DAYS[currentRound] if (days === null || days === undefined) return null const base = new Date(verifiedAt) base.setUTCDate(base.getUTCDate() + days) return base.toISOString().split('T')[0] } export function shouldSendRecheck( recheckDate: string | null, round: number, today: string = new Date().toISOString().split('T')[0], ): boolean { if (!recheckDate || round >= 3) return false return recheckDate <= today } export async function sendEffectivenessRecheckNotifications(): Promise<{ notified: number }> { const today = new Date().toISOString().split('T')[0] const capas = await asAdmin(db => db.select({ id: capaActions.id, description: capaActions.description, effectivenessRecheckDate: capaActions.effectivenessRecheckDate, effectivenessRecheckRound: capaActions.effectivenessRecheckRound, verifiedAt: capaActions.verifiedAt, incidentId: capaActions.incidentId, incidentRef: incidents.referenceNo, verifierEmail: users.email, verifierName: users.name, verifierPhone: users.phone, }) .from(capaActions) .leftJoin(incidents, eq(capaActions.incidentId, incidents.id)) .leftJoin(users, eq(capaActions.verifiedBy, users.id)) .where(and( inArray(capaActions.status, ['verified', 'closed']), isNotNull(capaActions.effectivenessRecheckDate), lt(capaActions.effectivenessRecheckRound, 3), )) ) if (!capas || capas.length === 0) return { notified: 0 } const resend = new Resend(process.env.RESEND_API_KEY) const from = process.env.RESEND_FROM_EMAIL ?? 'onboarding@resend.dev' const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000' let whatsappPhoneId: string | null = null let whatsappToken: string | null = null try { whatsappPhoneId = await getApiKey('META_WHATSAPP_PHONE_NUMBER_ID') whatsappToken = await getApiKey('META_WHATSAPP_ACCESS_TOKEN') } catch { // WhatsApp not configured — email only } let notified = 0 for (const capa of capas) { const recheckDate = capa.effectivenessRecheckDate const round = capa.effectivenessRecheckRound if (!shouldSendRecheck(recheckDate, round, today)) continue const verifierEmail = capa.verifierEmail const verifierName = capa.verifierName ?? 'HSE Officer' const verifierPhone = capa.verifierPhone ?? '' if (!verifierEmail) continue const incidentRef = capa.incidentRef ?? capa.incidentId const roundLabel = getRoundLabel(round) const capaDesc = capa.description const capaUrl = `${siteUrl}/ims/hse/capa/${capa.id}` const subject = `[IMS] ${roundLabel} effectiveness check — ${incidentRef}` const html = `
Hi ${verifierName},
This is the ${roundLabel} effectiveness check for a corrective action you verified on incident ${incidentRef}.
Action: ${capaDesc}
Please confirm the corrective action is still in place and effective.
` const text = `${roundLabel} effectiveness check for ${incidentRef}\nAction: ${capaDesc}\n${capaUrl}` const { error } = await resend.emails.send({ from, to: [verifierEmail], subject, html, text }) if (error) { console.error('Effectiveness recheck email error:', error) continue } // WhatsApp (non-blocking, best-effort) if (whatsappPhoneId && whatsappToken && verifierPhone) { sendWhatsAppMessage( verifierPhone, 'ims_effectiveness_recheck', [roundLabel, incidentRef ?? '', capaDesc], whatsappPhoneId, whatsappToken, ).catch(err => console.error('WhatsApp recheck error:', err)) } // Advance round const nextDate = getNextRecheckDate(capa.verifiedAt!.toISOString(), round) await asAdmin(db => db.update(capaActions) .set({ effectivenessRecheckRound: round + 1, effectivenessRecheckDate: nextDate ?? null, }) .where(eq(capaActions.id, capa.id)) ) notified++ } return { notified } }