Firts Upload
This commit is contained in:
14
client/index.html
Normal file
14
client/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>SigPro</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
20
client/package.json
Normal file
20
client/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "dare-client",
|
||||
"version": "2.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"kill": "pkill -9 bun && pkill -9 node || true",
|
||||
"del": "rm node_modules/.vite/deps -r",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.443",
|
||||
"@iconify/tailwind4": "^1.2.1",
|
||||
"vite": "^8.0.0"
|
||||
}
|
||||
}
|
||||
247
client/public/App.js
Normal file
247
client/public/App.js
Normal 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
38
client/public/app.css
Normal 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
BIN
client/public/geist.woff2
Normal file
Binary file not shown.
BIN
client/public/jakarta.woff2
Normal file
BIN
client/public/jakarta.woff2
Normal file
Binary file not shown.
4
client/public/main.js
Normal file
4
client/public/main.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import {$mount} from 'sigpro';
|
||||
import { App } from './App.js';
|
||||
import './app.css';
|
||||
$mount(App, '#app');
|
||||
16
client/vite.config.js
Normal file
16
client/vite.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from "vite";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
const dev = process.env.NODE_ENV === "development";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss()],
|
||||
base: dev ? "/absproxy/5173/" : "/",
|
||||
server: {
|
||||
allowedHosts: true,
|
||||
port: 5173,
|
||||
},
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1500,
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user