Update
This commit is contained in:
528
client/App.js
528
client/App.js
@@ -1,286 +1,300 @@
|
||||
// App.js
|
||||
import { $, watch, h, when } from "sigpro";
|
||||
import {
|
||||
Navbar,
|
||||
Drawer,
|
||||
DrawerToggle,
|
||||
DrawerContent,
|
||||
DrawerSide,
|
||||
DrawerOverlay,
|
||||
Menu,
|
||||
Tabs,
|
||||
Swap,
|
||||
SwapToggle,
|
||||
SwapOn,
|
||||
SwapOff,
|
||||
Icon,
|
||||
Avatar,
|
||||
Dropdown,
|
||||
DropdownButton,
|
||||
DropdownContent,
|
||||
Modal,
|
||||
Fieldset,
|
||||
Input,
|
||||
Button
|
||||
Navbar,
|
||||
Drawer,
|
||||
DrawerToggle,
|
||||
DrawerContent,
|
||||
DrawerSide,
|
||||
DrawerOverlay,
|
||||
Menu,
|
||||
MenuTitle,
|
||||
MenuItem,
|
||||
Tabs,
|
||||
Tab,
|
||||
Swap,
|
||||
SwapToggle,
|
||||
SwapOn,
|
||||
SwapOff,
|
||||
Icon,
|
||||
Avatar,
|
||||
Dropdown,
|
||||
DropdownButton,
|
||||
DropdownContent,
|
||||
Modal,
|
||||
ModalBox,
|
||||
ModalClose,
|
||||
ModalAction,
|
||||
ModalBackdrop,
|
||||
Fieldset,
|
||||
Input,
|
||||
LabelFloating,
|
||||
Button,
|
||||
Loading
|
||||
} from "sigpro-ui";
|
||||
import 'sigpro-ui/css';
|
||||
|
||||
import { Desktop } from "./tabs/Desktop.js";
|
||||
import { ModalSearch } from "./components/ModalSearch.js";
|
||||
|
||||
export const isDark = $(false, "theme-mode");
|
||||
|
||||
|
||||
export const App = () => {
|
||||
// Tema oscuro/claro
|
||||
|
||||
const showSearchModal = $(false);
|
||||
|
||||
// Tema oscuro/claro
|
||||
watch(isDark, (dark) => {
|
||||
document.documentElement.setAttribute("data-theme", dark ? "dark" : "light");
|
||||
});
|
||||
document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light");
|
||||
|
||||
watch(isDark, (dark) => {
|
||||
document.documentElement.setAttribute("data-theme", dark ? "dark" : "light");
|
||||
});
|
||||
|
||||
// Activar tema inicial
|
||||
document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light");
|
||||
|
||||
// Estado de login persistente
|
||||
const logged = $(false, "logged");
|
||||
|
||||
// Estado para buscador
|
||||
const searchQuery = $("");
|
||||
|
||||
// Estado para modal de login
|
||||
const showLoginModal = $(false);
|
||||
const loginForm = {
|
||||
username: $(""),
|
||||
password: $("")
|
||||
};
|
||||
|
||||
// Pestañas: la primera (Escritorio) no es cerrable
|
||||
const tabs = $([
|
||||
{
|
||||
label: "Escritorio",
|
||||
content: () => Desktop,
|
||||
closable: false
|
||||
}
|
||||
]);
|
||||
const activeTab = $(0);
|
||||
const openDrawer = $(false);
|
||||
|
||||
// Elementos del menú en el drawer
|
||||
const menuItems = [
|
||||
{
|
||||
label: "Clientes",
|
||||
children: [
|
||||
{ label: "Buscar Cliente", onclick: () => openTab("Clientes") },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Recibos",
|
||||
children: [
|
||||
{ label: "Buscar Recibo" },
|
||||
{ label: "Pendientes" },
|
||||
{ label: "Extornos" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Polizas",
|
||||
children: [
|
||||
{ label: "Buscar Póliza", onclick: () => openTab("Polizas") },
|
||||
{ label: "Nueva producción", onclick: () => openTab("Polizas") },
|
||||
{ label: "Anulaciones", onclick: () => openTab("Polizas") },
|
||||
{ label: "Renovación Cartera", onclick: () => openTab("Polizas") }
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
label: "Comercial",
|
||||
children: [
|
||||
{ label: "Oportunidades" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Siniestros",
|
||||
children: [
|
||||
{ label: "Nuevo Siniestro" },
|
||||
{ label: "Buscar Siniestro" },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Soporte",
|
||||
children: [
|
||||
{ label: "Tickets" },
|
||||
{ label: "Reportes" },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Referencia al contenedor de pestañas para manejar el foco
|
||||
let tabsContainerRef = null;
|
||||
let drawerToggleRef = null;
|
||||
|
||||
// Abre o crea una pestaña, cierra el drawer
|
||||
const openTab = (label) => {
|
||||
const currentTabs = tabs();
|
||||
|
||||
if (currentTabs.length >= 15) return;
|
||||
|
||||
const newTab = {
|
||||
label,
|
||||
content: () => `¡Bienvenido al escritorio!`,
|
||||
closable: true
|
||||
// Estado de login persistente
|
||||
const logged = $(false, "logged");
|
||||
const showLoginModal = $(false);
|
||||
const loginForm = {
|
||||
username: $(""),
|
||||
password: $("")
|
||||
};
|
||||
tabs([...currentTabs, newTab]);
|
||||
activeTab(tabs().length - 1);
|
||||
|
||||
closeDrawer();
|
||||
};
|
||||
// Pestañas: la primera (Escritorio) no es cerrable
|
||||
const tabs = $([
|
||||
{
|
||||
label: "Escritorio",
|
||||
content: () => Desktop,
|
||||
closable: false
|
||||
}
|
||||
]);
|
||||
const activeTab = $(0);
|
||||
const openDrawer = $(false);
|
||||
|
||||
const closeDrawer = () => {
|
||||
openDrawer(false);
|
||||
if (drawerToggleRef) drawerToggleRef.checked = false;
|
||||
};
|
||||
// Elementos del menú en el drawer
|
||||
const menuItems = [
|
||||
{
|
||||
label: "Clientes",
|
||||
children: [
|
||||
{ label: "Buscar Cliente", onclick: () => openTab("Clientes") },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Recibos",
|
||||
children: [
|
||||
{ label: "Buscar Recibo" },
|
||||
{ label: "Pendientes" },
|
||||
{ label: "Extornos" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Polizas",
|
||||
children: [
|
||||
{ label: "Buscar Póliza", onclick: () => openTab("Polizas") },
|
||||
{ label: "Nueva producción", onclick: () => openTab("Polizas") },
|
||||
{ label: "Anulaciones", onclick: () => openTab("Polizas") },
|
||||
{ label: "Renovación Cartera", onclick: () => openTab("Polizas") }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Comercial",
|
||||
children: [
|
||||
{ label: "Oportunidades" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Siniestros",
|
||||
children: [
|
||||
{ label: "Nuevo Siniestro" },
|
||||
{ label: "Buscar Siniestro" },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Soporte",
|
||||
children: [
|
||||
{ label: "Tickets" },
|
||||
{ label: "Reportes" },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Manejo del login
|
||||
const handleLogin = () => {
|
||||
logged(true);
|
||||
showLoginModal(false);
|
||||
loginForm.username("");
|
||||
loginForm.password("");
|
||||
};
|
||||
let tabsContainerRef = null;
|
||||
let drawerToggleRef = null;
|
||||
|
||||
const handleLogout = () => {
|
||||
logged(false);
|
||||
};
|
||||
const openTab = (label) => {
|
||||
const currentTabs = tabs();
|
||||
if (currentTabs.length >= 15) return;
|
||||
const newTab = {
|
||||
label,
|
||||
content: () => `¡Bienvenido a ${label}!`,
|
||||
closable: true
|
||||
};
|
||||
tabs([...currentTabs, newTab]);
|
||||
activeTab(tabs().length - 1);
|
||||
closeDrawer();
|
||||
};
|
||||
|
||||
return [
|
||||
Drawer({}, [
|
||||
// Control oculto del drawer
|
||||
DrawerToggle({
|
||||
id: "app-drawer",
|
||||
ref: (el) => drawerToggleRef = el,
|
||||
checked: openDrawer,
|
||||
onchange: (e) => openDrawer(e.target.checked)
|
||||
}),
|
||||
const closeDrawer = () => {
|
||||
openDrawer(false);
|
||||
if (drawerToggleRef) drawerToggleRef.checked = false;
|
||||
};
|
||||
|
||||
// Contenido principal
|
||||
DrawerContent({}, [
|
||||
Navbar({ class: "bg-base-100 shadow-lg align-center" }, [
|
||||
// Botón hamburguesa
|
||||
div({ class: "flex-none" }, [
|
||||
label({ for: "app-drawer", class: "btn btn-ghost btn-square" }, [
|
||||
Icon({}, "icon-[lucide--menu]")
|
||||
])
|
||||
]),
|
||||
const handleLogin = () => {
|
||||
if (!loginForm.username() || !loginForm.password()) {
|
||||
alert('Por favor, complete todos los campos');
|
||||
return;
|
||||
}
|
||||
logged(true);
|
||||
showLoginModal(false);
|
||||
loginForm.username("");
|
||||
loginForm.password("");
|
||||
};
|
||||
|
||||
// Buscador
|
||||
div({ class: "flex-1 max-w-md mx-4" }, [
|
||||
Input({
|
||||
type: "search",
|
||||
placeholder: "Buscar...",
|
||||
value: searchQuery,
|
||||
left: span({ class: "icon-[lucide--search]" }),
|
||||
oninput: (e) => console.log(e.target.value)
|
||||
})
|
||||
]),
|
||||
const handleLogout = () => {
|
||||
logged(false);
|
||||
};
|
||||
|
||||
// Espaciador central
|
||||
div({ class: "flex-1" }, []),
|
||||
return [
|
||||
Drawer({}, [
|
||||
DrawerToggle({
|
||||
id: "app-drawer",
|
||||
ref: (el) => drawerToggleRef = el,
|
||||
checked: openDrawer,
|
||||
onchange: (e) => openDrawer(e.target.checked)
|
||||
}),
|
||||
|
||||
// Swap para tema claro/oscuro
|
||||
Swap({ class: "text-xl" }, [
|
||||
SwapToggle({ value: isDark, class: "swap-rotate" }),
|
||||
SwapOn({}, span({ class: "icon-[lucide--moon]" })),
|
||||
SwapOff({}, span({ class: "icon-[lucide--sun]" })),
|
||||
]),
|
||||
DrawerContent({}, [
|
||||
Navbar({ class: "bg-base-100 shadow-lg align-center" }, [
|
||||
div({ class: "flex-none" }, [
|
||||
label({ for: "app-drawer", class: "btn btn-ghost btn-square" }, [
|
||||
Icon({}, "icon-[lucide--menu]")
|
||||
])
|
||||
]),
|
||||
Button({ class: "icon-[lucide--search] btn-ghost", onclick: () => showSearchModal(true) }),
|
||||
div({ class: "flex-1" }),
|
||||
Swap({ class: "text-xl" }, [
|
||||
SwapToggle({ value: isDark, class: "swap-rotate" }),
|
||||
SwapOn({}, h('span', { class: "icon-[lucide--moon]" })),
|
||||
SwapOff({}, h('span', { class: "icon-[lucide--sun]" })),
|
||||
]),
|
||||
when(logged,
|
||||
() => Dropdown({ class: "flex-none ml-2 dropdown-bottom dropdown-end" }, [
|
||||
DropdownButton({ class: "btn-circle btn btn-ghost", tabindex: "0", role: "button" }, [
|
||||
Avatar({ class: "placeholder" }, [
|
||||
div({ class: "bg-neutral text-neutral-content w-10 rounded-full" }, [
|
||||
h('span', { class: "text-xl" }, "U")
|
||||
])
|
||||
])
|
||||
]),
|
||||
DropdownContent(
|
||||
{ class: "menu bg-base-100 rounded-box w-52 p-2 shadow" },
|
||||
[
|
||||
Menu({ class: "bg-base-100 max-w-xs w-full" },
|
||||
[
|
||||
MenuItem({ label: "Mi Perfil", onclick: () => openTab("Mi Perfil") }),
|
||||
MenuItem({ label: "Configuración", onclick: () => openTab("Configuración") }),
|
||||
MenuItem({ label: "Cerrar Sesión", onclick: handleLogout }),
|
||||
]
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
() => Button({
|
||||
class: "flex-none ml-2 btn btn-ghost btn-circle relative",
|
||||
onclick: () => showLoginModal(true)
|
||||
}, [
|
||||
Icon({}, "icon-[lucide--user] text-xl"),
|
||||
])
|
||||
)
|
||||
]),
|
||||
|
||||
// Avatar con dropdown o botón de login
|
||||
when(logged,
|
||||
() => Dropdown({ class: "flex-none ml-2 dropdown-bottom dropdown-end" }, [
|
||||
DropdownButton({ class: "btn-circle btn btn-ghost", tabindex: "0", role: "button" }, [
|
||||
div({ class: "w-10 rounded-full flex items-center justify-center" }, [
|
||||
Icon({}, "icon-[lucide--user] text-xl")
|
||||
// Área principal con pestañas
|
||||
div({
|
||||
class: "p-4",
|
||||
ref: (el) => tabsContainerRef = el
|
||||
}, [
|
||||
h('div', { class: 'tabs tabs-box' },
|
||||
() => tabs().map((item, idx) =>
|
||||
Tab({
|
||||
name: "app-tabs",
|
||||
"aria-label": item.label,
|
||||
checked: activeTab() === idx,
|
||||
onchange: () => activeTab(idx),
|
||||
content: item.content,
|
||||
closable: item.closable,
|
||||
tabs,
|
||||
index: idx
|
||||
})
|
||||
)
|
||||
)
|
||||
])
|
||||
]),
|
||||
DropdownContent(
|
||||
{ class: "menu bg-base-100 rounded-box w-52 p-2 shadow" },
|
||||
[
|
||||
Menu({
|
||||
class: "bg-base-100 max-w-xs w-full",
|
||||
items: [
|
||||
{ label: "Mis mensajes", onclick: () => hide() },
|
||||
{ label: "Delete", onclick: () => hide() },
|
||||
{ label: "Cerrar Sesión", onclick: handleLogout },
|
||||
],
|
||||
}),
|
||||
],
|
||||
),
|
||||
]),
|
||||
() => Button({
|
||||
class: "flex-none ml-2 btn btn-ghost btn-circle relative",
|
||||
onclick: () => showLoginModal(true)
|
||||
}, [
|
||||
Icon({}, "icon-[lucide--user] text-xl"),
|
||||
|
||||
DrawerSide({ class: "z-50" }, [
|
||||
DrawerOverlay({ for: "app-drawer" }),
|
||||
div({
|
||||
class: "menu bg-base-200 text-base-content min-h-full w-80 p-4"
|
||||
}, [
|
||||
h('h2', { class: "text-lg font-bold mb-4" }, "Menú"),
|
||||
Menu({ class: "bg-base-200 max-w-xs w-full" },
|
||||
menuItems.flatMap(item => [
|
||||
MenuTitle({}, item.label),
|
||||
...item.children.map(child =>
|
||||
MenuItem({ label: child.label, onclick: child.onclick })
|
||||
)
|
||||
])
|
||||
)
|
||||
])
|
||||
])
|
||||
)
|
||||
]),
|
||||
// Área principal con las pestañas
|
||||
div({
|
||||
class: "p-4",
|
||||
ref: (el) => tabsContainerRef = el
|
||||
}, [
|
||||
Tabs({
|
||||
class: 'tabs-box',
|
||||
items: tabs,
|
||||
activeIndex: activeTab
|
||||
})
|
||||
])
|
||||
]),
|
||||
|
||||
// Lateral del drawer
|
||||
DrawerSide({ class: "z-50" }, [
|
||||
DrawerOverlay({ for: "app-drawer" }),
|
||||
div({
|
||||
class: "menu bg-base-200 text-base-content min-h-full w-80 p-4"
|
||||
}, [
|
||||
h2({ class: "text-lg font-bold mb-4" }, ["Menú"]),
|
||||
Menu({ items: menuItems, class: "bg-base-200 max-w-xs w-full" })
|
||||
])
|
||||
])
|
||||
]),
|
||||
ModalSearch({
|
||||
open: showSearchModal,
|
||||
onSelect: (item) => {
|
||||
console.log("Item seleccionado:", item);
|
||||
showSearchModal(false);
|
||||
}
|
||||
}),
|
||||
|
||||
// Modal de login
|
||||
Modal({
|
||||
open: showLoginModal,
|
||||
actions: [
|
||||
Button({
|
||||
class: "btn btn-ghost",
|
||||
onclick: () => showLoginModal(false)
|
||||
}, "Cancelar"),
|
||||
Button({
|
||||
class: "btn btn-primary",
|
||||
onclick: handleLogin
|
||||
}, "Entrar")
|
||||
]
|
||||
}, [
|
||||
Fieldset({ label: "Iniciar sesión", class: "bg-base-200 border-base-300 rounded-box border gap-3 p-4", }, [
|
||||
Input({
|
||||
class: "w-full",
|
||||
type: "text",
|
||||
label: "Usuario",
|
||||
float: true,
|
||||
placeholder: "Nombre de usuario",
|
||||
value: loginForm.username
|
||||
})
|
||||
,
|
||||
|
||||
Input({
|
||||
class: "w-full",
|
||||
type: "password",
|
||||
label: "Contraseña",
|
||||
float: true,
|
||||
placeholder: "Contraseña",
|
||||
value: loginForm.password
|
||||
})
|
||||
])
|
||||
|
||||
])
|
||||
];
|
||||
// Modal de Login adaptado
|
||||
when(showLoginModal, () =>
|
||||
Modal({ open: true, class: '' }, [
|
||||
ModalBox({}, [
|
||||
ModalClose({ onclick: () => showLoginModal(false) }),
|
||||
h('h3', { class: "text-lg font-bold" }, "Iniciar Sesión"),
|
||||
Fieldset({
|
||||
class: "bg-base-200 border-base-300 rounded-box border gap-3 p-4"
|
||||
}, [
|
||||
LabelFloating({ class: "w-full" }, [
|
||||
Input({
|
||||
class: "w-full",
|
||||
type: "text",
|
||||
placeholder: "Nombre de usuario",
|
||||
value: loginForm.username,
|
||||
oninput: (e) => loginForm.username(e.target.value)
|
||||
}),
|
||||
h('span', {}, "Usuario")
|
||||
]),
|
||||
LabelFloating({ class: "w-full" }, [
|
||||
Input({
|
||||
class: "w-full",
|
||||
type: "password",
|
||||
placeholder: "Contraseña",
|
||||
value: loginForm.password,
|
||||
oninput: (e) => loginForm.password(e.target.value)
|
||||
}),
|
||||
h('span', {}, "Contraseña")
|
||||
])
|
||||
]),
|
||||
ModalAction({}, [
|
||||
Button({
|
||||
class: "btn btn-ghost",
|
||||
onclick: () => showLoginModal(false)
|
||||
}, "Cancelar"),
|
||||
Button({
|
||||
class: "btn btn-primary",
|
||||
onclick: handleLogin
|
||||
}, "Entrar")
|
||||
])
|
||||
]),
|
||||
ModalBackdrop({ onclick: () => showLoginModal(false) })
|
||||
])
|
||||
)
|
||||
];
|
||||
};
|
||||
Reference in New Issue
Block a user