import React from "react"; import { Body, Head, Html, Preview, Tailwind } from "@react-email/components"; import { themeColors } from "./lib/theme"; import { EmailContainer, EmailFooter, EmailGreeting, EmailHeading, EmailInfoSection, EmailLetterHead, EmailSection, EmailSignature, EmailText } from "./components/Email"; import ButtonLink from "./components/ButtonLink"; export type AlertEventType = | "site_online" | "site_offline" | "site_toggle" | "health_check_healthy" | "health_check_unhealthy" | "health_check_toggle" | "resource_healthy" | "resource_unhealthy" | "resource_degraded" | "resource_toggle"; export type AlertNotificationProps = { eventType: AlertEventType; orgId: string; data: Record; dashboardLink: string; }; function getEventMeta(eventType: AlertEventType): { heading: string; previewText: string; summary: string; statusLabel: string | null; statusColor: string | null; } { switch (eventType) { case "site_online": return { heading: "Site Back Online", previewText: "A site in your organization is back online.", summary: "Good news – a site in your organization has come back online and is now reachable.", statusLabel: "Online", statusColor: "#16a34a" }; case "site_offline": return { heading: "Site Offline", previewText: "A site in your organization has gone offline.", summary: "A site in your organization has gone offline and is no longer reachable.", statusLabel: "Offline", statusColor: "#dc2626" }; case "site_toggle": return { heading: "Site Status Changed", previewText: "A site in your organization has changed status.", summary: "A site in your organization has changed status.", statusLabel: null, statusColor: null }; case "health_check_healthy": return { heading: "Health Check Recovered", previewText: "A health check in your organization is now healthy.", summary: "A health check in your organization has recovered and is now reporting a healthy status.", statusLabel: "Healthy", statusColor: "#16a34a" }; case "health_check_unhealthy": return { heading: "Health Check Failing", previewText: "A health check in your organization is not healthy.", summary: "A health check in your organization is currently failing.", statusLabel: "Not Healthy", statusColor: "#dc2626" }; case "health_check_toggle": return { heading: "Health Check Status Changed", previewText: "A health check in your organization has changed status.", summary: "A health check in your organization has changed status.", statusLabel: null, statusColor: null }; case "resource_healthy": return { heading: "Resource Healthy", previewText: "A resource in your organization is now healthy.", summary: "A resource in your organization has recovered and is now reporting a healthy status.", statusLabel: "Healthy", statusColor: "#16a34a" }; case "resource_unhealthy": return { heading: "Resource Unhealthy", previewText: "A resource in your organization is not healthy.", summary: "A resource in your organization is currently unhealthy.", statusLabel: "Unhealthy", statusColor: "#dc2626" }; case "resource_degraded": return { heading: "Resource Unhealthy", previewText: "A resource in your organization is not healthy.", summary: "A resource in your organization is currently unhealthy.", statusLabel: "Unhealthy", statusColor: "#dc2626" }; case "resource_toggle": return { heading: "Resource Status Changed", previewText: "A resource in your organization has changed status.", summary: "A resource in your organization has changed status.", statusLabel: null, statusColor: null }; default: return { heading: "Alert Notification", previewText: "An alert event has occurred in your organization.", summary: "An alert event has occurred in your organization.", statusLabel: "Alert", statusColor: "#f59e0b" }; } } function resolveToggleStatus(status: unknown): { label: string; color: string; } { switch (String(status).toLowerCase()) { case "online": return { label: "Online", color: "#16a34a" }; case "offline": return { label: "Offline", color: "#dc2626" }; case "healthy": return { label: "Healthy", color: "#16a34a" }; case "unhealthy": return { label: "Unhealthy", color: "#dc2626" }; default: return { label: String(status ?? "Unknown"), color: "#f59e0b" }; } } function formatDataItems( data: Record ): { label: string; value: React.ReactNode }[] { return Object.entries(data) .filter(([key]) => key !== "orgId" && key !== "status") .map(([key, value]) => ({ label: key .replace(/([A-Z])/g, " $1") .replace(/^./, (s) => s.toUpperCase()) .trim(), value: String(value ?? "-") })); } export const AlertNotification = (props: AlertNotificationProps) => { const { eventType, orgId, data, dashboardLink } = props; const meta = getEventMeta(eventType); const dataItems = formatDataItems(data); const isToggle = eventType === "site_toggle" || eventType === "health_check_toggle" || eventType === "resource_toggle"; const resolvedStatus = isToggle ? resolveToggleStatus(data.status) : meta.statusLabel != null ? { label: meta.statusLabel, color: meta.statusColor! } : null; const allItems: { label: string; value: React.ReactNode }[] = [ { label: "Organization", value: orgId }, ...(resolvedStatus != null ? [ { label: "Status", value: ( {resolvedStatus.label} ) } ] : []), { label: "Time", value: new Date().toUTCString() }, ...dataItems ]; return ( {meta.previewText} {meta.heading} Hi there, {meta.summary} Open your dashboard to view more details and manage your alert rules. Open Dashboard ); }; export default AlertNotification;