Showing
21 changed files
with
37 additions
and
819 deletions
| ... | @@ -9,7 +9,6 @@ | ... | @@ -9,7 +9,6 @@ |
| 9 | "version": "0.0.1", | 9 | "version": "0.0.1", |
| 10 | "dependencies": { | 10 | "dependencies": { |
| 11 | "@observablehq/plot": "^0.6.17", | 11 | "@observablehq/plot": "^0.6.17", |
| 12 | - "@supabase/supabase-js": "^2.98.0", | ||
| 13 | "@sveltejs/adapter-node": "^5.5.4", | 12 | "@sveltejs/adapter-node": "^5.5.4", |
| 14 | "d3": "^7.9.0", | 13 | "d3": "^7.9.0", |
| 15 | "fuse.js": "^7.1.0", | 14 | "fuse.js": "^7.1.0", |
| ... | @@ -939,86 +938,6 @@ | ... | @@ -939,86 +938,6 @@ |
| 939 | "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", | 938 | "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", |
| 940 | "license": "MIT" | 939 | "license": "MIT" |
| 941 | }, | 940 | }, |
| 942 | - "node_modules/@supabase/auth-js": { | ||
| 943 | - "version": "2.98.0", | ||
| 944 | - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.98.0.tgz", | ||
| 945 | - "integrity": "sha512-GBH361T0peHU91AQNzOlIrjUZw9TZbB9YDRiyFgk/3Kvr3/Z1NWUZ2athWTfHhwNNi8IrW00foyFxQD9IO/Trg==", | ||
| 946 | - "license": "MIT", | ||
| 947 | - "dependencies": { | ||
| 948 | - "tslib": "2.8.1" | ||
| 949 | - }, | ||
| 950 | - "engines": { | ||
| 951 | - "node": ">=20.0.0" | ||
| 952 | - } | ||
| 953 | - }, | ||
| 954 | - "node_modules/@supabase/functions-js": { | ||
| 955 | - "version": "2.98.0", | ||
| 956 | - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.98.0.tgz", | ||
| 957 | - "integrity": "sha512-N/xEyiNU5Org+d+PNCpv+TWniAXRzxIURxDYsS/m2I/sfAB/HcM9aM2Dmf5edj5oWb9GxID1OBaZ8NMmPXL+Lg==", | ||
| 958 | - "license": "MIT", | ||
| 959 | - "dependencies": { | ||
| 960 | - "tslib": "2.8.1" | ||
| 961 | - }, | ||
| 962 | - "engines": { | ||
| 963 | - "node": ">=20.0.0" | ||
| 964 | - } | ||
| 965 | - }, | ||
| 966 | - "node_modules/@supabase/postgrest-js": { | ||
| 967 | - "version": "2.98.0", | ||
| 968 | - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.98.0.tgz", | ||
| 969 | - "integrity": "sha512-v6e9WeZuJijzUut8HyXu6gMqWFepIbaeaMIm1uKzei4yLg9bC9OtEW9O14LE/9ezqNbSAnSLO5GtOLFdm7Bpkg==", | ||
| 970 | - "license": "MIT", | ||
| 971 | - "dependencies": { | ||
| 972 | - "tslib": "2.8.1" | ||
| 973 | - }, | ||
| 974 | - "engines": { | ||
| 975 | - "node": ">=20.0.0" | ||
| 976 | - } | ||
| 977 | - }, | ||
| 978 | - "node_modules/@supabase/realtime-js": { | ||
| 979 | - "version": "2.98.0", | ||
| 980 | - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.98.0.tgz", | ||
| 981 | - "integrity": "sha512-rOWt28uGyFipWOSd+n0WVMr9kUXiWaa7J4hvyLCIHjRFqWm1z9CaaKAoYyfYMC1Exn3WT8WePCgiVhlAtWC2yw==", | ||
| 982 | - "license": "MIT", | ||
| 983 | - "dependencies": { | ||
| 984 | - "@types/phoenix": "^1.6.6", | ||
| 985 | - "@types/ws": "^8.18.1", | ||
| 986 | - "tslib": "2.8.1", | ||
| 987 | - "ws": "^8.18.2" | ||
| 988 | - }, | ||
| 989 | - "engines": { | ||
| 990 | - "node": ">=20.0.0" | ||
| 991 | - } | ||
| 992 | - }, | ||
| 993 | - "node_modules/@supabase/storage-js": { | ||
| 994 | - "version": "2.98.0", | ||
| 995 | - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.98.0.tgz", | ||
| 996 | - "integrity": "sha512-tzr2mG+v7ILSAZSfZMSL9OPyIH4z1ikgQ8EcQTKfMRz4EwmlFt3UnJaGzSOxyvF5b+fc9So7qdSUWTqGgeLokQ==", | ||
| 997 | - "license": "MIT", | ||
| 998 | - "dependencies": { | ||
| 999 | - "iceberg-js": "^0.8.1", | ||
| 1000 | - "tslib": "2.8.1" | ||
| 1001 | - }, | ||
| 1002 | - "engines": { | ||
| 1003 | - "node": ">=20.0.0" | ||
| 1004 | - } | ||
| 1005 | - }, | ||
| 1006 | - "node_modules/@supabase/supabase-js": { | ||
| 1007 | - "version": "2.98.0", | ||
| 1008 | - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.98.0.tgz", | ||
| 1009 | - "integrity": "sha512-Ohc97CtInLwZyiSASz7tT9/Abm/vqnIbO9REp+PivVUII8UZsuI3bngRQnYgJdFoOIwvaEII1fX1qy8x0CyNiw==", | ||
| 1010 | - "license": "MIT", | ||
| 1011 | - "dependencies": { | ||
| 1012 | - "@supabase/auth-js": "2.98.0", | ||
| 1013 | - "@supabase/functions-js": "2.98.0", | ||
| 1014 | - "@supabase/postgrest-js": "2.98.0", | ||
| 1015 | - "@supabase/realtime-js": "2.98.0", | ||
| 1016 | - "@supabase/storage-js": "2.98.0" | ||
| 1017 | - }, | ||
| 1018 | - "engines": { | ||
| 1019 | - "node": ">=20.0.0" | ||
| 1020 | - } | ||
| 1021 | - }, | ||
| 1022 | "node_modules/@sveltejs/acorn-typescript": { | 941 | "node_modules/@sveltejs/acorn-typescript": { |
| 1023 | "version": "1.0.9", | 942 | "version": "1.0.9", |
| 1024 | "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", | 943 | "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", |
| ... | @@ -1415,21 +1334,6 @@ | ... | @@ -1415,21 +1334,6 @@ |
| 1415 | "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", | 1334 | "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", |
| 1416 | "license": "MIT" | 1335 | "license": "MIT" |
| 1417 | }, | 1336 | }, |
| 1418 | - "node_modules/@types/node": { | ||
| 1419 | - "version": "25.3.2", | ||
| 1420 | - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.2.tgz", | ||
| 1421 | - "integrity": "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==", | ||
| 1422 | - "license": "MIT", | ||
| 1423 | - "dependencies": { | ||
| 1424 | - "undici-types": "~7.18.0" | ||
| 1425 | - } | ||
| 1426 | - }, | ||
| 1427 | - "node_modules/@types/phoenix": { | ||
| 1428 | - "version": "1.6.7", | ||
| 1429 | - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", | ||
| 1430 | - "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", | ||
| 1431 | - "license": "MIT" | ||
| 1432 | - }, | ||
| 1433 | "node_modules/@types/resolve": { | 1337 | "node_modules/@types/resolve": { |
| 1434 | "version": "1.20.2", | 1338 | "version": "1.20.2", |
| 1435 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", | 1339 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", |
| ... | @@ -1442,15 +1346,6 @@ | ... | @@ -1442,15 +1346,6 @@ |
| 1442 | "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", | 1346 | "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", |
| 1443 | "license": "MIT" | 1347 | "license": "MIT" |
| 1444 | }, | 1348 | }, |
| 1445 | - "node_modules/@types/ws": { | ||
| 1446 | - "version": "8.18.1", | ||
| 1447 | - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", | ||
| 1448 | - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", | ||
| 1449 | - "license": "MIT", | ||
| 1450 | - "dependencies": { | ||
| 1451 | - "@types/node": "*" | ||
| 1452 | - } | ||
| 1453 | - }, | ||
| 1454 | "node_modules/acorn": { | 1349 | "node_modules/acorn": { |
| 1455 | "version": "8.16.0", | 1350 | "version": "8.16.0", |
| 1456 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", | 1351 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", |
| ... | @@ -2114,15 +2009,6 @@ | ... | @@ -2114,15 +2009,6 @@ |
| 2114 | "integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==", | 2009 | "integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==", |
| 2115 | "license": "MIT" | 2010 | "license": "MIT" |
| 2116 | }, | 2011 | }, |
| 2117 | - "node_modules/iceberg-js": { | ||
| 2118 | - "version": "0.8.1", | ||
| 2119 | - "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", | ||
| 2120 | - "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", | ||
| 2121 | - "license": "MIT", | ||
| 2122 | - "engines": { | ||
| 2123 | - "node": ">=20.0.0" | ||
| 2124 | - } | ||
| 2125 | - }, | ||
| 2126 | "node_modules/iconv-lite": { | 2012 | "node_modules/iconv-lite": { |
| 2127 | "version": "0.6.3", | 2013 | "version": "0.6.3", |
| 2128 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", | 2014 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", |
| ... | @@ -2775,13 +2661,9 @@ | ... | @@ -2775,13 +2661,9 @@ |
| 2775 | "version": "2.8.1", | 2661 | "version": "2.8.1", |
| 2776 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", | 2662 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", |
| 2777 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", | 2663 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", |
| 2778 | - "license": "0BSD" | 2664 | + "dev": true, |
| 2779 | - }, | 2665 | + "license": "0BSD", |
| 2780 | - "node_modules/undici-types": { | 2666 | + "optional": true |
| 2781 | - "version": "7.18.2", | ||
| 2782 | - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", | ||
| 2783 | - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", | ||
| 2784 | - "license": "MIT" | ||
| 2785 | }, | 2667 | }, |
| 2786 | "node_modules/vite": { | 2668 | "node_modules/vite": { |
| 2787 | "version": "7.3.1", | 2669 | "version": "7.3.1", |
| ... | @@ -2876,27 +2758,6 @@ | ... | @@ -2876,27 +2758,6 @@ |
| 2876 | } | 2758 | } |
| 2877 | } | 2759 | } |
| 2878 | }, | 2760 | }, |
| 2879 | - "node_modules/ws": { | ||
| 2880 | - "version": "8.19.0", | ||
| 2881 | - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", | ||
| 2882 | - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", | ||
| 2883 | - "license": "MIT", | ||
| 2884 | - "engines": { | ||
| 2885 | - "node": ">=10.0.0" | ||
| 2886 | - }, | ||
| 2887 | - "peerDependencies": { | ||
| 2888 | - "bufferutil": "^4.0.1", | ||
| 2889 | - "utf-8-validate": ">=5.0.2" | ||
| 2890 | - }, | ||
| 2891 | - "peerDependenciesMeta": { | ||
| 2892 | - "bufferutil": { | ||
| 2893 | - "optional": true | ||
| 2894 | - }, | ||
| 2895 | - "utf-8-validate": { | ||
| 2896 | - "optional": true | ||
| 2897 | - } | ||
| 2898 | - } | ||
| 2899 | - }, | ||
| 2900 | "node_modules/zimmerframe": { | 2761 | "node_modules/zimmerframe": { |
| 2901 | "version": "1.1.4", | 2762 | "version": "1.1.4", |
| 2902 | "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", | 2763 | "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", | ... | ... |
| ... | @@ -20,7 +20,6 @@ | ... | @@ -20,7 +20,6 @@ |
| 20 | }, | 20 | }, |
| 21 | "dependencies": { | 21 | "dependencies": { |
| 22 | "@observablehq/plot": "^0.6.17", | 22 | "@observablehq/plot": "^0.6.17", |
| 23 | - "@supabase/supabase-js": "^2.98.0", | ||
| 24 | "@sveltejs/adapter-node": "^5.5.4", | 23 | "@sveltejs/adapter-node": "^5.5.4", |
| 25 | "d3": "^7.9.0", | 24 | "d3": "^7.9.0", |
| 26 | "fuse.js": "^7.1.0", | 25 | "fuse.js": "^7.1.0", | ... | ... |
src/lib/services/search.js
deleted
100644 → 0
| 1 | -import Fuse from 'fuse.js'; | ||
| 2 | -import { supabase } from '$lib/supabase'; | ||
| 3 | -import { normalizeText } from '$lib/utils/normalize'; | ||
| 4 | - | ||
| 5 | -let fuseInstance = null; | ||
| 6 | -let searchData = []; | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * Configuración de Fuse.js | ||
| 10 | - */ | ||
| 11 | -const fuseOptions = { | ||
| 12 | - keys: [ | ||
| 13 | - { name: 'nombre_normalizado', weight: 0.6 }, | ||
| 14 | - { name: 'sigla_normalizada', weight: 0.2 }, | ||
| 15 | - { name: 'codigo_normalizado', weight: 0.2 } | ||
| 16 | - ], | ||
| 17 | - threshold: 0.4, // 0 = exacto, 1 = muy fuzzy | ||
| 18 | - distance: 100, | ||
| 19 | - includeScore: true, | ||
| 20 | - includeMatches: true, | ||
| 21 | - minMatchCharLength: 2, | ||
| 22 | - ignoreLocation: true | ||
| 23 | -}; | ||
| 24 | - | ||
| 25 | -/** | ||
| 26 | - * Carga las entidades y objetos de gasto desde Supabase y construye el índice | ||
| 27 | - */ | ||
| 28 | -export async function initSearchIndex() { | ||
| 29 | - if (fuseInstance) return fuseInstance; | ||
| 30 | - | ||
| 31 | - // Cargar entidades | ||
| 32 | - const { data: entidades, error: errorEntidades } = await supabase | ||
| 33 | - .schema('ppto') | ||
| 34 | - .from('clas_institucional') | ||
| 35 | - .select('entidad, desc_entidad, sigla_entidad, desc_area, n_gestiones'); | ||
| 36 | - | ||
| 37 | - if (errorEntidades) { | ||
| 38 | - console.error('Error cargando entidades:', errorEntidades); | ||
| 39 | - throw errorEntidades; | ||
| 40 | - } | ||
| 41 | - | ||
| 42 | - // Cargar objetos de gasto | ||
| 43 | - const { data: objetos, error: errorObjetos } = await supabase | ||
| 44 | - .schema('ppto') | ||
| 45 | - .from('clas_objetos') | ||
| 46 | - .select('objeto, desc_objeto, nivel'); | ||
| 47 | - | ||
| 48 | - if (errorObjetos) { | ||
| 49 | - console.error('Error cargando objetos:', errorObjetos); | ||
| 50 | - throw errorObjetos; | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - // Cargar rubros | ||
| 54 | - const { data: rubros, error: errorRubros } = await supabase | ||
| 55 | - .schema('ppto') | ||
| 56 | - .from('clas_rubros') | ||
| 57 | - .select('rubro, desc_rubro, nivel'); | ||
| 58 | - | ||
| 59 | - if (errorRubros) { | ||
| 60 | - console.error('Error cargando rubros:', errorRubros); | ||
| 61 | - throw errorRubros; | ||
| 62 | - } | ||
| 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 | - | ||
| 101 | - // Transformar entidades para el índice | ||
| 102 | - const entidadesIndex = entidades.map(item => ({ | ||
| 103 | - tipo: 'entidad', | ||
| 104 | - codigo: item.entidad, | ||
| 105 | - nombre: item.desc_entidad, | ||
| 106 | - sigla: item.sigla_entidad, | ||
| 107 | - contexto: item.desc_area, | ||
| 108 | - años: item.n_gestiones, | ||
| 109 | - nombre_normalizado: normalizeText(item.desc_entidad), | ||
| 110 | - sigla_normalizada: normalizeText(item.sigla_entidad), | ||
| 111 | - codigo_normalizado: item.entidad?.toString() || '' | ||
| 112 | - })); | ||
| 113 | - | ||
| 114 | - // Transformar objetos de gasto para el índice | ||
| 115 | - const nivelLabelsObjetos = { grupo: 'Grupo', subgrupo: 'Subgrupo', partida: 'Partida', subpartida: 'Subpartida' }; | ||
| 116 | - const objetosIndex = objetos.map(item => ({ | ||
| 117 | - tipo: 'objeto_gasto', | ||
| 118 | - codigo: item.objeto, | ||
| 119 | - nombre: item.desc_objeto, | ||
| 120 | - sigla: null, | ||
| 121 | - contexto: `Objeto del Gasto · ${nivelLabelsObjetos[item.nivel] || item.nivel}`, | ||
| 122 | - nivel: item.nivel, | ||
| 123 | - nombre_normalizado: normalizeText(item.desc_objeto), | ||
| 124 | - sigla_normalizada: '', | ||
| 125 | - codigo_normalizado: item.objeto || '' | ||
| 126 | - })); | ||
| 127 | - | ||
| 128 | - // Transformar rubros para el índice | ||
| 129 | - const nivelLabelsRubros = { tipo: 'Tipo', clase: 'Clase', cuenta: 'Cuenta', sub_cuenta: 'Subcuenta' }; | ||
| 130 | - const rubrosIndex = rubros.map(item => ({ | ||
| 131 | - tipo: 'rubro', | ||
| 132 | - codigo: item.rubro, | ||
| 133 | - nombre: item.desc_rubro, | ||
| 134 | - sigla: null, | ||
| 135 | - contexto: `Rubro · ${nivelLabelsRubros[item.nivel] || item.nivel}`, | ||
| 136 | - nivel: item.nivel, | ||
| 137 | - nombre_normalizado: normalizeText(item.desc_rubro), | ||
| 138 | - sigla_normalizada: '', | ||
| 139 | - codigo_normalizado: item.rubro?.toString() || '' | ||
| 140 | - })); | ||
| 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 | - | ||
| 183 | - // Combinar todos los conjuntos | ||
| 184 | - searchData = [...entidadesIndex, ...objetosIndex, ...rubrosIndex, ...finfunIndex, ...organismosIndex, ...fuentesIndex]; | ||
| 185 | - | ||
| 186 | - fuseInstance = new Fuse(searchData, fuseOptions); | ||
| 187 | - | ||
| 188 | - return fuseInstance; | ||
| 189 | -} | ||
| 190 | - | ||
| 191 | -/** | ||
| 192 | - * Busca en el índice | ||
| 193 | - * @param {string} query - Texto de búsqueda | ||
| 194 | - * @param {number} limit - Máximo de resultados (default 30) | ||
| 195 | - * @returns {Array} Resultados de búsqueda | ||
| 196 | - */ | ||
| 197 | -export function search(query, limit = 30) { | ||
| 198 | - if (!fuseInstance) { | ||
| 199 | - console.warn('Índice no inicializado. Llama initSearchIndex() primero.'); | ||
| 200 | - return []; | ||
| 201 | - } | ||
| 202 | - | ||
| 203 | - if (!query || query.length < 2) { | ||
| 204 | - return []; | ||
| 205 | - } | ||
| 206 | - | ||
| 207 | - const normalizedQuery = normalizeText(query); | ||
| 208 | - const results = fuseInstance.search(normalizedQuery, { limit }); | ||
| 209 | - | ||
| 210 | - return results.map(result => ({ | ||
| 211 | - ...result.item, | ||
| 212 | - score: result.score, | ||
| 213 | - matches: result.matches | ||
| 214 | - })); | ||
| 215 | -} | ||
| 216 | - | ||
| 217 | -/** | ||
| 218 | - * Obtiene todas las entidades (sin búsqueda) | ||
| 219 | - */ | ||
| 220 | -export function getAllEntidades() { | ||
| 221 | - return searchData.filter(item => item.tipo === 'entidad'); | ||
| 222 | -} | ||
| 223 | - | ||
| 224 | -/** | ||
| 225 | - * Obtiene todos los objetos de gasto (sin búsqueda) | ||
| 226 | - */ | ||
| 227 | -export function getAllObjetos() { | ||
| 228 | - return searchData.filter(item => item.tipo === 'objeto_gasto'); | ||
| 229 | -} | ||
| 230 | - | ||
| 231 | -/** | ||
| 232 | - * Verifica si el índice está cargado | ||
| 233 | - */ | ||
| 234 | -export function isIndexReady() { | ||
| 235 | - return fuseInstance !== null; | ||
| 236 | -} |
| 1 | -import { writable, derived, get } from 'svelte/store'; | 1 | +import { writable, derived } from 'svelte/store'; |
| 2 | -import { initSearchIndex, search, isIndexReady } from '$lib/services/search'; | ||
| 3 | 2 | ||
| 4 | -// Estado del buscador | ||
| 5 | export const query = writable(''); | 3 | export const query = writable(''); |
| 6 | export const results = writable([]); | 4 | export const results = writable([]); |
| 7 | export const isLoading = writable(false); | 5 | export const isLoading = writable(false); |
| 8 | -export const indexLoaded = writable(false); | 6 | +export const indexLoaded = writable(true); |
| 9 | export const error = writable(null); | 7 | export const error = writable(null); |
| 10 | export const selectedIndex = writable(-1); | 8 | export const selectedIndex = writable(-1); |
| 11 | -export const filterType = writable('todos'); // todos, entidad, objeto_gasto, rubro, finfun | 9 | +export const filterType = writable('todos'); |
| 12 | 10 | ||
| 13 | -// Estado derivado: si está mostrando resultados | ||
| 14 | export const showResults = derived( | 11 | export const showResults = derived( |
| 15 | [query, results, indexLoaded], | 12 | [query, results, indexLoaded], |
| 16 | ([$query, $results, $indexLoaded]) => $indexLoaded && $query.length >= 2 | 13 | ([$query, $results, $indexLoaded]) => $indexLoaded && $query.length >= 2 |
| 17 | ); | 14 | ); |
| 18 | 15 | ||
| 19 | -/** | ||
| 20 | - * Inicializa el índice de búsqueda | ||
| 21 | - */ | ||
| 22 | export async function initIndex() { | 16 | export async function initIndex() { |
| 23 | - if (isIndexReady()) { | 17 | + indexLoaded.set(true); |
| 24 | - indexLoaded.set(true); | ||
| 25 | - return; | ||
| 26 | - } | ||
| 27 | - | ||
| 28 | - isLoading.set(true); | ||
| 29 | - error.set(null); | ||
| 30 | - | ||
| 31 | - try { | ||
| 32 | - await initSearchIndex(); | ||
| 33 | - indexLoaded.set(true); | ||
| 34 | - } catch (e) { | ||
| 35 | - error.set('Error al cargar el índice de búsqueda'); | ||
| 36 | - console.error(e); | ||
| 37 | - } finally { | ||
| 38 | - isLoading.set(false); | ||
| 39 | - } | ||
| 40 | } | 18 | } |
| 41 | 19 | ||
| 42 | -/** | 20 | +export function performSearch() {} |
| 43 | - * Realiza la búsqueda | ||
| 44 | - */ | ||
| 45 | -export function performSearch(searchQuery, filter = null) { | ||
| 46 | - query.set(searchQuery); | ||
| 47 | - selectedIndex.set(-1); | ||
| 48 | - | ||
| 49 | - if (searchQuery.length < 2) { | ||
| 50 | - results.set([]); | ||
| 51 | - return; | ||
| 52 | - } | ||
| 53 | - | ||
| 54 | - let searchResults = search(searchQuery, 30); | ||
| 55 | - | ||
| 56 | - // Filtrar por tipo si no es "todos" | ||
| 57 | - const currentFilter = filter !== null ? filter : get(filterType); | ||
| 58 | - if (currentFilter !== 'todos') { | ||
| 59 | - searchResults = searchResults.filter(r => r.tipo === currentFilter); | ||
| 60 | - } | ||
| 61 | - | ||
| 62 | - // Limitar resultados | ||
| 63 | - results.set(searchResults.slice(0, 15)); | ||
| 64 | -} | ||
| 65 | - | ||
| 66 | -/** | ||
| 67 | - * Limpia la búsqueda | ||
| 68 | - */ | ||
| 69 | export function clearSearch() { | 21 | export function clearSearch() { |
| 70 | query.set(''); | 22 | query.set(''); |
| 71 | results.set([]); | 23 | results.set([]); |
| 72 | selectedIndex.set(-1); | 24 | selectedIndex.set(-1); |
| 73 | } | 25 | } |
| 74 | - | 26 | +export function setFilter() {} |
| 75 | -/** | 27 | +export function navigateResults() {} |
| 76 | - * Cambia el filtro de tipo | ||
| 77 | - */ | ||
| 78 | -export function setFilter(type) { | ||
| 79 | - filterType.set(type); | ||
| 80 | - // Re-ejecutar búsqueda con el nuevo filtro | ||
| 81 | - const currentQuery = get(query); | ||
| 82 | - if (currentQuery.length >= 2) { | ||
| 83 | - performSearch(currentQuery, type); | ||
| 84 | - } | ||
| 85 | -} | ||
| 86 | - | ||
| 87 | -/** | ||
| 88 | - * Navegación por teclado | ||
| 89 | - */ | ||
| 90 | -export function navigateResults(direction, totalResults) { | ||
| 91 | - selectedIndex.update(current => { | ||
| 92 | - if (direction === 'down') { | ||
| 93 | - return current < totalResults - 1 ? current + 1 : 0; | ||
| 94 | - } else if (direction === 'up') { | ||
| 95 | - return current > 0 ? current - 1 : totalResults - 1; | ||
| 96 | - } | ||
| 97 | - return current; | ||
| 98 | - }); | ||
| 99 | -} | ... | ... |
src/lib/supabase.js
deleted
100644 → 0
src/routes/api/test/+server.js
deleted
100644 → 0
| 1 | -import { supabase } from '$lib/supabase'; | ||
| 2 | -import { json } from '@sveltejs/kit'; | ||
| 3 | - | ||
| 4 | -export async function GET() { | ||
| 5 | - const { data, error } = await supabase | ||
| 6 | - .schema('ppto') | ||
| 7 | - .from('clas_institucional') | ||
| 8 | - .select('*') | ||
| 9 | - .limit(5); | ||
| 10 | - | ||
| 11 | - if (error) { | ||
| 12 | - return json({ error: error.message }, { status: 500 }); | ||
| 13 | - } | ||
| 14 | - | ||
| 15 | - return json({ | ||
| 16 | - success: true, | ||
| 17 | - count: data.length, | ||
| 18 | - columns: data.length > 0 ? Object.keys(data[0]) : [], | ||
| 19 | - sample: data | ||
| 20 | - }); | ||
| 21 | -} |
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 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 | -} |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
src/routes/test/+page.svelte
deleted
100644 → 0
This diff is collapsed. Click to expand it.
| 1 | -import { supabase } from '$lib/supabase'; | ||
| 2 | - | ||
| 3 | -// Hardcoded a objeto 11700 (Sueldos) para testing de layout | ||
| 4 | -const CODIGO = '11700'; | ||
| 5 | - | ||
| 6 | -function getGrupoCode(objeto) { return objeto.charAt(0) + '0000'; } | ||
| 7 | -function getSubgrupoCode(objeto) { return objeto.substring(0, 2) + '000'; } | ||
| 8 | -function getPartidaCode(objeto) { return objeto.substring(0, 3) + '00'; } | ||
| 9 | - | ||
| 10 | -export async function load() { | ||
| 11 | - const { data } = await supabase.schema('ppto').from('clas_objetos').select('*').eq('objeto', CODIGO); | ||
| 12 | - const objeto = data[0]; | ||
| 13 | - | ||
| 14 | - const grupoCode = getGrupoCode(CODIGO); | ||
| 15 | - const subgrupoCode = getSubgrupoCode(CODIGO); | ||
| 16 | - const partidaCode = getPartidaCode(CODIGO); | ||
| 17 | - | ||
| 18 | - const { data: padresData } = await supabase.schema('ppto').from('clas_objetos').select('*').in('objeto', [grupoCode, subgrupoCode, partidaCode]).order('objeto'); | ||
| 19 | - const padres = padresData || []; | ||
| 20 | - | ||
| 21 | - // Contar entidades | ||
| 22 | - const nEntidades = objeto.n_entidades || 0; | ||
| 23 | - | ||
| 24 | - return { objeto, padres, hijos: [], nEntidades }; | ||
| 25 | -} |
src/routes/test/layout/+page.svelte
deleted
100644 → 0
This diff is collapsed. Click to expand it.
| 1 | -import { supabase } from '$lib/supabase'; | ||
| 2 | -import { error } from '@sveltejs/kit'; | ||
| 3 | - | ||
| 4 | -// Funciones para derivar jerarquía del código objeto | ||
| 5 | -function getGrupoCode(objeto) { | ||
| 6 | - return objeto.charAt(0) + '0000'; | ||
| 7 | -} | ||
| 8 | - | ||
| 9 | -function getSubgrupoCode(objeto) { | ||
| 10 | - return objeto.substring(0, 2) + '000'; | ||
| 11 | -} | ||
| 12 | - | ||
| 13 | -function getPartidaCode(objeto) { | ||
| 14 | - return objeto.substring(0, 3) + '00'; | ||
| 15 | -} | ||
| 16 | - | ||
| 17 | -function getNivel(objeto) { | ||
| 18 | - if (objeto.endsWith('0000')) return 'grupo'; | ||
| 19 | - if (objeto.endsWith('000')) return 'subgrupo'; | ||
| 20 | - if (objeto.endsWith('00')) return 'partida'; | ||
| 21 | - return 'subpartida'; | ||
| 22 | -} | ||
| 23 | - | ||
| 24 | -export async function load({ params }) { | ||
| 25 | - console.time('[SERVER] Total load objeto'); | ||
| 26 | - const { codigo } = params; | ||
| 27 | - | ||
| 28 | - console.time('[SERVER] Query clas_objetos'); | ||
| 29 | - const { data, error: dbError } = await supabase | ||
| 30 | - .schema('ppto') | ||
| 31 | - .from('clas_objetos') | ||
| 32 | - .select('*') | ||
| 33 | - .eq('objeto', codigo); | ||
| 34 | - console.timeEnd('[SERVER] Query clas_objetos'); | ||
| 35 | - | ||
| 36 | - if (dbError || !data || data.length === 0) { | ||
| 37 | - throw error(404, 'Objeto no encontrado'); | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | - // Tomar el primer resultado | ||
| 41 | - const objeto = data[0]; | ||
| 42 | - const nivel = objeto.nivel || getNivel(codigo); | ||
| 43 | - | ||
| 44 | - // Obtener jerarquía (padres e hijos) | ||
| 45 | - let padres = []; | ||
| 46 | - let hijos = []; | ||
| 47 | - | ||
| 48 | - console.time('[SERVER] Query padres'); | ||
| 49 | - | ||
| 50 | - // Buscar padres según nivel | ||
| 51 | - if (nivel === 'subpartida') { | ||
| 52 | - // Padres: grupo, subgrupo, partida | ||
| 53 | - const grupoCode = getGrupoCode(codigo); | ||
| 54 | - const subgrupoCode = getSubgrupoCode(codigo); | ||
| 55 | - const partidaCode = getPartidaCode(codigo); | ||
| 56 | - | ||
| 57 | - const { data: padresData } = await supabase | ||
| 58 | - .schema('ppto') | ||
| 59 | - .from('clas_objetos') | ||
| 60 | - .select('*') | ||
| 61 | - .in('objeto', [grupoCode, subgrupoCode, partidaCode]) | ||
| 62 | - .order('objeto'); | ||
| 63 | - | ||
| 64 | - if (padresData) padres = padresData; | ||
| 65 | - } else if (nivel === 'partida') { | ||
| 66 | - // Padres: grupo, subgrupo | ||
| 67 | - const grupoCode = getGrupoCode(codigo); | ||
| 68 | - const subgrupoCode = getSubgrupoCode(codigo); | ||
| 69 | - | ||
| 70 | - const { data: padresData } = await supabase | ||
| 71 | - .schema('ppto') | ||
| 72 | - .from('clas_objetos') | ||
| 73 | - .select('*') | ||
| 74 | - .in('objeto', [grupoCode, subgrupoCode]) | ||
| 75 | - .order('objeto'); | ||
| 76 | - | ||
| 77 | - if (padresData) padres = padresData; | ||
| 78 | - } else if (nivel === 'subgrupo') { | ||
| 79 | - // Padre: grupo | ||
| 80 | - const grupoCode = getGrupoCode(codigo); | ||
| 81 | - | ||
| 82 | - const { data: padresData } = await supabase | ||
| 83 | - .schema('ppto') | ||
| 84 | - .from('clas_objetos') | ||
| 85 | - .select('*') | ||
| 86 | - .eq('objeto', grupoCode); | ||
| 87 | - | ||
| 88 | - if (padresData) padres = padresData; | ||
| 89 | - } | ||
| 90 | - // grupo no tiene padres | ||
| 91 | - | ||
| 92 | - console.timeEnd('[SERVER] Query padres'); | ||
| 93 | - | ||
| 94 | - console.time('[SERVER] Query hijos'); | ||
| 95 | - | ||
| 96 | - // Buscar hijos según nivel | ||
| 97 | - if (nivel === 'grupo') { | ||
| 98 | - // Hijos: subgrupos que empiecen con el mismo dígito | ||
| 99 | - const prefix = codigo.charAt(0); | ||
| 100 | - const { data: hijosData } = await supabase | ||
| 101 | - .schema('ppto') | ||
| 102 | - .from('clas_objetos') | ||
| 103 | - .select('*') | ||
| 104 | - .eq('nivel', 'subgrupo') | ||
| 105 | - .like('objeto', `${prefix}%`) | ||
| 106 | - .order('objeto'); | ||
| 107 | - | ||
| 108 | - hijos = hijosData || []; | ||
| 109 | - } else if (nivel === 'subgrupo') { | ||
| 110 | - // Hijos: partidas que empiecen con los mismos 2 dígitos | ||
| 111 | - const prefix = codigo.substring(0, 2); | ||
| 112 | - const { data: hijosData } = await supabase | ||
| 113 | - .schema('ppto') | ||
| 114 | - .from('clas_objetos') | ||
| 115 | - .select('*') | ||
| 116 | - .eq('nivel', 'partida') | ||
| 117 | - .like('objeto', `${prefix}%`) | ||
| 118 | - .order('objeto'); | ||
| 119 | - | ||
| 120 | - hijos = hijosData || []; | ||
| 121 | - } else if (nivel === 'partida') { | ||
| 122 | - // Hijos: subpartidas que empiecen con los mismos 3 dígitos | ||
| 123 | - const prefix = codigo.substring(0, 3); | ||
| 124 | - const { data: hijosData } = await supabase | ||
| 125 | - .schema('ppto') | ||
| 126 | - .from('clas_objetos') | ||
| 127 | - .select('*') | ||
| 128 | - .eq('nivel', 'subpartida') | ||
| 129 | - .like('objeto', `${prefix}%`) | ||
| 130 | - .order('objeto'); | ||
| 131 | - | ||
| 132 | - hijos = hijosData || []; | ||
| 133 | - } | ||
| 134 | - // subpartida no tiene hijos | ||
| 135 | - | ||
| 136 | - console.timeEnd('[SERVER] Query hijos'); | ||
| 137 | - | ||
| 138 | - console.timeEnd('[SERVER] Total load objeto'); | ||
| 139 | - return { | ||
| 140 | - objeto, | ||
| 141 | - padres, | ||
| 142 | - hijos | ||
| 143 | - }; | ||
| 144 | -} |
This diff is collapsed. Click to expand it.
src/routes/test/mapa/+page.svelte
deleted
100644 → 0
| 1 | -<script> | ||
| 2 | - import { onMount } from 'svelte'; | ||
| 3 | - import * as d3 from 'd3'; | ||
| 4 | - | ||
| 5 | - let mapaData = $state(null); | ||
| 6 | - let boliviaPath = $state(''); | ||
| 7 | - let deptoPath = $state(''); | ||
| 8 | - let muniPath = $state(''); | ||
| 9 | - let error = $state(''); | ||
| 10 | - let debug = $state(''); | ||
| 11 | - | ||
| 12 | - onMount(async () => { | ||
| 13 | - try { | ||
| 14 | - const res = await fetch('/mapa.json'); | ||
| 15 | - mapaData = await res.json(); | ||
| 16 | - | ||
| 17 | - debug = `bolivia: ${mapaData.bolivia.features.length} features, `; | ||
| 18 | - debug += `deptos: ${mapaData.departamentos.features.length}, `; | ||
| 19 | - debug += `munis: ${mapaData.municipios.features.length}`; | ||
| 20 | - | ||
| 21 | - // Quitar CRS y corregir winding order | ||
| 22 | - delete mapaData.bolivia.crs; | ||
| 23 | - delete mapaData.departamentos.crs; | ||
| 24 | - delete mapaData.municipios.crs; | ||
| 25 | - | ||
| 26 | - // Corregir winding order: invertir todos los anillos | ||
| 27 | - function fixWinding(geojson) { | ||
| 28 | - const fix = (coords, isHole) => { | ||
| 29 | - // Para geo coords: exterior debe ser counterclockwise (area < 0 en D3) | ||
| 30 | - // Simplemente invertimos todos los anillos exteriores | ||
| 31 | - coords.forEach((ring, i) => { | ||
| 32 | - if (i === 0) ring.reverse(); // exterior | ||
| 33 | - // holes quedan como están | ||
| 34 | - }); | ||
| 35 | - }; | ||
| 36 | - for (const feat of geojson.features || [geojson]) { | ||
| 37 | - const geom = (feat.geometry || feat); | ||
| 38 | - if (geom.type === 'Polygon') { | ||
| 39 | - fix(geom.coordinates); | ||
| 40 | - } else if (geom.type === 'MultiPolygon') { | ||
| 41 | - geom.coordinates.forEach(poly => fix(poly)); | ||
| 42 | - } | ||
| 43 | - } | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - fixWinding(mapaData.bolivia); | ||
| 47 | - fixWinding(mapaData.departamentos); | ||
| 48 | - fixWinding(mapaData.municipios); | ||
| 49 | - | ||
| 50 | - const bounds = d3.geoBounds(mapaData.bolivia); | ||
| 51 | - debug += ` | bounds: ${JSON.stringify(bounds)}`; | ||
| 52 | - | ||
| 53 | - const projection = d3.geoMercator().fitSize([380, 480], mapaData.bolivia); | ||
| 54 | - const pathGen = d3.geoPath(projection); | ||
| 55 | - | ||
| 56 | - boliviaPath = pathGen(mapaData.bolivia) || ''; | ||
| 57 | - debug += ` | boliviaPath length: ${boliviaPath.length}`; | ||
| 58 | - | ||
| 59 | - deptoPath = mapaData.departamentos.features.map(f => pathGen(f)).join(' '); | ||
| 60 | - debug += ` | deptoPath length: ${deptoPath.length}`; | ||
| 61 | - | ||
| 62 | - const muni = mapaData.municipios.features[0]; | ||
| 63 | - muniPath = pathGen(muni) || ''; | ||
| 64 | - debug += ` | muni code: ${muni.properties.codigo}`; | ||
| 65 | - | ||
| 66 | - } catch (e) { | ||
| 67 | - error = e.message; | ||
| 68 | - } | ||
| 69 | - }); | ||
| 70 | -</script> | ||
| 71 | - | ||
| 72 | -<div style="padding: 2rem; background: #1C1C1A; color: #F5F0E8; min-height: 100vh;"> | ||
| 73 | - <h1>Test Mapa</h1> | ||
| 74 | - <p style="font-size: 12px; color: #888;">{debug}</p> | ||
| 75 | - {#if error} | ||
| 76 | - <p style="color: red;">{error}</p> | ||
| 77 | - {/if} | ||
| 78 | - | ||
| 79 | - <svg width="400" height="500" viewBox="0 0 400 500" style="border: 1px solid #333;"> | ||
| 80 | - {#if boliviaPath} | ||
| 81 | - <path d={boliviaPath} fill="none" stroke="#555" stroke-width="1" /> | ||
| 82 | - {/if} | ||
| 83 | - {#if deptoPath} | ||
| 84 | - <path d={deptoPath} fill="rgba(255,255,255,0.1)" stroke="#777" stroke-width="0.5" /> | ||
| 85 | - {/if} | ||
| 86 | - {#if muniPath} | ||
| 87 | - <path d={muniPath} fill="#D4A574" stroke="#D4A574" stroke-width="1" /> | ||
| 88 | - {/if} | ||
| 89 | - </svg> | ||
| 90 | -</div> |
| 1 | -import { supabase } from '$lib/supabase'; | 1 | +export const ssr = false; |
| 2 | 2 | ||
| 3 | export async function load({ fetch }) { | 3 | export async function load({ fetch }) { |
| 4 | - // Cargar resumen y población en paralelo | 4 | + const pobRes = await fetch('/poblacion.csv').then(r => r.text()); |
| 5 | - const [resumenRes, pobRes] = await Promise.all([ | ||
| 6 | - supabase | ||
| 7 | - .schema('ppto') | ||
| 8 | - .from('entidad_resumen') | ||
| 9 | - .select('codigo, desc, gestion, devengado, ranking') | ||
| 10 | - .eq('tipo', 'gastos') | ||
| 11 | - .eq('tipo_codigo', 'municipio_ubigeo') | ||
| 12 | - .eq('gestion', 2025) | ||
| 13 | - .order('devengado', { ascending: false }) | ||
| 14 | - .limit(500), | ||
| 15 | - | ||
| 16 | - fetch('/poblacion.csv').then(r => r.text()) | ||
| 17 | - ]); | ||
| 18 | 5 | ||
| 19 | const poblacionMap = {}; | 6 | const poblacionMap = {}; |
| 20 | pobRes.split('\n').slice(1).forEach(line => { | 7 | pobRes.split('\n').slice(1).forEach(line => { |
| ... | @@ -27,7 +14,7 @@ export async function load({ fetch }) { | ... | @@ -27,7 +14,7 @@ export async function load({ fetch }) { |
| 27 | const gestiones = [2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025]; | 14 | const gestiones = [2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025]; |
| 28 | 15 | ||
| 29 | return { | 16 | return { |
| 30 | - resumen: resumenRes.data || [], | 17 | + resumen: [], |
| 31 | poblacionMap, | 18 | poblacionMap, |
| 32 | gestiones, | 19 | gestiones, |
| 33 | gestionInicial: 2025 | 20 | gestionInicial: 2025 | ... | ... |
| ... | @@ -4,7 +4,6 @@ | ... | @@ -4,7 +4,6 @@ |
| 4 | import { goto } from '$app/navigation'; | 4 | import { goto } from '$app/navigation'; |
| 5 | import { page } from '$app/stores'; | 5 | import { page } from '$app/stores'; |
| 6 | import * as d3 from 'd3'; | 6 | import * as d3 from 'd3'; |
| 7 | - import { supabase } from '$lib/supabase'; | ||
| 8 | import { mapaCache } from '$lib/stores/mapaCache'; | 7 | import { mapaCache } from '$lib/stores/mapaCache'; |
| 9 | 8 | ||
| 10 | let { data } = $props(); | 9 | let { data } = $props(); |
| ... | @@ -38,6 +37,22 @@ | ... | @@ -38,6 +37,22 @@ |
| 38 | let clasSearchVal = $state(''); | 37 | let clasSearchVal = $state(''); |
| 39 | let clasSearchResults = $state([]); | 38 | let clasSearchResults = $state([]); |
| 40 | let clasSearchFocused = $state(false); | 39 | let clasSearchFocused = $state(false); |
| 40 | + let clasSelectedIdx = $state(-1); | ||
| 41 | + | ||
| 42 | + function handleClasKeydown(e) { | ||
| 43 | + if (e.key === 'ArrowDown') { | ||
| 44 | + e.preventDefault(); | ||
| 45 | + clasSelectedIdx = clasSelectedIdx < clasSearchResults.length - 1 ? clasSelectedIdx + 1 : 0; | ||
| 46 | + } else if (e.key === 'ArrowUp') { | ||
| 47 | + e.preventDefault(); | ||
| 48 | + clasSelectedIdx = clasSelectedIdx > 0 ? clasSelectedIdx - 1 : clasSearchResults.length - 1; | ||
| 49 | + } else if (e.key === 'Enter' && clasSelectedIdx >= 0) { | ||
| 50 | + e.preventDefault(); | ||
| 51 | + seleccionarClasificador(clasSearchResults[clasSelectedIdx]); | ||
| 52 | + } else if (e.key === 'Escape') { | ||
| 53 | + clasSearchFocused = false; | ||
| 54 | + } | ||
| 55 | + } | ||
| 41 | let clasSearchLoading = $state(false); | 56 | let clasSearchLoading = $state(false); |
| 42 | let clasSeleccionado = $state(null); | 57 | let clasSeleccionado = $state(null); |
| 43 | let clasDebounce; | 58 | let clasDebounce; |
| ... | @@ -89,6 +104,7 @@ | ... | @@ -89,6 +104,7 @@ |
| 89 | 104 | ||
| 90 | function handleClasInput(e) { | 105 | function handleClasInput(e) { |
| 91 | clasSearchVal = e.target.value; | 106 | clasSearchVal = e.target.value; |
| 107 | + clasSelectedIdx = -1; | ||
| 92 | clearTimeout(clasDebounce); | 108 | clearTimeout(clasDebounce); |
| 93 | if (clasSearchVal.length >= 2) { | 109 | if (clasSearchVal.length >= 2) { |
| 94 | clasDebounce = setTimeout(() => buscarClasificador(clasSearchVal), 200); | 110 | clasDebounce = setTimeout(() => buscarClasificador(clasSearchVal), 200); |
| ... | @@ -362,20 +378,9 @@ | ... | @@ -362,20 +378,9 @@ |
| 362 | return; | 378 | return; |
| 363 | } | 379 | } |
| 364 | 380 | ||
| 365 | - // Modo total: cargar desde Supabase | 381 | + // Modo total: datos no disponibles sin Supabase |
| 366 | - if (!resumenCache[gestion]) { | 382 | + // TODO: implementar endpoint API para resumen por municipio |
| 367 | - const { data: rows } = await supabase | 383 | + resumen = []; |
| 368 | - .schema('ppto') | ||
| 369 | - .from('entidad_resumen') | ||
| 370 | - .select('codigo, desc, gestion, devengado, ranking') | ||
| 371 | - .eq('tipo', 'gastos') | ||
| 372 | - .eq('tipo_codigo', 'municipio_ubigeo') | ||
| 373 | - .eq('gestion', gestion) | ||
| 374 | - .order('devengado', { ascending: false }) | ||
| 375 | - .limit(500); | ||
| 376 | - resumenCache[gestion] = rows || []; | ||
| 377 | - } | ||
| 378 | - resumen = resumenCache[gestion]; | ||
| 379 | poblacionMap = await getPoblacionAno(gestion); | 384 | poblacionMap = await getPoblacionAno(gestion); |
| 380 | } | 385 | } |
| 381 | 386 | ||
| ... | @@ -588,6 +593,7 @@ | ... | @@ -588,6 +593,7 @@ |
| 588 | type="text" | 593 | type="text" |
| 589 | value={clasSearchVal} | 594 | value={clasSearchVal} |
| 590 | oninput={handleClasInput} | 595 | oninput={handleClasInput} |
| 596 | + onkeydown={handleClasKeydown} | ||
| 591 | onfocus={() => clasSearchFocused = true} | 597 | onfocus={() => clasSearchFocused = true} |
| 592 | onblur={() => setTimeout(() => clasSearchFocused = false, 150)} | 598 | onblur={() => setTimeout(() => clasSearchFocused = false, 150)} |
| 593 | placeholder="Buscar {clasificadorSeleccionado === 'objeto' ? 'partida' : clasificadorSeleccionado === 'finfun' ? 'finalidad o función' : 'sector'}..." | 599 | placeholder="Buscar {clasificadorSeleccionado === 'objeto' ? 'partida' : clasificadorSeleccionado === 'finfun' ? 'finalidad o función' : 'sector'}..." |
| ... | @@ -599,8 +605,8 @@ | ... | @@ -599,8 +605,8 @@ |
| 599 | {:else if clasSearchResults.length === 0} | 605 | {:else if clasSearchResults.length === 0} |
| 600 | <div class="objeto-search-msg">Sin resultados</div> | 606 | <div class="objeto-search-msg">Sin resultados</div> |
| 601 | {:else} | 607 | {:else} |
| 602 | - {#each clasSearchResults as result} | 608 | + {#each clasSearchResults as result, i} |
| 603 | - <button class="objeto-search-item" onclick={() => seleccionarClasificador(result)}> | 609 | + <button class="objeto-search-item" class:active={clasSelectedIdx === i} onclick={() => seleccionarClasificador(result)}> |
| 604 | <span class="objeto-search-code">{result.codigo}</span> | 610 | <span class="objeto-search-code">{result.codigo}</span> |
| 605 | <span class="objeto-search-name">{@html result.highlight}</span> | 611 | <span class="objeto-search-name">{@html result.highlight}</span> |
| 606 | </button> | 612 | </button> | ... | ... |
-
Please register or login to post a comment