diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..958a96f
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,39 @@
+{
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.wordWrapColumn": 150,
+ "editor.formatOnSave": true,
+
+ /* --- FORZAR PRETTIER PARA JAVASCRIPT --- */
+ "[javascript]": {
+ "editor.defaultFormatter": "vscode.typescript-language-features",
+ "editor.suggest.insertMode": "replace"
+ },
+
+ /* --- CONFIGURACIÓN DE PRETTIER --- */
+ "prettier.printWidth": 150,
+ "prettier.semi": true,
+ "prettier.singleQuote": false,
+ "prettier.bracketSameLine": true,
+
+ /* ESTO ES CLAVE: Evita que Prettier colapse o rompa espacios raros en tus html`` */
+ "prettier.htmlWhitespaceSensitivity": "ignore",
+
+ /* --- LO QUE YA TENÍAS (MANTENER) --- */
+ "files.associations": {
+ "*.js": "javascript"
+ },
+ "emmet.includeLanguages": {
+ "javascript": "html"
+ },
+ "emmet.triggerExpansionOnTab": true,
+
+ "tailwindCSS.experimental.classRegex": [
+ ["html`([^`]*?)`", "class=\"([^\"]*)\""],
+ ["html`([^`]*?)`", "class='([^']*)'"],
+ ["html`([^`]*?)`", "class=([^\\s\\/>]+)"]
+ ],
+
+ "editor.quickSuggestions": {
+ "strings": true
+ }
+}
diff --git a/bun.lock b/bun.lock
new file mode 100644
index 0000000..dfe7f02
--- /dev/null
+++ b/bun.lock
@@ -0,0 +1,336 @@
+{
+ "lockfileVersion": 1,
+ "configVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "dare",
+ },
+ "client": {
+ "name": "dare-client",
+ "version": "2.0.0",
+ "dependencies": {
+ "@tailwindcss/vite": "^4.2.1",
+ },
+ "devDependencies": {
+ "@iconify/json": "^2.2.443",
+ "@iconify/tailwind4": "^1.2.1",
+ "vite": "^8.0.0",
+ },
+ },
+ "server": {
+ "name": "dare-server",
+ "version": "2.0.0",
+ "dependencies": {
+ "hono": "^4.12.7",
+ "mariadb": "^3.5.2",
+ "nodemailer": "^8.0.2",
+ "sharp": "^0.34.5",
+ },
+ },
+ },
+ "packages": {
+ "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
+
+ "@cyberalien/svg-utils": ["@cyberalien/svg-utils@1.2.9", "", { "dependencies": { "@iconify/types": "^2.0.0" } }, "sha512-bd5I3TQ2k/xqcNB6P6GpFKeid3OgqgGSRz704+XHoGNBAsI7wwYKS6nuxAn26ciUtRjGKovfEy66ryEY9UGUwQ=="],
+
+ "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
+
+ "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
+
+ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
+
+ "@iconify/json": ["@iconify/json@2.2.463", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.3" } }, "sha512-VZ0n+99OWe9677b04KPF0NajDbFEyWNxMalXZA/4j8HrqyVvY+N1XN/EIER4ceQlKQJ501w9UxLJZjZ5mga0xA=="],
+
+ "@iconify/tailwind4": ["@iconify/tailwind4@1.2.3", "", { "dependencies": { "@iconify/tools": "^5.0.5", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0" }, "peerDependencies": { "tailwindcss": ">= 4.0.0" } }, "sha512-z8SKiMHRASJKF/IY//87MF88lcB7ulxh8vlhQXXLWsBkNtOh6ese9R41MyGpQeqXdRvQVt+/fX2glQtHFjQ+MA=="],
+
+ "@iconify/tools": ["@iconify/tools@5.0.11", "", { "dependencies": { "@cyberalien/svg-utils": "^1.2.8", "@iconify/types": "^2.0.0", "@iconify/utils": "^3.1.0", "fflate": "^0.8.2", "modern-tar": "^0.7.6", "pathe": "^2.0.3", "svgo": "^4.0.1" } }, "sha512-zur/06/zTSflUSoPARK5FfHNZQ9UYsoloPDQHLAZHbQqWhs0/tXS+KB70uOAt94dUB1F94JOkSqIOT2R4Deixg=="],
+
+ "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
+
+ "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
+
+ "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
+
+ "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
+
+ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
+
+ "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
+
+ "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
+
+ "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
+
+ "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
+
+ "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
+
+ "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
+
+ "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
+
+ "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
+
+ "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
+
+ "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
+
+ "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
+
+ "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
+
+ "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
+
+ "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
+
+ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
+
+ "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
+
+ "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
+
+ "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
+
+ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
+
+ "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
+
+ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
+
+ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
+
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
+
+ "@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="],
+
+ "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="],
+
+ "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="],
+
+ "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="],
+
+ "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="],
+
+ "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="],
+
+ "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="],
+
+ "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="],
+
+ "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="],
+
+ "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="],
+
+ "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="],
+
+ "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="],
+
+ "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="],
+
+ "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="],
+
+ "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="],
+
+ "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="],
+
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="],
+
+ "@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="],
+
+ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
+
+ "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
+
+ "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
+
+ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
+
+ "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
+
+ "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
+
+ "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
+
+ "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
+
+ "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
+
+ "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
+
+ "dare-client": ["dare-client@workspace:client"],
+
+ "dare-server": ["dare-server@workspace:server"],
+
+ "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
+
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+
+ "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
+
+ "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
+
+ "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
+
+ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
+
+ "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
+ "fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "hono": ["hono@4.12.14", "", {}, "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w=="],
+
+ "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
+
+ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
+
+ "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
+
+ "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
+
+ "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
+
+ "mariadb": ["mariadb@3.5.2", "", { "dependencies": { "@types/geojson": "^7946.0.16", "@types/node": ">=18", "denque": "^2.1.0", "iconv-lite": "^0.7.2", "lru-cache": "^10.4.3" } }, "sha512-9rztrI4nouxAY/82a+RlzzZ5ie2vxu2eYclkBvTy1ATXH1B9cnvZ0O71Pzsy/mlfDb5P3HhOg0JzQKkDRhctyA=="],
+
+ "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="],
+
+ "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
+
+ "modern-tar": ["modern-tar@0.7.6", "", {}, "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "nodemailer": ["nodemailer@8.0.5", "", {}, "sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w=="],
+
+ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
+
+ "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
+
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
+
+ "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
+
+ "postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="],
+
+ "rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="],
+
+ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
+
+ "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
+
+ "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
+
+ "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
+
+ "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
+
+ "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
+
+ "tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="],
+
+ "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
+
+ "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
+
+ "vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="],
+
+ "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
+
+ "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
+ }
+}
diff --git a/client/index.html b/client/index.html
new file mode 100644
index 0000000..1bfbb28
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ SigPro
+
+
+
+
+
+
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..2836511
--- /dev/null
+++ b/client/package.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/client/public/App.js b/client/public/App.js
new file mode 100644
index 0000000..b1ea722
--- /dev/null
+++ b/client/public/App.js
@@ -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",
+ ),
+ ]);
+};
diff --git a/client/public/app.css b/client/public/app.css
new file mode 100644
index 0000000..ad9598d
--- /dev/null
+++ b/client/public/app.css
@@ -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);
+ }
+}
diff --git a/client/public/geist.woff2 b/client/public/geist.woff2
new file mode 100644
index 0000000..b189a1b
Binary files /dev/null and b/client/public/geist.woff2 differ
diff --git a/client/public/jakarta.woff2 b/client/public/jakarta.woff2
new file mode 100644
index 0000000..954c272
Binary files /dev/null and b/client/public/jakarta.woff2 differ
diff --git a/client/public/main.js b/client/public/main.js
new file mode 100644
index 0000000..681ec97
--- /dev/null
+++ b/client/public/main.js
@@ -0,0 +1,4 @@
+import {$mount} from 'sigpro';
+import { App } from './App.js';
+import './app.css';
+$mount(App, '#app');
\ No newline at end of file
diff --git a/client/vite.config.js b/client/vite.config.js
new file mode 100644
index 0000000..f0e4c91
--- /dev/null
+++ b/client/vite.config.js
@@ -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,
+ }
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e50fc52
--- /dev/null
+++ b/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "dare",
+ "workspaces": [
+ "client",
+ "server"
+ ],
+ "scripts": {
+ "dev:client": "bun run --cwd client dev",
+ "dev:server": "bun run --cwd server dev",
+ "dev": "bun --filter '*' dev"
+ }
+}
diff --git a/server/actions/db.actions.js b/server/actions/db.actions.js
new file mode 100644
index 0000000..f8e2186
--- /dev/null
+++ b/server/actions/db.actions.js
@@ -0,0 +1,52 @@
+/**
+ * Acciones base para operaciones CRUD y búsquedas
+ */
+export const actions = {
+ /**
+ * Inserta o actualiza un registro y devuelve el objeto completo actualizado
+ */
+ _set: async (db, { table, view, data }) => {
+ // Si el id viene vacío o es 0, lo eliminamos para que MariaDB genere uno nuevo (AUTO_INCREMENT)
+ if (!data.id) delete data.id;
+
+ const result = await db.upsert(table, data);
+
+ // Normalizamos la respuesta del driver de MariaDB
+ const res = Array.isArray(result) ? result[0] : result;
+
+ // Obtenemos el ID: o bien el que ya venía en data, o el nuevo generado por el insert
+ const idToFetch = data.id || res?.insertId;
+
+ if (!idToFetch) throw new Error("No se pudo determinar el ID del registro");
+
+ // Devolvemos el registro fresco (útil si usamos una 'view' con campos calculados)
+ return db.one(view || table, { id: idToFetch });
+ },
+
+ /**
+ * Elimina un registro por ID
+ */
+ _del: async (db, { table, id }) => {
+ if (!id) throw new Error("ID requerido para eliminar");
+ return db.delete(table, { id });
+ },
+
+ /**
+ * Obtiene todos los clientes (compatibilidad con ClientesOld)
+ */
+ Clientes: async (db) => {
+ return db.select('ClientesOld');
+ },
+ Recibos: async (db) => {
+ return db.select('Recibos');
+ },
+
+ /**
+ * Búsqueda avanzada usando parámetros nombrados (raw query)
+ */
+ Search: async (db, { text }) => {
+ const query = 'SELECT * FROM ClientesOld WHERE Cliente LIKE :term OR NIF LIKE :term';
+ const params = { term: `%${text}%` };
+ return db.raw(query, params);
+ }
+};
\ No newline at end of file
diff --git a/server/actions/soap.actions.js b/server/actions/soap.actions.js
new file mode 100644
index 0000000..edc68a5
--- /dev/null
+++ b/server/actions/soap.actions.js
@@ -0,0 +1,138 @@
+import schemaDataRaw from '../lib/schema.json' assert { type: 'json' };
+
+// El schema se usa para validar qué columnas existen realmente en la DB
+const schemaData = schemaDataRaw;
+
+/**
+ * Formatea fechas a estándar ISO (YYYY-MM-DD) para MariaDB
+ */
+const fmt = (d) =>
+ (d instanceof Date && !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : null);
+
+/**
+ * Filtra y limpia las columnas según el schema.json
+ */
+const filterColumns = (tableName, rowData) => {
+ const schema = schemaData[tableName];
+ if (!schema) return rowData;
+
+ return Object.keys(rowData).reduce((cleanRow, col) => {
+ const colDef = schema[col];
+ if (!colDef) return cleanRow; // Si la columna no existe en el JSON, se ignora
+
+ let val = rowData[col];
+ const type = colDef.type || ''; // Usamos el campo 'type' generado por tu script anterior
+
+ if (val != null) {
+ // Lógica de fechas
+ if (type.includes('date')) {
+ try {
+ const s = val.toString().trim();
+ const isISO = val instanceof Date || s.includes('T');
+
+ val = isISO ? fmt(new Date(val)) :
+ /^\d{8}$/.test(s) ? `${s.slice(4, 8)}-${s.slice(2, 4)}-${s.slice(0, 2)}` :
+ s.includes('.') ? s.split('.').reverse().join('-') : s.slice(0, 10);
+ } catch {
+ val = null;
+ }
+ }
+ // Lógica de texto: trim y recorte por seguridad según definición
+ else if (type === 'text' && typeof val === 'string') {
+ val = val.trim().slice(0, 255);
+ }
+ }
+ cleanRow[col] = val;
+ return cleanRow;
+ }, {});
+};
+
+/**
+ * Orquestador para guardar datos transformados de SOAP a DB
+ */
+const saveSoap = async (db, raw, config) => {
+ if (!raw || !raw.length) return 0;
+
+ const rawArray = Array.isArray(raw) ? raw : [raw];
+
+ for (const [table, transform] of Object.entries(config)) {
+ const data = rawArray.flatMap(item => {
+ const res = transform ? transform(item) : item;
+ if (!res) return [];
+
+ return Array.isArray(res)
+ ? res.map(sub => filterColumns(table, sub))
+ : [filterColumns(table, res)];
+ });
+
+ if (data.length) {
+ await db.upsert(table, data, true); // true para INSERT IGNORE
+ }
+ }
+ return rawArray.length;
+};
+
+/**
+ * Acciones exportables para el motor TRX
+ * Estas funciones son llamadas dinámicamente según el nombre del servicio SOAP
+ */
+export const actions = {
+ DescargaCompletaRecibos: async (db, { data }) => {
+ const raw = data.ListaRecibos?.ReciboAmpliado || data || [];
+ const total = await saveSoap(db, raw, { 'Recibos': null, 'RecibosEstados': null });
+ return { totalProcesado: total };
+ },
+
+ DescargaCartera: async (db, { data }) => {
+ const raw = data.DatosCartera?.Cartera || data || [];
+ const total = await saveSoap(db, raw, { 'Cartera': null });
+ return { totalProcesado: total };
+ },
+
+ DescargaSiniestros: async (db, { data }) => {
+ const raw = data.ListaSiniestros?.SiniestroAmpliado || data || [];
+ const total = await saveSoap(db, raw, { 'Siniestros': null, 'SiniestrosEstados': null });
+ return { totalProcesado: total };
+ },
+
+ ConsultaAgendaTramitacion: async (db, { data }) => {
+ const raw = data.Datos?.DatosTramitacionResponse || data || [];
+ const total = await saveSoap(db, raw, { 'SiniestrosTramites': null });
+ return { totalProcesado: total };
+ },
+
+ DescargaPolizas: async (db, { data }) => {
+ const raw = data.ListaPolizas?.Poliza || data || [];
+ const total = await saveSoap(db, raw, {
+ 'Polizas': i => ({
+ ...i.DatosGenerales,
+ Riesgo: [
+ i.DatosAccidentes?.NombreAsegurado,
+ i.DatosAutos?.CodigoModelo && `${i.DatosAutos.CodigoModelo} ${i.DatosAutos.Matricula || ''}`.trim(),
+ i.DatosMultirriesgos?.SituacionRiesgo
+ ].filter(Boolean).join(' | ')
+ }),
+ 'PolizasDetalle': i => i.DatosGenerales
+ ? { ...i.DatosGenerales.DatosTomador, ...i.DatosGenerales, CodigoPoliza: i.DatosGenerales.CodigoPoliza, CodigoSuplemento: i.DatosGenerales.CodigoSuplemento }
+ : null,
+ 'PolizasAutos': i => i.DatosAutos?.CodigoModelo
+ ? { ...i.DatosAutos, CodigoPoliza: i.DatosGenerales.CodigoPoliza, CodigoSuplemento: i.DatosGenerales.CodigoSuplemento }
+ : null,
+ 'PolizasAutosConductor': i => {
+ const { CodigoPoliza, CodigoSuplemento } = i.DatosGenerales || {};
+ const { DatosConductor: hab, DatosConductorOcasional: oca } = i.DatosAutos || {};
+ return [
+ hab?.Nombre ? { ...hab, CodigoPoliza, CodigoSuplemento, TipoConductor: 'Habitual' } : null,
+ oca?.Nombre ? { ...oca, CodigoPoliza, CodigoSuplemento, TipoConductor: 'Ocasional' } : null
+ ].filter(Boolean);
+ },
+ 'PolizasMultirriesgos': i => i.DatosMultirriesgos
+ ? { ...i.DatosMultirriesgos, CodigoPoliza: i.DatosGenerales.CodigoPoliza, CodigoSuplemento: i.DatosGenerales.CodigoSuplemento }
+ : null,
+ 'PolizasAccidentes': i => i.DatosAccidentes
+ ? { ...i.DatosAccidentes, CodigoPoliza: i.DatosGenerales.CodigoPoliza, CodigoSuplemento: i.DatosGenerales.CodigoSuplemento }
+ : null
+ });
+ return { totalProcesado: total };
+ }
+};
\ No newline at end of file
diff --git a/server/api/auth.js b/server/api/auth.js
new file mode 100644
index 0000000..7b88dc5
--- /dev/null
+++ b/server/api/auth.js
@@ -0,0 +1,74 @@
+import { Hono } from 'hono'
+import { sign } from 'hono/jwt'
+import { pool } from '../services/db.service.js'
+
+const auth = new Hono()
+
+// Configuración externa
+const SYNO_BASE = 'http://192.168.1.100:5000/webapi/auth.cgi'
+const JWT_SECRET = process.env.JWT_SECRET || 'cambia_esto_en_el_env'
+
+auth.post('/login', async (c) => {
+ const { username, password } = await c.req.json().catch(() => ({}))
+
+ if (!username || !password) {
+ return c.json({ success: false, message: 'Faltan credenciales' }, 400)
+ }
+
+ try {
+ // 1. Preparar parámetros para el NAS Synology
+ const params = new URLSearchParams({
+ api: 'SYNO.API.Auth',
+ version: '6',
+ method: 'login',
+ account: username,
+ passwd: password,
+ format: 'sid'
+ })
+
+ // 2. Petición al NAS usando el fetch nativo
+ const response = await fetch(`${SYNO_BASE}?${params.toString()}`)
+ const data = await response.json()
+
+ if (data.success && data.data) {
+ // 3. Generar JWT (expiración en 8 horas)
+ const payload = {
+ username,
+ sid: data.data.sid,
+ exp: Math.floor(Date.now() / 1000) + (60 * 60 * 8)
+ }
+
+ const token = await sign(payload, JWT_SECRET)
+
+ // 4. Asegurar el usuario en la base de datos (MariaDB)
+ try {
+ await pool.query('INSERT IGNORE INTO Usuarios (Usuario) VALUES (?)', [username])
+ } catch (dbError) {
+ console.error('Error al registrar usuario en DB:', dbError)
+ }
+
+ return c.json({
+ success: true,
+ token,
+ data: {
+ user: { username }
+ }
+ })
+ }
+
+ // Error de autenticación del NAS
+ return c.json({
+ success: false,
+ message: `Error de autenticación (Código: ${data.error?.code || 'unknown'})`
+ }, 401)
+
+ } catch (error) {
+ console.error('Error crítico en Auth:', error)
+ return c.json({
+ success: false,
+ message: 'NAS Unreachable / Internal Error'
+ }, 500)
+ }
+})
+
+export default auth
\ No newline at end of file
diff --git a/server/api/db.js b/server/api/db.js
new file mode 100644
index 0000000..8f104b9
--- /dev/null
+++ b/server/api/db.js
@@ -0,0 +1,132 @@
+import { Hono } from 'hono'
+import { jwt } from 'hono/jwt'
+import { db, trx } from '../services/db.service.js'
+import { actions } from '../actions/db.actions.js'
+import sharp from 'sharp'
+import path from 'path'
+import mime from 'mime-types'
+
+const api = new Hono()
+
+const JWT_SECRET = process.env.JWT_SECRET || 'tu_clave_secreta'
+
+// --- MIDDLEWARE AUTH CON BYPASS ---
+// api.use('/*', async (c, next) => {
+// if (process.env.DISABLE_AUTH === 'true') {
+// c.set('jwtPayload', { username: 'TEST_USER', role: 'admin' })
+// return await next()
+// }
+// const jwtMiddleware = jwt({ secret: JWT_SECRET, alg: 'HS256' })
+// return jwtMiddleware(c, next)
+// })
+
+// --- RUTA PARA TRANSACCIONES (TRX) ---
+api.on(['GET', 'POST'], '/', async (c) => {
+ const body = c.req.method === 'POST' ? await c.req.json().catch(() => ({})) : {}
+ const query = c.req.query()
+ const postData = { ...query, ...body }
+
+ if (Object.keys(postData).length === 0) {
+ return c.json({ success: false, error: 'No data provided' }, 400)
+ }
+
+ try {
+ const data = await trx(postData, actions)
+ // Manejo de BigInt para evitar errores en JSON.stringify
+ const cleanData = JSON.parse(JSON.stringify(data, (_, v) => typeof v === 'bigint' ? v.toString() : v))
+ return c.json({ success: true, data: cleanData })
+ } catch (error) {
+ return c.json({ success: false, error: error.message }, 500)
+ }
+})
+
+// --- RUTA FICHAR ---
+api.on(['GET', 'POST'], '/fichar/:query', async (c) => {
+ const payload = c.get('jwtPayload')
+ const user = payload?.username || 'ANONYMOUS'
+ const queryType = c.req.param('query')
+
+ try {
+ const ret = queryType === 'nuevo'
+ ? await db.raw('CALL RegistrarFichaje(:user)', { user })
+ : await db.raw('SELECT Fecha, Tipo, Duracion FROM UsuariosFichajes WHERE Usuario = :user ORDER BY id DESC LIMIT 1', { user })
+
+ return c.json({ success: true, data: ret })
+ } catch (error) {
+ return c.json({ success: false, error: error.message }, 500)
+ }
+})
+
+// --- RUTA DE SUBIDA (UPLOAD) ---
+api.post('/upload', async (c) => {
+ try {
+ const body = await c.req.parseBody()
+ const file = body.file
+ if (!file) throw new Error('No se ha enviado ningún archivo')
+
+ const arrayBuffer = await file.arrayBuffer()
+ let finalBuffer = Buffer.from(arrayBuffer)
+ const isImg = file.type.startsWith('image/')
+
+ if (isImg) {
+ finalBuffer = await sharp(finalBuffer)
+ .resize(1024, null, { withoutEnlargement: true })
+ .jpeg({ quality: 75 })
+ .toBuffer()
+ }
+
+ const res = await db.insert('Documentos', {
+ CodigoPoliza: body.CodigoPoliza || null,
+ CodigoRecibo: body.CodigoRecibo ? BigInt(body.CodigoRecibo) : null,
+ CodigoSiniestro: body.CodigoSiniestro ? BigInt(body.CodigoSiniestro) : null,
+ NIF: body.NIF || null,
+ Nombre: file.name,
+ Extension: isImg ? '.jpg' : path.extname(file.name).toLowerCase(),
+ Archivo: finalBuffer
+ })
+
+ const insertId = Array.isArray(res) ? res[0].insertId : res?.insertId
+ return c.json({ success: true, id: insertId?.toString() })
+ } catch (err) {
+ return c.json({ success: false, error: err.message }, 500)
+ }
+})
+
+// --- RUTA DE DESCARGA ---
+api.get('/download', async (c) => {
+ try {
+ const { id, CodigoPoliza, CodigoRecibo, CodigoSiniestro, NIF, download } = c.req.query()
+ const filtros = Object.fromEntries(
+ Object.entries({ id, CodigoPoliza, CodigoRecibo, CodigoSiniestro, NIF })
+ .filter(([_, v]) => v)
+ )
+
+ if (Object.keys(filtros).length === 0) return c.json({ success: false, error: 'Faltan parámetros' }, 400)
+
+ const docs = await db.select('Documentos', filtros)
+ if (!docs.length) return c.json({ success: false, error: 'Sin resultados' }, 404)
+
+ if (id) {
+ const doc = docs[0]
+ const contentType = mime.lookup(doc.Extension) || 'application/octet-stream'
+ c.header('Content-Type', contentType)
+ c.header('Content-Disposition', `${download === 'true' ? 'attachment' : 'inline'}; filename="${doc.Nombre}"`)
+
+ return c.body(doc.Archivo)
+ }
+
+ return c.json(docs.map((doc) => {
+ const { Archivo, ...info } = doc
+ return {
+ ...info,
+ id: info.id.toString(),
+ url: `/api/db/download?id=${info.id}`,
+ urlDownload: `/api/db/download?id=${info.id}&download=true`
+ }
+ }))
+ } catch (err) {
+ return c.json({ success: false, error: err.message }, 500)
+ }
+})
+
+export default api
\ No newline at end of file
diff --git a/server/api/mail.js b/server/api/mail.js
new file mode 100644
index 0000000..f230e50
--- /dev/null
+++ b/server/api/mail.js
@@ -0,0 +1,33 @@
+import { Hono } from 'hono'
+import { send } from '../services/mail.service.js'
+
+const mail = new Hono()
+
+/**
+ * Endpoint para envío de correo
+ * Soporta GET (para pruebas rápidas) y POST (para producción)
+ */
+mail.on(['GET', 'POST'], '/', async (c) => {
+ try {
+ // Llamamos al servicio de correo
+ // Nota: Aquí podrías extraer to, subject y html de c.req.json() o c.req.query()
+ await send(
+ 'natxocc@natxocc.com',
+ 'Prueba desde Hono',
+ 'Este es un correo de prueba
'
+ )
+
+ return c.json({
+ success: true,
+ message: 'Email enviado correctamente'
+ })
+ } catch (error) {
+ console.error('Error en ruta mail:', error)
+ return c.json({
+ success: false,
+ error: error.message
+ }, 500)
+ }
+})
+
+export default mail
\ No newline at end of file
diff --git a/server/api/soap.js b/server/api/soap.js
new file mode 100644
index 0000000..45b8fee
--- /dev/null
+++ b/server/api/soap.js
@@ -0,0 +1,61 @@
+import { Hono } from 'hono'
+import { soapCall } from '../services/soap2.service.js'
+
+const api = new Hono()
+
+/**
+ * Endpoint simplificado: /api/soap/12345/Polizas
+ * El servicio soapCall se encarga de traducir "Polizas" a "DescargaPolizas"
+ */
+api.get('/:CodigoMediador/:alias', async (c) => {
+ const params = c.req.param() // Contiene CodigoMediador y alias
+ const query = c.req.query()
+
+ try {
+ // Pasamos params directamente porque soapCall ya sabe buscar el 'alias'
+ const result = await soapCall(params, query)
+
+ return c.json({
+ success: true,
+ data: result
+ })
+ } catch (error) {
+ console.error(`[Router Error] ${params.alias}:`, error.message)
+ return c.json({
+ success: false,
+ message: error.message || 'Error en el servicio externo SOAP'
+ }, 500)
+ }
+})
+
+export default api
+/*
+import { Hono } from 'hono'
+import { soapCall } from '../services/soap2.service.js'
+
+const api = new Hono()
+
+api.get('/:CodigoMediador/:service/:method', async (c) => {
+ const params = c.req.param()
+ const query = c.req.query()
+
+ try {
+ const result = await soapCall(params, query)
+
+ return c.json({
+ success: true,
+ message: false,
+ data: result
+ })
+ } catch (error) {
+ console.error(`Error en llamada SOAP ${params.service}/${params.method}:`, error)
+
+ return c.json({
+ success: false,
+ message: error.message || 'Error en el servicio externo SOAP'
+ }, 500)
+ }
+})
+
+export default api
+*/
\ No newline at end of file
diff --git a/server/index.js b/server/index.js
new file mode 100644
index 0000000..a9d6a5b
--- /dev/null
+++ b/server/index.js
@@ -0,0 +1,41 @@
+import { Hono } from 'hono'
+import { logger } from 'hono/logger'
+import { cors } from 'hono/cors'
+import { jwt } from 'hono/jwt'
+
+// Importación de APIs (Rutas)
+import auth from './api/auth'
+import db from './api/db'
+import mail from './api/mail'
+import soap from './api/soap'
+
+const app = new Hono()
+
+// --- Middlewares Globales ---
+app.use('*', logger())
+app.use('*', cors())
+
+// --- Gestión de Errores Global ---
+app.onError((err, c) => {
+ console.error(`[Global Error]: ${err.message}`)
+ const status = err.name === 'JwtTokenInvalid' ? 401 : 500
+ return c.json({
+ success: false,
+ message: err.message || 'Internal Server Error'
+ }, status)
+})
+
+// --- Registro de Rutas ---
+// Nota: La protección JWT ya la pusimos dentro de api/db.ts y api/soap.ts
+// para permitir que auth y mail (quizás) sean públicos.
+app.route('/api/auth', auth)
+app.route('/api/db', db)
+app.route('/api/mail', mail)
+app.route('/api/soap', soap)
+
+// --- Servidor Bun ---
+export default {
+ port: 3000,
+ fetch: app.fetch,
+ idleTimeout: 120,
+}
\ No newline at end of file
diff --git a/server/lib/schema.json b/server/lib/schema.json
new file mode 100644
index 0000000..9e265c7
--- /dev/null
+++ b/server/lib/schema.json
@@ -0,0 +1,458 @@
+{
+ "Cartera": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoGestor": { "field": "CodigoGestor", "header": "CODIGOGESTOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Contenido": { "field": "Contenido", "header": "CONTENIDO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Continente": { "field": "Continente", "header": "CONTINENTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaEfecto": { "field": "FechaEfecto", "header": "FECHAEFECTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaVencimiento": { "field": "FechaVencimiento", "header": "FECHAVENCIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FormaPago": { "field": "FormaPago", "header": "FORMAPAGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteBonificacion": { "field": "ImporteBonificacion", "header": "IMPORTEBONIFICACION", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteComision": { "field": "ImporteComision", "header": "IMPORTECOMISION", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteImpuestos": { "field": "ImporteImpuestos", "header": "IMPORTEIMPUESTOS", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteLiquido": { "field": "ImporteLiquido", "header": "IMPORTELIQUIDO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteNeto": { "field": "ImporteNeto", "header": "IMPORTENETO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteTotalRecibo": { "field": "ImporteTotalRecibo", "header": "IMPORTETOTALRECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "NombreTomador": { "field": "NombreTomador", "header": "NOMBRETOMADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "RCGeneral": { "field": "RCGeneral", "header": "RCGENERAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "SubcodigoMediador": { "field": "SubcodigoMediador", "header": "SUBCODIGOMEDIADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Chat": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "De": { "field": "De", "header": "DE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Para": { "field": "Para", "header": "PARA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Mensaje": { "field": "Mensaje", "header": "MENSAJE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaMensaje": { "field": "FechaMensaje", "header": "FECHAMENSAJE", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "Leido": { "field": "Leido", "header": "LEIDO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Editado": { "field": "Editado", "header": "EDITADO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Eliminado": { "field": "Eliminado", "header": "ELIMINADO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Clientes": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": "UK", "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Apellidos": { "field": "Apellidos", "header": "APELLIDOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Cliente": { "field": "Cliente", "header": "CLIENTE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Grupo": { "field": "Grupo", "header": "GRUPO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaNacimiento": { "field": "FechaNacimiento", "header": "FECHANACIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaCarnet": { "field": "FechaCarnet", "header": "FECHACARNET", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "EstadoCivil": { "field": "EstadoCivil", "header": "ESTADOCIVIL", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Profesion": { "field": "Profesion", "header": "PROFESION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Hijos": { "field": "Hijos", "header": "HIJOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "ClientesCorreos": {
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Correo": { "field": "Correo", "header": "CORREO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Tipo": { "field": "Tipo", "header": "TIPO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "ClientesDirecciones": {
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Domicilio": { "field": "Domicilio", "header": "DOMICILIO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoPostal": { "field": "CodigoPostal", "header": "CODIGOPOSTAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Localidad": { "field": "Localidad", "header": "LOCALIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Provincia": { "field": "Provincia", "header": "PROVINCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "ClientesOld": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "Mediador": { "field": "Mediador", "header": "MEDIADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "NIF": { "field": "NIF", "header": "NIF", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "Cliente": { "field": "Cliente", "header": "CLIENTE", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "Grupo": { "field": "Grupo", "header": "GRUPO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo": { "field": "Correo", "header": "CORREO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo2": { "field": "Correo2", "header": "CORREO2", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Fijo": { "field": "Fijo", "header": "FIJO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Movil": { "field": "Movil", "header": "MOVIL", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Movil2": { "field": "Movil2", "header": "MOVIL2", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CP": { "field": "CP", "header": "CP", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Direccion": { "field": "Direccion", "header": "DIRECCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Poblacion": { "field": "Poblacion", "header": "POBLACION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Provincia": { "field": "Provincia", "header": "PROVINCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Banco": { "field": "Banco", "header": "BANCO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaNacimiento": { "field": "FechaNacimiento", "header": "FECHANACIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaCarnet": { "field": "FechaCarnet", "header": "FECHACARNET", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "EstadoCivil": { "field": "EstadoCivil", "header": "ESTADOCIVIL", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Profesion": { "field": "Profesion", "header": "PROFESION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Hijos": { "field": "Hijos", "header": "HIJOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Promo": { "field": "Promo", "header": "PROMO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaPromo": { "field": "FechaPromo", "header": "FECHAPROMO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaBienvenida": { "field": "FechaBienvenida", "header": "FECHABIENVENIDA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "NoMolestar": { "field": "NoMolestar", "header": "NOMOLESTAR", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Notas": { "field": "Notas", "header": "NOTAS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaRegistro": { "field": "FechaRegistro", "header": "FECHAREGISTRO", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "SM": { "field": "SM", "header": "SM", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "upd": { "field": "upd", "header": "UPD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Activo": { "field": "Activo", "header": "ACTIVO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "MP": { "field": "MP", "header": "MP", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "ClientesTelefonos": {
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Telefono": { "field": "Telefono", "header": "TELEFONO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Tipo": { "field": "Tipo", "header": "TIPO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Colaboradores": {
+ "SM": { "field": "SM", "header": "SM", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CIF": { "field": "CIF", "header": "CIF", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ResponsableRecibos": { "field": "ResponsableRecibos", "header": "RESPONSABLERECIBOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ResponsableSiniestros": { "field": "ResponsableSiniestros", "header": "RESPONSABLESINIESTROS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ResponsablePolizas": { "field": "ResponsablePolizas", "header": "RESPONSABLEPOLIZAS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Cartera": { "field": "Cartera", "header": "CARTERA", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Recibos": { "field": "Recibos", "header": "RECIBOS", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Pro": { "field": "Pro", "header": "PRO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Activo": { "field": "Activo", "header": "ACTIVO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Porc": { "field": "Porc", "header": "PORC", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Fact": { "field": "Fact", "header": "FACT", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "IRPF": { "field": "IRPF", "header": "IRPF", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Banco": { "field": "Banco", "header": "BANCO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo": { "field": "Correo", "header": "CORREO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Movil": { "field": "Movil", "header": "MOVIL", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Fijo": { "field": "Fijo", "header": "FIJO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Direccion": { "field": "Direccion", "header": "DIRECCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CP": { "field": "CP", "header": "CP", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Notas": { "field": "Notas", "header": "NOTAS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Contactos": {
+ "Empresa": { "field": "Empresa", "header": "EMPRESA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Seccion": { "field": "Seccion", "header": "SECCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Departamento": { "field": "Departamento", "header": "DEPARTAMENTO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Fijo": { "field": "Fijo", "header": "FIJO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Movil": { "field": "Movil", "header": "MOVIL", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Fax": { "field": "Fax", "header": "FAX", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo": { "field": "Correo", "header": "CORREO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo2": { "field": "Correo2", "header": "CORREO2", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Notas": { "field": "Notas", "header": "NOTAS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Correos": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "id_config": { "field": "id_config", "header": "ID CONFIG", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "Asunto": { "field": "Asunto", "header": "ASUNTO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Cuerpo": { "field": "Cuerpo", "header": "CUERPO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaProgramada": { "field": "FechaProgramada", "header": "FECHAPROGRAMADA", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "Estado": { "field": "Estado", "header": "ESTADO", "type": "select", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "CorreosConfig": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Frecuencia": { "field": "Frecuencia", "header": "FRECUENCIA", "type": "select", "hide": false, "virt": false, "key": false, "editable": true },
+ "DiaSemana": { "field": "DiaSemana", "header": "DIASEMANA", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "DiaMes": { "field": "DiaMes", "header": "DIAMES", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "HoraEnvio": { "field": "HoraEnvio", "header": "HORAENVIO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Cron": { "field": "Cron", "header": "CRON", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "id_plantilla": { "field": "id_plantilla", "header": "ID PLANTILLA", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "Activo": { "field": "Activo", "header": "ACTIVO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "CorreosDestinatarios": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "id_correo": { "field": "id_correo", "header": "ID CORREO", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_cliente": { "field": "id_cliente", "header": "ID CLIENTE", "type": "numeric", "hide": true, "virt": false, "key": false, "editable": false },
+ "EmailDestino": { "field": "EmailDestino", "header": "EMAILDESTINO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaEnviado": { "field": "FechaEnviado", "header": "FECHAENVIADO", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "ErrorLog": { "field": "ErrorLog", "header": "ERRORLOG", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "CorreosPlantillas": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "AsuntoBase": { "field": "AsuntoBase", "header": "ASUNTOBASE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ContenidoHtml": { "field": "ContenidoHtml", "header": "CONTENIDOHTML", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Documentos": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoSiniestro": { "field": "CodigoSiniestro", "header": "CODIGOSINIESTRO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "NIF": { "field": "NIF", "header": "NIF", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaRegistro": { "field": "FechaRegistro", "header": "FECHAREGISTRO", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Extension": { "field": "Extension", "header": "EXTENSION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Archivo": { "field": "Archivo", "header": "ARCHIVO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Empresas": {
+ "Empresa": { "field": "Empresa", "header": "EMPRESA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CIF": { "field": "CIF", "header": "CIF", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Direccion": { "field": "Direccion", "header": "DIRECCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Notas": { "field": "Notas", "header": "NOTAS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Etiquetas": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "Etiqueta": { "field": "Etiqueta", "header": "ETIQUETA", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "Tabla": { "field": "Tabla", "header": "TABLA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Color": { "field": "Color", "header": "COLOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Grupos": {
+ "Grupo": { "field": "Grupo", "header": "GRUPO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Detalle": { "field": "Detalle", "header": "DETALLE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Liquidaciones": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "FechaRegistro": { "field": "FechaRegistro", "header": "FECHAREGISTRO", "type": "date", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "FechaEfecto": { "field": "FechaEfecto", "header": "FECHAEFECTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Detalle": { "field": "Detalle", "header": "DETALLE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "NumLiq": { "field": "NumLiq", "header": "NUMLIQ", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "id_cuenta_Origen": { "field": "id_cuenta_Origen", "header": "ID CUENTA ORIGEN", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_cuenta_Destino": { "field": "id_cuenta_Destino", "header": "ID CUENTA DESTINO", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "Importe": { "field": "Importe", "header": "IMPORTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Usuario": { "field": "Usuario", "header": "USUARIO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Validado": { "field": "Validado", "header": "VALIDADO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Liquidado": { "field": "Liquidado", "header": "LIQUIDADO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "LiquidacionesCuentas": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Cuenta": { "field": "Cuenta", "header": "CUENTA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Activo": { "field": "Activo", "header": "ACTIVO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Tipo": { "field": "Tipo", "header": "TIPO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "LiquidacionesMovimientos": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "id_liquidacion": { "field": "id_liquidacion", "header": "ID LIQUIDACION", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_cuenta": { "field": "id_cuenta", "header": "ID CUENTA", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "Importe": { "field": "Importe", "header": "IMPORTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Accion": { "field": "Accion", "header": "ACCION", "type": "select", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "Detalle": { "field": "Detalle", "header": "DETALLE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaRegistro": { "field": "FechaRegistro", "header": "FECHAREGISTRO", "type": "datetime", "hide": false, "virt": false, "key": "FK", "editable": true }
+ },
+ "Mediadores": {
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Descripcion": { "field": "Descripcion", "header": "DESCRIPCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Domicilio": { "field": "Domicilio", "header": "DOMICILIO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPostal": { "field": "CodigoPostal", "header": "CODIGOPOSTAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Localidad": { "field": "Localidad", "header": "LOCALIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Provincia": { "field": "Provincia", "header": "PROVINCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo": { "field": "Correo", "header": "CORREO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Telefono": { "field": "Telefono", "header": "TELEFONO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Whatsapp": { "field": "Whatsapp", "header": "WHATSAPP", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Oportunidades": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "id_cliente": { "field": "id_cliente", "header": "ID CLIENTE", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_estado": { "field": "id_estado", "header": "ID ESTADO", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_usuario": { "field": "id_usuario", "header": "ID USUARIO", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "Empresa": { "field": "Empresa", "header": "EMPRESA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaVencimiento": { "field": "FechaVencimiento", "header": "FECHAVENCIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Riesgo": { "field": "Riesgo", "header": "RIESGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Importe": { "field": "Importe", "header": "IMPORTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Pago": { "field": "Pago", "header": "PAGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaCreacion": { "field": "FechaCreacion", "header": "FECHACREACION", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "UltimaActualizacion": { "field": "UltimaActualizacion", "header": "ULTIMAACTUALIZACION", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "OportunidadesSeguimiento": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "id_oportunidad": { "field": "id_oportunidad", "header": "ID OPORTUNIDAD", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_usuario": { "field": "id_usuario", "header": "ID USUARIO", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_responsable": { "field": "id_responsable", "header": "ID RESPONSABLE", "type": "numeric", "hide": true, "virt": false, "key": false, "editable": false },
+ "id_estado_ant": { "field": "id_estado_ant", "header": "ID ESTADO ANT", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "id_estado_act": { "field": "id_estado_act", "header": "ID ESTADO ACT", "type": "numeric", "hide": true, "virt": false, "key": "FK", "editable": false },
+ "Fecha": { "field": "Fecha", "header": "FECHA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Accion": { "field": "Accion", "header": "ACCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Polizas": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaAlta": { "field": "FechaAlta", "header": "FECHAALTA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaBaja": { "field": "FechaBaja", "header": "FECHABAJA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Riesgo": { "field": "Riesgo", "header": "RIESGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Ramo": { "field": "Ramo", "header": "RAMO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Modalidad": { "field": "Modalidad", "header": "MODALIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "GestorCobroInicial": { "field": "GestorCobroInicial", "header": "GESTORCOBROINICIAL", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "VigorSucursal": { "field": "VigorSucursal", "header": "VIGORSUCURSAL", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "PolizasAccidentes": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoSuplemento": { "field": "CodigoSuplemento", "header": "CODIGOSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "DocumentoAsegurado": { "field": "DocumentoAsegurado", "header": "DOCUMENTOASEGURADO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaNacimientoAsegurado": { "field": "FechaNacimientoAsegurado", "header": "FECHANACIMIENTOASEGURADO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteIncapacidadTemporal": { "field": "ImporteIncapacidadTemporal", "header": "IMPORTEINCAPACIDADTEMPORAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteInvalidezPorAccidente": { "field": "ImporteInvalidezPorAccidente", "header": "IMPORTEINVALIDEZPORACCIDENTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteInvalidezPorInfarto": { "field": "ImporteInvalidezPorInfarto", "header": "IMPORTEINVALIDEZPORINFARTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteMuertePorInfarto": { "field": "ImporteMuertePorInfarto", "header": "IMPORTEMUERTEPORINFARTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Invalidez": { "field": "Invalidez", "header": "INVALIDEZ", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Muerte": { "field": "Muerte", "header": "MUERTE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "NombreAsegurado": { "field": "NombreAsegurado", "header": "NOMBREASEGURADO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "NumeroAsegurados": { "field": "NumeroAsegurados", "header": "NUMEROASEGURADOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "TipoInvalidez": { "field": "TipoInvalidez", "header": "TIPOINVALIDEZ", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "PolizasAutos": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoSuplemento": { "field": "CodigoSuplemento", "header": "CODIGOSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "BonusDP": { "field": "BonusDP", "header": "BONUSDP", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "BonusRC": { "field": "BonusRC", "header": "BONUSRC", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoModelo": { "field": "CodigoModelo", "header": "CODIGOMODELO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoModeloBase7": { "field": "CodigoModeloBase7", "header": "CODIGOMODELOBASE7", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Matricula": { "field": "Matricula", "header": "MATRICULA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Remolque": { "field": "Remolque", "header": "REMOLQUE", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "PolizasAutosConductor": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoSuplemento": { "field": "CodigoSuplemento", "header": "CODIGOSUPLEMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "TipoConductor": { "field": "TipoConductor", "header": "TIPOCONDUCTOR", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "FechaCarnet": { "field": "FechaCarnet", "header": "FECHACARNET", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaNacimiento": { "field": "FechaNacimiento", "header": "FECHANACIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Sexo": { "field": "Sexo", "header": "SEXO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "PolizasDetalle": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoSuplemento": { "field": "CodigoSuplemento", "header": "CODIGOSUPLEMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoRiesgo": { "field": "CodigoRiesgo", "header": "CODIGORIESGO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "DatosBancarios": { "field": "DatosBancarios", "header": "DATOSBANCARIOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Apellidos": { "field": "Apellidos", "header": "APELLIDOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Domicilio": { "field": "Domicilio", "header": "DOMICILIO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Localidad": { "field": "Localidad", "header": "LOCALIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPostal": { "field": "CodigoPostal", "header": "CODIGOPOSTAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Provincia": { "field": "Provincia", "header": "PROVINCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaEfectoSuplemento": { "field": "FechaEfectoSuplemento", "header": "FECHAEFECTOSUPLEMENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaEquipo": { "field": "FechaEquipo", "header": "FECHAEQUIPO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaVencimientoSuplemento": { "field": "FechaVencimientoSuplemento", "header": "FECHAVENCIMIENTOSUPLEMENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FormaPago": { "field": "FormaPago", "header": "FORMAPAGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "GestorCobro": { "field": "GestorCobro", "header": "GESTORCOBRO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteBonificacionAnualSuplemento": { "field": "ImporteBonificacionAnualSuplemento", "header": "IMPORTEBONIFICACIONANUALSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteBonificacionRecibo": { "field": "ImporteBonificacionRecibo", "header": "IMPORTEBONIFICACIONRECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteComisionAnualSuplemento": { "field": "ImporteComisionAnualSuplemento", "header": "IMPORTECOMISIONANUALSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteComisionRecibo": { "field": "ImporteComisionRecibo", "header": "IMPORTECOMISIONRECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteImpuestosAnualSuplemento": { "field": "ImporteImpuestosAnualSuplemento", "header": "IMPORTEIMPUESTOSANUALSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteImpuestosRecibo": { "field": "ImporteImpuestosRecibo", "header": "IMPORTEIMPUESTOSRECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteLiquidoAnualSuplemento": { "field": "ImporteLiquidoAnualSuplemento", "header": "IMPORTELIQUIDOANUALSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteLiquidoRecibo": { "field": "ImporteLiquidoRecibo", "header": "IMPORTELIQUIDORECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteNetoAnualSuplemento": { "field": "ImporteNetoAnualSuplemento", "header": "IMPORTENETOANUALSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteNetoRecibo": { "field": "ImporteNetoRecibo", "header": "IMPORTENETORECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteTotalAnualSuplemento": { "field": "ImporteTotalAnualSuplemento", "header": "IMPORTETOTALANUALSUPLEMENTO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteTotalRecibo": { "field": "ImporteTotalRecibo", "header": "IMPORTETOTALRECIBO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteNeto": { "field": "ImporteNeto", "header": "IMPORTENETO", "type": "numeric", "hide": false, "virt": true, "key": false, "editable": false },
+ "TipoInformacion": { "field": "TipoInformacion", "header": "TIPOINFORMACION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "SubCodigoMediador": { "field": "SubCodigoMediador", "header": "SUBCODIGOMEDIADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "PolizasMultirriesgos": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoSuplemento": { "field": "CodigoSuplemento", "header": "CODIGOSUPLEMENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "DocumentoAsegurado": { "field": "DocumentoAsegurado", "header": "DOCUMENTOASEGURADO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteContenido": { "field": "ImporteContenido", "header": "IMPORTECONTENIDO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteContinente": { "field": "ImporteContinente", "header": "IMPORTECONTINENTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteRC": { "field": "ImporteRC", "header": "IMPORTERC", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "NombreAsegurado": { "field": "NombreAsegurado", "header": "NOMBREASEGURADO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "SituacionRiesgo": { "field": "SituacionRiesgo", "header": "SITUACIONRIESGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Recibos": {
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoEmpresa": { "field": "CodigoEmpresa", "header": "CODIGOEMPRESA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaEfecto": { "field": "FechaEfecto", "header": "FECHAEFECTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Importe": { "field": "Importe", "header": "IMPORTE", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "RecibosEstados": {
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Situacion": { "field": "Situacion", "header": "SITUACION", "type": "date", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Estado": { "field": "Estado", "header": "ESTADO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "NombreTomador": { "field": "NombreTomador", "header": "NOMBRETOMADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "AutoLiquidacion": { "field": "AutoLiquidacion", "header": "AUTOLIQUIDACION", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoGestor": { "field": "CodigoGestor", "header": "CODIGOGESTOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoRiesgo": { "field": "CodigoRiesgo", "header": "CODIGORIESGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "DescripcionGestor": { "field": "DescripcionGestor", "header": "DESCRIPCIONGESTOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Divisa": { "field": "Divisa", "header": "DIVISA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaVencimiento": { "field": "FechaVencimiento", "header": "FECHAVENCIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FormaPago": { "field": "FormaPago", "header": "FORMAPAGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteBonificacion": { "field": "ImporteBonificacion", "header": "IMPORTEBONIFICACION", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteComision": { "field": "ImporteComision", "header": "IMPORTECOMISION", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteIRPF": { "field": "ImporteIRPF", "header": "IMPORTEIRPF", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteImpuestos": { "field": "ImporteImpuestos", "header": "IMPORTEIMPUESTOS", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "ImporteNeto": { "field": "ImporteNeto", "header": "IMPORTENETO", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "SubCodigoMediador": { "field": "SubCodigoMediador", "header": "SUBCODIGOMEDIADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Siniestros": {
+ "CodigoSiniestro": { "field": "CodigoSiniestro", "header": "CODIGOSINIESTRO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "CodigoMediador": { "field": "CodigoMediador", "header": "CODIGOMEDIADOR", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoEmpresa": { "field": "CodigoEmpresa", "header": "CODIGOEMPRESA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoSuplemento": { "field": "CodigoSuplemento", "header": "CODIGOSUPLEMENTO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Descripcion": { "field": "Descripcion", "header": "DESCRIPCION", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaOcurrencia": { "field": "FechaOcurrencia", "header": "FECHAOCURRENCIA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaDenuncia": { "field": "FechaDenuncia", "header": "FECHADENUNCIA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaTerminacion": { "field": "FechaTerminacion", "header": "FECHATERMINACION", "type": "date", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "SiniestrosEstados": {
+ "CodigoSiniestro": { "field": "CodigoSiniestro", "header": "CODIGOSINIESTRO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Estado": { "field": "Estado", "header": "ESTADO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "TipoMovimiento": { "field": "TipoMovimiento", "header": "TIPOMOVIMIENTO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "ClaveConsecuencia": { "field": "ClaveConsecuencia", "header": "CLAVECONSECUENCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoActividad": { "field": "CodigoActividad", "header": "CODIGOACTIVIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPostal": { "field": "CodigoPostal", "header": "CODIGOPOSTAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoSucursalTramitadora": { "field": "CodigoSucursalTramitadora", "header": "CODIGOSUCURSALTRAMITADORA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "DescripcionClaveConsecuencia": { "field": "DescripcionClaveConsecuencia", "header": "DESCRIPCIONCLAVECONSECUENCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaInformacion": { "field": "FechaInformacion", "header": "FECHAINFORMACION", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaReapertura": { "field": "FechaReapertura", "header": "FECHAREAPERTURA", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaTerminacion": { "field": "FechaTerminacion", "header": "FECHATERMINACION", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Hora": { "field": "Hora", "header": "HORA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Localidad": { "field": "Localidad", "header": "LOCALIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Lugar": { "field": "Lugar", "header": "LUGAR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "NombreTomador": { "field": "NombreTomador", "header": "NOMBRETOMADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Observaciones": { "field": "Observaciones", "header": "OBSERVACIONES", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Situacion": { "field": "Situacion", "header": "SITUACION", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "SubCodigoMediador": { "field": "SubCodigoMediador", "header": "SUBCODIGOMEDIADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Tramitador": { "field": "Tramitador", "header": "TRAMITADOR", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Usuario": { "field": "Usuario", "header": "USUARIO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "SiniestrosTramites": {
+ "CodigoSiniestro": { "field": "CodigoSiniestro", "header": "CODIGOSINIESTRO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "FechaInicio": { "field": "FechaInicio", "header": "FECHAINICIO", "type": "datetime", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Observaciones": { "field": "Observaciones", "header": "OBSERVACIONES", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoExpediente": { "field": "CodigoExpediente", "header": "CODIGOEXPEDIENTE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "TipoTramite": { "field": "TipoTramite", "header": "TIPOTRAMITE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "Usuarios": {
+ "Usuario": { "field": "Usuario", "header": "USUARIO", "type": "text", "hide": false, "virt": false, "key": "PK", "editable": false },
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo": { "field": "Correo", "header": "CORREO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Activo": { "field": "Activo", "header": "ACTIVO", "type": "boolean", "hide": false, "virt": false, "key": false, "editable": true },
+ "Suplente": { "field": "Suplente", "header": "SUPLENTE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaInicio": { "field": "FechaInicio", "header": "FECHAINICIO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaFinal": { "field": "FechaFinal", "header": "FECHAFINAL", "type": "date", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "UsuariosFichajes": {
+ "id": { "field": "id", "header": "ID", "type": "numeric", "hide": true, "virt": false, "key": "PK", "editable": false },
+ "Usuario": { "field": "Usuario", "header": "USUARIO", "type": "text", "hide": false, "virt": false, "key": "FK", "editable": true },
+ "Fecha": { "field": "Fecha", "header": "FECHA", "type": "datetime", "hide": false, "virt": false, "key": false, "editable": true },
+ "Tipo": { "field": "Tipo", "header": "TIPO", "type": "select", "hide": false, "virt": false, "key": false, "editable": true },
+ "Duracion": { "field": "Duracion", "header": "DURACION", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "vClientes": {
+ "Nombre": { "field": "Nombre", "header": "NOMBRE", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Apellidos": { "field": "Apellidos", "header": "APELLIDOS", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Domicilio": { "field": "Domicilio", "header": "DOMICILIO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Localidad": { "field": "Localidad", "header": "LOCALIDAD", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPostal": { "field": "CodigoPostal", "header": "CODIGOPOSTAL", "type": "numeric", "hide": false, "virt": false, "key": false, "editable": true },
+ "Provincia": { "field": "Provincia", "header": "PROVINCIA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaCarnet": { "field": "FechaCarnet", "header": "FECHACARNET", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaNacimiento": { "field": "FechaNacimiento", "header": "FECHANACIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "vClientesCorreos": {
+ "Documento": { "field": "Documento", "header": "DOCUMENTO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "Correo2": { "field": "Correo2", "header": "CORREO2", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ },
+ "vRecibos": {
+ "CodigoRecibo": { "field": "CodigoRecibo", "header": "CODIGORECIBO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoEmpresa": { "field": "CodigoEmpresa", "header": "CODIGOEMPRESA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "CodigoPoliza": { "field": "CodigoPoliza", "header": "CODIGOPOLIZA", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaEfecto": { "field": "FechaEfecto", "header": "FECHAEFECTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Situacion": { "field": "Situacion", "header": "SITUACION", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "Estado": { "field": "Estado", "header": "ESTADO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true },
+ "FechaVencimiento": { "field": "FechaVencimiento", "header": "FECHAVENCIMIENTO", "type": "date", "hide": false, "virt": false, "key": false, "editable": true },
+ "FormaPago": { "field": "FormaPago", "header": "FORMAPAGO", "type": "text", "hide": false, "virt": false, "key": false, "editable": true }
+ }
+}
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..68c4d0f
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "dare-server",
+ "version": "2.0.0",
+ "type": "module",
+ "scripts": {
+ "kill": "pkill -9 bun && pkill -9 node || true",
+ "del": "rm node_modules/.vite/deps -r",
+ "gen:schema": "bun run scripts/generate-schema.js",
+ "dev": "bun gen:schema && bun --watch index.js",
+ "build": "bun build index.js --target bun --outdir ./dist"
+ },
+ "dependencies": {
+ "hono": "^4.12.7",
+ "mariadb": "^3.5.2",
+ "nodemailer": "^8.0.2",
+ "sharp": "^0.34.5"
+ }
+}
\ No newline at end of file
diff --git a/server/scripts/generate-schema.js b/server/scripts/generate-schema.js
new file mode 100644
index 0000000..df8b12e
--- /dev/null
+++ b/server/scripts/generate-schema.js
@@ -0,0 +1,74 @@
+import { pool } from '../services/db.service';
+import { resolve } from 'node:path';
+
+/**
+ * Genera el esquema de la base de datos automáticamente
+ */
+async function generateSchema() {
+ try {
+ const rows = await pool.query(`
+ SELECT
+ TABLE_NAME as t,
+ COLUMN_NAME as f,
+ DATA_TYPE as T,
+ COLUMN_TYPE as fT,
+ EXTRA as e,
+ COLUMN_KEY as k
+ FROM information_schema.columns
+ WHERE table_schema = DATABASE()
+ ORDER BY TABLE_NAME, ORDINAL_POSITION
+ `);
+
+ const data = rows.reduce((acc, { t, f, T, fT, e, k }) => {
+ if (!acc[t]) acc[t] = {};
+
+ // Mapeo de tipos
+ let type = 'text';
+
+ if (['int', 'decimal', 'float', 'double'].includes(T)) {
+ type = 'numeric';
+ } else if (['date', 'datetime', 'timestamp'].includes(T)) {
+ type = T === 'date' ? 'date' : 'datetime';
+ } else if (T === 'tinyint' && fT.includes('(1)')) {
+ type = 'boolean';
+ } else if (T === 'enum') {
+ type = 'select';
+ }
+
+ const hide = f.startsWith('id') || f === 'password' || f === 'token';
+ const virt = e.toLowerCase().includes('generated');
+ const key = k === 'PRI' ? 'PK' : k === 'UNI' ? 'UK' : k === 'MUL' ? 'FK' : false;
+
+ acc[t][f] = {
+ field: f,
+ header: f.replace(/_/g, ' ').toUpperCase(),
+ type,
+ hide,
+ virt,
+ key,
+ editable: !(hide || key === 'PK' || virt)
+ };
+
+ return acc;
+ }, {});
+
+ const outPath = resolve('./server/lib/schema.json');
+
+ // JSON compacto para ahorrar bytes pero manteniendo legibilidad por tabla
+ const json = JSON.stringify(data, null, 2)
+ .replace(/\{\n\s+"field":[\s\S]+?\n\s+\}/g, m => m.replace(/\s*\n\s*/g, ' '));
+
+ await Bun.write(outPath, json);
+
+ console.log(`✅ Esquema generado: ${outPath}`);
+ console.log(`📊 Tablas: ${Object.keys(data).length}`);
+
+ } catch (error) {
+ console.error('❌ Error:', error);
+ } finally {
+ await pool.end();
+ process.exit(0);
+ }
+}
+
+generateSchema();
\ No newline at end of file
diff --git a/server/services/db.service.js b/server/services/db.service.js
new file mode 100644
index 0000000..227cb09
--- /dev/null
+++ b/server/services/db.service.js
@@ -0,0 +1,135 @@
+// import mariadb from 'mariadb';
+import * as mariadb from 'mariadb';
+
+// Fix para BigInt en la serialización JSON (necesario para IDs grandes en MariaDB)
+BigInt.prototype.toJSON = function () {
+ return this.toString();
+};
+
+// Configuración del Pool
+export const pool = mariadb.createPool({
+ host: process.env.DB_HOST,
+ user: process.env.DB_USER,
+ password: process.env.DB_PASSWORD,
+ database: 'Reale',
+ connectionLimit: 20,
+ timezone: 'Z',
+ insertIdAsNumber: true
+});
+
+// Helper para escapar identificadores (tablas/columnas)
+const b = (id) => `\`${id.replace(/`/g, '``')}\``;
+
+export class Database {
+ constructor(connector) {
+ /** @type {mariadb.Pool | mariadb.PoolConnection} */
+ this.connector = connector;
+ }
+
+ async insert(table, data) {
+ const rows = Array.isArray(data) ? data : [data];
+ if (!rows.length) return null;
+ const fields = Object.keys(rows[0]);
+ const query = `INSERT INTO ${b(table)} (${fields.map(b).join(',')}) VALUES (${fields.map(() => '?').join(',')})`;
+ return this.connector.batch(query, rows.map(r => fields.map(f => r[f])));
+ }
+
+ async update(table, data, where) {
+ const fields = Object.keys(data);
+ const wFields = Object.keys(where);
+ const query = `UPDATE ${b(table)} SET ${fields.map(f => `${b(f)}=?`).join(',')} WHERE ${wFields.map(f => `${b(f)}=?`).join(' AND ')}`;
+ return this.connector.query(query, [...fields.map(f => data[f]), ...wFields.map(f => where[f])]);
+ }
+
+ async upsert(table, data, ignore = false) {
+ const rows = Array.isArray(data) ? data : [data];
+ if (!rows.length) return null;
+ const fields = Object.keys(rows[0]);
+ const set = fields.filter(f => f !== 'id').map(f => `${b(f)}=VALUES(${b(f)})`).join(',');
+ const query = `INSERT ${ignore ? 'IGNORE' : ''} INTO ${b(table)} (${fields.map(b).join(',')}) VALUES (${fields.map(() => '?').join(',')}) ON DUPLICATE KEY UPDATE ${set}`;
+ return this.connector.batch(query, rows.map(r => fields.map(f => r[f])));
+ }
+
+ async delete(table, where) {
+ const keys = Object.keys(where);
+ if (!keys.length) throw new Error('Delete requiere condiciones');
+ return this.connector.query(`DELETE FROM ${b(table)} WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`, keys.map(k => where[k]));
+ }
+
+ async select(table, where = {}, opts = { limit: 5000, order: null }) {
+ const keys = Object.keys(where);
+ let sqlStr = `SELECT * FROM ${b(table)}`;
+ if (keys.length) sqlStr += ` WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`;
+ if (opts.order) sqlStr += ` ORDER BY ${b(opts.order)}`;
+ return this.connector.query(sqlStr + ` LIMIT ${opts.limit}`, keys.map(k => where[k]));
+ }
+
+ async one(table, where) {
+ const res = await this.select(table, where, { limit: 1 });
+ return res[0] || null;
+ }
+
+ async count(table, where = {}) {
+ const keys = Object.keys(where);
+ let sqlStr = `SELECT COUNT(*) as total FROM ${b(table)}`;
+ if (keys.length) sqlStr += ` WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`;
+ const res = await this.connector.query(sqlStr, keys.map(k => where[k]));
+ return Number(res[0].total);
+ }
+
+ async search(table, fields, term, limit = 1000) {
+ if (!fields.length || !term) return [];
+ const where = fields.map(f => `${b(f)} LIKE ?`).join(' OR ');
+ return this.connector.query(`SELECT * FROM ${b(table)} WHERE ${where} LIMIT ${limit}`, fields.map(() => `%${term}%`));
+ }
+
+ async unique(table, fields, where = {}) {
+ const f = Array.isArray(fields) ? fields.map(b).join(',') : b(fields);
+ const keys = Object.keys(where);
+ let sqlStr = `SELECT DISTINCT ${f} FROM ${b(table)}`;
+ if (keys.length) sqlStr += ` WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`;
+ const sort = Array.isArray(fields) ? b(fields[0]) : f;
+ return this.connector.query(sqlStr + ` ORDER BY ${sort} ASC`, keys.map(k => where[k]));
+ }
+
+ async raw(query, data = {}) {
+ const params = [];
+ const sqlStr = query.replace(/:(\w+)/g, (_, key) => {
+ params.push(data[key]);
+ return '?';
+ });
+ return this.connector.query(sqlStr, params);
+ }
+}
+
+/**
+ * Lógica de Transacciones
+ */
+export const trx = async (opts, actions) => {
+ const tasks = Array.isArray(opts) ? opts : [opts];
+ const conn = await pool.getConnection();
+ try {
+ await conn.beginTransaction();
+ const transactionDb = new Database(conn);
+ const results = {};
+
+ for (const item of tasks) {
+ if (typeof actions[item.action] !== 'function') {
+ throw new Error(`Acción ${item.action} no existe`);
+ }
+ results[item.action] = await actions[item.action](transactionDb, item);
+ }
+
+ await conn.commit();
+ return results;
+ } catch (err) {
+ console.error('Error en la transacción:', err);
+ await conn.rollback();
+ throw err;
+ } finally {
+ if (conn) conn.release();
+ }
+};
+
+// Exportamos la instancia principal vinculada al Pool
+export const db = new Database(pool);
\ No newline at end of file
diff --git a/server/services/mail.service.js b/server/services/mail.service.js
new file mode 100644
index 0000000..de6eb8c
--- /dev/null
+++ b/server/services/mail.service.js
@@ -0,0 +1,47 @@
+import nodemailer from 'nodemailer';
+
+// Configuración del transportador
+const transporter = nodemailer.createTransport({
+ host: process.env.db, // Nota: Asegúrate de que esta variable sea el host SMTP (ej. smtp.tuproveedor.com)
+ port: 587,
+ secure: false, // true para 465, false para otros puertos
+ auth: {
+ user: "natxocc",
+ pass: "Ncc629406731.",
+ },
+});
+
+/**
+ * Envía un correo electrónico
+ * @param {string} to - Destinatario
+ * @param {string} subject - Asunto
+ * @param {string} html - Cuerpo en HTML
+ * @param {Object|null} file - Opcional: { filename, content, contentType }
+ */
+export const send = async (to, subject, html, file = null) => {
+
+ const mailOptions = {
+ from: '"Natxocc" ',
+ to,
+ subject,
+ html,
+ };
+
+ // Si pasas un archivo (ej: buffer de Sharp), lo añadimos como attachment
+ if (file) {
+ mailOptions.attachments = [
+ {
+ filename: file.filename,
+ content: file.content,
+ contentType: file.contentType || 'image/jpeg'
+ }
+ ];
+ }
+
+ try {
+ await transporter.sendMail(mailOptions);
+ } catch (error) {
+ console.error('Error enviando email:', error);
+ throw error; // Re-lanzamos para que la ruta de Hono capture el error
+ }
+};
\ No newline at end of file
diff --git a/server/services/soap.service.js b/server/services/soap.service.js
new file mode 100644
index 0000000..d148be6
--- /dev/null
+++ b/server/services/soap.service.js
@@ -0,0 +1,112 @@
+import * as soap from 'soap';
+import { HttpClient } from 'soap';
+import { trx } from './db.service.js';
+import { actions } from '../actions/soap.actions.js';
+
+// --- Helpers de Fecha ---
+const fmt = (d) => (!isNaN(d.getTime()) ? d.toLocaleDateString('sv-SE') : null);
+
+const getFirstMonth = (m = 0) => {
+ const now = new Date();
+ return fmt(new Date(now.getFullYear(), now.getMonth() + m, 1));
+};
+
+const getDate = (d = 0) => {
+ const t = new Date();
+ t.setDate(t.getDate() + d);
+ return fmt(t);
+};
+
+const SOAP_SERVICES = {
+ DescargaPolizas: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/DescargaPolizas.svc?wsdl",
+ LiquidacionMediador: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/LiquidacionMediador.svc?wsdl",
+ DescargaSiniestros: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/DescargaSiniestros.svc?wsdl",
+ DescargaCompletaRecibos: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/DescargaCompletaRecibos.svc?wsdl",
+ DescargaCartera: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/DescargaCartera.svc?wsdl",
+ ConsultaPolizas: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/ConsultaPolizas.svc?wsdl",
+ ConsultaRecibos: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/ConsultaRecibos.svc?wsdl",
+ ConsultaAgendaTramitacion: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/ConsultaAgendaTramitacion.svc?wsdl",
+ TramitacionSiniestro: "https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/TramitacionSiniestro.svc?wsdl",
+};
+
+export const soapCall = async (params, query, save = true) => {
+ const { CodigoMediador, service, method } = params;
+ const url = SOAP_SERVICES[service];
+
+ if (!url) throw new Error(`Servicio SOAP '${service}' no encontrado`);
+
+ // --- Identificador desde ENV o Query ---
+ const Identificador = query.Identificador || process.env[`SOAP_${CodigoMediador}`];
+ if (!Identificador) throw new Error(`Identificador no configurado para mediador ${CodigoMediador}`);
+
+ // --- Normalización de Argumentos ---
+ const Mediador = query.Mediador || CodigoMediador;
+ const CodigoPoliza = query.CodigoPoliza || "0";
+ const CodigoSiniestro = query.CodigoSiniestro || "0";
+ const CodigoExpediente = query.CodigoExpediente || "0";
+ const Observaciones = query.Observaciones || "";
+ const CodigoRecibo = query.CodigoRecibo || "0";
+ const CodigoSuplemento = query.CodigoSuplemento || "1000";
+ const Clave = query.Clave || "10000";
+ const Empresa = query.Empresa || "4";
+ const CodigoEmpresa = query.CodigoEmpresa || Empresa;
+ const Plataforma = query.Plataforma || "10000";
+ const CodigoRamo = query.CodigoRamo || "0";
+ const FechaFinal = query.FechaFinal || getDate();
+ const FechaRenovacion = query.FechaRenovacion || getFirstMonth(-1);
+ const FechaInicial = query.FechaInicial || getFirstMonth(-1);
+ const FechaLiquidacion = query.FechaLiquidacion || getDate();
+
+ const HEADER = `${Plataforma}${Identificador}`;
+
+ // --- Diccionario de Configuración ---
+ const SOAP_CONFIG = {
+ DescargaPolizas: { Descargar: { args: { Clave, CodigoRamo, Empresa, FechaFinal, FechaInicial, Identificador, Plataforma, TipoSuplemento1: "NP", TipoSuplemento2: "AN", TipoSuplemento3: "RE", TipoSuplemento4: "SP" }, header: null } },
+ LiquidacionMediador: {
+ Consulta: { args: { CodigoMediador, Empresa, FechaLiquidacion }, header: HEADER },
+ ObtenerFechasLiquidacion: { args: { CodigoMediador, Empresa }, header: HEADER }
+ },
+ DescargaSiniestros: { Descargar: { args: { AceptarCicos: true, AceptarSdm: true, Clave, CodigoRamo, Empresa, FechaFinal, FechaInicial, Identificador, IncluirAperturas: true, IncluirAperturasCicos: true, IncluirAperturasSdm: true, IncluirPagosImdemnizacion: true, IncluirTerminados: true, Plataforma }, header: HEADER } },
+ DescargaCompletaRecibos: { DescargarNew: { args: { Clave, CodigoRamo, Empresa, FechaFinal, FechaInicial, Identificador, Plataforma, IncluirAnulados: true, IncluirCobrados: true, IncluirDevueltos: true, IncluirNuevos: true }, header: null } },
+ DescargaCartera: {
+ DescargarCartera: { args: { CodigoMediador, Empresa, FechaRenovacion, Identificador, Plataforma }, header: null },
+ ObtenerListaRamos: { args: { Empresa }, header: null },
+ ObtenerListaRenovaciones: { args: { Empresa, Mediador }, header: null }
+ },
+ ConsultaPolizas: {
+ ConsultarPoliza: { args: { CodigoPoliza, CodigoRamo, CodigoSuplemento, Empresa, Identificador, Plataforma }, header: null },
+ ObtenerListaPolizasMediador: { args: { CodigoMediador, Empresa, Identificador }, header: null },
+ ObtenerListaSuplementosPoliza: { args: { CodigoPoliza, CodigoRamo, Empresa, Identificador }, header: null }
+ },
+ ConsultaRecibos: {
+ ConsultarRecibo: { args: { CodigoPoliza, CodigoRamo, CodigoRecibo, CodigoSuplemento, Empresa, Identificador, Plataforma }, header: null },
+ ObtenerListaRecibosPoliza: { args: { CodigoPoliza, CodigoRamo, Empresa, Identificador }, header: null }
+ },
+ ConsultaAgendaTramitacion: { ConsultaTramitacion: { args: { Datos: { CodigoExpediente, CodigoPoliza, CodigoSiniestro, Empresa } }, header: HEADER } },
+ TramitacionSiniestro: { InsertarTramite: { args: { CodigoEmpresa, CodigoExpediente, CodigoSiniestro, Identificador, Observaciones, Plataforma }, header: null } }
+ };
+
+ const config = SOAP_CONFIG[service]?.[method];
+ if (!config) throw new Error(`Método '${method}' no configurado para el servicio '${service}'`);
+
+ try {
+ const client = await soap.createClientAsync(url, {
+ httpClient: new HttpClient(),
+ request: { timeout: 120000 }
+ });
+
+ if (config.header) client.addSoapHeader(config.header);
+
+ const methodName = `${method}Async`;
+ const [result] = await client[methodName](config.args);
+
+ if (save) {
+ await trx({ action: service, data: result }, actions);
+ }
+
+ return result;
+ } catch (error) {
+ console.error(`[SOAP ERROR] ${service}/${method}:`, error.message);
+ throw error;
+ }
+};
\ No newline at end of file
diff --git a/server/services/soap2.service.js b/server/services/soap2.service.js
new file mode 100644
index 0000000..6a1c160
--- /dev/null
+++ b/server/services/soap2.service.js
@@ -0,0 +1,151 @@
+import { XMLParser } from "fast-xml-parser";
+import { trx } from "./db.service.js";
+import { actions } from "../actions/soap.actions.js";
+
+const parser = new XMLParser({
+ removeNSPrefix: true,
+ ignoreAttributes: false,
+ parseTagValue: true,
+ trimValues: true,
+});
+
+const ALIAS_MAP = {
+ Polizas: { service: "DescargaPolizas", method: "Descargar" },
+ Recibos: { service: "DescargaCompletaRecibos", method: "Descargar" },
+ Siniestros: { service: "DescargaSiniestros", method: "Descargar" },
+ Cartera: { service: "DescargaCartera", method: "DescargarCartera" },
+ TramiteSiniestro: { service: "ConsultaAgendaTramitacion", method: "ConsultaTramitacion" },
+ Poliza: { service: "ConsultaPolizas", method: "ConsultarPoliza" },
+ Recibo: { service: "ConsultaRecibos", method: "ConsultarRecibo" },
+ Siniestro: { service: "ConsultaSiniestros", method: "ConsultarSiniestro" },
+};
+
+const cleanNil = (obj) => {
+ if (obj !== null && typeof obj === "object") {
+ if (obj["@_nil"] === "true" || obj["@_nil"] === true) return null;
+ if (Array.isArray(obj)) return obj.map(cleanNil);
+ const newObj = {};
+ for (const [key, value] of Object.entries(obj)) {
+ newObj[key] = cleanNil(value);
+ }
+ return newObj;
+ }
+ return obj;
+};
+
+const fmt = (d) => (!isNaN(d.getTime()) ? d.toLocaleDateString("sv-SE") : null);
+const getFirstMonth = (m = 0) => {
+ const now = new Date();
+ return fmt(new Date(now.getFullYear(), now.getMonth() + m, 1));
+};
+const getDate = (d = 0) => {
+ const t = new Date();
+ t.setDate(t.getDate() + d);
+ return fmt(t);
+};
+
+const getXmlBody = (service, method, q) => {
+ const templates = {
+ DescargaPolizas: `${q.Clave}${q.CodigoRamo}${q.Empresa}${q.FechaFinal}${q.FechaInicial}${q.Identificador}${q.Plataforma}NPANRESP`,
+ DescargaSiniestros: `truetrue${q.Clave}${q.CodigoRamo}${q.Empresa}${q.FechaFinal}${q.FechaInicial}${q.Identificador}true${q.Plataforma}`,
+ DescargaCompletaRecibos: `${q.Clave}${q.Empresa}${q.FechaFinal}${q.FechaInicial}${q.Identificador}truetruetruetrue`,
+ DescargaCartera: `${q.CodigoMediador}${q.Empresa}${q.FechaRenovacion}${q.Identificador}${q.Plataforma}`,
+ ConsultaAgendaTramitacion: `${q.CodigoExpediente}${q.CodigoPoliza}${q.CodigoSiniestro}${q.Empresa}`,
+ ConsultaPolizas: `${q.CodigoPoliza}${q.CodigoRamo}${q.CodigoSuplemento || 0}${q.Empresa}${q.Identificador}${q.Plataforma}`,
+ ConsultaRecibos: `${q.CodigoPoliza}${q.CodigoRamo}${q.CodigoRecibo || 0}${q.CodigoSuplemento || 0}${q.Empresa}${q.Identificador}${q.Plataforma}`,
+ ConsultaSiniestros: `${q.CodigoPoliza}${q.CodigoRamo}${q.CodigoSiniestro}${q.CodigoSuplemento || 0}${q.Empresa}${q.Identificador}${q.Plataforma}`,
+ };
+ return templates[service] || `${q.Identificador}`;
+};
+
+export const soapCall = async (params, query, save = true) => {
+ const { CodigoMediador, alias } = params;
+ const target = ALIAS_MAP[alias] || { service: params.service, method: params.method };
+ const { service, method } = target;
+
+ if (!service) throw new Error(`Servicio o Alias '${alias || params.service}' no definido`);
+
+ const url = `https://lba.realeonline.net/Reale.B2b.Services.Multitarificadores.IisHost/${service}.svc`;
+ const Identificador = query.Identificador || process.env[`SOAP_${CodigoMediador}`];
+ if (!Identificador) throw new Error(`Identificador no configurado para ${CodigoMediador}`);
+
+ const q = {
+ ...query,
+ Identificador,
+ CodigoMediador,
+ Clave: query.Clave || "10000",
+ Empresa: query.Empresa || "4",
+ Plataforma: query.Plataforma || "10000",
+ CodigoRamo: query.CodigoRamo || "0",
+ CodigoSuplemento: query.CodigoSuplemento || "0",
+ CodigoPoliza: query.CodigoPoliza || "0",
+ CodigoSiniestro: query.CodigoSiniestro || "0",
+ CodigoExpediente: query.CodigoExpediente || "0",
+ FechaFinal: query.FechaFinal || getDate(),
+ FechaInicial: query.FechaInicial || (alias === "Cartera" ? getFirstMonth(0) : getFirstMonth(-1)),
+ FechaRenovacion: query.FechaRenovacion || getFirstMonth(1),
+ };
+
+ let nsBase = "http://reale.net/B2b/Multitarificadores";
+ let nsName = service;
+ let contract = `I${service}`;
+ let actMethod = method;
+ let soapHeader = "";
+
+ if (service === "DescargaCartera") {
+ nsName = "DescargaFicheros";
+ contract = "IDescargaCartera";
+ }
+ else if (service === "ConsultaPolizas") {
+ nsName = "ConsultaPolizas";
+ contract = "IConsultaPolizas";
+ }
+ else if (service === "DescargaCompletaRecibos" && method === "Descargar") {
+ actMethod = "DescargarNew";
+ }
+ else if (service === "ConsultaAgendaTramitacion") {
+ nsBase = "http://reale.net/wcf/internalServices";
+ nsName = "";
+ contract = "IAgendaTramitacion";
+ actMethod = "ConsultaTramitacion";
+ soapHeader = `${q.Plataforma}${q.Identificador}`;
+ }
+
+ const fullNs = nsName ? `${nsBase}/${nsName}` : nsBase;
+ const soapAction = `"${fullNs}/${contract}/${actMethod}"`.replace(/([^:])\/\//g, "$1/");
+
+ const soapEnvelope = `
+
+ ${soapHeader}
+ ${getXmlBody(service, method, q)}
+ `.trim();
+
+ try {
+ const response = await fetch(url, {
+ method: "POST",
+ headers: { "Content-Type": "text/xml;charset=UTF-8", "SOAPAction": soapAction },
+ body: soapEnvelope,
+ });
+
+ const xmlData = await response.text();
+ const rawJson = parser.parse(xmlData);
+
+ if (!response.ok) {
+ const fault = rawJson.Envelope?.Body?.Fault?.faultstring;
+ throw new Error(typeof fault === "object" ? JSON.stringify(fault) : fault || `Error ${response.status}`);
+ }
+
+ const resultBody = rawJson.Envelope?.Body;
+ const responseName = Object.keys(resultBody || {})[0];
+ const result = cleanNil(resultBody?.[responseName] || {});
+
+ // CUIDADO DA ERROR CUANDO USAS /Poliza /Siniestro /Recibo porque no hay un action para ellos
+ if (save) await trx({ action: service, data: result }, actions);
+ return result;
+ } catch (error) {
+ console.error(`[SOAP ERROR] ${service}/${method}:`, error.message);
+ throw error;
+ }
+};
\ No newline at end of file