Showing
24 changed files
with
492 additions
and
109 deletions
| ... | @@ -8,9 +8,11 @@ | ... | @@ -8,9 +8,11 @@ |
| 8 | "name": "ppto", | 8 | "name": "ppto", |
| 9 | "version": "0.0.1", | 9 | "version": "0.0.1", |
| 10 | "dependencies": { | 10 | "dependencies": { |
| 11 | + "@observablehq/plot": "^0.6.17", | ||
| 11 | "@supabase/supabase-js": "^2.98.0", | 12 | "@supabase/supabase-js": "^2.98.0", |
| 12 | "d3": "^7.9.0", | 13 | "d3": "^7.9.0", |
| 13 | "fuse.js": "^7.1.0", | 14 | "fuse.js": "^7.1.0", |
| 15 | + "hyparquet": "^1.25.1", | ||
| 14 | "marked": "^17.0.4" | 16 | "marked": "^17.0.4" |
| 15 | }, | 17 | }, |
| 16 | "devDependencies": { | 18 | "devDependencies": { |
| ... | @@ -515,6 +517,20 @@ | ... | @@ -515,6 +517,20 @@ |
| 515 | "@jridgewell/sourcemap-codec": "^1.4.14" | 517 | "@jridgewell/sourcemap-codec": "^1.4.14" |
| 516 | } | 518 | } |
| 517 | }, | 519 | }, |
| 520 | + "node_modules/@observablehq/plot": { | ||
| 521 | + "version": "0.6.17", | ||
| 522 | + "resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.6.17.tgz", | ||
| 523 | + "integrity": "sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==", | ||
| 524 | + "license": "ISC", | ||
| 525 | + "dependencies": { | ||
| 526 | + "d3": "^7.9.0", | ||
| 527 | + "interval-tree-1d": "^1.0.0", | ||
| 528 | + "isoformat": "^0.2.0" | ||
| 529 | + }, | ||
| 530 | + "engines": { | ||
| 531 | + "node": ">=12" | ||
| 532 | + } | ||
| 533 | + }, | ||
| 518 | "node_modules/@polka/url": { | 534 | "node_modules/@polka/url": { |
| 519 | "version": "1.0.0-next.29", | 535 | "version": "1.0.0-next.29", |
| 520 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", | 536 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", |
| ... | @@ -1410,6 +1426,12 @@ | ... | @@ -1410,6 +1426,12 @@ |
| 1410 | "node": ">= 0.4" | 1426 | "node": ">= 0.4" |
| 1411 | } | 1427 | } |
| 1412 | }, | 1428 | }, |
| 1429 | + "node_modules/binary-search-bounds": { | ||
| 1430 | + "version": "2.0.5", | ||
| 1431 | + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz", | ||
| 1432 | + "integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==", | ||
| 1433 | + "license": "MIT" | ||
| 1434 | + }, | ||
| 1413 | "node_modules/clsx": { | 1435 | "node_modules/clsx": { |
| 1414 | "version": "2.1.1", | 1436 | "version": "2.1.1", |
| 1415 | "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", | 1437 | "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", |
| ... | @@ -1998,6 +2020,12 @@ | ... | @@ -1998,6 +2020,12 @@ |
| 1998 | "dev": true, | 2020 | "dev": true, |
| 1999 | "license": "ISC" | 2021 | "license": "ISC" |
| 2000 | }, | 2022 | }, |
| 2023 | + "node_modules/hyparquet": { | ||
| 2024 | + "version": "1.25.1", | ||
| 2025 | + "resolved": "https://registry.npmjs.org/hyparquet/-/hyparquet-1.25.1.tgz", | ||
| 2026 | + "integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==", | ||
| 2027 | + "license": "MIT" | ||
| 2028 | + }, | ||
| 2001 | "node_modules/iceberg-js": { | 2029 | "node_modules/iceberg-js": { |
| 2002 | "version": "0.8.1", | 2030 | "version": "0.8.1", |
| 2003 | "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", | 2031 | "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", |
| ... | @@ -2028,6 +2056,15 @@ | ... | @@ -2028,6 +2056,15 @@ |
| 2028 | "node": ">=12" | 2056 | "node": ">=12" |
| 2029 | } | 2057 | } |
| 2030 | }, | 2058 | }, |
| 2059 | + "node_modules/interval-tree-1d": { | ||
| 2060 | + "version": "1.0.4", | ||
| 2061 | + "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz", | ||
| 2062 | + "integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==", | ||
| 2063 | + "license": "MIT", | ||
| 2064 | + "dependencies": { | ||
| 2065 | + "binary-search-bounds": "^2.0.0" | ||
| 2066 | + } | ||
| 2067 | + }, | ||
| 2031 | "node_modules/is-reference": { | 2068 | "node_modules/is-reference": { |
| 2032 | "version": "3.0.3", | 2069 | "version": "3.0.3", |
| 2033 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", | 2070 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", |
| ... | @@ -2038,6 +2075,12 @@ | ... | @@ -2038,6 +2075,12 @@ |
| 2038 | "@types/estree": "^1.0.6" | 2075 | "@types/estree": "^1.0.6" |
| 2039 | } | 2076 | } |
| 2040 | }, | 2077 | }, |
| 2078 | + "node_modules/isoformat": { | ||
| 2079 | + "version": "0.2.1", | ||
| 2080 | + "resolved": "https://registry.npmjs.org/isoformat/-/isoformat-0.2.1.tgz", | ||
| 2081 | + "integrity": "sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==", | ||
| 2082 | + "license": "ISC" | ||
| 2083 | + }, | ||
| 2041 | "node_modules/jiti": { | 2084 | "node_modules/jiti": { |
| 2042 | "version": "2.6.1", | 2085 | "version": "2.6.1", |
| 2043 | "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", | 2086 | "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", | ... | ... |
| ... | @@ -19,9 +19,11 @@ | ... | @@ -19,9 +19,11 @@ |
| 19 | "vite": "^7.3.1" | 19 | "vite": "^7.3.1" |
| 20 | }, | 20 | }, |
| 21 | "dependencies": { | 21 | "dependencies": { |
| 22 | + "@observablehq/plot": "^0.6.17", | ||
| 22 | "@supabase/supabase-js": "^2.98.0", | 23 | "@supabase/supabase-js": "^2.98.0", |
| 23 | "d3": "^7.9.0", | 24 | "d3": "^7.9.0", |
| 24 | "fuse.js": "^7.1.0", | 25 | "fuse.js": "^7.1.0", |
| 26 | + "hyparquet": "^1.25.1", | ||
| 25 | "marked": "^17.0.4" | 27 | "marked": "^17.0.4" |
| 26 | } | 28 | } |
| 27 | } | 29 | } | ... | ... |
| ... | @@ -32,8 +32,14 @@ | ... | @@ -32,8 +32,14 @@ |
| 32 | }); | 32 | }); |
| 33 | </script> | 33 | </script> |
| 34 | 34 | ||
| 35 | -<!-- MÓVIL: Búsqueda + Theme + hamburguesa arriba a la derecha --> | 35 | +<!-- MÓVIL: Home + Búsqueda + Theme + hamburguesa arriba a la derecha --> |
| 36 | <div class="navbar-mobile"> | 36 | <div class="navbar-mobile"> |
| 37 | + <a href="/" class="nav-home-link" aria-label="Ir al inicio"> | ||
| 38 | + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| 39 | + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> | ||
| 40 | + <polyline points="9 22 9 12 15 12 15 22"/> | ||
| 41 | + </svg> | ||
| 42 | + </a> | ||
| 37 | <button | 43 | <button |
| 38 | onclick={onOpenSearch} | 44 | onclick={onOpenSearch} |
| 39 | class="nav-btn-icon-mobile" | 45 | class="nav-btn-icon-mobile" |
| ... | @@ -70,10 +76,25 @@ | ... | @@ -70,10 +76,25 @@ |
| 70 | </button> | 76 | </button> |
| 71 | </div> | 77 | </div> |
| 72 | 78 | ||
| 73 | -<!-- DESKTOP: Barra completa con Search + Theme + Menu --> | 79 | +<!-- DESKTOP: Barra completa con Home + Search + Theme + Menu --> |
| 74 | <div class="navbar-desktop"> | 80 | <div class="navbar-desktop"> |
| 75 | <div class="navbar-desktop-inner"> | 81 | <div class="navbar-desktop-inner"> |
| 76 | 82 | ||
| 83 | + <!-- BOTÓN HOME --> | ||
| 84 | + <div class="relative group/home"> | ||
| 85 | + <a href="/" class="nav-btn-icon" aria-label="Ir al inicio"> | ||
| 86 | + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | ||
| 87 | + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> | ||
| 88 | + <polyline points="9 22 9 12 15 12 15 22"/> | ||
| 89 | + </svg> | ||
| 90 | + </a> | ||
| 91 | + <!-- Tooltip --> | ||
| 92 | + <div class="absolute right-0 top-full mt-2 px-3 py-1.5 bg-gray-900/90 text-white text-xs rounded-lg whitespace-nowrap opacity-0 invisible group-hover/home:opacity-100 group-hover/home:visible transition-all duration-200 pointer-events-none backdrop-blur-sm"> | ||
| 93 | + Inicio | ||
| 94 | + <div class="absolute right-3 bottom-full mb-[-4px] w-2 h-2 bg-gray-900/90 rotate-45"></div> | ||
| 95 | + </div> | ||
| 96 | + </div> | ||
| 97 | + | ||
| 77 | <!-- BOTÓN BÚSQUEDA GLOBAL --> | 98 | <!-- BOTÓN BÚSQUEDA GLOBAL --> |
| 78 | <div class="relative group/search"> | 99 | <div class="relative group/search"> |
| 79 | <button | 100 | <button |
| ... | @@ -152,6 +173,13 @@ | ... | @@ -152,6 +173,13 @@ |
| 152 | z-index: 110; | 173 | z-index: 110; |
| 153 | align-items: center; | 174 | align-items: center; |
| 154 | gap: 0.25rem; | 175 | gap: 0.25rem; |
| 176 | + padding: 6px; | ||
| 177 | + background: rgba(40, 40, 38, 0.7); | ||
| 178 | + backdrop-filter: blur(20px) saturate(180%); | ||
| 179 | + -webkit-backdrop-filter: blur(20px) saturate(180%); | ||
| 180 | + border: 1px solid rgba(255, 255, 255, 0.1); | ||
| 181 | + border-radius: 14px; | ||
| 182 | + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15); | ||
| 155 | } | 183 | } |
| 156 | 184 | ||
| 157 | .navbar-desktop { | 185 | .navbar-desktop { |
| ... | @@ -160,6 +188,20 @@ | ... | @@ -160,6 +188,20 @@ |
| 160 | top: 1.5rem; | 188 | top: 1.5rem; |
| 161 | right: 1.5rem; | 189 | right: 1.5rem; |
| 162 | z-index: 110; | 190 | z-index: 110; |
| 191 | + padding: 6px; | ||
| 192 | + background: rgba(40, 40, 38, 0.7); | ||
| 193 | + backdrop-filter: blur(20px) saturate(180%); | ||
| 194 | + -webkit-backdrop-filter: blur(20px) saturate(180%); | ||
| 195 | + border: 1px solid rgba(255, 255, 255, 0.1); | ||
| 196 | + border-radius: 14px; | ||
| 197 | + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + :global(html:not(.dark)) .navbar-mobile, | ||
| 201 | + :global(html:not(.dark)) .navbar-desktop { | ||
| 202 | + background: rgba(255, 255, 255, 0.7); | ||
| 203 | + border-color: rgba(0, 0, 0, 0.08); | ||
| 204 | + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08); | ||
| 163 | } | 205 | } |
| 164 | 206 | ||
| 165 | .navbar-desktop-inner { | 207 | .navbar-desktop-inner { |
| ... | @@ -201,16 +243,16 @@ | ... | @@ -201,16 +243,16 @@ |
| 201 | align-items: center; | 243 | align-items: center; |
| 202 | gap: 10px; | 244 | gap: 10px; |
| 203 | padding: 7px 12px 7px 12px; | 245 | padding: 7px 12px 7px 12px; |
| 204 | - background: rgba(255, 255, 255, 0.05); | 246 | + background: rgba(255, 255, 255, 0.08); |
| 205 | - border: 1px solid rgba(255, 255, 255, 0.12); | 247 | + border: 1px solid rgba(255, 255, 255, 0.1); |
| 206 | border-radius: 10px; | 248 | border-radius: 10px; |
| 207 | - color: #9B9890; | 249 | + color: #B8B5AD; |
| 208 | cursor: pointer; | 250 | cursor: pointer; |
| 209 | transition: all 0.2s ease; | 251 | transition: all 0.2s ease; |
| 210 | } | 252 | } |
| 211 | 253 | ||
| 212 | .nav-search-btn:hover { | 254 | .nav-search-btn:hover { |
| 213 | - background: rgba(255, 255, 255, 0.1); | 255 | + background: rgba(255, 255, 255, 0.15); |
| 214 | border-color: rgba(201, 167, 81, 0.4); | 256 | border-color: rgba(201, 167, 81, 0.4); |
| 215 | color: #F5F0E8; | 257 | color: #F5F0E8; |
| 216 | } | 258 | } |
| ... | @@ -252,28 +294,28 @@ | ... | @@ -252,28 +294,28 @@ |
| 252 | align-items: center; | 294 | align-items: center; |
| 253 | justify-content: center; | 295 | justify-content: center; |
| 254 | padding: 8px; | 296 | padding: 8px; |
| 255 | - background: rgba(255, 255, 255, 0.05); | 297 | + background: rgba(255, 255, 255, 0.08); |
| 256 | - border: 1px solid rgba(255, 255, 255, 0.12); | 298 | + border: 1px solid rgba(255, 255, 255, 0.1); |
| 257 | border-radius: 10px; | 299 | border-radius: 10px; |
| 258 | - color: #9B9890; | 300 | + color: #B8B5AD; |
| 259 | cursor: pointer; | 301 | cursor: pointer; |
| 260 | transition: all 0.2s ease; | 302 | transition: all 0.2s ease; |
| 261 | } | 303 | } |
| 262 | 304 | ||
| 263 | .nav-btn-icon:hover { | 305 | .nav-btn-icon:hover { |
| 264 | - background: rgba(255, 255, 255, 0.1); | 306 | + background: rgba(255, 255, 255, 0.15); |
| 265 | border-color: rgba(201, 167, 81, 0.4); | 307 | border-color: rgba(201, 167, 81, 0.4); |
| 266 | color: #F5F0E8; | 308 | color: #F5F0E8; |
| 267 | } | 309 | } |
| 268 | 310 | ||
| 269 | :global(html:not(.dark)) .nav-btn-icon { | 311 | :global(html:not(.dark)) .nav-btn-icon { |
| 270 | - background: rgba(0, 0, 0, 0.04); | 312 | + background: rgba(0, 0, 0, 0.05); |
| 271 | - border-color: rgba(0, 0, 0, 0.12); | 313 | + border-color: rgba(0, 0, 0, 0.08); |
| 272 | - color: #666; | 314 | + color: #555; |
| 273 | } | 315 | } |
| 274 | 316 | ||
| 275 | :global(html:not(.dark)) .nav-btn-icon:hover { | 317 | :global(html:not(.dark)) .nav-btn-icon:hover { |
| 276 | - background: rgba(0, 0, 0, 0.08); | 318 | + background: rgba(0, 0, 0, 0.1); |
| 277 | border-color: rgba(201, 167, 81, 0.5); | 319 | border-color: rgba(201, 167, 81, 0.5); |
| 278 | color: #1A1A18; | 320 | color: #1A1A18; |
| 279 | } | 321 | } |
| ... | @@ -285,23 +327,66 @@ | ... | @@ -285,23 +327,66 @@ |
| 285 | box-shadow: 0 1px 2px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.8); | 327 | box-shadow: 0 1px 2px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.8); |
| 286 | } | 328 | } |
| 287 | 329 | ||
| 330 | + /* Home link - hereda estilos de nav-btn-icon */ | ||
| 331 | + .nav-home-link { | ||
| 332 | + display: flex; | ||
| 333 | + align-items: center; | ||
| 334 | + justify-content: center; | ||
| 335 | + padding: 8px; | ||
| 336 | + background: rgba(255, 255, 255, 0.08); | ||
| 337 | + border: 1px solid rgba(255, 255, 255, 0.1); | ||
| 338 | + border-radius: 10px; | ||
| 339 | + color: #B8B5AD; | ||
| 340 | + cursor: pointer; | ||
| 341 | + transition: all 0.2s ease; | ||
| 342 | + text-decoration: none; | ||
| 343 | + } | ||
| 344 | + | ||
| 345 | + .nav-home-link:hover { | ||
| 346 | + background: rgba(255, 255, 255, 0.15); | ||
| 347 | + border-color: rgba(201, 167, 81, 0.4); | ||
| 348 | + color: #F5F0E8; | ||
| 349 | + } | ||
| 350 | + | ||
| 351 | + :global(html:not(.dark)) .nav-home-link { | ||
| 352 | + background: rgba(0, 0, 0, 0.05); | ||
| 353 | + border-color: rgba(0, 0, 0, 0.08); | ||
| 354 | + color: #555; | ||
| 355 | + } | ||
| 356 | + | ||
| 357 | + :global(html:not(.dark)) .nav-home-link:hover { | ||
| 358 | + background: rgba(0, 0, 0, 0.1); | ||
| 359 | + border-color: rgba(201, 167, 81, 0.5); | ||
| 360 | + color: #1A1A18; | ||
| 361 | + } | ||
| 362 | + | ||
| 288 | /* Mobile icon buttons */ | 363 | /* Mobile icon buttons */ |
| 289 | .nav-btn-icon-mobile { | 364 | .nav-btn-icon-mobile { |
| 290 | display: flex; | 365 | display: flex; |
| 291 | align-items: center; | 366 | align-items: center; |
| 292 | justify-content: center; | 367 | justify-content: center; |
| 293 | padding: 8px; | 368 | padding: 8px; |
| 294 | - background: rgba(255, 255, 255, 0.05); | 369 | + background: rgba(255, 255, 255, 0.1); |
| 295 | - border: 1px solid rgba(255, 255, 255, 0.12); | 370 | + border: 1px solid rgba(255, 255, 255, 0.08); |
| 296 | border-radius: 8px; | 371 | border-radius: 8px; |
| 297 | - color: #9B9890; | 372 | + color: #B8B5AD; |
| 298 | cursor: pointer; | 373 | cursor: pointer; |
| 299 | transition: all 0.2s ease; | 374 | transition: all 0.2s ease; |
| 300 | } | 375 | } |
| 301 | 376 | ||
| 377 | + .nav-btn-icon-mobile:hover { | ||
| 378 | + background: rgba(255, 255, 255, 0.18); | ||
| 379 | + color: #F5F0E8; | ||
| 380 | + } | ||
| 381 | + | ||
| 302 | :global(html:not(.dark)) .nav-btn-icon-mobile { | 382 | :global(html:not(.dark)) .nav-btn-icon-mobile { |
| 303 | - background: rgba(0, 0, 0, 0.04); | 383 | + background: rgba(0, 0, 0, 0.06); |
| 304 | - border-color: rgba(0, 0, 0, 0.12); | 384 | + border-color: rgba(0, 0, 0, 0.06); |
| 305 | - color: #666; | 385 | + color: #555; |
| 386 | + } | ||
| 387 | + | ||
| 388 | + :global(html:not(.dark)) .nav-btn-icon-mobile:hover { | ||
| 389 | + background: rgba(0, 0, 0, 0.12); | ||
| 390 | + color: #1A1A18; | ||
| 306 | } | 391 | } |
| 307 | </style> | 392 | </style> | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -61,6 +61,43 @@ export async function initSearchIndex() { | ... | @@ -61,6 +61,43 @@ export async function initSearchIndex() { |
| 61 | throw errorRubros; | 61 | throw errorRubros; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | + // Cargar finalidad y función | ||
| 65 | + const { data: finfun, error: errorFinfun } = await supabase | ||
| 66 | + .schema('ppto') | ||
| 67 | + .from('clas_finfun') | ||
| 68 | + .select('finfun, desc_finfun, nivel'); | ||
| 69 | + | ||
| 70 | + if (errorFinfun) { | ||
| 71 | + console.error('Error cargando finalidad/función:', errorFinfun); | ||
| 72 | + throw errorFinfun; | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + // Cargar organismos (no bloquea si falla) | ||
| 76 | + let organismos = []; | ||
| 77 | + const { data: organismosData, error: errorOrganismos } = await supabase | ||
| 78 | + .schema('ppto') | ||
| 79 | + .from('clas_organismos') | ||
| 80 | + .select('organismo, organismo_desc_organismo, organismo_grupo, organismo_sub_grupo'); | ||
| 81 | + | ||
| 82 | + if (errorOrganismos) { | ||
| 83 | + console.warn('Tabla clas_organismos no disponible:', errorOrganismos.message); | ||
| 84 | + } else { | ||
| 85 | + organismos = organismosData || []; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + // Cargar fuentes (no bloquea si falla) | ||
| 89 | + let fuentes = []; | ||
| 90 | + const { data: fuentesData, error: errorFuentes } = await supabase | ||
| 91 | + .schema('ppto') | ||
| 92 | + .from('clas_fuentes') | ||
| 93 | + .select('fuente, desc_fuente, grupo_fuente, desc_grupo_fuente'); | ||
| 94 | + | ||
| 95 | + if (errorFuentes) { | ||
| 96 | + console.warn('Tabla clas_fuentes no disponible:', errorFuentes.message); | ||
| 97 | + } else { | ||
| 98 | + fuentes = fuentesData || []; | ||
| 99 | + } | ||
| 100 | + | ||
| 64 | // Transformar entidades para el índice | 101 | // Transformar entidades para el índice |
| 65 | const entidadesIndex = entidades.map(item => ({ | 102 | const entidadesIndex = entidades.map(item => ({ |
| 66 | tipo: 'entidad', | 103 | tipo: 'entidad', |
| ... | @@ -102,8 +139,49 @@ export async function initSearchIndex() { | ... | @@ -102,8 +139,49 @@ export async function initSearchIndex() { |
| 102 | codigo_normalizado: item.rubro?.toString() || '' | 139 | codigo_normalizado: item.rubro?.toString() || '' |
| 103 | })); | 140 | })); |
| 104 | 141 | ||
| 142 | + // Transformar finalidad y función para el índice | ||
| 143 | + const nivelLabelsFinfun = { finalidad: 'Finalidad', grpfuncion: 'Grupo Función', funcion: 'Función' }; | ||
| 144 | + const finfunIndex = finfun.map(item => ({ | ||
| 145 | + tipo: 'finfun', | ||
| 146 | + codigo: item.finfun, | ||
| 147 | + nombre: item.desc_finfun, | ||
| 148 | + sigla: null, | ||
| 149 | + contexto: `Finalidad y Función · ${nivelLabelsFinfun[item.nivel] || item.nivel}`, | ||
| 150 | + nivel: item.nivel, | ||
| 151 | + nombre_normalizado: normalizeText(item.desc_finfun), | ||
| 152 | + sigla_normalizada: '', | ||
| 153 | + codigo_normalizado: item.finfun?.toString() || '' | ||
| 154 | + })); | ||
| 155 | + | ||
| 156 | + // Transformar organismos para el índice | ||
| 157 | + // Todos los registros en la tabla son organismos (nivel hoja) | ||
| 158 | + const organismosIndex = organismos.map(item => ({ | ||
| 159 | + tipo: 'organismo', | ||
| 160 | + codigo: item.organismo, | ||
| 161 | + nombre: item.organismo_desc_organismo, | ||
| 162 | + sigla: null, | ||
| 163 | + contexto: `Organismo · Grupo ${item.organismo_grupo}`, | ||
| 164 | + nivel: 'organismo', | ||
| 165 | + nombre_normalizado: normalizeText(item.organismo_desc_organismo), | ||
| 166 | + sigla_normalizada: '', | ||
| 167 | + codigo_normalizado: item.organismo?.toString() || '' | ||
| 168 | + })); | ||
| 169 | + | ||
| 170 | + // Transformar fuentes para el índice | ||
| 171 | + const fuentesIndex = fuentes.map(item => ({ | ||
| 172 | + tipo: 'fuente', | ||
| 173 | + codigo: item.fuente, | ||
| 174 | + nombre: item.desc_fuente, | ||
| 175 | + sigla: null, | ||
| 176 | + contexto: `Fuente · ${item.desc_grupo_fuente}`, | ||
| 177 | + nivel: 'fuente', | ||
| 178 | + nombre_normalizado: normalizeText(item.desc_fuente), | ||
| 179 | + sigla_normalizada: '', | ||
| 180 | + codigo_normalizado: item.fuente?.toString() || '' | ||
| 181 | + })); | ||
| 182 | + | ||
| 105 | // Combinar todos los conjuntos | 183 | // Combinar todos los conjuntos |
| 106 | - searchData = [...entidadesIndex, ...objetosIndex, ...rubrosIndex]; | 184 | + searchData = [...entidadesIndex, ...objetosIndex, ...rubrosIndex, ...finfunIndex, ...organismosIndex, ...fuentesIndex]; |
| 107 | 185 | ||
| 108 | fuseInstance = new Fuse(searchData, fuseOptions); | 186 | fuseInstance = new Fuse(searchData, fuseOptions); |
| 109 | 187 | ... | ... |
| ... | @@ -8,7 +8,7 @@ export const isLoading = writable(false); | ... | @@ -8,7 +8,7 @@ export const isLoading = writable(false); |
| 8 | export const indexLoaded = writable(false); | 8 | export const indexLoaded = writable(false); |
| 9 | export const error = writable(null); | 9 | export const error = writable(null); |
| 10 | export const selectedIndex = writable(-1); | 10 | export const selectedIndex = writable(-1); |
| 11 | -export const filterType = writable('todos'); // todos, entidad, objeto_gasto, rubro | 11 | +export const filterType = writable('todos'); // todos, entidad, objeto_gasto, rubro, finfun |
| 12 | 12 | ||
| 13 | // Estado derivado: si está mostrando resultados | 13 | // Estado derivado: si está mostrando resultados |
| 14 | export const showResults = derived( | 14 | export const showResults = derived( | ... | ... |
This diff is collapsed. Click to expand it.
| 1 | -<script> | 1 | +me pare<script> |
| 2 | import { supabase } from '$lib/supabase'; | 2 | import { supabase } from '$lib/supabase'; |
| 3 | import { onMount, untrack } from 'svelte'; | 3 | import { onMount, untrack } from 'svelte'; |
| 4 | import { page } from '$app/stores'; | 4 | import { page } from '$app/stores'; |
| ... | @@ -79,17 +79,24 @@ | ... | @@ -79,17 +79,24 @@ |
| 79 | '10': 'Protección Social', | 79 | '10': 'Protección Social', |
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | - // Función para obtener color según la finalidad (primer dígito del código finfun) | 82 | + // Helper para obtener la finalidad de un código (maneja caso especial de "10") |
| 83 | + function getFinalidadKey(finfun) { | ||
| 84 | + const code = String(finfun); | ||
| 85 | + if (code.startsWith('10')) return '10'; | ||
| 86 | + return code.charAt(0); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + // Función para obtener color según la finalidad | ||
| 83 | function getNodeColor(finfun, opacity = 1) { | 90 | function getNodeColor(finfun, opacity = 1) { |
| 84 | - const finalidadDigit = String(finfun).charAt(0); | 91 | + const finalidadKey = getFinalidadKey(finfun); |
| 85 | - const baseColor = FINALIDAD_COLORS[finalidadDigit] || '#888888'; | 92 | + const baseColor = FINALIDAD_COLORS[finalidadKey] || '#888888'; |
| 86 | if (opacity === 1) return baseColor; | 93 | if (opacity === 1) return baseColor; |
| 87 | return d3.color(baseColor).copy({opacity}).formatRgb(); | 94 | return d3.color(baseColor).copy({opacity}).formatRgb(); |
| 88 | } | 95 | } |
| 89 | 96 | ||
| 90 | function getTextColor(finfun) { | 97 | function getTextColor(finfun) { |
| 91 | - const finalidadDigit = String(finfun).charAt(0); | 98 | + const finalidadKey = getFinalidadKey(finfun); |
| 92 | - const baseColor = FINALIDAD_COLORS[finalidadDigit] || '#888888'; | 99 | + const baseColor = FINALIDAD_COLORS[finalidadKey] || '#888888'; |
| 93 | const color = d3.color(baseColor); | 100 | const color = d3.color(baseColor); |
| 94 | const r = color.r / 255; | 101 | const r = color.r / 255; |
| 95 | const g = color.g / 255; | 102 | const g = color.g / 255; |
| ... | @@ -99,8 +106,8 @@ | ... | @@ -99,8 +106,8 @@ |
| 99 | } | 106 | } |
| 100 | 107 | ||
| 101 | function getTextColorSecondary(finfun) { | 108 | function getTextColorSecondary(finfun) { |
| 102 | - const finalidadDigit = String(finfun).charAt(0); | 109 | + const finalidadKey = getFinalidadKey(finfun); |
| 103 | - const baseColor = FINALIDAD_COLORS[finalidadDigit] || '#888888'; | 110 | + const baseColor = FINALIDAD_COLORS[finalidadKey] || '#888888'; |
| 104 | const color = d3.color(baseColor); | 111 | const color = d3.color(baseColor); |
| 105 | const r = color.r / 255; | 112 | const r = color.r / 255; |
| 106 | const g = color.g / 255; | 113 | const g = color.g / 255; |
| ... | @@ -610,13 +617,26 @@ | ... | @@ -610,13 +617,26 @@ |
| 610 | }); | 617 | }); |
| 611 | 618 | ||
| 612 | // Helpers para derivar jerarquía desde código finfun | 619 | // Helpers para derivar jerarquía desde código finfun |
| 613 | - // Jerarquía: Finalidad (1 dígito) → Grupo Función (2 dígitos) → Función (3+ dígitos) | 620 | + // Jerarquía: Finalidad (1-9 o 10) → Grupo Función (2-3 dígitos) → Función (3+ dígitos) |
| 621 | + // Nota: Finalidad 10 es especial - tiene 2 dígitos, sus hijos empiezan con "10" | ||
| 614 | function getFinalidadFromFinfun(finfun) { | 622 | function getFinalidadFromFinfun(finfun) { |
| 615 | - return finfun.charAt(0); | 623 | + const code = String(finfun); |
| 624 | + // Si empieza con "10", la finalidad es "10" | ||
| 625 | + if (code.startsWith('10')) { | ||
| 626 | + return '10'; | ||
| 627 | + } | ||
| 628 | + // Si no, la finalidad es el primer dígito | ||
| 629 | + return code.charAt(0); | ||
| 616 | } | 630 | } |
| 617 | 631 | ||
| 618 | function getGrpFuncionFromFinfun(finfun) { | 632 | function getGrpFuncionFromFinfun(finfun) { |
| 619 | - return finfun.substring(0, 2); | 633 | + const code = String(finfun); |
| 634 | + // Si empieza con "10", el grupo función son los primeros 3 dígitos (ej: "101", "102") | ||
| 635 | + if (code.startsWith('10')) { | ||
| 636 | + return code.substring(0, 3); | ||
| 637 | + } | ||
| 638 | + // Si no, son los primeros 2 dígitos (ej: "11", "21", "93") | ||
| 639 | + return code.substring(0, 2); | ||
| 620 | } | 640 | } |
| 621 | 641 | ||
| 622 | function getItemsForFinalidad(finalidadCode) { | 642 | function getItemsForFinalidad(finalidadCode) { | ... | ... |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
| ... | @@ -48,16 +48,20 @@ | ... | @@ -48,16 +48,20 @@ |
| 48 | }); | 48 | }); |
| 49 | 49 | ||
| 50 | // Helpers para derivar jerarquía desde código rubro | 50 | // Helpers para derivar jerarquía desde código rubro |
| 51 | + // Jerarquía: Tipo (XX000) → Clase (XXX00) → Cuenta (XXXX0) → Subcuenta (XXXXX) | ||
| 51 | function getTipoFromRubro(rubro) { | 52 | function getTipoFromRubro(rubro) { |
| 52 | - return rubro.charAt(0); | 53 | + // Tipo = primeros 2 dígitos (XX de XX000) |
| 54 | + return rubro.substring(0, 2); | ||
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | function getClaseFromRubro(rubro) { | 57 | function getClaseFromRubro(rubro) { |
| 56 | - return rubro.substring(0, 2); | 58 | + // Clase = primeros 3 dígitos (XXX de XXX00) |
| 59 | + return rubro.substring(0, 3); | ||
| 57 | } | 60 | } |
| 58 | 61 | ||
| 59 | function getCuentaFromRubro(rubro) { | 62 | function getCuentaFromRubro(rubro) { |
| 60 | - return rubro.substring(0, 3); | 63 | + // Cuenta = primeros 4 dígitos (XXXX de XXXX0) |
| 64 | + return rubro.substring(0, 4); | ||
| 61 | } | 65 | } |
| 62 | 66 | ||
| 63 | function getItemsForTipo(tipoCode) { | 67 | function getItemsForTipo(tipoCode) { | ... | ... |
| ... | @@ -2,18 +2,40 @@ import { supabase } from '$lib/supabase'; | ... | @@ -2,18 +2,40 @@ import { supabase } from '$lib/supabase'; |
| 2 | import { error } from '@sveltejs/kit'; | 2 | import { error } from '@sveltejs/kit'; |
| 3 | 3 | ||
| 4 | // Funciones para derivar jerarquía del código finfun | 4 | // Funciones para derivar jerarquía del código finfun |
| 5 | -// Jerarquía: Finalidad (1 dígito) → Grupo Función (2 dígitos) → Función (3+ dígitos) | 5 | +// Jerarquía: Finalidad (1-9 o 10) → Grupo Función (2-3 dígitos) → Función (3+ dígitos) |
| 6 | +// Nota: Finalidad 10 es especial - tiene 2 dígitos, sus hijos empiezan con "10" | ||
| 7 | + | ||
| 6 | function getFinalidadCode(finfun) { | 8 | function getFinalidadCode(finfun) { |
| 7 | - return finfun.charAt(0); | 9 | + const code = String(finfun); |
| 10 | + // Si empieza con "10", la finalidad es "10" | ||
| 11 | + if (code.startsWith('10')) { | ||
| 12 | + return '10'; | ||
| 13 | + } | ||
| 14 | + // Si no, la finalidad es el primer dígito | ||
| 15 | + return code.charAt(0); | ||
| 8 | } | 16 | } |
| 9 | 17 | ||
| 10 | function getGrpFuncionCode(finfun) { | 18 | function getGrpFuncionCode(finfun) { |
| 11 | - return finfun.substring(0, 2); | 19 | + const code = String(finfun); |
| 20 | + // Si empieza con "10", el grupo función son los primeros 3 dígitos (ej: "101", "102") | ||
| 21 | + if (code.startsWith('10')) { | ||
| 22 | + return code.substring(0, 3); | ||
| 23 | + } | ||
| 24 | + // Si no, son los primeros 2 dígitos (ej: "11", "21", "93") | ||
| 25 | + return code.substring(0, 2); | ||
| 12 | } | 26 | } |
| 13 | 27 | ||
| 14 | function getNivel(finfun) { | 28 | function getNivel(finfun) { |
| 15 | - if (finfun.length === 1) return 'finalidad'; | 29 | + const code = String(finfun); |
| 16 | - if (finfun.length === 2) return 'grpfuncion'; | 30 | + // Finalidades 1-9 tienen longitud 1, finalidad 10 tiene longitud 2 |
| 31 | + if (code.length === 1) return 'finalidad'; | ||
| 32 | + if (code === '10') return 'finalidad'; | ||
| 33 | + // GrpFuncion: 2 dígitos para 1-9, 3 dígitos para 10 | ||
| 34 | + if (code.startsWith('10')) { | ||
| 35 | + if (code.length === 3) return 'grpfuncion'; | ||
| 36 | + return 'funcion'; | ||
| 37 | + } | ||
| 38 | + if (code.length === 2) return 'grpfuncion'; | ||
| 17 | return 'funcion'; | 39 | return 'funcion'; |
| 18 | } | 40 | } |
| 19 | 41 | ||
| ... | @@ -77,27 +99,40 @@ export async function load({ params }) { | ... | @@ -77,27 +99,40 @@ export async function load({ params }) { |
| 77 | 99 | ||
| 78 | // Buscar hijos según nivel | 100 | // Buscar hijos según nivel |
| 79 | if (nivel === 'finalidad') { | 101 | if (nivel === 'finalidad') { |
| 80 | - // Hijos: grupos de función que empiecen con el mismo dígito | 102 | + // Hijos: grupos de función de esta finalidad |
| 81 | const { data: hijosData } = await supabase | 103 | const { data: hijosData } = await supabase |
| 82 | .schema('ppto') | 104 | .schema('ppto') |
| 83 | .from('clas_finfun') | 105 | .from('clas_finfun') |
| 84 | .select('*') | 106 | .select('*') |
| 85 | .eq('nivel', 'grpfuncion') | 107 | .eq('nivel', 'grpfuncion') |
| 86 | - .like('finfun', `${codigo}%`) | ||
| 87 | .order('finfun'); | 108 | .order('finfun'); |
| 88 | 109 | ||
| 89 | - hijos = hijosData || []; | 110 | + // Filtrar solo los hijos directos de esta finalidad |
| 111 | + // Para finalidad "1": hijos son "11", "12", ..., "19" (NO "10x") | ||
| 112 | + // Para finalidad "10": hijos son "101", "102", ..., "109" | ||
| 113 | + if (hijosData) { | ||
| 114 | + hijos = hijosData.filter(h => { | ||
| 115 | + const hijoPadre = getFinalidadCode(h.finfun); | ||
| 116 | + return hijoPadre === codigo; | ||
| 117 | + }); | ||
| 118 | + } | ||
| 90 | } else if (nivel === 'grpfuncion') { | 119 | } else if (nivel === 'grpfuncion') { |
| 91 | - // Hijos: funciones que empiecen con los mismos 2 dígitos | 120 | + // Hijos: funciones de este grupo |
| 121 | + const grpCode = getGrpFuncionCode(codigo); | ||
| 92 | const { data: hijosData } = await supabase | 122 | const { data: hijosData } = await supabase |
| 93 | .schema('ppto') | 123 | .schema('ppto') |
| 94 | .from('clas_finfun') | 124 | .from('clas_finfun') |
| 95 | .select('*') | 125 | .select('*') |
| 96 | .eq('nivel', 'funcion') | 126 | .eq('nivel', 'funcion') |
| 97 | - .like('finfun', `${codigo}%`) | ||
| 98 | .order('finfun'); | 127 | .order('finfun'); |
| 99 | 128 | ||
| 100 | - hijos = hijosData || []; | 129 | + // Filtrar solo los hijos directos de este grupo |
| 130 | + if (hijosData) { | ||
| 131 | + hijos = hijosData.filter(h => { | ||
| 132 | + const hijoGrp = getGrpFuncionCode(h.finfun); | ||
| 133 | + return hijoGrp === grpCode; | ||
| 134 | + }); | ||
| 135 | + } | ||
| 101 | } | 136 | } |
| 102 | // funcion no tiene hijos | 137 | // funcion no tiene hijos |
| 103 | 138 | ... | ... |
| ... | @@ -1029,9 +1029,9 @@ | ... | @@ -1029,9 +1029,9 @@ |
| 1029 | <p class="context-hint">Funciones relacionadas. <span class="desktop-hint">Haz click</span><span class="mobile-hint">Toca</span> para explorar.</p> | 1029 | <p class="context-hint">Funciones relacionadas. <span class="desktop-hint">Haz click</span><span class="mobile-hint">Toca</span> para explorar.</p> |
| 1030 | <div class="tree"> | 1030 | <div class="tree"> |
| 1031 | {#each padres as padre, i} | 1031 | {#each padres as padre, i} |
| 1032 | - <a href="/finfun/{padre.objeto}" class="tree-node has-tooltip" style="--indent: {i}" data-tooltip="{padre.desc_finfun}"> | 1032 | + <a href="/finfun/{padre.finfun}" class="tree-node has-tooltip" style="--indent: {i}" data-tooltip="{padre.desc_finfun}"> |
| 1033 | <span class="tree-dot"></span> | 1033 | <span class="tree-dot"></span> |
| 1034 | - <span class="tree-code">{padre.objeto}</span> | 1034 | + <span class="tree-code">{padre.finfun}</span> |
| 1035 | <span class="tree-name">{padre.desc_finfun}</span> | 1035 | <span class="tree-name">{padre.desc_finfun}</span> |
| 1036 | </a> | 1036 | </a> |
| 1037 | {/each} | 1037 | {/each} |
| ... | @@ -1045,9 +1045,9 @@ | ... | @@ -1045,9 +1045,9 @@ |
| 1045 | {#if hijos.length > 0} | 1045 | {#if hijos.length > 0} |
| 1046 | <div class="tree-children" style="--indent: {padres.length + 1}"> | 1046 | <div class="tree-children" style="--indent: {padres.length + 1}"> |
| 1047 | {#each hijos as hijo} | 1047 | {#each hijos as hijo} |
| 1048 | - <a href="/finfun/{hijo.objeto}" class="tree-node tree-node-child has-tooltip" data-tooltip="{hijo.desc_finfun}"> | 1048 | + <a href="/finfun/{hijo.finfun}" class="tree-node tree-node-child has-tooltip" data-tooltip="{hijo.desc_finfun}"> |
| 1049 | <span class="tree-dot"></span> | 1049 | <span class="tree-dot"></span> |
| 1050 | - <span class="tree-code">{hijo.objeto}</span> | 1050 | + <span class="tree-code">{hijo.finfun}</span> |
| 1051 | <span class="tree-name">{hijo.desc_finfun}</span> | 1051 | <span class="tree-name">{hijo.desc_finfun}</span> |
| 1052 | </a> | 1052 | </a> |
| 1053 | {/each} | 1053 | {/each} | ... | ... |
src/routes/fuente/[codigo]/+page.server.js
0 → 100644
| 1 | +import { supabase } from '$lib/supabase'; | ||
| 2 | +import { error } from '@sveltejs/kit'; | ||
| 3 | + | ||
| 4 | +export async function load({ params }) { | ||
| 5 | + console.time('[SERVER] Total load fuente'); | ||
| 6 | + const { codigo } = params; | ||
| 7 | + | ||
| 8 | + console.time('[SERVER] Query clas_fuentes'); | ||
| 9 | + const { data, error: dbError } = await supabase | ||
| 10 | + .schema('ppto') | ||
| 11 | + .from('clas_fuentes') | ||
| 12 | + .select('*') | ||
| 13 | + .eq('fuente', codigo); | ||
| 14 | + console.timeEnd('[SERVER] Query clas_fuentes'); | ||
| 15 | + | ||
| 16 | + if (dbError || !data || data.length === 0) { | ||
| 17 | + throw error(404, 'Fuente no encontrada'); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + const fuente = data[0]; | ||
| 21 | + | ||
| 22 | + // Obtener hermanos (otras fuentes del mismo grupo) | ||
| 23 | + let hermanos = []; | ||
| 24 | + | ||
| 25 | + console.time('[SERVER] Query hermanos'); | ||
| 26 | + const { data: hermanosData } = await supabase | ||
| 27 | + .schema('ppto') | ||
| 28 | + .from('clas_fuentes') | ||
| 29 | + .select('*') | ||
| 30 | + .eq('grupo_fuente', fuente.grupo_fuente) | ||
| 31 | + .neq('fuente', codigo) | ||
| 32 | + .order('fuente'); | ||
| 33 | + | ||
| 34 | + if (hermanosData) { | ||
| 35 | + hermanos = hermanosData; | ||
| 36 | + } | ||
| 37 | + console.timeEnd('[SERVER] Query hermanos'); | ||
| 38 | + | ||
| 39 | + console.timeEnd('[SERVER] Total load fuente'); | ||
| 40 | + return { | ||
| 41 | + fuente, | ||
| 42 | + hermanos | ||
| 43 | + }; | ||
| 44 | +} |
src/routes/fuente/[codigo]/+page.svelte
0 → 100644
This diff is collapsed. Click to expand it.
| 1 | +import { supabase } from '$lib/supabase'; | ||
| 2 | +import { error } from '@sveltejs/kit'; | ||
| 3 | + | ||
| 4 | +export async function load({ params }) { | ||
| 5 | + console.time('[SERVER] Total load organismo'); | ||
| 6 | + const { codigo } = params; | ||
| 7 | + | ||
| 8 | + console.time('[SERVER] Query clas_organismos'); | ||
| 9 | + const { data, error: dbError } = await supabase | ||
| 10 | + .schema('ppto') | ||
| 11 | + .from('clas_organismos') | ||
| 12 | + .select('*') | ||
| 13 | + .eq('organismo', codigo); | ||
| 14 | + console.timeEnd('[SERVER] Query clas_organismos'); | ||
| 15 | + | ||
| 16 | + if (dbError || !data || data.length === 0) { | ||
| 17 | + throw error(404, 'Organismo no encontrado'); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + const organismo = data[0]; | ||
| 21 | + | ||
| 22 | + // Obtener hermanos (otros organismos del mismo subgrupo) | ||
| 23 | + let hermanos = []; | ||
| 24 | + | ||
| 25 | + console.time('[SERVER] Query hermanos'); | ||
| 26 | + const { data: hermanosData } = await supabase | ||
| 27 | + .schema('ppto') | ||
| 28 | + .from('clas_organismos') | ||
| 29 | + .select('*') | ||
| 30 | + .eq('organismo_grupo', organismo.organismo_grupo) | ||
| 31 | + .eq('organismo_sub_grupo', organismo.organismo_sub_grupo) | ||
| 32 | + .neq('organismo', codigo) | ||
| 33 | + .order('organismo'); | ||
| 34 | + | ||
| 35 | + if (hermanosData) { | ||
| 36 | + hermanos = hermanosData; | ||
| 37 | + } | ||
| 38 | + console.timeEnd('[SERVER] Query hermanos'); | ||
| 39 | + | ||
| 40 | + console.timeEnd('[SERVER] Total load organismo'); | ||
| 41 | + return { | ||
| 42 | + organismo, | ||
| 43 | + hermanos | ||
| 44 | + }; | ||
| 45 | +} |
src/routes/organismo/[codigo]/+page.svelte
0 → 100644
This diff is collapsed. Click to expand it.
| 1 | import { supabase } from '$lib/supabase'; | 1 | import { supabase } from '$lib/supabase'; |
| 2 | import { error } from '@sveltejs/kit'; | 2 | import { error } from '@sveltejs/kit'; |
| 3 | 3 | ||
| 4 | +// Funciones para derivar jerarquía del código rubro | ||
| 5 | +// Jerarquía: Tipo (XX000) → Clase (XXX00) → Cuenta (XXXX0) → Subcuenta (XXXXX) | ||
| 6 | + | ||
| 7 | +function getTipoCode(rubro) { | ||
| 8 | + // Primeros 2 dígitos + 000 → ej: 11000, 12000 | ||
| 9 | + return rubro.substring(0, 2) + '000'; | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +function getClaseCode(rubro) { | ||
| 13 | + // Primeros 3 dígitos + 00 → ej: 11100, 11200 | ||
| 14 | + return rubro.substring(0, 3) + '00'; | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +function getCuentaCode(rubro) { | ||
| 18 | + // Primeros 4 dígitos + 0 → ej: 11110, 11120 | ||
| 19 | + return rubro.substring(0, 4) + '0'; | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +function getNivelFromCode(rubro) { | ||
| 23 | + // Determinar nivel basado en el patrón de ceros | ||
| 24 | + if (rubro.endsWith('000')) return 'tipo'; // XX000 | ||
| 25 | + if (rubro.endsWith('00')) return 'clase'; // XXX00 | ||
| 26 | + if (rubro.endsWith('0')) return 'cuenta'; // XXXX0 | ||
| 27 | + return 'sub_cuenta'; // XXXXX | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +function getNextNivel(nivel) { | ||
| 31 | + const niveles = { tipo: 'clase', clase: 'cuenta', cuenta: 'sub_cuenta' }; | ||
| 32 | + return niveles[nivel] || null; | ||
| 33 | +} | ||
| 34 | + | ||
| 4 | export async function load({ params }) { | 35 | export async function load({ params }) { |
| 5 | const { codigo } = params; | 36 | const { codigo } = params; |
| 6 | 37 | ||
| ... | @@ -15,88 +46,81 @@ export async function load({ params }) { | ... | @@ -15,88 +46,81 @@ export async function load({ params }) { |
| 15 | } | 46 | } |
| 16 | 47 | ||
| 17 | const rubro = data[0]; | 48 | const rubro = data[0]; |
| 49 | + const nivel = rubro.nivel || getNivelFromCode(codigo); | ||
| 18 | 50 | ||
| 19 | // Obtener jerarquía (padres e hijos) | 51 | // Obtener jerarquía (padres e hijos) |
| 20 | let padres = []; | 52 | let padres = []; |
| 21 | let hijos = []; | 53 | let hijos = []; |
| 22 | 54 | ||
| 23 | - // Buscar padre según nivel | 55 | + // Buscar padres según nivel |
| 24 | - if (rubro.nivel === 'sub_cuenta') { | 56 | + if (nivel === 'sub_cuenta') { |
| 25 | - // Padre es cuenta | 57 | + // Padres: tipo, clase, cuenta |
| 26 | - const { data: padreData } = await supabase | 58 | + const tipoCode = getTipoCode(codigo); |
| 59 | + const claseCode = getClaseCode(codigo); | ||
| 60 | + const cuentaCode = getCuentaCode(codigo); | ||
| 61 | + | ||
| 62 | + const { data: padresData } = await supabase | ||
| 27 | .schema('ppto') | 63 | .schema('ppto') |
| 28 | .from('clas_rubros') | 64 | .from('clas_rubros') |
| 29 | .select('*') | 65 | .select('*') |
| 30 | - .eq('nivel', 'cuenta') | 66 | + .in('rubro', [tipoCode, claseCode, cuentaCode]) |
| 31 | - .eq('tipo', rubro.tipo) | 67 | + .order('rubro'); |
| 32 | - .eq('clase', rubro.clase) | ||
| 33 | - .eq('cuenta', rubro.cuenta); | ||
| 34 | - if (padreData?.length) padres.push(padreData[0]); | ||
| 35 | - } | ||
| 36 | 68 | ||
| 37 | - if (rubro.nivel === 'cuenta' || rubro.nivel === 'sub_cuenta') { | 69 | + if (padresData) padres = padresData; |
| 38 | - // Padre es clase | 70 | + } else if (nivel === 'cuenta') { |
| 39 | - const { data: padreData } = await supabase | 71 | + // Padres: tipo, clase |
| 72 | + const tipoCode = getTipoCode(codigo); | ||
| 73 | + const claseCode = getClaseCode(codigo); | ||
| 74 | + | ||
| 75 | + const { data: padresData } = await supabase | ||
| 40 | .schema('ppto') | 76 | .schema('ppto') |
| 41 | .from('clas_rubros') | 77 | .from('clas_rubros') |
| 42 | .select('*') | 78 | .select('*') |
| 43 | - .eq('nivel', 'clase') | 79 | + .in('rubro', [tipoCode, claseCode]) |
| 44 | - .eq('tipo', rubro.tipo) | 80 | + .order('rubro'); |
| 45 | - .eq('clase', rubro.clase); | 81 | + |
| 46 | - if (padreData?.length) padres.unshift(padreData[0]); | 82 | + if (padresData) padres = padresData; |
| 47 | - } | 83 | + } else if (nivel === 'clase') { |
| 84 | + // Padre: tipo | ||
| 85 | + const tipoCode = getTipoCode(codigo); | ||
| 48 | 86 | ||
| 49 | - if (rubro.nivel !== 'tipo') { | 87 | + const { data: padresData } = await supabase |
| 50 | - // Padre es tipo | ||
| 51 | - const { data: padreData } = await supabase | ||
| 52 | .schema('ppto') | 88 | .schema('ppto') |
| 53 | .from('clas_rubros') | 89 | .from('clas_rubros') |
| 54 | .select('*') | 90 | .select('*') |
| 55 | - .eq('nivel', 'tipo') | 91 | + .eq('rubro', tipoCode); |
| 56 | - .eq('tipo', rubro.tipo); | 92 | + |
| 57 | - if (padreData?.length) padres.unshift(padreData[0]); | 93 | + if (padresData) padres = padresData; |
| 58 | } | 94 | } |
| 95 | + // tipo no tiene padres | ||
| 59 | 96 | ||
| 60 | // Buscar hijos según nivel | 97 | // Buscar hijos según nivel |
| 61 | - if (rubro.nivel === 'tipo') { | 98 | + const nextNivel = getNextNivel(nivel); |
| 62 | - const { data: hijosData } = await supabase | 99 | + if (nextNivel) { |
| 63 | - .schema('ppto') | ||
| 64 | - .from('clas_rubros') | ||
| 65 | - .select('*') | ||
| 66 | - .eq('nivel', 'clase') | ||
| 67 | - .eq('tipo', rubro.tipo) | ||
| 68 | - .order('rubro'); | ||
| 69 | - hijos = hijosData?.reduce((acc, item) => { | ||
| 70 | - if (!acc.find(h => h.rubro === item.rubro)) acc.push(item); | ||
| 71 | - return acc; | ||
| 72 | - }, []) || []; | ||
| 73 | - } else if (rubro.nivel === 'clase') { | ||
| 74 | const { data: hijosData } = await supabase | 100 | const { data: hijosData } = await supabase |
| 75 | .schema('ppto') | 101 | .schema('ppto') |
| 76 | .from('clas_rubros') | 102 | .from('clas_rubros') |
| 77 | .select('*') | 103 | .select('*') |
| 78 | - .eq('nivel', 'cuenta') | 104 | + .eq('nivel', nextNivel) |
| 79 | - .eq('tipo', rubro.tipo) | 105 | + .like('rubro', `${codigo.replace(/0+$/, '')}%`) |
| 80 | - .eq('clase', rubro.clase) | ||
| 81 | .order('rubro'); | 106 | .order('rubro'); |
| 82 | - hijos = hijosData?.reduce((acc, item) => { | 107 | + |
| 83 | - if (!acc.find(h => h.rubro === item.rubro)) acc.push(item); | 108 | + if (hijosData) { |
| 84 | - return acc; | 109 | + // Filtrar solo hijos directos (siguiente nivel) |
| 85 | - }, []) || []; | 110 | + hijos = hijosData.filter(h => { |
| 86 | - } else if (rubro.nivel === 'cuenta') { | 111 | + if (nivel === 'tipo') { |
| 87 | - const { data: hijosData } = await supabase | 112 | + // Hijos de tipo son clase: XXX00 donde XX = tipo |
| 88 | - .schema('ppto') | 113 | + return h.rubro.startsWith(codigo.substring(0, 2)) && h.nivel === 'clase'; |
| 89 | - .from('clas_rubros') | 114 | + } else if (nivel === 'clase') { |
| 90 | - .select('*') | 115 | + // Hijos de clase son cuenta: XXXX0 donde XXX = clase |
| 91 | - .eq('nivel', 'sub_cuenta') | 116 | + return h.rubro.startsWith(codigo.substring(0, 3)) && h.nivel === 'cuenta'; |
| 92 | - .eq('tipo', rubro.tipo) | 117 | + } else if (nivel === 'cuenta') { |
| 93 | - .eq('clase', rubro.clase) | 118 | + // Hijos de cuenta son sub_cuenta: XXXXX donde XXXX = cuenta |
| 94 | - .eq('cuenta', rubro.cuenta) | 119 | + return h.rubro.startsWith(codigo.substring(0, 4)) && h.nivel === 'sub_cuenta'; |
| 95 | - .order('rubro'); | 120 | + } |
| 96 | - hijos = hijosData?.reduce((acc, item) => { | 121 | + return false; |
| 97 | - if (!acc.find(h => h.rubro === item.rubro)) acc.push(item); | 122 | + }); |
| 98 | - return acc; | 123 | + } |
| 99 | - }, []) || []; | ||
| 100 | } | 124 | } |
| 101 | 125 | ||
| 102 | return { | 126 | return { | ... | ... |
src/routes/vista-diaria/+page.js
0 → 100644
src/routes/vista-diaria/+page.svelte
0 → 100644
This diff is collapsed. Click to expand it.
static/ejecucion_clasificadores@3.parquet
0 → 100644
No preview for this file type
static/ejecucion_entidades@1.parquet
0 → 100644
No preview for this file type
static/timeline@1.parquet
0 → 100644
No preview for this file type
static/timeserie@4.parquet
0 → 100644
No preview for this file type
-
Please register or login to post a comment