fase 1 productizacion: capa de configuracion (glosario multi-giro, sucursales y categorias configurables, ajustes de clinica por instancia, colores de marca, horarios, URLs de pago dinamicas) + script de provisioning de instancia nueva con datos demo + frontend consumiendo la config

This commit is contained in:
2026-10-11 05:50:18 +00:00
parent 28118ce732
commit f92018f88e
50 changed files with 4314 additions and 210 deletions

View File

@@ -43,6 +43,7 @@ import GlobalSearch from './GlobalSearch';
import ThemePicker from './ThemePicker';
import NotificationBell from './NotificationBell';
import { useAuth } from '../lib/auth';
import { useGlosario, traducirMenu, traducirEtiqueta } from '../lib/glosario';
import { useChatUnread } from '../lib/chat';
import { useQuickLinks } from '../lib/quicklinks';
import { odooApi } from '../services/odoo';
@@ -53,14 +54,14 @@ interface LayoutProps {
subtitle?: string;
}
const topNavItems = [
const TOP_NAV_ITEMS = [
{ label: 'Dashboard', icon: LayoutDashboard, href: '/' },
{ label: 'Agenda', icon: Calendar, href: '/agenda' },
{ label: 'Pacientes', icon: Users, href: '/pacientes' },
{ label: 'Ventas', icon: ShoppingCart, href: '/ventas' },
];
const menuSections = [
const MENU_SECTIONS = [
{
label: 'Clínica',
items: [
@@ -113,6 +114,12 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
const location = useLocation();
const navigate = useNavigate();
const { user, canSee, hasRole, logout, brand } = useAuth();
const { term } = useGlosario();
const topNavItems = useMemo(
() => TOP_NAV_ITEMS.map((it) => ({ ...it, label: traducirEtiqueta(it.label, term) })),
[term]
);
const menuSections = useMemo(() => traducirMenu(MENU_SECTIONS, term), [term]);
const [menuOpen, setMenuOpen] = useState(false);
const [expanded, setExpanded] = useState<string[]>(['Clínica']);
const chatUnread = useChatUnread();
@@ -122,7 +129,7 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
menuSections
.map((s) => ({ ...s, items: s.items.filter((it) => canSee(it.href)) }))
.filter((s) => s.items.length > 0),
[canSee, user]
[canSee, user, menuSections]
);
// Accesos rápidos personalizables (localStorage por usuario)
@@ -135,7 +142,7 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
seen.add(it.href);
return true;
});
}, [canSee]);
}, [canSee, topNavItems, menuSections]);
const quickItems = useMemo(
() =>
quick.links
@@ -148,7 +155,7 @@ export const HomeNestLayout: FC<LayoutProps> = ({ children, title: _title, subti
return null;
})
.filter((it): it is (typeof menuSections)[number]['items'][number] => it !== null),
[quick.links, canSee]
[quick.links, canSee, menuSections]
);
const initials = (user?.name || 'US')