fix(notificaciones): merge renewals into one screen, fix MailModule DI
MailModule's provider used a `useFactory` with no `inject`, so the factory received `undefined` and `new MailService(config)` threw on `config.get`, taking the whole API down at boot. The module also wasn't actually `@Global()` even though both NotificationsModule and RenewalsModule inject MailService without importing it — that would have failed next. Replaced the factory with a plain provider (ConfigModule is already `isGlobal`) and marked the module global. On the web side, mass email and renewal notices were two menu entries doing the same job — telling a customer something by email. They are now two tabs of `/notificaciones` (Servicios and Pólizas), following the Captura pattern: `/renovaciones` still resolves, opening the same screen on its Pólizas tab so existing bookmarks keep working. The notifications page was also the last screen written in raw inline styles, with blue buttons and filter pills that appear nowhere else in the app. It now uses the shared design system: btn-primary/btn-outline, the seg segmented control, card, tx-table, pager, and the servicios/fideicomiso badges. Two supporting fixes found on the way: NOTIFICATION_STATUS_COLORS hardcoded hex instead of the theme's positive/negative/muted vars, and `.small` was referenced in 19 places across the app but never defined in globals.css. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,9 @@ type NavLink = {
|
||||
href: string;
|
||||
label: string;
|
||||
ability?: Ability;
|
||||
/** Shown when the user holds *any* of these — for a screen that merges two
|
||||
* separately-gated jobs (Notificaciones: servicios + pólizas). */
|
||||
anyAbility?: Ability[];
|
||||
exact?: boolean;
|
||||
/** Extra path prefixes that belong to this entry (e.g. a second route into
|
||||
* the same screen), so they highlight it instead of nothing. */
|
||||
@@ -78,13 +81,15 @@ const NAV: NavEntry[] = [
|
||||
label: "Cuentas de chequera",
|
||||
ability: "bank:manage-accounts",
|
||||
},
|
||||
// Mass email (servicios) and renewal notices (pólizas) are two tabs of
|
||||
// one screen; `/renovaciones` opens the same page on its pólizas tab.
|
||||
{
|
||||
href: "/notificaciones",
|
||||
label: "Notificaciones masivas",
|
||||
ability: "notification:send",
|
||||
label: "Notificaciones",
|
||||
anyAbility: ["notification:send", "renewal:send"],
|
||||
aliases: ["/renovaciones"],
|
||||
},
|
||||
{ href: "/usuarios", label: "Usuarios", ability: "user:manage" },
|
||||
{ href: "/renovaciones", label: "Renovaciones", ability: "renewal:send" },
|
||||
{ href: "/operaciones", label: "Operaciones", ability: "db:manage" },
|
||||
],
|
||||
},
|
||||
@@ -97,7 +102,9 @@ const NAV_LINKS: NavLink[] = NAV.flatMap((entry) =>
|
||||
|
||||
/** The nav the given user may see, with empty groups dropped. */
|
||||
function visibleNav(user: AuthUser | null): NavEntry[] {
|
||||
const allowed = (item: NavLink) => !item.ability || can(user, item.ability);
|
||||
const allowed = (item: NavLink) =>
|
||||
(!item.ability || can(user, item.ability)) &&
|
||||
(!item.anyAbility || item.anyAbility.some((a) => can(user, a)));
|
||||
const out: NavEntry[] = [];
|
||||
for (const entry of NAV) {
|
||||
if (entry.kind === "link") {
|
||||
|
||||
Reference in New Issue
Block a user