Firts Upload

This commit is contained in:
2026-04-17 19:26:02 +02:00
parent caa55bad77
commit 3c5b3b4c1e
25 changed files with 2252 additions and 0 deletions

247
client/public/App.js Normal file
View File

@@ -0,0 +1,247 @@
// 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",
),
]);
};

38
client/public/app.css Normal file
View File

@@ -0,0 +1,38 @@
@import "tailwindcss";
@plugin "@iconify/tailwind4";
@plugin "daisyui" {
themes:
light --default,
dark --prefersdark;
include:
alert, avatar, badge, button, card, checkbox, collapse, drawer, dropdown, fab, fieldset, loading, indicator, input, kbd, label, list, menu, modal,
navbar, radio, range, select, skeleton, tab, textarea, toast, toggle, tooltip, validator, rating, mask, swap;
}
@font-face {
font-family: "Plus Jakarta Sans";
src: url("/jakarta.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
:root {
font-size: 14px;
/* font-family: "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif; */
}
.btn-ghost {
border-color: transparent !important;
}
.floating-label > span {
font-size: 1.2rem;
}
@utility input {
@apply transition-all duration-300 ease-in-out outline-none shrink appearance-none items-center;
&:hover {
background-color: var(--color-base-300);
}
}

BIN
client/public/geist.woff2 Normal file

Binary file not shown.

BIN
client/public/jakarta.woff2 Normal file

Binary file not shown.

4
client/public/main.js Normal file
View File

@@ -0,0 +1,4 @@
import {$mount} from 'sigpro';
import { App } from './App.js';
import './app.css';
$mount(App, '#app');