fix(notificaciones): quitar badge Próximamente de notificaciones ya existentes

- weekly_update y subscription_expiring están implementadas; se marcan como active.
- Se indica con badge 'A nivel despacho' cuando una notificación no se puede
  desactivar por contribuyente y se deshabilita el toggle.
This commit is contained in:
Horux Dev
2026-06-16 22:55:53 +00:00
parent 3f3253d41b
commit 8a1fbceb38
2 changed files with 20 additions and 10 deletions

View File

@@ -6,9 +6,9 @@ import type { Pool } from 'pg';
* (welcome, password-reset, payment-*) — esos siempre se envían. * (welcome, password-reset, payment-*) — esos siempre se envían.
* *
* Estado de implementación: * Estado de implementación:
* - documento_subido: ✅ implementado (notify-upload.service.ts) * - documento_subido: ✅ implementado y configurable por contribuyente
* - weekly_update: ⏳ pendiente (job es tenant-wide hoy) * - weekly_update: ✅ implementado (job tenant-wide, no configurable)
* - subscription_expiring: ⏳ pendiente (no es per-contribuyente hoy) * - subscription_expiring: ✅ implementado (aviso a owner, no configurable)
* - recordatorio_fiscal: ⏳ placeholder para futuras alertas * - recordatorio_fiscal: ⏳ placeholder para futuras alertas
*/ */
export const EMAIL_TYPES = [ export const EMAIL_TYPES = [

View File

@@ -8,26 +8,30 @@ import { apiClient } from '@/lib/api/client';
import { useContribuyenteStore } from '@/stores/contribuyente-store'; import { useContribuyenteStore } from '@/stores/contribuyente-store';
import { Bell, Loader2 } from 'lucide-react'; import { Bell, Loader2 } from 'lucide-react';
const EMAIL_LABELS: Record<string, { label: string; description: string; status: 'active' | 'pending' }> = { const EMAIL_LABELS: Record<string, { label: string; description: string; status: 'active' | 'pending'; configurable: boolean }> = {
documento_subido: { documento_subido: {
label: 'Documento subido', label: 'Documento subido',
description: 'Notificación cuando se sube una declaración o documento extra del contribuyente.', description: 'Notificación cuando se sube una declaración o documento extra del contribuyente.',
status: 'active', status: 'active',
configurable: true,
}, },
weekly_update: { weekly_update: {
label: 'Reporte semanal', label: 'Reporte semanal',
description: 'Resumen de KPIs, alertas y discrepancias enviado los lunes 8:00 AM.', description: 'Resumen de KPIs, alertas y discrepancias enviado los lunes 8:00 AM.',
status: 'pending', status: 'active',
configurable: false,
}, },
subscription_expiring: { subscription_expiring: {
label: 'Vencimiento de suscripción', label: 'Vencimiento de suscripción',
description: 'Aviso cuando la suscripción del despacho está por vencer.', description: 'Aviso cuando la suscripción del despacho está por vencer.',
status: 'pending', status: 'active',
configurable: false,
}, },
recordatorio_fiscal: { recordatorio_fiscal: {
label: 'Recordatorios fiscales', label: 'Recordatorios fiscales',
description: 'Avisos de obligaciones próximas a vencer (declaraciones, pagos provisionales).', description: 'Avisos de obligaciones próximas a vencer (declaraciones, pagos provisionales).',
status: 'pending', status: 'pending',
configurable: false,
}, },
}; };
@@ -138,24 +142,30 @@ export default function NotificacionesPage() {
if (!meta) return null; if (!meta) return null;
const checked = contrib.preferences[type] !== false; const checked = contrib.preferences[type] !== false;
const isPending = meta.status === 'pending'; const isPending = meta.status === 'pending';
const isConfigurable = meta.configurable;
return ( return (
<div key={type} className="flex items-start justify-between gap-4 py-2 border-b last:border-0"> <div key={type} className="flex items-start justify-between gap-4 py-2 border-b last:border-0">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
<span className="text-sm font-medium">{meta.label}</span> <span className="text-sm font-medium">{meta.label}</span>
{isPending && ( {isPending ? (
<span className="text-[10px] uppercase tracking-wide bg-muted text-muted-foreground rounded px-1.5 py-0.5"> <span className="text-[10px] uppercase tracking-wide bg-muted text-muted-foreground rounded px-1.5 py-0.5">
Próximamente Próximamente
</span> </span>
)} ) : !isConfigurable ? (
<span className="text-[10px] uppercase tracking-wide bg-blue-50 text-blue-700 dark:bg-blue-950 dark:text-blue-300 rounded px-1.5 py-0.5">
A nivel despacho
</span>
) : null}
</div> </div>
<p className="text-xs text-muted-foreground mt-0.5">{meta.description}</p> <p className="text-xs text-muted-foreground mt-0.5">{meta.description}</p>
</div> </div>
<label className="inline-flex items-center cursor-pointer flex-shrink-0"> <label className={`inline-flex items-center flex-shrink-0 ${isConfigurable && !isPending ? 'cursor-pointer' : 'cursor-not-allowed opacity-60'}`}>
<input <input
type="checkbox" type="checkbox"
className="sr-only peer" className="sr-only peer"
checked={checked} checked={checked}
disabled={!isConfigurable || isPending}
onChange={e => onChange={e =>
mutation.mutate({ mutation.mutate({
contribuyenteId: contrib.contribuyenteId, contribuyenteId: contrib.contribuyenteId,