// App.js /** * Vistas de la aplicación (pueden ir en archivos separados luego) */ import { Navbar, Swap, Menu, Checkbox, Input, Button, Autocomplete, Datepicker, Colorpicker, Fileinput, Toast } from "sigpro-ui"; const toggle = $(false); // const consoleToggle = $(()=>console.log(toggle())) const Home = () => { const valor = $("jjj"); const miCheck = $(false); // Creamos la señal return Div({ class: "gap-3 flex flex-col" }, [ H1("Dashboard Principal"), Button({ tooltip: "tooltip", badge: "22" }, "Hola"), Input({ label: "Correo Electrónico", type: "email", placeholder: "ejemplo@correo.com", value: valor, // Binding automático }), Span({ class: "gap-4 text-4xl" }, () => valor()), P("Bienvenido a la interfaz reactiva de SigPro. Aquí puedes ver el estado global."), Checkbox({ tooltip: "Tooltip", toggle: toggle, value: miCheck, label: "Checkbox" }), miCheck, toggle, Button( { class: "btn-primary", onclick: () => { Toast("Cambio de toggle"); toggle(!toggle()); }, }, "Lanzar Toast", ), ]); }; const Profile = (params) => { const miFecha = $(); const miRango = $(); const selectedFruit = $("Apple"); const fruits = ["Apple", "Banana", "Cherry", "Dragonfruit", "Elderberry"]; const colorFondo = $("#ef4444"); const misArchivos = $([]); const textoInput = $(() => { const f = miFecha; if (!f.start) return ""; return f.end ? `${f.start} - ${f.end}` : `${f.start}...`; }); return Div({ class: "p-4 space-y-4" }, [ H2({ class: "text-xl font-bold" }, `Perfil: ${params.id}`), Autocomplete({ label: "Selecciona una fruta", value: selectedFruit, options: fruits, onSelect: (val) => console.log("Seleccionado:", val), }), Datepicker({ value: miFecha, label: "Fecha", placeholder: textoInput, hour: true }), Datepicker({ value: miRango, label: "Fecha", placeholder: textoInput, range: true, hour: true }), Colorpicker({ show: true, label: "Color del tema", value: colorFondo }), Input({ type: "number", label: "Number" }), Input({ type: "email", label: "Email" }), Input({ label: "Text" }), Input({ type: "date", label: "Date" }), Input({ type: "password", label: "Password" }), $html("p", {}, () => `Has elegido: ${selectedFruit()}`), Fileinput({ tooltip: "Formatos: PDF, JPG", max: 5, // Cada vez que la lista cambia, se dispara esto onSelect: async (files) => { if (files.length === 0) return; console.log("Subiendo archivos automáticamente...", files); const formData = new FormData(); files.forEach((file) => formData.append("files[]", file)); // await fetch('/api/upload', { method: 'POST', body: formData }); }, }), Div({ class: "pt-4" }, [ Button( { class: "btn-sm btn-outline", onclick: () => $router.to("/"), }, "Volver", ), ]), ]); }; /** * Componente Principal */ export const App = () => { // Estado local de la App (ejemplo: tema o usuario) const isDark = $(false, "sigpro-theme"); // Efecto para cambiar el tema en el HTML $watch(() => { document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light"); }); const menuItems = [ { label: "Inicio", onclick: () => $router.to("/") }, { label: "Mi Perfil", onclick: () => $router.to("/profile/42") }, { label: "Tareas TODO", onclick: () => $router.to("/todo") }, // <-- Nueva opción { label: "Ajustes", onclick: () => Toast("Ajustes no disponibles", "alert-error") }, ]; return Div({ class: "min-h-screen flex flex-col" }, [ Navbar({ class: "sticky top-0 z-50 bg-base-100/80 backdrop-blur border-b border-base-300" }, [ Div({ class: "flex-1" }, [ A( { class: "btn btn-ghost text-xl font-black tracking-tighter", onclick: () => $router.to("/"), }, "SIGPRO", ), ]), Button({ disabled: true }, "Disabled"), Div({ class: "flex items-center gap-2" }, [ Swap({ class: "swap-rotate", value: isDark, on: "🌙", off: "☀️", }), Button({ class: "btn-circle btn-ghost", icon: "👤" }), ]), ]), // --- LAYOUT PRINCIPAL --- Div({ class: "flex flex-1" }, [ // Sidebar Lateral Aside({ class: "w-64 bg-base-200 p-4 hidden md:block" }, [Menu({ items: menuItems, class: "bg-transparent" })]), // Contenido Dinámico (Router) Main({ class: "flex-1 p-6 bg-base-100" }, [ $router([ { path: "/", component: Home }, { path: "/profile/:id", component: Profile }, { path: "/todo", component: TodoPage }, { path: "*", component: () => Div({ class: "text-center py-20" }, [H1({ class: "text-9xl font-bold opacity-20" }, "404"), P("La página que buscas no existe.")]), }, ]), ]), ]), // --- FOOTER --- Footer({ class: "footer footer-center p-4 bg-base-300 text-base-content text-xs" }, [P("© 2026 - Built with SigPro Engine")]), ]); }; const TodoPage = () => { const newTask = $(""); // Persistencia automática en localStorage gracias a tu Core const tasks = $([], "sigpro-todo-list"); const addTask = () => { const val = newTask().trim(); if (!val) return Toast("Escribe algo...", "alert-warning"); tasks([...tasks(), { id: crypto.randomUUID(), text: val, done: false }]); newTask(""); // Limpia el input }; const removeTask = (id) => { tasks(tasks().filter((t) => t.id !== id)); }; const toggleTask = (id) => { tasks(tasks().map((t) => (t.id === id ? { ...t, done: !t.done } : t))); }; // Señal computada para el contador const pendingCount = $(() => tasks().filter((t) => !t.done).length); const misDatos = $([{ id: 1 }]); return Div({ class: "max-w-md mx-auto space-y-6" }, [ H1({ class: "text-3xl font-black italic" }, "TODO LIST"), // Input Group Div({ class: "join w-full" }, [ Input({ class: "join-item w-full", placeholder: "Nueva tarea...", value: newTask, onkeydown: (e) => e.key === "Enter" && addTask(), }), Button({ class: "btn-primary join-item", onclick: addTask }, "Añadir"), ]), // Stats Div({ class: "flex justify-between items-center opacity-70 text-sm" }, [ Span(() => `Total: ${tasks().length}`), Span({ class: "badge badge-secondary" }, () => `${pendingCount()} pendientes`), ]), // Lista Reactiva (Aquí evaluamos tu append con sweep) Ul({ class: "menu bg-base-200 rounded-box w-full p-2" }, () => tasks().length === 0 ? Li({ class: "p-4 text-center opacity-50" }, "No hay tareas pendientes") : tasks().map((task) => Li({ class: "flex flex-row items-center gap-2 p-2 border-b border-base-300 last:border-0" }, [ Checkbox({ class: "checkbox-sm", value: () => task.done, onclick: () => toggleTask(task.id), }), Span( { class: `flex-1 ${task.done ? "line-through opacity-40" : ""}`, onclick: () => toggleTask(task.id), }, task.text, ), Button( { class: "btn-ghost btn-xs text-error", onclick: () => removeTask(task.id), }, "✕", ), ]), ), ), Button( { class: "btn-link btn-xs text-error p-0", onclick: () => tasks([]), }, "Limpiar todo", ), ]); };