feat(notificaciones): one send log across servicios and pólizas
Renewal avisos left behind only a `RenewalNotice` row, whose sole job is gating: a row with `sentAt` drops the policy off the pending list. It cannot represent a failed send or a customer with no address, so the Pólizas tab had no "Registro de envíos" to show and a sent notice simply vanished from the list. Renewals now write `email_notification_log` — the same table the four bulk jobs write — as `RENEWAL_NOTICE` / `POLICIES`, with rows for failures and no-email skips too. `RenewalNotice` keeps its gating role unchanged; the two are complementary, not redundant. - extend `EmailNotificationType` (+RENEWAL_NOTICE) and `EmailNotificationServicio` (+POLICIES); `level` now carries the aviso generation on renewal rows, so every reader must branch on the type first (`notificationLevelLabel()` is the one place that lives) - backfill emailed notices (`channel = 'EMAIL'`) into the log; MAIL-channel rows are legacy printed letters and are deliberately left out - extract `NotificationLogService`/`NotificationLogModule` as the single writer, so a feature that sends mail records it without pulling the bulk-job pipelines into its module - `GET /notifications/log` and `/stats` take a comma-separated `servicio` list; each tab reads its own slice. This also fixes the "Omitidos" view, which mapped to no filter at all and showed every row - share one `NotificationLogPanel` between both tabs - pass SES_* / NOTIFICATION_ADMIN_EMAILS through the galactus compose, which was missing them entirely — mail is runtime config, not a CI secret, and the prod image sets NODE_ENV=production so a blank config fails loudly instead of falling back to stdout Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+17
-6
@@ -979,9 +979,14 @@ export type NotificationType =
|
||||
| "OUTSTANDING_PAYMENT"
|
||||
| "PAYMENT_CONFIRMATION"
|
||||
| "ACCOUNT_STATUS"
|
||||
| "TRUST_PAYMENT_CONFIRMATION";
|
||||
| "TRUST_PAYMENT_CONFIRMATION"
|
||||
| "RENEWAL_NOTICE";
|
||||
|
||||
export type NotificationServicio = "CUSTOMERS" | "TRUST";
|
||||
export type NotificationServicio = "CUSTOMERS" | "TRUST" | "POLICIES";
|
||||
|
||||
/** Which servicios each /notificaciones tab reads out of the shared log. */
|
||||
export const SERVICIOS_LOG_SCOPE: NotificationServicio[] = ["CUSTOMERS", "TRUST"];
|
||||
export const POLIZAS_LOG_SCOPE: NotificationServicio[] = ["POLICIES"];
|
||||
|
||||
export type NotificationStatus =
|
||||
| "SENT"
|
||||
@@ -1161,7 +1166,8 @@ export interface NotificationLogQuery {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
type?: NotificationType;
|
||||
servicio?: NotificationServicio;
|
||||
/** One or more servicios; omitted = the whole log. */
|
||||
servicio?: NotificationServicio[];
|
||||
status?: NotificationStatus;
|
||||
view?: "sent" | "failed" | "skipped" | "all";
|
||||
}
|
||||
@@ -1173,15 +1179,20 @@ export function listNotificationLog(
|
||||
if (q.page) qs.set("page", String(q.page));
|
||||
if (q.pageSize) qs.set("pageSize", String(q.pageSize));
|
||||
if (q.type) qs.set("type", q.type);
|
||||
if (q.servicio) qs.set("servicio", q.servicio);
|
||||
if (q.servicio?.length) qs.set("servicio", q.servicio.join(","));
|
||||
if (q.status) qs.set("status", q.status);
|
||||
if (q.view) qs.set("view", q.view);
|
||||
const tail = qs.toString();
|
||||
return apiFetch<NotificationLogPage>(`/notifications/log${tail ? `?${tail}` : ""}`);
|
||||
}
|
||||
|
||||
export function getNotificationStats(): Promise<NotificationStats> {
|
||||
return apiFetch<NotificationStats>("/notifications/stats");
|
||||
export function getNotificationStats(
|
||||
servicio?: NotificationServicio[],
|
||||
): Promise<NotificationStats> {
|
||||
const tail = servicio?.length
|
||||
? `?servicio=${encodeURIComponent(servicio.join(","))}`
|
||||
: "";
|
||||
return apiFetch<NotificationStats>(`/notifications/stats${tail}`);
|
||||
}
|
||||
|
||||
/** Build a download URL for a report's file output. The session cookie
|
||||
|
||||
Reference in New Issue
Block a user