bloque 4: campanas por segmentos con medicion (modulo skeen_campanas: segmentos reutilizables con conteo verificado, campanas por WhatsApp y correo con programacion, prueba previa, limite por minuto, pausa por errores, consentimiento por canal y atribucion de citas) + encuesta al salir con NPS (campo propio, envio automatico y recordatorio, reporte con tendencia y desglose); apagado por defecto

This commit is contained in:
2026-10-12 02:34:34 +00:00
parent 705b5a2842
commit a6d6df4ebd
37 changed files with 4619 additions and 44 deletions

View File

@@ -38,6 +38,8 @@ import {
Download,
Repeat,
Truck,
Megaphone,
Smile,
} from 'lucide-react';
import { Button, Modal, Input } from './ui';
import QuickLinksDock, { QuickLinksModePicker } from './QuickLinksDock';
@@ -118,6 +120,8 @@ const MENU_SECTIONS = [
const HOME_MEMBRESIA_ITEM = { label: 'Membresías', icon: Repeat, href: '/membresias' };
const HOME_COMPRAS_ITEM = { label: 'Compras', icon: Truck, href: '/compras' };
const HOME_COMISIONES_ITEM = { label: 'Liquidaciones', icon: Percent, href: '/comisiones/liquidaciones' };
const HOME_CAMPANAS_ITEM = { label: 'Campañas', icon: Megaphone, href: '/campanas' };
const HOME_SATISFACCION_ITEM = { label: 'Satisfacción', icon: Smile, href: '/satisfaccion' };
export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subtitle: _subtitle }) => {
const location = useLocation();
@@ -126,7 +130,7 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
const { term } = useGlosario();
const { t } = useI18n();
const navLabel = useNavLabel();
const { membresias: membresiasCfg, compras: comprasCfg, comisiones: comisionesCfg, licencia: licenciaCfg } = useClinicSettings();
const { membresias: membresiasCfg, compras: comprasCfg, comisiones: comisionesCfg, licencia: licenciaCfg, campanas: campanasCfg, encuestas: encuestasCfg } = useClinicSettings();
const topNavItems = useMemo(
() => TOP_NAV_ITEMS.map((it) => ({ ...it, label: navLabel(traducirEtiqueta(it.label, term)) })),
[term, navLabel]
@@ -148,6 +152,16 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
? { ...s, items: [...s.items, HOME_COMISIONES_ITEM] }
: s));
}
if (campanasCfg.activo) {
base = base.map((s) => (s.label === 'Analítica y más'
? { ...s, items: [...s.items, HOME_CAMPANAS_ITEM] }
: s));
}
if (encuestasCfg.activo) {
base = base.map((s) => (s.label === 'Analítica y más'
? { ...s, items: [...s.items, HOME_SATISFACCION_ITEM] }
: s));
}
// Licencia en modo bloqueo sin WhatsApp: se ocultan el chat y la sección WACRM.
if (!licenciaModuloIncluido(licenciaCfg, 'whatsapp')) {
base = base
@@ -163,7 +177,7 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
label: navLabel(s.label),
items: s.items.map((it) => ({ ...it, label: navLabel(it.label) })),
}));
}, [term, navLabel, membresiasCfg.activo, comprasCfg.activo, comisionesCfg.activo, licenciaCfg]);
}, [term, navLabel, membresiasCfg.activo, comprasCfg.activo, comisionesCfg.activo, campanasCfg.activo, encuestasCfg.activo, licenciaCfg]);
const [menuOpen, setMenuOpen] = useState(false);
const [expanded, setExpanded] = useState<string[]>(['Clínica']);
const chatUnread = useChatUnread();