From 8ff13ceb9f6e4df9cd064316198aacc4b5ee26e3 Mon Sep 17 00:00:00 2001 From: Isaac Aracena Date: Fri, 11 Sep 2026 09:51:42 -0400 Subject: [PATCH] Agregar Puerto Rico y actualizar dist validado para produccion --- .env.example | 5 + ...ECCION_CONCILIACION_HONDURAS_2026-09-08.md | 22 + IMPLEMENTACION_PUERTO_RICO_READY.md | 16 + ...Analizar N├│mina con Gemini GT TT PA SV HN PR.json | 280 ++++ ...ina - Analizar N├│mina con Gemini GT TT PA SV.json | 274 ---- ...e Verificaci├│n de N├│mina - Honduras.json | 81 +- ...erificaci├│n de N├│mina - Puerto Rico.json | 1196 +++++++++++++++++ ...tar correcci├│n BambooHR a RRHH - GT TT PA SV.json | 510 ------- ...e N├│mina Puerto Rico - Env├¡o de Reporte.json | 347 +++++ ...│n de N├│mina Puerto Rico - Hist├│ricos.json | 163 +++ SUPABASE_Puerto_Rico_READY.sql | 311 +++++ VALIDACION_PUERTO_RICO_FINAL.md | 18 + dist/assets/index-BmyDbce7.js | 270 ---- dist/assets/index-saVPcx_I.js | 270 ++++ dist/index.html | 2 +- src/components/BambooCorrectionModal.tsx | 1 + src/components/Dashboard.tsx | 60 +- src/components/History.tsx | 18 +- src/config/countries.ts | 8 + src/types.ts | 2 +- src/vite-env.d.ts | 3 + ... - Analizar N├│mina con Gemini GT TT PA SV HN.json | 279 ---- ...e Verificaci├│n de N├│mina - Honduras.json | 81 +- ...erificaci├│n de N├│mina - Puerto Rico.json | 1196 +++++++++++++++++ ... correcci├│n BambooHR a RRHH - GT TT PA SV HN.json | 514 ------- ...e N├│mina Puerto Rico - Env├¡o de Reporte.json | 347 +++++ ...│n de N├│mina Puerto Rico - Hist├│ricos.json | 163 +++ 27 files changed, 4487 insertions(+), 1950 deletions(-) create mode 100644 CORRECCION_CONCILIACION_HONDURAS_2026-09-08.md create mode 100644 IMPLEMENTACION_PUERTO_RICO_READY.md create mode 100644 Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN PR.json delete mode 100644 Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV.json create mode 100644 Portal de Verificaci├│n de N├│mina - Puerto Rico.json delete mode 100644 Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV.json create mode 100644 Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json create mode 100644 Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json create mode 100644 SUPABASE_Puerto_Rico_READY.sql create mode 100644 VALIDACION_PUERTO_RICO_FINAL.md delete mode 100644 dist/assets/index-BmyDbce7.js create mode 100644 dist/assets/index-saVPcx_I.js delete mode 100644 workflows_n8n/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN.json create mode 100644 workflows_n8n/Portal de Verificaci├│n de N├│mina - Puerto Rico.json delete mode 100644 workflows_n8n/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV HN.json create mode 100644 workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json create mode 100644 workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json diff --git a/.env.example b/.env.example index c176bf1..369c173 100644 --- a/.env.example +++ b/.env.example @@ -32,6 +32,11 @@ VITE_N8N_HONDURAS_WEBHOOK_URL="https://agenteit.digitalcompass.agency/webhook/no VITE_HISTORICOS_HN_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-historicos" VITE_MARCAR_RESUELTO_HN_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-marcar-resuelto" +# Puerto Rico: cruce, históricos y resolución. +VITE_N8N_PUERTO_RICO_WEBHOOK_URL="https://agenteit.digitalcompass.agency/webhook/nominapr-bamboo-test" +VITE_HISTORICOS_PR_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pr-historicos" +VITE_MARCAR_RESUELTO_PR_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pr-marcar-resuelto" + # Endpoint opcional de n8n + Gemini para analizar el nombre del archivo de nómina. # La app ya incluye detección local como respaldo. VITE_ANALIZAR_NOMINA_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-analizar-nomina" diff --git a/CORRECCION_CONCILIACION_HONDURAS_2026-09-08.md b/CORRECCION_CONCILIACION_HONDURAS_2026-09-08.md new file mode 100644 index 0000000..b493750 --- /dev/null +++ b/CORRECCION_CONCILIACION_HONDURAS_2026-09-08.md @@ -0,0 +1,22 @@ +# Corrección final de conciliación — Honduras · 08-09-2026 + +Se corrigió el workflow principal de Honduras después de comparar la ejecución real de Junio Q30 con los archivos fuente y con el reporte de referencia. + +Cambios aplicados: + +- Tolerancia monetaria de ±0.05 para diferencias puramente de redondeo. +- La primera pasada de conciliación busca montos dentro de la misma tolerancia, no solo el centavo exacto. +- `Banco sin Nómina` se conserva por transacción bancaria individual y no se agrupa por persona/cuenta. +- Los truncamientos bancarios de un carácter al final (ej. `ALVARAD` → `ALVARADO`) no se reportan como diferencia de nombre. +- No se toleran sustituciones ortográficas internas (ej. `GONZALES` → `GONZALEZ`), que siguen requiriendo revisión. +- Para BambooHR, el nombre legal del cuentahabiente y la pertenencia al período tienen prioridad sobre un nombre corto ambiguo. Esto corrige el falso positivo de Suyapa Palma. +- La auditoría directa del Excel original confirmó que Harold Panchame tiene `756669711` en nómina y banco; por lo tanto no es una cuenta mal digitada. El caso real confirmado es Brenda Baquedano (`745899511` → `745899611`). + +Resultados de referencia finales: + +| Período | Coincidencias | Discrepancias | Banco sin nómina | Banco sin Bamboo | Diferencias nombre banco | Cuenta mal digitada | +|---|---:|---:|---:|---:|---:|---:| +| Junio Q15 | 143 | 122 | 0 | 5 | 0 | 1 | +| Junio Q30 | 331 | 0 | 176 | 5 | 5 | 1 | +| Julio Q15 | 263 | 0 | 0 | 4 | 1 | 1 | +| Julio Q30 | 335 | 0 | 1 | 5 | 3 | 1 | diff --git a/IMPLEMENTACION_PUERTO_RICO_READY.md b/IMPLEMENTACION_PUERTO_RICO_READY.md new file mode 100644 index 0000000..8f5c37b --- /dev/null +++ b/IMPLEMENTACION_PUERTO_RICO_READY.md @@ -0,0 +1,16 @@ +# Puerto Rico — integración READY + +Esta versión parte del paquete corregido existente y conserva sin reemplazo la autenticación Supabase/Google y la lógica de históricos de los países existentes. + +Puerto Rico: +- Código: PR +- Moneda: USD +- Locale: es-PR +- Nómina: un Excel por período +- Banco: PDF de Banco Popular de Puerto Rico +- Webhook principal: /webhook/nominapr-bamboo-test +- Históricos: /webhook/cruce-cuentas-pr-historicos +- Marcar resuelto: /webhook/cruce-cuentas-pr-marcar-resuelto +- Correcciones BambooHR: Noralmy Laureano + +La compilación `dist/` se genera usando el mismo `.env.local` del paquete base, con las tres variables PR agregadas. Esto conserva el login y la conexión a Supabase usada por Históricos. diff --git a/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN PR.json b/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN PR.json new file mode 100644 index 0000000..850a2e1 --- /dev/null +++ b/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN PR.json @@ -0,0 +1,280 @@ +{ + "name": "Portal de Verificación de Nómina - Analizar Nómina con Gemini GT TT PA SV HN PR", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "cruce-cuentas-analizar-nomina", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + 9008, + 3376 + ], + "id": "dd5223e5-ed61-442c-b87a-c3a11c4c2b38", + "name": "Webhook - Analizar Nómina", + "webhookId": "f265d1ec-437f-4a05-a699-76dae8c4d89e" + }, + { + "parameters": { + "jsCode": "const input = $input.first().json || {};\n\nlet body = input.body ?? input;\n\nif (typeof body === 'string') {\n try {\n body = JSON.parse(body);\n } catch (error) {\n body = {};\n }\n}\n\nif (!body || typeof body !== 'object' || Array.isArray(body)) {\n body = {};\n}\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst fileName = clean(\n body.fileName ??\n body.filename ??\n body.file_name ??\n ''\n);\n\nconst country = clean(\n body.country ?? 'GT'\n).toUpperCase();\n\nconst supportedCountries = {\n GT: 'Guatemala',\n TT: 'Trinidad y Tobago',\n PA: 'Panamá',\n SV: 'El Salvador',\n HN: 'Honduras',\n PR: 'Puerto Rico',\n};\n\nconst countryName = clean(\n body.countryName ??\n body.country_name ??\n supportedCountries[country] ??\n ''\n);\n\nconst errors = [];\n\nif (!fileName) {\n errors.push(\n 'No se recibió el nombre del archivo de nómina.'\n );\n}\n\nif (!Object.prototype.hasOwnProperty.call(\n supportedCountries,\n country\n)) {\n errors.push(\n 'El analizador solo está habilitado para Guatemala, Trinidad y Tobago, Panamá, El Salvador, Honduras y Puerto Rico.'\n );\n}\n\nconst supportedExtensions = [\n '.xlsx',\n '.xls',\n '.xlsm',\n '.csv',\n];\n\nconst lowerFileName = fileName.toLowerCase();\n\nconst hasSupportedExtension =\n supportedExtensions.some((extension) =>\n lowerFileName.endsWith(extension)\n );\n\nif (fileName && !hasSupportedExtension) {\n errors.push(\n 'El archivo recibido no tiene una extensión de nómina compatible.'\n );\n}\n\nreturn [\n {\n json: {\n ok: errors.length === 0,\n errors,\n\n fileName,\n country,\n countryName:\n countryName ||\n supportedCountries[country] ||\n '',\n\n receivedAt: new Date().toISOString(),\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 9216, + 3376 + ], + "id": "a9f66bf1-a4d5-40fc-bff1-d09b34b880e2", + "name": "Validar y preparar solicitud" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "bd9fd675-d72b-4dba-ac03-a44f1123ecb0", + "leftValue": "={{ $json.ok }}", + "rightValue": "", + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + 9424, + 3376 + ], + "id": "82cc723d-470e-40f4-8edd-afb9de5b27ed", + "name": "¿Solicitud válida?" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{\n{\n ok: false,\n message:\n 'No fue posible analizar el nombre del archivo.',\n errors: Array.isArray($json.errors)\n ? $json.errors\n : ['La solicitud recibida no es válida.'],\n detected: null\n}\n}}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 9632, + 3472 + ], + "id": "15bd9a4e-cd52-46e3-818a-e316b0132524", + "name": "Responder error" + }, + { + "parameters": { + "text": "={{\n`Analiza este nombre de archivo de nómina:\n\nPaís seleccionado: ${$json.countryName}\nCódigo del país: ${$json.country}\nNombre del archivo: ${$json.fileName}`\n}}", + "schemaType": "manual", + "inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"country\": {\n \"type\": \"string\",\n \"enum\": [\n \"GT\",\n \"TT\",\n \"PA\",\n \"SV\",\n \"HN\",\n \"PR\"\n ]\n },\n \"year\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 2100\n },\n \"month\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 12\n },\n \"period_type\": {\n \"type\": \"string\",\n \"enum\": [\n \"quincena_15\",\n \"quincena_30\",\n \"mensual\",\n \"desconocido\"\n ]\n },\n \"confidence\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1\n },\n \"explanation\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"country\",\n \"year\",\n \"month\",\n \"period_type\",\n \"confidence\",\n \"explanation\"\n ],\n \"additionalProperties\": false\n}", + "options": { + "systemPromptTemplate": "Eres un clasificador determinista de nombres de archivos de nómina.\n\nTu tarea es identificar el país seleccionado, el año, el mes y el período de pago utilizando únicamente la información presente en el nombre del archivo y el país recibido.\n\nNo inventes datos que no estén contenidos o razonablemente codificados en el nombre.\n\nREGLAS GENERALES:\n\n1. Devuelve exactamente el código de país recibido:\n - GT para Guatemala.\n - TT para Trinidad y Tobago.\n - PA para Panamá.\n - SV para El Salvador.\n - HN para Honduras.\n - PR para Puerto Rico.\n\n2. Detecta el año de cuatro dígitos. También puede aparecer dentro de una fecha compacta como YYYYMMDD; por ejemplo, 20260617 representa el año 2026.\n\n3. Convierte el mes a un número aceptando nombres en español o inglés:\n Enero / January = 1\n Febrero / February = 2\n Marzo / March = 3\n Abril / April = 4\n Mayo / May = 5\n Junio / June = 6\n Julio / July = 7\n Agosto / August = 8\n Septiembre / September = 9\n Octubre / October = 10\n Noviembre / November = 11\n Diciembre / December = 12\n\n4. Usa period_type = quincena_15 cuando aparezca cualquiera de estas señales:\n - 1Q\n - Q1\n - Primera quincena\n - 1ra quincena\n - First fortnight\n - First half\n - Mid month\n - Día 15\n - 15th\n\n5. Usa period_type = quincena_30 cuando aparezca cualquiera de estas señales:\n - 2Q\n - Q2\n - Segunda quincena\n - 2da quincena\n - Second fortnight\n - Second half\n - Fin de mes\n - End of month\n - Month end\n - Día 30\n - Día 31\n - 30th\n - 31st\n - El último día real del mes, incluyendo 28 o 29 de febrero\n\n6. Aunque el último día real del mes no sea 30, utiliza quincena_30 para representar la segunda quincena o cierre de mes.\n\n7. Si solamente aparecen el mes y el año, sin ninguna señal de quincena ni día de cierre, utiliza mensual.\n\n8. Para nombres como \"GLM_TT_Payroll June 30TH_20260617.xlsx\":\n - country = TT\n - year = 2026\n - month = 6\n - period_type = quincena_30\n\n9. Para Honduras también reconoce nombres como \"Nomina 7.1 2026 GLM HN.xlsx\" (mes 7, primera quincena) y \"Nomina 7.2 2026 GLM HN.xlsx\" (mes 7, segunda quincena). En general, cuando ese patrón forma parte del nombre de nómina, M.1 = quincena_15 y M.2 = quincena_30.\n\n10. Si no puedes identificar de manera confiable el año o el mes, utiliza:\n year = 0\n month = 0\n period_type = desconocido\n\n11. confidence debe estar entre 0 y 1.\n\n12. explanation debe ser breve y mencionar las señales encontradas en el nombre.\n\nNo agregues campos diferentes a los definidos por el esquema." + } + }, + "type": "@n8n/n8n-nodes-langchain.informationExtractor", + "typeVersion": 1.2, + "position": [ + 9632, + 3104 + ], + "id": "183e2202-0bbd-4bcc-bc57-0b92e12b5d5f", + "name": "Extraer período con Gemini", + "retryOnFail": true, + "waitBetweenTries": 2000, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "modelName": "models/gemini-2.5-pro", + "options": {} + }, + "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini", + "typeVersion": 1.1, + "position": [ + 9632, + 3280 + ], + "id": "149a7c92-4c82-4348-9f88-58539d79e75f", + "name": "Gemini - Analizar título nómina", + "credentials": { + "googlePalmApi": { + "id": "jvsXYwL6IOoY2DBU", + "name": "Isaac - Gemini Api Pago" + } + } + }, + { + "parameters": { + "jsCode": "const request =\n $('Validar y preparar solicitud').first().json || {};\n\nconst incoming = $input.first().json || {};\n\nfunction cleanText(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeText(value) {\n return cleanText(value)\n .toUpperCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[_\\-.,()[\\]{}]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction parseObject(value) {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n return value;\n }\n\n if (typeof value !== 'string') {\n return {};\n }\n\n const cleaned = value\n .replace(/```json/gi, '')\n .replace(/```/g, '')\n .trim();\n\n try {\n return JSON.parse(cleaned);\n } catch (error) {\n return {};\n }\n}\n\nfunction clamp(value, minimum, maximum) {\n const number = Number(value);\n\n if (!Number.isFinite(number)) {\n return minimum;\n }\n\n return Math.min(\n maximum,\n Math.max(minimum, number)\n );\n}\n\nfunction pad(number) {\n return String(number).padStart(2, '0');\n}\n\nfunction getLastDay(year, month) {\n return new Date(\n Date.UTC(year, month, 0)\n ).getUTCDate();\n}\n\nfunction buildPeriodDates(\n year,\n month,\n periodType\n) {\n if (\n !Number.isInteger(year) ||\n year < 2000 ||\n !Number.isInteger(month) ||\n month < 1 ||\n month > 12\n ) {\n return {\n periodStart: null,\n periodEnd: null,\n };\n }\n\n const lastDay = getLastDay(year, month);\n const yearMonth = `${year}-${pad(month)}`;\n\n if (periodType === 'quincena_15') {\n return {\n periodStart: `${yearMonth}-01`,\n periodEnd: `${yearMonth}-15`,\n };\n }\n\n if (periodType === 'quincena_30') {\n return {\n periodStart: `${yearMonth}-16`,\n periodEnd:\n `${yearMonth}-${pad(lastDay)}`,\n };\n }\n\n if (periodType === 'mensual') {\n return {\n periodStart: `${yearMonth}-01`,\n periodEnd:\n `${yearMonth}-${pad(lastDay)}`,\n };\n }\n\n return {\n periodStart: null,\n periodEnd: null,\n };\n}\n\nconst countryNames = {\n GT: 'Guatemala',\n TT: 'Trinidad y Tobago',\n PA: 'Panamá',\n SV: 'El Salvador',\n HN: 'Honduras',\n PR: 'Puerto Rico',\n};\n\nconst country = ['GT', 'TT', 'PA', 'SV', 'HN', 'PR'].includes(\n String(request.country || '').toUpperCase()\n)\n ? String(request.country).toUpperCase()\n : 'GT';\n\nconst countryName =\n request.countryName ||\n countryNames[country];\n\nconst monthNames = {\n 1: 'Enero',\n 2: 'Febrero',\n 3: 'Marzo',\n 4: 'Abril',\n 5: 'Mayo',\n 6: 'Junio',\n 7: 'Julio',\n 8: 'Agosto',\n 9: 'Septiembre',\n 10: 'Octubre',\n 11: 'Noviembre',\n 12: 'Diciembre',\n};\n\nconst monthPatterns = [\n ['ENERO', 1],\n ['JANUARY', 1],\n ['JAN', 1],\n\n ['FEBRERO', 2],\n ['FEBRUARY', 2],\n ['FEB', 2],\n\n ['MARZO', 3],\n ['MARCH', 3],\n ['MAR', 3],\n\n ['ABRIL', 4],\n ['APRIL', 4],\n ['APR', 4],\n\n ['MAYO', 5],\n ['MAY', 5],\n\n ['JUNIO', 6],\n ['JUNE', 6],\n ['JUN', 6],\n\n ['JULIO', 7],\n ['JULY', 7],\n ['JUL', 7],\n\n ['AGOSTO', 8],\n ['AUGUST', 8],\n ['AUG', 8],\n\n ['SEPTIEMBRE', 9],\n ['SETIEMBRE', 9],\n ['SEPTEMBER', 9],\n ['SEPT', 9],\n ['SEP', 9],\n\n ['OCTUBRE', 10],\n ['OCTOBER', 10],\n ['OCT', 10],\n\n ['NOVIEMBRE', 11],\n ['NOVEMBER', 11],\n ['NOV', 11],\n\n ['DICIEMBRE', 12],\n ['DECEMBER', 12],\n ['DEC', 12],\n];\n\nconst monthPatternText =\n monthPatterns\n .map(([name]) => name)\n .sort((a, b) => b.length - a.length)\n .join('|');\n\nconst validPeriodTypes = new Set([\n 'quincena_15',\n 'quincena_30',\n 'mensual',\n 'desconocido',\n]);\n\nconst fileName = cleanText(request.fileName);\nconst normalizedFileName =\n normalizeText(fileName);\n\nconst hondurasDecimal = country === 'HN'\n ? fileName.match(/(?:nomina|nómina)?\\s*(\\d{1,2})\\.(1|2)\\s+(20\\d{2})/i)\n : null;\n\nconst compactDateMatch =\n normalizedFileName.match(\n /\\b(20\\d{2})(0[1-9]|1[0-2])([0-2]\\d|3[01])\\b/\n );\n\nconst standaloneYearMatch =\n normalizedFileName.match(/\\b(20\\d{2})\\b/);\n\nconst fallbackYear = hondurasDecimal\n ? Number(hondurasDecimal[3])\n : standaloneYearMatch\n ? Number(standaloneYearMatch[1])\n : compactDateMatch\n ? Number(compactDateMatch[1])\n : 0;\n\nlet fallbackMonth = hondurasDecimal ? Number(hondurasDecimal[1]) : 0;\n\nfor (const [monthName, monthNumber] of monthPatterns) {\n const monthRegex = new RegExp(\n `\\\\b${monthName}\\\\b`\n );\n\n if (monthRegex.test(normalizedFileName)) {\n fallbackMonth = monthNumber;\n break;\n }\n}\n\nif (!fallbackMonth && compactDateMatch) {\n fallbackMonth = Number(compactDateMatch[2]);\n}\n\nconst monthThenDayRegex = new RegExp(\n `\\\\b(?:${monthPatternText})\\\\s+([0-3]?\\\\d)(?:ST|ND|RD|TH)?\\\\b`\n);\n\nconst dayThenMonthRegex = new RegExp(\n `\\\\b([0-3]?\\\\d)(?:ST|ND|RD|TH)?\\\\s+(?:DE\\\\s+)?(?:${monthPatternText})\\\\b`\n);\n\nconst explicitDayRegex =\n /\\b(?:DIA|DAY)\\s+([0-3]?\\d)(?:ST|ND|RD|TH)?\\b/;\n\nconst monthThenDayMatch =\n normalizedFileName.match(monthThenDayRegex);\n\nconst dayThenMonthMatch =\n normalizedFileName.match(dayThenMonthRegex);\n\nconst explicitDayMatch =\n normalizedFileName.match(explicitDayRegex);\n\nconst detectedDay = monthThenDayMatch\n ? Number(monthThenDayMatch[1])\n : dayThenMonthMatch\n ? Number(dayThenMonthMatch[1])\n : explicitDayMatch\n ? Number(explicitDayMatch[1])\n : 0;\n\nconst firstFortnight =\n /\\b(1Q|Q1)\\b/.test(normalizedFileName) ||\n normalizedFileName.includes(\n 'PRIMERA QUINCENA'\n ) ||\n normalizedFileName.includes(\n '1RA QUINCENA'\n ) ||\n normalizedFileName.includes(\n 'FIRST FORTNIGHT'\n ) ||\n normalizedFileName.includes(\n 'FIRST HALF'\n ) ||\n normalizedFileName.includes(\n 'MID MONTH'\n ) ||\n normalizedFileName.includes(\n 'MIDMONTH'\n );\n\nconst secondFortnight =\n /\\b(2Q|Q2)\\b/.test(normalizedFileName) ||\n normalizedFileName.includes(\n 'SEGUNDA QUINCENA'\n ) ||\n normalizedFileName.includes(\n '2DA QUINCENA'\n ) ||\n normalizedFileName.includes(\n 'SECOND FORTNIGHT'\n ) ||\n normalizedFileName.includes(\n 'SECOND HALF'\n ) ||\n normalizedFileName.includes(\n 'FIN DE MES'\n ) ||\n normalizedFileName.includes(\n 'END OF MONTH'\n ) ||\n normalizedFileName.includes(\n 'MONTH END'\n ) ||\n normalizedFileName.includes(\n 'MONTHEND'\n );\n\nlet fallbackPeriodType = 'desconocido';\nlet fallbackConfidence = 0.2;\nlet strongPeriodEvidence = false;\n\nif (hondurasDecimal) {\n fallbackPeriodType = hondurasDecimal[2] === '1' ? 'quincena_15' : 'quincena_30';\n fallbackConfidence = 1;\n strongPeriodEvidence = true;\n} else if (firstFortnight) {\n fallbackPeriodType = 'quincena_15';\n fallbackConfidence = 0.98;\n strongPeriodEvidence = true;\n} else if (secondFortnight) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.98;\n strongPeriodEvidence = true;\n} else if (detectedDay === 15) {\n fallbackPeriodType = 'quincena_15';\n fallbackConfidence = 0.95;\n strongPeriodEvidence = true;\n} else if (\n fallbackYear &&\n fallbackMonth &&\n detectedDay === getLastDay(\n fallbackYear,\n fallbackMonth\n )\n) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.95;\n strongPeriodEvidence = true;\n} else if (\n detectedDay === 30 ||\n detectedDay === 31\n) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.94;\n strongPeriodEvidence = true;\n} else if (\n fallbackYear &&\n fallbackMonth\n) {\n fallbackPeriodType = 'mensual';\n fallbackConfidence = 0.75;\n}\n\nlet rawAI =\n incoming.output ??\n incoming.result ??\n incoming.text ??\n incoming;\n\nrawAI = parseObject(rawAI);\n\nconst aiCountry =\n String(rawAI.country || '').toUpperCase();\n\nconst aiYear = Number(rawAI.year);\nconst aiMonth = Number(rawAI.month);\n\nconst aiPeriodType =\n validPeriodTypes.has(rawAI.period_type)\n ? rawAI.period_type\n : 'desconocido';\n\nconst aiConfidence = clamp(\n rawAI.confidence,\n 0,\n 1\n);\n\nconst aiIsUsable =\n Number.isInteger(aiYear) &&\n aiYear >= 2000 &&\n aiYear <= 2100 &&\n Number.isInteger(aiMonth) &&\n aiMonth >= 1 &&\n aiMonth <= 12 &&\n aiPeriodType !== 'desconocido';\n\nlet year =\n fallbackYear ||\n (aiIsUsable ? aiYear : 0);\n\nlet month =\n fallbackMonth ||\n (aiIsUsable ? aiMonth : 0);\n\nlet periodType =\n aiIsUsable\n ? aiPeriodType\n : fallbackPeriodType;\n\nlet source =\n aiIsUsable\n ? 'gemini'\n : 'local_fallback';\n\nlet confidence =\n aiIsUsable\n ? aiConfidence\n : fallbackConfidence;\n\nlet conflictResolved = false;\n\nif (\n aiCountry &&\n aiCountry !== country\n) {\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n strongPeriodEvidence &&\n periodType !== fallbackPeriodType\n) {\n periodType = fallbackPeriodType;\n source = 'gemini_validated_with_rules';\n confidence = Math.max(\n fallbackConfidence,\n Math.min(aiConfidence, 0.9)\n );\n conflictResolved = true;\n}\n\nif (\n fallbackYear &&\n aiIsUsable &&\n aiYear !== fallbackYear\n) {\n year = fallbackYear;\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n fallbackMonth &&\n aiIsUsable &&\n aiMonth !== fallbackMonth\n) {\n month = fallbackMonth;\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n !aiIsUsable &&\n fallbackYear &&\n fallbackMonth\n) {\n year = fallbackYear;\n month = fallbackMonth;\n periodType = fallbackPeriodType;\n confidence = fallbackConfidence;\n source = 'local_fallback';\n}\n\nconst complete =\n year >= 2000 &&\n month >= 1 &&\n month <= 12 &&\n periodType !== 'desconocido';\n\nconst dates = buildPeriodDates(\n year,\n month,\n periodType\n);\n\nlet periodDescription = 'Período desconocido';\n\nif (periodType === 'quincena_15') {\n periodDescription = 'Quincena 15';\n}\n\nif (periodType === 'quincena_30') {\n periodDescription =\n 'Quincena 30 / fin de mes';\n}\n\nif (periodType === 'mensual') {\n periodDescription = 'Mensual';\n}\n\nconst monthLabel =\n monthNames[month] || 'Mes desconocido';\n\nconst periodLabel = complete\n ? `${monthLabel} ${year} · ${periodDescription}`\n : 'Período pendiente de confirmar';\n\nconst requiresManualReview =\n !complete ||\n confidence < 0.75;\n\nconst explanation =\n cleanText(rawAI.explanation) ||\n (\n source === 'local_fallback'\n ? 'El período fue identificado mediante las reglas locales de respaldo.'\n : 'El período fue identificado mediante Gemini y validado con las reglas del portal.'\n );\n\nreturn [\n {\n json: {\n ok: true,\n\n message: requiresManualReview\n ? 'El archivo fue analizado, pero el período debe confirmarse manualmente.'\n : 'El período de la nómina fue detectado correctamente.',\n\n detected: {\n country,\n countryName,\n\n fileName,\n\n year,\n month,\n\n periodType,\n period_type: periodType,\n\n periodLabel,\n period_label: periodLabel,\n\n periodStart: dates.periodStart,\n period_start: dates.periodStart,\n\n periodEnd: dates.periodEnd,\n period_end: dates.periodEnd,\n\n confidence:\n Math.round(confidence * 100) / 100,\n\n complete,\n requiresManualReview,\n requires_manual_review:\n requiresManualReview,\n\n source,\n conflictResolved,\n explanation,\n },\n\n debug: {\n fallback: {\n country,\n year: fallbackYear,\n month: fallbackMonth,\n periodType: fallbackPeriodType,\n detectedDay,\n confidence: fallbackConfidence,\n strongPeriodEvidence,\n compactDate:\n compactDateMatch?.[0] || null,\n },\n\n gemini: {\n country: aiCountry || null,\n year: aiYear || 0,\n month: aiMonth || 0,\n periodType: aiPeriodType,\n confidence: aiConfidence,\n explanation:\n cleanText(rawAI.explanation),\n },\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 9968, + 3120 + ], + "id": "abda0cdb-60df-4249-bb67-3697aba37a3f", + "name": "Normalizar análisis IA" + }, + { + "parameters": { + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 10176, + 3120 + ], + "id": "32ae875a-8f3e-45b2-9d5c-4ee3f28eec77", + "name": "Responder análisis" + }, + { + "parameters": { + "content": "# 🧠 ANÁLISIS AUTOMÁTICO DE NÓMINA — GT, TT, PA y SV\n\nAnaliza el nombre o título del archivo de nómina para identificar automáticamente el período correspondiente antes de ejecutar la verificación.\n\n## Recepción y validación\n\nEl Webhook recibe desde el Portal de Verificación de Nóminas la información del archivo seleccionado.\n\nLa solicitud puede incluir:\n\n- Nombre del archivo.\n- Título o descripción disponible.\n- País o portal de origen.\n- Identificador de la solicitud.\n- Información complementaria enviada por la aplicación.\n\nAntes de utilizar inteligencia artificial, el flujo valida que exista suficiente información para analizar el archivo.\n\n## Extracción con Gemini\n\nCuando la solicitud es válida, Gemini interpreta el nombre o título de la nómina e intenta determinar:\n\n- Año.\n- Mes.\n- Tipo de período.\n- Quincena 15.\n- Quincena 30 o fin de mes.\n- Nómina mensual.\n- País, cuando puede inferirse de forma confiable.\n- Nivel de confianza del análisis.\n\nGemini se utiliza únicamente para interpretar el texto recibido; no ejecuta el cruce de nómina ni modifica información.\n\n## Normalización del resultado\n\nLa respuesta de Gemini se transforma al formato requerido por la aplicación.\n\nEste bloque:\n\n- Limpia texto adicional generado por la IA.\n- Normaliza el año a formato numérico.\n- Convierte el mes al valor esperado por el portal.\n- Estandariza el tipo de período.\n- Corrige variaciones como “segunda quincena”, “fin de mes” o “30”.\n- Conserva los valores originales cuando la IA no puede determinar un dato.\n- Prepara una respuesta consistente para Guatemala, Trinidad y Tobago, Panamá y El Salvador.\n\n## Respuesta al portal\n\nCuando el análisis termina, el workflow devuelve a la aplicación:\n\n- Indicador de éxito.\n- Año detectado.\n- Mes detectado.\n- Tipo de período detectado.\n- País identificado, cuando corresponda.\n- Nivel de confianza.\n- Datos que requieren revisión manual.\n\nLos valores detectados sirven para completar automáticamente el formulario, pero el usuario puede corregirlos antes de ejecutar el proceso.\n\n## Ruta de error\n\nCuando la solicitud es inválida:\n\n- No se llama a Gemini.\n- No se intenta interpretar información incompleta.\n- Se devuelve una respuesta de error al portal.\n- Se indica que falta el nombre del archivo u otro dato obligatorio.\n\n## Reglas\n\n- No inventar un período cuando el título no contiene información suficiente.\n- No ejecutar el cruce de nómina desde este workflow.\n- No tratar una predicción de baja confianza como un dato definitivo.\n- Mantener la opción de corrección manual en la aplicación.\n- Responder siempre al Webhook, tanto en la ruta válida como en la ruta de error.\n- Utilizar una estructura de respuesta compatible con los portales de Guatemala, Trinidad y Tobago, Panamá y El Salvador.", + "height": 1488, + "width": 2256, + "color": 2 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 8144, + 2560 + ], + "id": "d5711cc9-9637-4129-bab9-8ec07b551774", + "name": "Sticky Note" + } + ], + "pinData": {}, + "connections": { + "Webhook - Analizar Nómina": { + "main": [ + [ + { + "node": "Validar y preparar solicitud", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar y preparar solicitud": { + "main": [ + [ + { + "node": "¿Solicitud válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Solicitud válida?": { + "main": [ + [ + { + "node": "Extraer período con Gemini", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder error", + "type": "main", + "index": 0 + } + ] + ] + }, + "Gemini - Analizar título nómina": { + "ai_languageModel": [ + [ + { + "node": "Extraer período con Gemini", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Extraer período con Gemini": { + "main": [ + [ + { + "node": "Normalizar análisis IA", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar análisis IA": { + "main": [ + [ + { + "node": "Responder análisis", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate", + "availableInMCP": true, + "timeSavedMode": "fixed", + "errorWorkflow": "puF4LUczoSz3hcek", + "timezone": "America/Santo_Domingo", + "callerPolicy": "workflowsFromSameOwner" + }, + "versionId": "d58ae020-ce4f-429b-ba40-1262c79492f9", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "id": "E9cNPlUjeGnhFqW2", + "tags": [] +} diff --git a/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV.json b/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV.json deleted file mode 100644 index 24af911..0000000 --- a/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV.json +++ /dev/null @@ -1,274 +0,0 @@ -{ - "name": "Portal de Verificación de Nómina - Analizar Nómina con Gemini Guatemala Trinidad y Tobago Panamá y El Salvador", - "nodes": [ - { - "parameters": { - "httpMethod": "POST", - "path": "cruce-cuentas-analizar-nomina", - "responseMode": "responseNode", - "options": {} - }, - "type": "n8n-nodes-base.webhook", - "typeVersion": 2.1, - "position": [ - 5856, - 2192 - ], - "id": "74962393-93af-4069-ad07-4fdb7ed061cd", - "name": "Webhook - Analizar Nómina", - "webhookId": "eeac8670-bf13-4907-8b28-209fada284d0" - }, - { - "parameters": { - "jsCode": "const input = $input.first().json || {};\n\nlet body = input.body ?? input;\n\nif (typeof body === 'string') {\n try {\n body = JSON.parse(body);\n } catch (error) {\n body = {};\n }\n}\n\nif (!body || typeof body !== 'object' || Array.isArray(body)) {\n body = {};\n}\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst fileName = clean(\n body.fileName ??\n body.filename ??\n body.file_name ??\n ''\n);\n\nconst country = clean(\n body.country ?? 'GT'\n).toUpperCase();\n\nconst supportedCountries = {\n GT: 'Guatemala',\n TT: 'Trinidad y Tobago',\n PA: 'Panamá',\n SV: 'El Salvador',\n};\n\nconst countryName = clean(\n body.countryName ??\n body.country_name ??\n supportedCountries[country] ??\n ''\n);\n\nconst errors = [];\n\nif (!fileName) {\n errors.push(\n 'No se recibió el nombre del archivo de nómina.'\n );\n}\n\nif (!Object.prototype.hasOwnProperty.call(\n supportedCountries,\n country\n)) {\n errors.push(\n 'El analizador solo está habilitado para Guatemala, Trinidad y Tobago, Panamá y El Salvador.'\n );\n}\n\nconst supportedExtensions = [\n '.xlsx',\n '.xls',\n '.xlsm',\n '.csv',\n];\n\nconst lowerFileName = fileName.toLowerCase();\n\nconst hasSupportedExtension =\n supportedExtensions.some((extension) =>\n lowerFileName.endsWith(extension)\n );\n\nif (fileName && !hasSupportedExtension) {\n errors.push(\n 'El archivo recibido no tiene una extensión de nómina compatible.'\n );\n}\n\nreturn [\n {\n json: {\n ok: errors.length === 0,\n errors,\n\n fileName,\n country,\n countryName:\n countryName ||\n supportedCountries[country] ||\n '',\n\n receivedAt: new Date().toISOString(),\n },\n },\n];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 6064, - 2192 - ], - "id": "115af80d-9c31-4ce3-80b0-bdd9abe93397", - "name": "Validar y preparar solicitud" - }, - { - "parameters": { - "conditions": { - "options": { - "caseSensitive": true, - "leftValue": "", - "typeValidation": "strict", - "version": 3 - }, - "conditions": [ - { - "id": "bd9fd675-d72b-4dba-ac03-a44f1123ecb0", - "leftValue": "={{ $json.ok }}", - "rightValue": "", - "operator": { - "type": "boolean", - "operation": "true", - "singleValue": true - } - } - ], - "combinator": "and" - }, - "options": {} - }, - "type": "n8n-nodes-base.if", - "typeVersion": 2.3, - "position": [ - 6272, - 2192 - ], - "id": "37b913db-c906-4527-9c73-0831ea9765be", - "name": "¿Solicitud válida?" - }, - { - "parameters": { - "respondWith": "json", - "responseBody": "={{\n{\n ok: false,\n message:\n 'No fue posible analizar el nombre del archivo.',\n errors: Array.isArray($json.errors)\n ? $json.errors\n : ['La solicitud recibida no es válida.'],\n detected: null\n}\n}}", - "options": { - "responseCode": 400, - "responseHeaders": { - "entries": [ - { - "name": "Content-Type", - "value": "application/json" - } - ] - } - } - }, - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.5, - "position": [ - 6480, - 2288 - ], - "id": "2e8fd49a-0db0-49ba-8354-6414b570f593", - "name": "Responder error" - }, - { - "parameters": { - "text": "={{\n`Analiza este nombre de archivo de nómina:\n\nPaís seleccionado: ${$json.countryName}\nCódigo del país: ${$json.country}\nNombre del archivo: ${$json.fileName}`\n}}", - "schemaType": "manual", - "inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"country\": {\n \"type\": \"string\",\n \"enum\": [\n \"GT\",\n \"TT\",\n \"PA\",\n \"SV\"\n ]\n },\n \"year\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 2100\n },\n \"month\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 12\n },\n \"period_type\": {\n \"type\": \"string\",\n \"enum\": [\n \"quincena_15\",\n \"quincena_30\",\n \"mensual\",\n \"desconocido\"\n ]\n },\n \"confidence\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1\n },\n \"explanation\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"country\",\n \"year\",\n \"month\",\n \"period_type\",\n \"confidence\",\n \"explanation\"\n ],\n \"additionalProperties\": false\n}", - "options": { - "systemPromptTemplate": "Eres un clasificador determinista de nombres de archivos de nómina.\n\nTu tarea es identificar el país seleccionado, el año, el mes y el período de pago utilizando únicamente la información presente en el nombre del archivo y el país recibido.\n\nNo inventes datos que no estén contenidos o razonablemente codificados en el nombre.\n\nREGLAS GENERALES:\n\n1. Devuelve exactamente el código de país recibido:\n - GT para Guatemala.\n - TT para Trinidad y Tobago.\n - PA para Panamá.\n - SV para El Salvador.\n\n2. Detecta el año de cuatro dígitos. También puede aparecer dentro de una fecha compacta como YYYYMMDD; por ejemplo, 20260617 representa el año 2026.\n\n3. Convierte el mes a un número aceptando nombres en español o inglés:\n Enero / January = 1\n Febrero / February = 2\n Marzo / March = 3\n Abril / April = 4\n Mayo / May = 5\n Junio / June = 6\n Julio / July = 7\n Agosto / August = 8\n Septiembre / September = 9\n Octubre / October = 10\n Noviembre / November = 11\n Diciembre / December = 12\n\n4. Usa period_type = quincena_15 cuando aparezca cualquiera de estas señales:\n - 1Q\n - Q1\n - Primera quincena\n - 1ra quincena\n - First fortnight\n - First half\n - Mid month\n - Día 15\n - 15th\n\n5. Usa period_type = quincena_30 cuando aparezca cualquiera de estas señales:\n - 2Q\n - Q2\n - Segunda quincena\n - 2da quincena\n - Second fortnight\n - Second half\n - Fin de mes\n - End of month\n - Month end\n - Día 30\n - Día 31\n - 30th\n - 31st\n - El último día real del mes, incluyendo 28 o 29 de febrero\n\n6. Aunque el último día real del mes no sea 30, utiliza quincena_30 para representar la segunda quincena o cierre de mes.\n\n7. Si solamente aparecen el mes y el año, sin ninguna señal de quincena ni día de cierre, utiliza mensual.\n\n8. Para nombres como \"GLM_TT_Payroll June 30TH_20260617.xlsx\":\n - country = TT\n - year = 2026\n - month = 6\n - period_type = quincena_30\n\n9. Si no puedes identificar de manera confiable el año o el mes, utiliza:\n year = 0\n month = 0\n period_type = desconocido\n\n10. confidence debe estar entre 0 y 1.\n\n11. explanation debe ser breve y mencionar las señales encontradas en el nombre.\n\nNo agregues campos diferentes a los definidos por el esquema." - } - }, - "type": "@n8n/n8n-nodes-langchain.informationExtractor", - "typeVersion": 1.2, - "position": [ - 6480, - 1920 - ], - "id": "515d1b33-6516-47c8-ab38-ecc18169f2bf", - "name": "Extraer período con Gemini", - "retryOnFail": true, - "waitBetweenTries": 2000, - "onError": "continueRegularOutput" - }, - { - "parameters": { - "modelName": "models/gemini-2.5-pro", - "options": {} - }, - "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini", - "typeVersion": 1.1, - "position": [ - 6480, - 2096 - ], - "id": "b17d19bf-cbb7-4f77-b2fd-227eb66a8f4f", - "name": "Gemini - Analizar título nómina", - "credentials": { - "googlePalmApi": { - "id": "jvsXYwL6IOoY2DBU", - "name": "Isaac - Gemini Api Pago" - } - } - }, - { - "parameters": { - "jsCode": "const request =\n $('Validar y preparar solicitud').first().json || {};\n\nconst incoming = $input.first().json || {};\n\nfunction cleanText(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeText(value) {\n return cleanText(value)\n .toUpperCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[_\\-.,()[\\]{}]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction parseObject(value) {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n return value;\n }\n\n if (typeof value !== 'string') {\n return {};\n }\n\n const cleaned = value\n .replace(/```json/gi, '')\n .replace(/```/g, '')\n .trim();\n\n try {\n return JSON.parse(cleaned);\n } catch (error) {\n return {};\n }\n}\n\nfunction clamp(value, minimum, maximum) {\n const number = Number(value);\n\n if (!Number.isFinite(number)) {\n return minimum;\n }\n\n return Math.min(\n maximum,\n Math.max(minimum, number)\n );\n}\n\nfunction pad(number) {\n return String(number).padStart(2, '0');\n}\n\nfunction getLastDay(year, month) {\n return new Date(\n Date.UTC(year, month, 0)\n ).getUTCDate();\n}\n\nfunction buildPeriodDates(\n year,\n month,\n periodType\n) {\n if (\n !Number.isInteger(year) ||\n year < 2000 ||\n !Number.isInteger(month) ||\n month < 1 ||\n month > 12\n ) {\n return {\n periodStart: null,\n periodEnd: null,\n };\n }\n\n const lastDay = getLastDay(year, month);\n const yearMonth = `${year}-${pad(month)}`;\n\n if (periodType === 'quincena_15') {\n return {\n periodStart: `${yearMonth}-01`,\n periodEnd: `${yearMonth}-15`,\n };\n }\n\n if (periodType === 'quincena_30') {\n return {\n periodStart: `${yearMonth}-16`,\n periodEnd:\n `${yearMonth}-${pad(lastDay)}`,\n };\n }\n\n if (periodType === 'mensual') {\n return {\n periodStart: `${yearMonth}-01`,\n periodEnd:\n `${yearMonth}-${pad(lastDay)}`,\n };\n }\n\n return {\n periodStart: null,\n periodEnd: null,\n };\n}\n\nconst countryNames = {\n GT: 'Guatemala',\n TT: 'Trinidad y Tobago',\n PA: 'Panamá',\n SV: 'El Salvador',\n};\n\nconst country = ['GT', 'TT', 'PA', 'SV'].includes(\n String(request.country || '').toUpperCase()\n)\n ? String(request.country).toUpperCase()\n : 'GT';\n\nconst countryName =\n request.countryName ||\n countryNames[country];\n\nconst monthNames = {\n 1: 'Enero',\n 2: 'Febrero',\n 3: 'Marzo',\n 4: 'Abril',\n 5: 'Mayo',\n 6: 'Junio',\n 7: 'Julio',\n 8: 'Agosto',\n 9: 'Septiembre',\n 10: 'Octubre',\n 11: 'Noviembre',\n 12: 'Diciembre',\n};\n\nconst monthPatterns = [\n ['ENERO', 1],\n ['JANUARY', 1],\n ['JAN', 1],\n\n ['FEBRERO', 2],\n ['FEBRUARY', 2],\n ['FEB', 2],\n\n ['MARZO', 3],\n ['MARCH', 3],\n ['MAR', 3],\n\n ['ABRIL', 4],\n ['APRIL', 4],\n ['APR', 4],\n\n ['MAYO', 5],\n ['MAY', 5],\n\n ['JUNIO', 6],\n ['JUNE', 6],\n ['JUN', 6],\n\n ['JULIO', 7],\n ['JULY', 7],\n ['JUL', 7],\n\n ['AGOSTO', 8],\n ['AUGUST', 8],\n ['AUG', 8],\n\n ['SEPTIEMBRE', 9],\n ['SETIEMBRE', 9],\n ['SEPTEMBER', 9],\n ['SEPT', 9],\n ['SEP', 9],\n\n ['OCTUBRE', 10],\n ['OCTOBER', 10],\n ['OCT', 10],\n\n ['NOVIEMBRE', 11],\n ['NOVEMBER', 11],\n ['NOV', 11],\n\n ['DICIEMBRE', 12],\n ['DECEMBER', 12],\n ['DEC', 12],\n];\n\nconst monthPatternText =\n monthPatterns\n .map(([name]) => name)\n .sort((a, b) => b.length - a.length)\n .join('|');\n\nconst validPeriodTypes = new Set([\n 'quincena_15',\n 'quincena_30',\n 'mensual',\n 'desconocido',\n]);\n\nconst fileName = cleanText(request.fileName);\nconst normalizedFileName =\n normalizeText(fileName);\n\nconst compactDateMatch =\n normalizedFileName.match(\n /\\b(20\\d{2})(0[1-9]|1[0-2])([0-2]\\d|3[01])\\b/\n );\n\nconst standaloneYearMatch =\n normalizedFileName.match(/\\b(20\\d{2})\\b/);\n\nconst fallbackYear = standaloneYearMatch\n ? Number(standaloneYearMatch[1])\n : compactDateMatch\n ? Number(compactDateMatch[1])\n : 0;\n\nlet fallbackMonth = 0;\n\nfor (const [monthName, monthNumber] of monthPatterns) {\n const monthRegex = new RegExp(\n `\\\\b${monthName}\\\\b`\n );\n\n if (monthRegex.test(normalizedFileName)) {\n fallbackMonth = monthNumber;\n break;\n }\n}\n\nif (!fallbackMonth && compactDateMatch) {\n fallbackMonth = Number(compactDateMatch[2]);\n}\n\nconst monthThenDayRegex = new RegExp(\n `\\\\b(?:${monthPatternText})\\\\s+([0-3]?\\\\d)(?:ST|ND|RD|TH)?\\\\b`\n);\n\nconst dayThenMonthRegex = new RegExp(\n `\\\\b([0-3]?\\\\d)(?:ST|ND|RD|TH)?\\\\s+(?:DE\\\\s+)?(?:${monthPatternText})\\\\b`\n);\n\nconst explicitDayRegex =\n /\\b(?:DIA|DAY)\\s+([0-3]?\\d)(?:ST|ND|RD|TH)?\\b/;\n\nconst monthThenDayMatch =\n normalizedFileName.match(monthThenDayRegex);\n\nconst dayThenMonthMatch =\n normalizedFileName.match(dayThenMonthRegex);\n\nconst explicitDayMatch =\n normalizedFileName.match(explicitDayRegex);\n\nconst detectedDay = monthThenDayMatch\n ? Number(monthThenDayMatch[1])\n : dayThenMonthMatch\n ? Number(dayThenMonthMatch[1])\n : explicitDayMatch\n ? Number(explicitDayMatch[1])\n : 0;\n\nconst firstFortnight =\n /\\b(1Q|Q1)\\b/.test(normalizedFileName) ||\n normalizedFileName.includes(\n 'PRIMERA QUINCENA'\n ) ||\n normalizedFileName.includes(\n '1RA QUINCENA'\n ) ||\n normalizedFileName.includes(\n 'FIRST FORTNIGHT'\n ) ||\n normalizedFileName.includes(\n 'FIRST HALF'\n ) ||\n normalizedFileName.includes(\n 'MID MONTH'\n ) ||\n normalizedFileName.includes(\n 'MIDMONTH'\n );\n\nconst secondFortnight =\n /\\b(2Q|Q2)\\b/.test(normalizedFileName) ||\n normalizedFileName.includes(\n 'SEGUNDA QUINCENA'\n ) ||\n normalizedFileName.includes(\n '2DA QUINCENA'\n ) ||\n normalizedFileName.includes(\n 'SECOND FORTNIGHT'\n ) ||\n normalizedFileName.includes(\n 'SECOND HALF'\n ) ||\n normalizedFileName.includes(\n 'FIN DE MES'\n ) ||\n normalizedFileName.includes(\n 'END OF MONTH'\n ) ||\n normalizedFileName.includes(\n 'MONTH END'\n ) ||\n normalizedFileName.includes(\n 'MONTHEND'\n );\n\nlet fallbackPeriodType = 'desconocido';\nlet fallbackConfidence = 0.2;\nlet strongPeriodEvidence = false;\n\nif (firstFortnight) {\n fallbackPeriodType = 'quincena_15';\n fallbackConfidence = 0.98;\n strongPeriodEvidence = true;\n} else if (secondFortnight) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.98;\n strongPeriodEvidence = true;\n} else if (detectedDay === 15) {\n fallbackPeriodType = 'quincena_15';\n fallbackConfidence = 0.95;\n strongPeriodEvidence = true;\n} else if (\n fallbackYear &&\n fallbackMonth &&\n detectedDay === getLastDay(\n fallbackYear,\n fallbackMonth\n )\n) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.95;\n strongPeriodEvidence = true;\n} else if (\n detectedDay === 30 ||\n detectedDay === 31\n) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.94;\n strongPeriodEvidence = true;\n} else if (\n fallbackYear &&\n fallbackMonth\n) {\n fallbackPeriodType = 'mensual';\n fallbackConfidence = 0.75;\n}\n\nlet rawAI =\n incoming.output ??\n incoming.result ??\n incoming.text ??\n incoming;\n\nrawAI = parseObject(rawAI);\n\nconst aiCountry =\n String(rawAI.country || '').toUpperCase();\n\nconst aiYear = Number(rawAI.year);\nconst aiMonth = Number(rawAI.month);\n\nconst aiPeriodType =\n validPeriodTypes.has(rawAI.period_type)\n ? rawAI.period_type\n : 'desconocido';\n\nconst aiConfidence = clamp(\n rawAI.confidence,\n 0,\n 1\n);\n\nconst aiIsUsable =\n Number.isInteger(aiYear) &&\n aiYear >= 2000 &&\n aiYear <= 2100 &&\n Number.isInteger(aiMonth) &&\n aiMonth >= 1 &&\n aiMonth <= 12 &&\n aiPeriodType !== 'desconocido';\n\nlet year =\n fallbackYear ||\n (aiIsUsable ? aiYear : 0);\n\nlet month =\n fallbackMonth ||\n (aiIsUsable ? aiMonth : 0);\n\nlet periodType =\n aiIsUsable\n ? aiPeriodType\n : fallbackPeriodType;\n\nlet source =\n aiIsUsable\n ? 'gemini'\n : 'local_fallback';\n\nlet confidence =\n aiIsUsable\n ? aiConfidence\n : fallbackConfidence;\n\nlet conflictResolved = false;\n\nif (\n aiCountry &&\n aiCountry !== country\n) {\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n strongPeriodEvidence &&\n periodType !== fallbackPeriodType\n) {\n periodType = fallbackPeriodType;\n source = 'gemini_validated_with_rules';\n confidence = Math.max(\n fallbackConfidence,\n Math.min(aiConfidence, 0.9)\n );\n conflictResolved = true;\n}\n\nif (\n fallbackYear &&\n aiIsUsable &&\n aiYear !== fallbackYear\n) {\n year = fallbackYear;\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n fallbackMonth &&\n aiIsUsable &&\n aiMonth !== fallbackMonth\n) {\n month = fallbackMonth;\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n !aiIsUsable &&\n fallbackYear &&\n fallbackMonth\n) {\n year = fallbackYear;\n month = fallbackMonth;\n periodType = fallbackPeriodType;\n confidence = fallbackConfidence;\n source = 'local_fallback';\n}\n\nconst complete =\n year >= 2000 &&\n month >= 1 &&\n month <= 12 &&\n periodType !== 'desconocido';\n\nconst dates = buildPeriodDates(\n year,\n month,\n periodType\n);\n\nlet periodDescription = 'Período desconocido';\n\nif (periodType === 'quincena_15') {\n periodDescription = 'Quincena 15';\n}\n\nif (periodType === 'quincena_30') {\n periodDescription =\n 'Quincena 30 / fin de mes';\n}\n\nif (periodType === 'mensual') {\n periodDescription = 'Mensual';\n}\n\nconst monthLabel =\n monthNames[month] || 'Mes desconocido';\n\nconst periodLabel = complete\n ? `${monthLabel} ${year} · ${periodDescription}`\n : 'Período pendiente de confirmar';\n\nconst requiresManualReview =\n !complete ||\n confidence < 0.75;\n\nconst explanation =\n cleanText(rawAI.explanation) ||\n (\n source === 'local_fallback'\n ? 'El período fue identificado mediante las reglas locales de respaldo.'\n : 'El período fue identificado mediante Gemini y validado con las reglas del portal.'\n );\n\nreturn [\n {\n json: {\n ok: true,\n\n message: requiresManualReview\n ? 'El archivo fue analizado, pero el período debe confirmarse manualmente.'\n : 'El período de la nómina fue detectado correctamente.',\n\n detected: {\n country,\n countryName,\n\n fileName,\n\n year,\n month,\n\n periodType,\n period_type: periodType,\n\n periodLabel,\n period_label: periodLabel,\n\n periodStart: dates.periodStart,\n period_start: dates.periodStart,\n\n periodEnd: dates.periodEnd,\n period_end: dates.periodEnd,\n\n confidence:\n Math.round(confidence * 100) / 100,\n\n complete,\n requiresManualReview,\n requires_manual_review:\n requiresManualReview,\n\n source,\n conflictResolved,\n explanation,\n },\n\n debug: {\n fallback: {\n country,\n year: fallbackYear,\n month: fallbackMonth,\n periodType: fallbackPeriodType,\n detectedDay,\n confidence: fallbackConfidence,\n strongPeriodEvidence,\n compactDate:\n compactDateMatch?.[0] || null,\n },\n\n gemini: {\n country: aiCountry || null,\n year: aiYear || 0,\n month: aiMonth || 0,\n periodType: aiPeriodType,\n confidence: aiConfidence,\n explanation:\n cleanText(rawAI.explanation),\n },\n },\n },\n },\n];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 6816, - 1936 - ], - "id": "ea14695e-2fec-4c28-b181-5c3c925b1182", - "name": "Normalizar análisis IA" - }, - { - "parameters": { - "options": { - "responseCode": 200, - "responseHeaders": { - "entries": [ - { - "name": "Content-Type", - "value": "application/json" - } - ] - } - } - }, - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.5, - "position": [ - 7024, - 1936 - ], - "id": "6908b84d-597b-48d0-b448-58086d9f3b90", - "name": "Responder análisis" - }, - { - "parameters": { - "content": "# 🧠 ANÁLISIS AUTOMÁTICO DE NÓMINA — GT, TT y PA\n\nAnaliza el nombre o título del archivo de nómina para identificar automáticamente el período correspondiente antes de ejecutar la verificación.\n\n## Recepción y validación\n\nEl Webhook recibe desde el Portal de Verificación de Nóminas la información del archivo seleccionado.\n\nLa solicitud puede incluir:\n\n- Nombre del archivo.\n- Título o descripción disponible.\n- País o portal de origen.\n- Identificador de la solicitud.\n- Información complementaria enviada por la aplicación.\n\nAntes de utilizar inteligencia artificial, el flujo valida que exista suficiente información para analizar el archivo.\n\n## Extracción con Gemini\n\nCuando la solicitud es válida, Gemini interpreta el nombre o título de la nómina e intenta determinar:\n\n- Año.\n- Mes.\n- Tipo de período.\n- Quincena 15.\n- Quincena 30 o fin de mes.\n- Nómina mensual.\n- País, cuando puede inferirse de forma confiable.\n- Nivel de confianza del análisis.\n\nGemini se utiliza únicamente para interpretar el texto recibido; no ejecuta el cruce de nómina ni modifica información.\n\n## Normalización del resultado\n\nLa respuesta de Gemini se transforma al formato requerido por la aplicación.\n\nEste bloque:\n\n- Limpia texto adicional generado por la IA.\n- Normaliza el año a formato numérico.\n- Convierte el mes al valor esperado por el portal.\n- Estandariza el tipo de período.\n- Corrige variaciones como “segunda quincena”, “fin de mes” o “30”.\n- Conserva los valores originales cuando la IA no puede determinar un dato.\n- Prepara una respuesta consistente para Guatemala, Trinidad y Tobago, Panamá y El Salvador.\n\n## Respuesta al portal\n\nCuando el análisis termina, el workflow devuelve a la aplicación:\n\n- Indicador de éxito.\n- Año detectado.\n- Mes detectado.\n- Tipo de período detectado.\n- País identificado, cuando corresponda.\n- Nivel de confianza.\n- Datos que requieren revisión manual.\n\nLos valores detectados sirven para completar automáticamente el formulario, pero el usuario puede corregirlos antes de ejecutar el proceso.\n\n## Ruta de error\n\nCuando la solicitud es inválida:\n\n- No se llama a Gemini.\n- No se intenta interpretar información incompleta.\n- Se devuelve una respuesta de error al portal.\n- Se indica que falta el nombre del archivo u otro dato obligatorio.\n\n## Reglas\n\n- No inventar un período cuando el título no contiene información suficiente.\n- No ejecutar el cruce de nómina desde este workflow.\n- No tratar una predicción de baja confianza como un dato definitivo.\n- Mantener la opción de corrección manual en la aplicación.\n- Responder siempre al Webhook, tanto en la ruta válida como en la ruta de error.\n- Utilizar una estructura de respuesta compatible con los portales de Guatemala, Trinidad y Tobago, Panamá y El Salvador.", - "height": 1488, - "width": 2256, - "color": 2 - }, - "type": "n8n-nodes-base.stickyNote", - "typeVersion": 1, - "position": [ - 4992, - 1376 - ], - "id": "527d1476-9a32-474a-b493-1070e9ee34f6", - "name": "Sticky Note" - } - ], - "pinData": {}, - "connections": { - "Webhook - Analizar Nómina": { - "main": [ - [ - { - "node": "Validar y preparar solicitud", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validar y preparar solicitud": { - "main": [ - [ - { - "node": "¿Solicitud válida?", - "type": "main", - "index": 0 - } - ] - ] - }, - "¿Solicitud válida?": { - "main": [ - [ - { - "node": "Extraer período con Gemini", - "type": "main", - "index": 0 - } - ], - [ - { - "node": "Responder error", - "type": "main", - "index": 0 - } - ] - ] - }, - "Gemini - Analizar título nómina": { - "ai_languageModel": [ - [ - { - "node": "Extraer período con Gemini", - "type": "ai_languageModel", - "index": 0 - } - ] - ] - }, - "Extraer período con Gemini": { - "main": [ - [ - { - "node": "Normalizar análisis IA", - "type": "main", - "index": 0 - } - ] - ] - }, - "Normalizar análisis IA": { - "main": [ - [ - { - "node": "Responder análisis", - "type": "main", - "index": 0 - } - ] - ] - } - }, - "active": false, - "settings": { - "executionOrder": "v1", - "binaryMode": "separate", - "availableInMCP": true, - "timeSavedMode": "fixed", - "errorWorkflow": "puF4LUczoSz3hcek", - "timezone": "America/Santo_Domingo", - "callerPolicy": "workflowsFromSameOwner" - }, - "tags": [] -} \ No newline at end of file diff --git a/Portal de Verificaci├│n de N├│mina - Honduras.json b/Portal de Verificaci├│n de N├│mina - Honduras.json index 090230c..d5163e7 100644 --- a/Portal de Verificaci├│n de N├│mina - Honduras.json +++ b/Portal de Verificaci├│n de N├│mina - Honduras.json @@ -14,7 +14,7 @@ -1088, 512 ], - "id": "9a529a55-ec46-4e0d-8901-d4a2e5c059f9", + "id": "5d3b8ad0-7348-447e-b5e2-7a00235bb69c", "name": "Webhook", "webhookId": "517b24db-a370-4701-8281-06b7d277b403" }, @@ -28,7 +28,7 @@ -848, 512 ], - "id": "7daf7b32-2a8f-4724-b912-da5d6f80c86d", + "id": "0983b28d-86ea-4453-85e2-90f2b0fd159f", "name": "Preparar entrada app" }, { @@ -41,7 +41,7 @@ -608, 352 ], - "id": "73b4ccb3-b19f-415e-8d2b-f7ef1d43dcfe", + "id": "c7d5a5c0-0044-4570-800a-1069eef2a92e", "name": "Parsear CSV banco HN" }, { @@ -54,7 +54,7 @@ -608, 672 ], - "id": "5d72ed5f-ca78-4d72-9b64-2f92bc7735dc", + "id": "01c05dab-e323-4d59-97ab-348cb3e2e25b", "name": "Preparar archivos nómina HN" }, { @@ -62,8 +62,8 @@ "operation": "xlsx", "options": { "headerRow": true, - "sheetName": "={{ $json.sheet_name }}", - "range": "={{ $json.range }}" + "range": "={{ $json.range }}", + "sheetName": "={{ $json.sheet_name }}" } }, "type": "n8n-nodes-base.extractFromFile", @@ -72,7 +72,7 @@ -368, 672 ], - "id": "460f89f9-a7dd-48ab-a6bf-7d96a33c2f8e", + "id": "d10dc605-66f2-4f44-9ed5-60473d57fd24", "name": "Extract - Archivo Nómina HN", "retryOnFail": false, "onError": "continueRegularOutput" @@ -87,7 +87,7 @@ -128, 672 ], - "id": "1495fff4-c34d-4885-b1a9-bf648765b25b", + "id": "da41c543-d4dd-44e1-90fe-39c81866adce", "name": "Normalizar Nómina HN" }, { @@ -102,7 +102,7 @@ 128, 512 ], - "id": "951d4aea-afdd-4e3b-bcff-30ac7232ccbe", + "id": "a0eac81e-b6b4-475a-b9e9-f72e421e052b", "name": "Merge Banco + Nómina HN" }, { @@ -159,7 +159,7 @@ 368, 352 ], - "id": "fe29b239-2467-40f5-b84f-822376bd4b10", + "id": "b0c95a03-b147-4380-beb3-f822cb7025dd", "name": "HTTP - Empleados BambooHR HN", "retryOnFail": true, "maxTries": 3, @@ -181,7 +181,7 @@ 608, 352 ], - "id": "52f020c5-b1d0-4a51-803b-2576f1c7ab4e", + "id": "36f26790-e15c-4900-be40-e6d031053a4f", "name": "Normalizar BambooHR HN" }, { @@ -196,7 +196,7 @@ 848, 512 ], - "id": "87f27436-4cbd-44e6-926e-b66d2998741b", + "id": "318d9c84-08fc-4124-b360-fafecc6ff320", "name": "Merge - Agregar BambooHR HN" }, { @@ -244,7 +244,7 @@ 1088, 512 ], - "id": "ec09ee1f-6974-4ac5-9f24-42c7747375ee", + "id": "e1578802-aadc-4b81-ad17-a623912a20d4", "name": "Obtener alias BambooHR Supabase", "retryOnFail": true, "maxTries": 2, @@ -253,7 +253,7 @@ }, { "parameters": { - "jsCode": "const data = $('Merge - Agregar BambooHR HN').first().json || {};\nconst aliasResponse = $input.first().json || {};\nconst validatedBambooAliases = Array.isArray(aliasResponse.aliases)\n ? aliasResponse.aliases\n : Array.isArray(aliasResponse)\n ? aliasResponse\n : [];\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst normalizedNameCache = new Map();\nconst nameWordsCache = new Map();\nconst nameMatchScoreCache = new Map();\n\nfunction normalizeName(value) {\n const raw = clean(value);\n if (normalizedNameCache.has(raw)) {\n return normalizedNameCache.get(raw);\n }\n\n const normalized = raw\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n normalizedNameCache.set(raw, normalized);\n return normalized;\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction moneyDiff(payroll, bank) {\n return roundMoney((Number(payroll) || 0) - (Number(bank) || 0));\n}\n\n// Los archivos de Honduras presentan diferencias de redondeo de 0.01 y,\n// en algunos pagos USD, de hasta 0.03 entre la hoja y el plano bancario.\n// Esas variaciones no representan una discrepancia operativa.\nfunction sameCent(a, b) {\n return Math.abs(moneyDiff(a, b)) <= 0.05;\n}\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los', 'y', 'e', 'el',\n 'da', 'do', 'dos', 'das', 'van', 'von',\n]);\n\nfunction nameWords(value) {\n const normalized = normalizeName(value);\n if (nameWordsCache.has(normalized)) {\n return nameWordsCache.get(normalized);\n }\n\n const words = normalized\n .split(' ')\n .filter((word) => word.length > 1 && !IGNORED_NAME_TOKENS.has(word));\n\n nameWordsCache.set(normalized, words);\n return words;\n}\n\nfunction editDistance(a, b) {\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n let previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n const current = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);\n }\n previous = current;\n }\n return previous[b.length];\n}\n\nfunction tokenMatches(a, b) {\n if (a === b) return true;\n const minLength = Math.min(a.length, b.length);\n if (minLength >= 8 && editDistance(a, b) <= 2) return true;\n if (minLength >= 5 && editDistance(a, b) <= 1) return true;\n return false;\n}\n\nfunction samePersonName(a, b) {\n const normalizedA = normalizeName(a);\n const normalizedB = normalizeName(b);\n if (!normalizedA || !normalizedB) return false;\n if (normalizedA === normalizedB) return true;\n\n const wordsA = nameWords(a);\n const wordsB = nameWords(b);\n if (!wordsA.length || !wordsB.length) return false;\n\n const usedB = new Set();\n let matches = 0;\n for (const wordA of wordsA) {\n const matchIndex = wordsB.findIndex((wordB, index) =>\n !usedB.has(index) && tokenMatches(wordA, wordB)\n );\n if (matchIndex >= 0) {\n usedB.add(matchIndex);\n matches += 1;\n }\n }\n\n const smallerLength = Math.min(wordsA.length, wordsB.length);\n const ratio = matches / smallerLength;\n if (smallerLength <= 2) return matches === smallerLength && matches >= 2;\n return matches >= 2 && ratio >= 0.6;\n}\n\nfunction bankNameLooksConsistent(fileName, holderName) {\n const fileWords = nameWords(fileName);\n const holderWords = nameWords(holderName);\n if (!fileWords.length || !holderWords.length) return true;\n\n const used = new Set();\n for (const fw of fileWords) {\n let idx = holderWords.findIndex((hw, i) => !used.has(i) && hw === fw);\n\n // Los archivos BAC pueden truncar el último carácter del nombre\n // (ej. ALVARAD vs ALVARADO). Aceptamos solo truncamiento por prefijo,\n // no errores internos/transposiciones; por eso SANTAIGO vs SANTIAGO\n // sigue reportándose como diferencia de nombre.\n if (idx < 0 && fw.length >= 5) {\n idx = holderWords.findIndex((hw, i) =>\n !used.has(i) &&\n Math.abs(hw.length - fw.length) <= 2 &&\n (hw.startsWith(fw) || fw.startsWith(hw))\n );\n }\n\n if (idx < 0 && fw.length <= 3) {\n idx = holderWords.findIndex((hw, i) => !used.has(i) && hw.startsWith(fw));\n }\n\n if (idx < 0) return false;\n used.add(idx);\n }\n return true;\n}\n\nfunction nameMatchScore(a, b) {\n const na = normalizeName(a);\n const nb = normalizeName(b);\n if (!na || !nb) return 0;\n\n const cacheKey = na <= nb ? `${na}\\u0000${nb}` : `${nb}\\u0000${na}`;\n if (nameMatchScoreCache.has(cacheKey)) {\n return nameMatchScoreCache.get(cacheKey);\n }\n\n if (na === nb) {\n nameMatchScoreCache.set(cacheKey, 1);\n return 1;\n }\n\n const wa = nameWords(a);\n const wb = nameWords(b);\n if (!samePersonName(a, b)) {\n nameMatchScoreCache.set(cacheKey, 0);\n return 0;\n }\n\n const setA = new Set(wa);\n const setB = new Set(wb);\n let exact = 0;\n for (const w of setA) if (setB.has(w)) exact += 1;\n const min = Math.max(1, Math.min(setA.size, setB.size));\n const score = Math.min(\n 0.99,\n 0.75 + (exact / min) * 0.20 + (wa.length === wb.length ? 0.04 : 0)\n );\n\n nameMatchScoreCache.set(cacheKey, score);\n return score;\n}\n\nfunction bankNames(row) {\n return [row.bank_name_file, row.bank_account_holder]\n .map(clean)\n .filter(Boolean);\n}\n\nfunction bestNameScore(payrollName, bankRow) {\n return Math.max(0, ...bankNames(bankRow).map((name) => nameMatchScore(payrollName, name)));\n}\n\nfunction bankDisplayName(row) {\n return clean(row.bank_name_file) || clean(row.bank_account_holder) || 'Pago bancario';\n}\n\nfunction bankRowKey(row, index) {\n return clean(row.bank_row_key) || `${clean(row.source_file)}|${row.row_number || index + 1}`;\n}\n\n\nconst metadata = data.metadata || {};\n\nfunction parseBankDate(value, sourceFile = '') {\n const raw = clean(value);\n let match = raw.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (match) {\n return {\n year: Number(match[3]),\n month: Number(match[2]),\n day: Number(match[1]),\n };\n }\n\n match = raw.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})/);\n if (match) {\n return {\n year: Number(match[1]),\n month: Number(match[2]),\n day: Number(match[3]),\n };\n }\n\n // Respaldo por el patrón habitual del nombre: _DDMMYY / _DDMMYYYY.\n const fileMatch = clean(sourceFile).match(/_(\\d{2})(\\d{2})(\\d{2}|\\d{4})(?:\\D|$)/);\n if (!fileMatch) return null;\n\n const yearPart = Number(fileMatch[3]);\n return {\n year: fileMatch[3].length === 2 ? 2000 + yearPart : yearPart,\n month: Number(fileMatch[2]),\n day: Number(fileMatch[1]),\n };\n}\n\nfunction selectedPeriodKind() {\n const type = clean(metadata.period_type).toLowerCase();\n if (type === 'quincena_15') return 'q15';\n if (type === 'quincena_30') return 'q30';\n return 'unknown';\n}\n\nfunction conceptPeriodHint(row) {\n const concept = normalizeName(row.concept || '');\n if (!concept) return 'unknown';\n\n if (\n /(?:^| )1(?:ra|er|era)? quincena(?: |$)/.test(concept) ||\n concept.includes('primera quincena') ||\n concept.includes('quincena 1ra') ||\n concept.includes('quincena 1')\n ) {\n return 'q15';\n }\n\n if (\n /(?:^| )2(?:da|do|nda)? quincena(?: |$)/.test(concept) ||\n concept.includes('segunda quincena') ||\n concept.includes('quincena 2da') ||\n concept.includes('quincena 2') ||\n concept.includes('fin de mes')\n ) {\n return 'q30';\n }\n\n return 'unknown';\n}\n\nfunction periodInfoForBankRow(row) {\n const selected = selectedPeriodKind();\n const rowDate = parseBankDate(row.application_date, row.source_file);\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n const hint = conceptPeriodHint(row);\n\n if (selected === 'unknown') {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (hint !== 'unknown' && hint === selected) {\n return { known: true, selected, inPeriod: true, affinity: 4, rowDate, hint };\n }\n\n if (!rowDate || !selectedYear || !selectedMonth) {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (rowDate.year !== selectedYear || rowDate.month !== selectedMonth) {\n return { known: true, selected, inPeriod: false, affinity: 0, rowDate, hint };\n }\n\n const inHalf = selected === 'q15'\n ? rowDate.day <= 15\n : rowDate.day >= 16;\n\n return {\n known: true,\n selected,\n inPeriod: inHalf,\n affinity: inHalf ? 3 : 0,\n rowDate,\n hint,\n };\n}\n\nfunction isSupplementPayroll(payroll) {\n return clean(payroll?.source_kind).toLowerCase() !== 'quincenal';\n}\n\nfunction bankCandidatePeriodScore(payroll, bank) {\n const info = periodInfoForBankRow(bank);\n if (!info.known || info.inPeriod) return info.affinity || 1;\n\n // Una extraordinaria/vacación/XIII Mes puede pagarse fuera de la mitad\n // estricta del mes. Se permite como respaldo, siempre detrás de cualquier\n // candidato del período seleccionado.\n if (isSupplementPayroll(payroll) && info.rowDate) {\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n if (info.rowDate.year === selectedYear && info.rowDate.month === selectedMonth) {\n return 0.5;\n }\n }\n\n return -1;\n}\n\nconst payrollRows = Array.isArray(data.payroll?.rows) ? data.payroll.rows : [];\nconst bankRows = Array.isArray(data.bank?.rows) ? data.bank.rows : [];\n\n// Guardia de seguridad: nunca generar un reporte \"válido\" cuando el request\n// traía archivos pero el parser no consiguió ninguna fila. Esto evita que\n// todas las personas de nómina aparezcan falsamente como discrepancias.\nif (payrollRows.length === 0) {\n throw new Error(\n 'Honduras: no se pudo leer ninguna fila válida de nómina. ' +\n 'Revisa los archivos Excel de Honduras y las hojas de nómina configuradas.'\n );\n}\n\nif (bankRows.length === 0) {\n const detectedFiles = Number(data.bank?.files_count || data.summary?.bank_files_count || 0);\n const invalidFiles = Array.isArray(data.bank?.file_summaries)\n ? data.bank.file_summaries.filter((file) => file?.ok === false)\n : [];\n\n throw new Error(\n 'Honduras: no se pudo leer ninguna transacción bancaria. ' +\n `Archivos bancarios detectados: ${detectedFiles}. ` +\n (invalidFiles.length\n ? `Archivos con error de formato: ${invalidFiles.map((file) => file.file_name).join(', ')}.`\n : 'Verifica que los CSV sean los planos bancarios esperados.')\n );\n}\n\nconst bambooEmployees = Array.isArray(data.bamboo?.employees) ? data.bamboo.employees : [];\nconst resolvedNameMatches = data.bamboo?.resolved_name_matches || {};\nconst bambooValidationAvailable = data.bamboo?.fetch_complete === true &&\n data.bamboo?.validation_available === true && bambooEmployees.length > 0;\n\nconst bambooByEmployeeNumber = new Map();\nconst bambooById = new Map();\nconst bambooByExactName = new Map();\nfor (const employee of bambooEmployees) {\n const number = clean(employee.employee_number || employee.employeeNumber);\n const id = clean(employee.bamboo_id || employee.id);\n if (number) {\n const arr = bambooByEmployeeNumber.get(number) || [];\n arr.push(employee);\n bambooByEmployeeNumber.set(number, arr);\n }\n if (id) bambooById.set(id, employee);\n for (const alias of employee.aliases || [employee.full_name]) {\n const normalized = normalizeName(alias);\n if (!normalized) continue;\n const arr = bambooByExactName.get(normalized) || [];\n arr.push(employee);\n bambooByExactName.set(normalized, arr);\n }\n}\n\nfunction employeeKey(employee) {\n return clean(employee?.bamboo_id || employee?.id) ||\n clean(employee?.employee_number || employee?.employeeNumber) ||\n normalizeName(employee?.full_name || employee?.displayName);\n}\n\nfunction validatedAliasDecision(row) {\n if (!validatedBambooAliases.length) return null;\n const rowAccount = normalizeAccount(row.account);\n const names = bankNames(row).map(normalizeName).filter(Boolean);\n const candidates = validatedBambooAliases.filter((alias) => {\n const country = clean(alias.pais || alias.country).toUpperCase();\n if (country && country !== 'HN') return false;\n const source = normalizeName(alias.nombre_origen || alias.source_name || alias.alias || '');\n if (!source || !names.includes(source)) return false;\n const aliasAccount = normalizeAccount(alias.cuenta_bancaria || alias.cuenta_banco || alias.bank_account || '');\n return !aliasAccount || !rowAccount || aliasAccount === rowAccount;\n });\n if (candidates.length !== 1) return null;\n const alias = candidates[0];\n let employee = null;\n const employeeNumber = clean(alias.bamboo_employee_number || '');\n const bambooId = clean(alias.bamboo_employee_id || '');\n const bambooName = normalizeName(alias.bamboo_nombre || '');\n if (employeeNumber && (bambooByEmployeeNumber.get(employeeNumber) || []).length === 1) {\n employee = bambooByEmployeeNumber.get(employeeNumber)[0];\n } else if (bambooId && bambooById.has(bambooId)) {\n employee = bambooById.get(bambooId);\n } else if (bambooName && (bambooByExactName.get(bambooName) || []).length === 1) {\n employee = bambooByExactName.get(bambooName)[0];\n }\n if (!employee) return null;\n return {\n found: true,\n matched_by: 'validated_bamboo_alias',\n confidence: 1,\n employee,\n employee_key: employeeKey(employee),\n bamboo_alias: clean(alias.bamboo_nombre) || employee.full_name || '',\n };\n}\n\nfunction precomputedDecision(name) {\n const normalized = normalizeName(name);\n if (!normalized) return null;\n const decision = resolvedNameMatches[normalized];\n if (!decision || decision.found !== true) return null;\n const index = Number(decision.employee_index);\n const employee = Number.isInteger(index) && index >= 0 ? bambooEmployees[index] : null;\n const key = clean(decision.employee_key) || employeeKey(employee);\n if (!key) return null;\n return {\n found: true,\n matched_by: decision.matched_by || 'precomputed_name',\n confidence: Number(decision.confidence || 0),\n employee,\n employee_key: key,\n bamboo_alias: clean(decision.bamboo_alias) || employee?.full_name || '',\n query_name: clean(name),\n };\n}\n\nfunction resolveBamboo(row, linkedPayrollNames = []) {\n if (!bambooValidationAvailable) return { found: false, reason: 'validation_unavailable' };\n const validated = validatedAliasDecision(row);\n if (validated) return validated;\n\n const names = Array.from(new Set([...bankNames(row), ...linkedPayrollNames.map(clean)].filter(Boolean)));\n const decisions = names.map(precomputedDecision).filter(Boolean);\n if (!decisions.length) return { found: false, reason: 'no_confident_name_match' };\n\n const byKey = new Map();\n for (const d of decisions) {\n const current = byKey.get(d.employee_key);\n if (!current || d.confidence > current.confidence) byKey.set(d.employee_key, d);\n }\n const ranked = Array.from(byKey.values()).sort((a, b) => b.confidence - a.confidence);\n if (ranked.length === 1) return ranked[0];\n if ((ranked[0].confidence - ranked[1].confidence) >= 0.08) return ranked[0];\n\n // Si el nombre corto del archivo es ambiguo pero el nombre legal del\n // cuentahabiente apunta de forma única a BambooHR, damos prioridad a este\n // último. Caso real validado: \"Suyapa Palma\" vs\n // \"SUYAPA MELISA PALMA AGUILAR\".\n const holderNormalized = normalizeName(row.bank_account_holder || '');\n if (holderNormalized) {\n const holderMatches = ranked.filter((decision) =>\n normalizeName(decision.query_name || '') === holderNormalized\n );\n if (holderMatches.length === 1) return holderMatches[0];\n }\n\n return { found: false, reason: 'conflicting_precomputed_name_matches', candidates: ranked.slice(0, 3) };\n}\n\nconst usedBankKeys = new Set();\nconst matchedPayrollIndexes = new Set();\nconst matchedPairs = [];\nconst bankIndexByKey = new Map();\n\n// Índice por centavos para que la pasada principal no compare cada fila de\n// nómina contra todas las transacciones del banco. Esto preserva exactamente\n// la lógica original: la primera pasada ya exigía el mismo monto al centavo.\nconst bankIndexesByAmountCents = new Map();\n\nfunction amountCents(value) {\n return Math.round(roundMoney(value) * 100);\n}\n\nbankRows.forEach((row, index) => {\n const key = bankRowKey(row, index);\n bankIndexByKey.set(key, index);\n\n const amountKey = `${clean(row.currency || 'HNL').toUpperCase()}|${amountCents(row.amount)}`;\n const bucket = bankIndexesByAmountCents.get(amountKey) || [];\n bucket.push(index);\n bankIndexesByAmountCents.set(amountKey, bucket);\n});\n\nfunction availableNameCandidates(payroll, requireExactAmount) {\n const candidates = [];\n\n // La pasada exacta solo inspecciona filas con el mismo importe. La segunda\n // pasada conserva el barrido completo y el mismo criterio de persona fuerte\n // + menor diferencia que tenía el workflow validado.\n const candidateIndexes = requireExactAmount\n ? (bankIndexesByAmountCents.get(`${clean(payroll.currency || 'HNL').toUpperCase()}|${amountCents(payroll.payroll_amount)}`) || [])\n : null;\n\n const inspectBank = (bankIndex) => {\n const bank = bankRows[bankIndex];\n if (!bank) return;\n\n const key = bankRowKey(bank, bankIndex);\n if (usedBankKeys.has(key)) return;\n if (clean(payroll.currency || 'HNL').toUpperCase() !== clean(bank.currency || 'HNL').toUpperCase()) return;\n\n if (requireExactAmount && !sameCent(payroll.payroll_amount, bank.amount)) return;\n\n const periodScore = bankCandidatePeriodScore(payroll, bank);\n if (periodScore < 0) return;\n\n const score = bestNameScore(payroll.employee_name, bank);\n if (score <= 0) return;\n\n candidates.push({\n bank,\n bankIndex,\n key,\n score,\n periodScore,\n amountDiff: Math.abs(moneyDiff(payroll.payroll_amount, bank.amount)),\n });\n };\n\n if (candidateIndexes) {\n for (const bankIndex of candidateIndexes) {\n inspectBank(bankIndex);\n }\n } else {\n for (let bankIndex = 0; bankIndex < bankRows.length; bankIndex++) {\n inspectBank(bankIndex);\n }\n }\n\n candidates.sort((a, b) => {\n if (b.periodScore !== a.periodScore) return b.periodScore - a.periodScore;\n if (b.score !== a.score) return b.score - a.score;\n if (a.amountDiff !== b.amountDiff) return a.amountDiff - b.amountDiff;\n return a.bankIndex - b.bankIndex;\n });\n\n return candidates;\n}\n\n// Primera pasada: persona + monto exacto. Es la evidencia más fuerte y evita\n// que extraordinarias/vacaciones de una misma persona se mezclen entre sí.\npayrollRows.forEach((payroll, payrollIndex) => {\n const candidates = availableNameCandidates(payroll, true);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\n// Segunda pasada: persona fuerte aunque el monto sea distinto. Solo se toma\n// una contraparte bancaria por fila de nómina y se prioriza la menor diferencia.\npayrollRows.forEach((payroll, payrollIndex) => {\n if (matchedPayrollIndexes.has(payrollIndex)) return;\n const candidates = availableNameCandidates(payroll, false);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\nconst linkedPayrollNamesByBankKey = new Map();\nfor (const pair of matchedPairs) {\n const current = linkedPayrollNamesByBankKey.get(pair.key) || [];\n current.push(pair.payroll.employee_name);\n linkedPayrollNamesByBankKey.set(pair.key, current);\n}\n\n// Filas bancarias del período de Honduras.\n// Toda fila emparejada entra siempre. Para una fila no emparejada se usa la\n// fecha/concepto del plano para evitar arrastrar accidentalmente otra quincena.\nconst includedBankRows = [];\nconst excludedBankRows = [];\n\nfor (let index = 0; index < bankRows.length; index++) {\n const row = bankRows[index];\n const key = bankRowKey(row, index);\n\n if (usedBankKeys.has(key)) {\n includedBankRows.push({ row, key, index, inclusion: 'matched_payroll' });\n continue;\n }\n\n const periodInfo = periodInfoForBankRow(row);\n if (periodInfo.known && !periodInfo.inPeriod) {\n excludedBankRows.push({ row, key, reason: 'outside_selected_payroll_period' });\n continue;\n }\n\n includedBankRows.push({ row, key, index, inclusion: 'unmatched_current_period' });\n}\n\nconst includedBankKeySet = new Set(includedBankRows.map((entry) => entry.key));\n\nconst coreRows = [];\nconst wrongAccountGroups = new Map();\nfor (const pair of matchedPairs) {\n if (!includedBankKeySet.has(pair.key)) continue;\n const payrollAmount = roundMoney(pair.payroll.payroll_amount);\n const bankAmount = roundMoney(pair.bank.amount);\n const difference = moneyDiff(payrollAmount, bankAmount);\n const currency = clean(pair.payroll.currency || pair.bank.currency || 'HNL').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAccount = normalizeAccount(pair.payroll.account || pair.payroll.payroll_account);\n const bankAccount = normalizeAccount(pair.bank.account);\n const accountMismatch = Boolean(payrollAccount && bankAccount && payrollAccount !== bankAccount);\n if (accountMismatch) {\n const groupKey = `${normalizeName(pair.payroll.employee_name)}|${currency}|${payrollAccount}|${bankAccount}`;\n const current = wrongAccountGroups.get(groupKey) || {\n id: `hn_wrong_account_${wrongAccountGroups.size + 1}`,\n employee: pair.payroll.employee_name,\n employee_name: pair.payroll.employee_name,\n employeeNumber: pair.payroll.employee_number || '', employee_number: pair.payroll.employee_number || '',\n account: bankAccount, payrollAccount, payroll_account: payrollAccount, bankAccount, bank_account: bankAccount,\n currency, payrollAmount:0, payroll_amount:0, bankAmount:0, bank_amount:0, difference:0,\n status:'Pendiente revisión', category:'posible_cuenta_mal_digitada', subcategory:'nombre_y_monto_coinciden_cuenta_diferente',\n observation:`El nombre y el monto coinciden, pero la cuenta de nómina (${payrollAccount}) es diferente a la cuenta del banco (${bankAccount}).`,\n source_rows:[], source_sheets:new Set(), source_files:new Set(), shipment_numbers:new Set(), plan_numbers:new Set(),\n bank_name_file: pair.bank.bank_name_file || '', bank_account_holder: pair.bank.bank_account_holder || ''\n };\n current.payrollAmount=roundMoney(current.payrollAmount+payrollAmount); current.payroll_amount=current.payrollAmount;\n current.bankAmount=roundMoney(current.bankAmount+bankAmount); current.bank_amount=current.bankAmount; current.difference=moneyDiff(current.payrollAmount,current.bankAmount);\n current.source_rows.push(pair.payroll); if(pair.payroll.source_sheet)current.source_sheets.add(pair.payroll.source_sheet); if(pair.bank.source_file)current.source_files.add(pair.bank.source_file); if(pair.bank.shipment_number)current.shipment_numbers.add(pair.bank.shipment_number); if(pair.bank.plan_number)current.plan_numbers.add(pair.bank.plan_number);\n wrongAccountGroups.set(groupKey,current); continue;\n }\n const exact = sameCent(payrollAmount, bankAmount);\n coreRows.push({id:`hn_match_${pair.payrollIndex+1}_${pair.bankIndex+1}`,employee:pair.payroll.employee_name,employee_name:pair.payroll.employee_name,employeeNumber:pair.payroll.employee_number||'',employee_number:pair.payroll.employee_number||'',account:bankAccount||payrollAccount,payrollAccount:payrollAccount,payroll_account:payrollAccount,bankAccount:bankAccount,bank_account:bankAccount,currency,payrollAmount,payroll_amount:payrollAmount,bankAmount,bank_amount:bankAmount,difference,status:exact?'Coincide':'Pendiente revisión',category:exact?'coincidencia':'discrepancia',subcategory:exact?'persona_cuenta_y_monto_exactos':'persona_y_cuenta_coinciden_monto_diferente',observation:exact?'Coincidencia de persona, cuenta y monto.':difference>0?'Se pagó de menos.':'Se pagó de más.',source_kind:pair.payroll.source_kind||'',source_sheet:pair.payroll.source_sheet||'',source_file:pair.bank.source_file||'',shipment_number:pair.bank.shipment_number||'',plan_number:pair.bank.plan_number||'',plan_label:pair.bank.plan_label||'',reference:pair.bank.reference||'',bank_name_file:pair.bank.bank_name_file||'',bank_account_holder:pair.bank.bank_account_holder||''});\n}\nfor (let payrollIndex=0; payrollIndex({...row,source_sheets:Array.from(row.source_sheets),source_files:Array.from(row.source_files),shipment_numbers:Array.from(row.shipment_numbers),plan_numbers:Array.from(row.plan_numbers),source_sheet:Array.from(row.source_sheets).join(' / '),source_file:Array.from(row.source_files).join(' / '),shipment_number:Array.from(row.shipment_numbers).join(' / '),plan_number:Array.from(row.plan_numbers).join(' / ')}));\ncoreRows.push(...wrongAccountRows);\n\n// Banco sin Nómina se agrupa para que repeticiones de la misma persona/cuenta\n// se presenten como un solo hallazgo, tal como en los reportes manuales.\n// Banco sin nómina se conserva por transacción, no agrupado por cuenta.\n// Esto es importante porque una misma persona/cuenta puede aparecer en más de\n// un envío dentro del período y cada pago debe quedar auditado por separado.\nconst bankWithoutPayrollRows = [];\nfor (const entry of includedBankRows) {\n if (usedBankKeys.has(entry.key)) continue;\n const row = entry.row;\n const displayName = bankDisplayName(row);\n const rowCurrency = clean(row.currency || 'HNL').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const bankAmount = roundMoney(Number(row.amount || 0));\n\n bankWithoutPayrollRows.push({\n id: `hn_bank_without_payroll_${entry.index + 1}`,\n employee: displayName,\n employee_name: displayName,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount,\n bank_amount: bankAmount,\n difference: roundMoney(0 - bankAmount),\n status: 'Pendiente revisión',\n category: 'banco_sin_nomina',\n subcategory: 'pago_banco_sin_registro_nomina',\n observation: 'Pago bancario sin registro equivalente en las planillas cargadas.',\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n source_files: row.source_file ? [row.source_file] : [],\n shipment_numbers: row.shipment_number ? [row.shipment_number] : [],\n plan_numbers: row.plan_number ? [row.plan_number] : [],\n source_rows: [row],\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\ncoreRows.push(...bankWithoutPayrollRows);\n\nconst nameDifferenceRows = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n if (!clean(row.bank_name_file) || !clean(row.bank_account_holder)) continue;\n if (bankNameLooksConsistent(row.bank_name_file, row.bank_account_holder)) continue;\n nameDifferenceRows.push({\n id: `hn_bank_name_difference_${entry.index + 1}`,\n employee: row.bank_name_file,\n employee_name: row.bank_name_file,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount: roundMoney(row.amount),\n bank_amount: roundMoney(row.amount),\n difference: 0,\n status: 'Pendiente revisión',\n category: 'diferencia_nombre_banco',\n subcategory: 'nombre_archivo_vs_cuentahabiente',\n observation: `El Nombre en Archivo (${row.bank_name_file}) no coincide con el Nombre del Cuentahabiente (${row.bank_account_holder}).`,\n bank_name_file: row.bank_name_file,\n bank_account_holder: row.bank_account_holder,\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\n\nconst bankWithoutBambooMap = new Map();\nconst bambooMatchDetails = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const linkedNames = linkedPayrollNamesByBankKey.get(entry.key) || [];\n const decision = resolveBamboo(row, linkedNames);\n bambooMatchDetails.push({\n bank_row_key: entry.key,\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n account: row.account || '',\n amount: row.amount || 0,\n linked_payroll_names: linkedNames,\n ...decision,\n });\n if (decision.found) continue;\n\n const displayName = bankDisplayName(row);\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n const groupingKey = `${normalizeAccount(row.account)}|${normalizeName(displayName)}|${rowCurrency}`;\n const current = bankWithoutBambooMap.get(groupingKey) || {\n id: `hn_bank_without_bamboo_${bankWithoutBambooMap.size + 1}`,\n bank_name_file: row.bank_name_file || displayName,\n bank_account_holder: row.bank_account_holder || '',\n employee_name: row.bank_name_file || displayName,\n account: row.account || '',\n currency: rowCurrency,\n bank_amount: 0,\n amount: 0,\n status: 'Pendiente revisión',\n category: 'banco_sin_bamboo',\n shipment_numbers: new Set(),\n references: new Set(),\n source_files: new Set(),\n source_rows: [],\n };\n current.bank_amount = roundMoney(current.bank_amount + Number(row.amount || 0));\n current.amount = current.bank_amount;\n if (row.shipment_number) current.shipment_numbers.add(row.shipment_number);\n if (row.reference) current.references.add(row.reference);\n if (row.source_file) current.source_files.add(row.source_file);\n current.source_rows.push(row);\n bankWithoutBambooMap.set(groupingKey, current);\n}\n\nconst bankWithoutBamboo = Array.from(bankWithoutBambooMap.values()).map((row) => ({\n ...row,\n shipment_numbers: Array.from(row.shipment_numbers),\n shipment_number: Array.from(row.shipment_numbers).join(' / '),\n references: Array.from(row.references),\n source_files: Array.from(row.source_files),\n source_file: Array.from(row.source_files).join(' / '),\n difference: roundMoney(0 - row.bank_amount),\n}));\n\nfunction priority(row) {\n const category = clean(row.category).toLowerCase();\n if (category === 'posible_cuenta_mal_digitada') return 1;\n if (category === 'discrepancia') return 2;\n if (category === 'banco_sin_nomina') return 3;\n if (category === 'diferencia_nombre_banco') return 5;\n if (category === 'coincidencia') return 99;\n return 50;\n}\n\nconst rowsFinales = [...coreRows, ...nameDifferenceRows].sort((a, b) => {\n const p = priority(a) - priority(b);\n if (p !== 0) return p;\n return clean(a.employee_name).localeCompare(clean(b.employee_name), 'es');\n});\n\nconst coincidencias = coreRows.filter((row) => row.category === 'coincidencia').length;\nconst discrepancias = coreRows.filter((row) => row.category === 'discrepancia').length;\nconst bancoSinNomina = bankWithoutPayrollRows.length;\nconst diferenciasNombreBanco = nameDifferenceRows.length;\nconst posiblesCuentasMalDigitadas = wrongAccountRows.length;\nconst pendientes = discrepancias + bancoSinNomina + diferenciasNombreBanco;\nconst totalNominaByCurrency = {HNL:0,USD:0};\nfor(const row of payrollRows){const c=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalNominaByCurrency[c]=roundMoney(totalNominaByCurrency[c]+Number(row.payroll_amount||0));}\nconst totalBancoByCurrency = {HNL:0,USD:0};\nfor(const entry of includedBankRows){const c=clean(entry.row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalBancoByCurrency[c]=roundMoney(totalBancoByCurrency[c]+Number(entry.row.amount||0));}\nconst diferenciaByCurrency={HNL:moneyDiff(totalNominaByCurrency.HNL,totalBancoByCurrency.HNL),USD:moneyDiff(totalNominaByCurrency.USD,totalBancoByCurrency.USD)};\nconst totalNomina=totalNominaByCurrency.HNL || totalNominaByCurrency.USD;\nconst totalBanco=totalBancoByCurrency.HNL || totalBancoByCurrency.USD;\nconst diferenciaTotal=totalNominaByCurrency.HNL||totalBancoByCurrency.HNL?diferenciaByCurrency.HNL:diferenciaByCurrency.USD;\n\nconst bambooSummaryCompact = { ...(data.bamboo || {}) };\ndelete bambooSummaryCompact.employees;\n\nreturn [{\n json: {\n ok: true,\n stage: 'cruce_nomina_banco_honduras',\n errors: [],\n metadata: data.metadata || {},\n summary: {\n coincidencias,\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo: bankWithoutBamboo.length,\n nominaSinCuenta: 0,\n diferenciasNombreBanco,\n posiblesCuentasMalDigitadas,\n pendientes,\n filasNominaValidas: Number(data.payroll?.base_rows_count || data.payroll?.valid_rows_count || 0),\n filasNominaSinCuenta: 0,\n suplementosPotenciales: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaAplicados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAplicados: 0,\n suplementosNominaAdjuntados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAdjuntados: Number(data.payroll?.unattached_supplements_count || 0),\n reconciliacionesExactasFinales: coincidencias,\n cuentasNominaAgrupadas: 0,\n transaccionesBanco: includedBankRows.length,\n cuentasBancoAgrupadas: new Set(includedBankRows.map((entry) => normalizeAccount(entry.row.account)).filter(Boolean)).size,\n empleadosBambooHN: Number(data.bamboo?.honduras_count || 0),\n empleadosBambooEnPeriodo: Number(data.bamboo?.active_in_period_count || 0),\n bambooPaginasDescargadas: Number(data.bamboo?.pages_fetched || 0),\n bambooEmpleadosEsperados: Number(data.bamboo?.expected_total || 0),\n bambooDescargaCompleta: Boolean(data.bamboo?.fetch_complete),\n bambooValidacionDisponible: bambooValidationAvailable,\n totalNominaBase: roundMoney(data.payroll?.base_total || 0),\n totalSuplementosAplicados: roundMoney(data.payroll?.supplements_total || 0),\n totalNomina,\n totalBanco,\n diferenciaTotal,\n totalNominaPorMoneda: totalNominaByCurrency,\n totalBancoPorMoneda: totalBancoByCurrency,\n diferenciaTotalPorMoneda: diferenciaByCurrency,\n },\n rows: rowsFinales,\n bankWithoutBamboo,\n nameDifferences: nameDifferenceRows,\n bambooSummary: bambooSummaryCompact,\n reportUrl: null,\n debug: {\n matched_pairs_count: matchedPairs.length,\n included_bank_rows_count: includedBankRows.length,\n excluded_bank_rows_count: excludedBankRows.length,\n excluded_bank_rows_preview: excludedBankRows.slice(0, 100),\n validated_bamboo_aliases_loaded: validatedBambooAliases.length,\n bamboo_matches: bambooMatchDetails,\n bamboo_validation_available: bambooValidationAvailable,\n banco_sin_bamboo: bankWithoutBamboo,\n payroll_preview: payrollRows.slice(0, 20),\n bank_included_preview: includedBankRows.slice(0, 20).map((entry) => entry.row),\n },\n },\n}];\n" + "jsCode": "const data = $('Merge - Agregar BambooHR HN').first().json || {};\nconst aliasResponse = $input.first().json || {};\nconst validatedBambooAliases = Array.isArray(aliasResponse.aliases)\n ? aliasResponse.aliases\n : Array.isArray(aliasResponse)\n ? aliasResponse\n : [];\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst normalizedNameCache = new Map();\nconst nameWordsCache = new Map();\nconst nameMatchScoreCache = new Map();\n\nfunction normalizeName(value) {\n const raw = clean(value);\n if (normalizedNameCache.has(raw)) {\n return normalizedNameCache.get(raw);\n }\n\n const normalized = raw\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n normalizedNameCache.set(raw, normalized);\n return normalized;\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction moneyDiff(payroll, bank) {\n return roundMoney((Number(payroll) || 0) - (Number(bank) || 0));\n}\n\nconst MONEY_TOLERANCE = 0.05;\nconst MONEY_TOLERANCE_CENTS = Math.round(MONEY_TOLERANCE * 100);\n\nfunction sameCent(a, b) {\n return Math.abs(moneyDiff(a, b)) <= MONEY_TOLERANCE + 1e-9;\n}\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los', 'y', 'e', 'el',\n 'da', 'do', 'dos', 'das', 'van', 'von',\n]);\n\nfunction nameWords(value) {\n const normalized = normalizeName(value);\n if (nameWordsCache.has(normalized)) {\n return nameWordsCache.get(normalized);\n }\n\n const words = normalized\n .split(' ')\n .filter((word) => word.length > 1 && !IGNORED_NAME_TOKENS.has(word));\n\n nameWordsCache.set(normalized, words);\n return words;\n}\n\nfunction editDistance(a, b) {\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n let previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n const current = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);\n }\n previous = current;\n }\n return previous[b.length];\n}\n\nfunction tokenMatches(a, b) {\n if (a === b) return true;\n const minLength = Math.min(a.length, b.length);\n if (minLength >= 8 && editDistance(a, b) <= 2) return true;\n if (minLength >= 5 && editDistance(a, b) <= 1) return true;\n return false;\n}\n\nfunction samePersonName(a, b) {\n const normalizedA = normalizeName(a);\n const normalizedB = normalizeName(b);\n if (!normalizedA || !normalizedB) return false;\n if (normalizedA === normalizedB) return true;\n\n const wordsA = nameWords(a);\n const wordsB = nameWords(b);\n if (!wordsA.length || !wordsB.length) return false;\n\n const usedB = new Set();\n let matches = 0;\n for (const wordA of wordsA) {\n const matchIndex = wordsB.findIndex((wordB, index) =>\n !usedB.has(index) && tokenMatches(wordA, wordB)\n );\n if (matchIndex >= 0) {\n usedB.add(matchIndex);\n matches += 1;\n }\n }\n\n const smallerLength = Math.min(wordsA.length, wordsB.length);\n const ratio = matches / smallerLength;\n if (smallerLength <= 2) return matches === smallerLength && matches >= 2;\n return matches >= 2 && ratio >= 0.6;\n}\n\nfunction bankNameLooksConsistent(fileName, holderName) {\n const fileWords = nameWords(fileName);\n const holderWords = nameWords(holderName);\n\n if (!fileWords.length || !holderWords.length) return true;\n\n const used = new Set();\n\n for (const fileWord of fileWords) {\n let matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord === fileWord\n );\n\n // Algunos planos de Honduras recortan el último carácter del apellido\n // por longitud de campo (ej.: ALVARAD -> ALVARADO). Eso no debe\n // convertirse en una diferencia de nombre. No se aceptan sustituciones\n // ortográficas (ej.: GONZALES -> GONZALEZ), porque sí requieren revisión.\n if (matchIndex < 0) {\n matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord.startsWith(fileWord) &&\n (\n (fileWord.length <= 3) ||\n (\n fileWord.length >= 4 &&\n holderWord.length === fileWord.length + 1\n )\n )\n );\n }\n\n if (matchIndex < 0) return false;\n used.add(matchIndex);\n }\n\n return true;\n}\n\nfunction nameMatchScore(a, b) {\n const na = normalizeName(a);\n const nb = normalizeName(b);\n if (!na || !nb) return 0;\n\n const cacheKey = na <= nb ? `${na}\\u0000${nb}` : `${nb}\\u0000${na}`;\n if (nameMatchScoreCache.has(cacheKey)) {\n return nameMatchScoreCache.get(cacheKey);\n }\n\n if (na === nb) {\n nameMatchScoreCache.set(cacheKey, 1);\n return 1;\n }\n\n const wa = nameWords(a);\n const wb = nameWords(b);\n if (!samePersonName(a, b)) {\n nameMatchScoreCache.set(cacheKey, 0);\n return 0;\n }\n\n const setA = new Set(wa);\n const setB = new Set(wb);\n let exact = 0;\n for (const w of setA) if (setB.has(w)) exact += 1;\n const min = Math.max(1, Math.min(setA.size, setB.size));\n const score = Math.min(\n 0.99,\n 0.75 + (exact / min) * 0.20 + (wa.length === wb.length ? 0.04 : 0)\n );\n\n nameMatchScoreCache.set(cacheKey, score);\n return score;\n}\n\nfunction bankNames(row) {\n return [row.bank_name_file, row.bank_account_holder]\n .map(clean)\n .filter(Boolean);\n}\n\nfunction bestNameScore(payrollName, bankRow) {\n return Math.max(0, ...bankNames(bankRow).map((name) => nameMatchScore(payrollName, name)));\n}\n\nfunction bankDisplayName(row) {\n return clean(row.bank_name_file) || clean(row.bank_account_holder) || 'Pago bancario';\n}\n\nfunction bankRowKey(row, index) {\n return clean(row.bank_row_key) || `${clean(row.source_file)}|${row.row_number || index + 1}`;\n}\n\n\nconst metadata = data.metadata || {};\n\nfunction parseBankDate(value, sourceFile = '') {\n const raw = clean(value);\n let match = raw.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (match) {\n return {\n year: Number(match[3]),\n month: Number(match[2]),\n day: Number(match[1]),\n };\n }\n\n match = raw.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})/);\n if (match) {\n return {\n year: Number(match[1]),\n month: Number(match[2]),\n day: Number(match[3]),\n };\n }\n\n // Respaldo por el patrón habitual del nombre: _DDMMYY / _DDMMYYYY.\n const fileMatch = clean(sourceFile).match(/_(\\d{2})(\\d{2})(\\d{2}|\\d{4})(?:\\D|$)/);\n if (!fileMatch) return null;\n\n const yearPart = Number(fileMatch[3]);\n return {\n year: fileMatch[3].length === 2 ? 2000 + yearPart : yearPart,\n month: Number(fileMatch[2]),\n day: Number(fileMatch[1]),\n };\n}\n\nfunction selectedPeriodKind() {\n const type = clean(metadata.period_type).toLowerCase();\n if (type === 'quincena_15') return 'q15';\n if (type === 'quincena_30') return 'q30';\n return 'unknown';\n}\n\nfunction conceptPeriodHint(row) {\n const concept = normalizeName(row.concept || '');\n if (!concept) return 'unknown';\n\n if (\n /(?:^| )1(?:ra|er|era)? quincena(?: |$)/.test(concept) ||\n concept.includes('primera quincena') ||\n concept.includes('quincena 1ra') ||\n concept.includes('quincena 1')\n ) {\n return 'q15';\n }\n\n if (\n /(?:^| )2(?:da|do|nda)? quincena(?: |$)/.test(concept) ||\n concept.includes('segunda quincena') ||\n concept.includes('quincena 2da') ||\n concept.includes('quincena 2') ||\n concept.includes('fin de mes')\n ) {\n return 'q30';\n }\n\n return 'unknown';\n}\n\nfunction periodInfoForBankRow(row) {\n const selected = selectedPeriodKind();\n const rowDate = parseBankDate(row.application_date, row.source_file);\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n const hint = conceptPeriodHint(row);\n\n if (selected === 'unknown') {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (hint !== 'unknown' && hint === selected) {\n return { known: true, selected, inPeriod: true, affinity: 4, rowDate, hint };\n }\n\n if (!rowDate || !selectedYear || !selectedMonth) {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (rowDate.year !== selectedYear || rowDate.month !== selectedMonth) {\n return { known: true, selected, inPeriod: false, affinity: 0, rowDate, hint };\n }\n\n const inHalf = selected === 'q15'\n ? rowDate.day <= 15\n : rowDate.day >= 16;\n\n return {\n known: true,\n selected,\n inPeriod: inHalf,\n affinity: inHalf ? 3 : 0,\n rowDate,\n hint,\n };\n}\n\nfunction isSupplementPayroll(payroll) {\n return clean(payroll?.source_kind).toLowerCase() !== 'quincenal';\n}\n\nfunction bankCandidatePeriodScore(payroll, bank) {\n const info = periodInfoForBankRow(bank);\n if (!info.known || info.inPeriod) return info.affinity || 1;\n\n // Una extraordinaria/vacación/XIII Mes puede pagarse fuera de la mitad\n // estricta del mes. Se permite como respaldo, siempre detrás de cualquier\n // candidato del período seleccionado.\n if (isSupplementPayroll(payroll) && info.rowDate) {\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n if (info.rowDate.year === selectedYear && info.rowDate.month === selectedMonth) {\n return 0.5;\n }\n }\n\n return -1;\n}\n\nconst payrollRows = Array.isArray(data.payroll?.rows) ? data.payroll.rows : [];\nconst bankRows = Array.isArray(data.bank?.rows) ? data.bank.rows : [];\n\n// Guardia de seguridad: nunca generar un reporte \"válido\" cuando el request\n// traía archivos pero el parser no consiguió ninguna fila. Esto evita que\n// todas las personas de nómina aparezcan falsamente como discrepancias.\nif (payrollRows.length === 0) {\n throw new Error(\n 'Honduras: no se pudo leer ninguna fila válida de nómina. ' +\n 'Revisa los archivos Excel de Honduras y las hojas de nómina configuradas.'\n );\n}\n\nif (bankRows.length === 0) {\n const detectedFiles = Number(data.bank?.files_count || data.summary?.bank_files_count || 0);\n const invalidFiles = Array.isArray(data.bank?.file_summaries)\n ? data.bank.file_summaries.filter((file) => file?.ok === false)\n : [];\n\n throw new Error(\n 'Honduras: no se pudo leer ninguna transacción bancaria. ' +\n `Archivos bancarios detectados: ${detectedFiles}. ` +\n (invalidFiles.length\n ? `Archivos con error de formato: ${invalidFiles.map((file) => file.file_name).join(', ')}.`\n : 'Verifica que los CSV sean los planos bancarios esperados.')\n );\n}\n\nconst bambooEmployees = Array.isArray(data.bamboo?.employees) ? data.bamboo.employees : [];\nconst resolvedNameMatches = data.bamboo?.resolved_name_matches || {};\nconst bambooValidationAvailable = data.bamboo?.fetch_complete === true &&\n data.bamboo?.validation_available === true && bambooEmployees.length > 0;\n\nconst bambooByEmployeeNumber = new Map();\nconst bambooById = new Map();\nconst bambooByExactName = new Map();\nfor (const employee of bambooEmployees) {\n const number = clean(employee.employee_number || employee.employeeNumber);\n const id = clean(employee.bamboo_id || employee.id);\n if (number) {\n const arr = bambooByEmployeeNumber.get(number) || [];\n arr.push(employee);\n bambooByEmployeeNumber.set(number, arr);\n }\n if (id) bambooById.set(id, employee);\n for (const alias of employee.aliases || [employee.full_name]) {\n const normalized = normalizeName(alias);\n if (!normalized) continue;\n const arr = bambooByExactName.get(normalized) || [];\n arr.push(employee);\n bambooByExactName.set(normalized, arr);\n }\n}\n\nfunction employeeKey(employee) {\n return clean(employee?.bamboo_id || employee?.id) ||\n clean(employee?.employee_number || employee?.employeeNumber) ||\n normalizeName(employee?.full_name || employee?.displayName);\n}\n\nfunction validatedAliasDecision(row) {\n if (!validatedBambooAliases.length) return null;\n const rowAccount = normalizeAccount(row.account);\n const names = bankNames(row).map(normalizeName).filter(Boolean);\n const candidates = validatedBambooAliases.filter((alias) => {\n const country = clean(alias.pais || alias.country).toUpperCase();\n if (country && country !== 'HN') return false;\n const source = normalizeName(alias.nombre_origen || alias.source_name || alias.alias || '');\n if (!source || !names.includes(source)) return false;\n const aliasAccount = normalizeAccount(alias.cuenta_bancaria || alias.cuenta_banco || alias.bank_account || '');\n return !aliasAccount || !rowAccount || aliasAccount === rowAccount;\n });\n if (candidates.length !== 1) return null;\n const alias = candidates[0];\n let employee = null;\n const employeeNumber = clean(alias.bamboo_employee_number || '');\n const bambooId = clean(alias.bamboo_employee_id || '');\n const bambooName = normalizeName(alias.bamboo_nombre || '');\n if (employeeNumber && (bambooByEmployeeNumber.get(employeeNumber) || []).length === 1) {\n employee = bambooByEmployeeNumber.get(employeeNumber)[0];\n } else if (bambooId && bambooById.has(bambooId)) {\n employee = bambooById.get(bambooId);\n } else if (bambooName && (bambooByExactName.get(bambooName) || []).length === 1) {\n employee = bambooByExactName.get(bambooName)[0];\n }\n if (!employee) return null;\n return {\n found: true,\n matched_by: 'validated_bamboo_alias',\n confidence: 1,\n employee,\n employee_key: employeeKey(employee),\n bamboo_alias: clean(alias.bamboo_nombre) || employee.full_name || '',\n };\n}\n\nfunction precomputedDecision(name) {\n const normalized = normalizeName(name);\n if (!normalized) return null;\n const decision = resolvedNameMatches[normalized];\n if (!decision || decision.found !== true) return null;\n const index = Number(decision.employee_index);\n const employee = Number.isInteger(index) && index >= 0 ? bambooEmployees[index] : null;\n const key = clean(decision.employee_key) || employeeKey(employee);\n if (!key) return null;\n return {\n found: true,\n matched_by: decision.matched_by || 'precomputed_name',\n confidence: Number(decision.confidence || 0),\n employee,\n employee_key: key,\n bamboo_alias: clean(decision.bamboo_alias) || employee?.full_name || '',\n query_name: clean(name),\n };\n}\n\nfunction resolveBamboo(row, linkedPayrollNames = []) {\n if (!bambooValidationAvailable) {\n return {\n found: false,\n reason: 'validation_unavailable',\n };\n }\n\n const validated = validatedAliasDecision(row);\n if (validated) return validated;\n\n const holderName = clean(row.bank_account_holder);\n const fileName = clean(row.bank_name_file);\n\n // El nombre legal del cuentahabiente es la evidencia bancaria más fuerte.\n // Si identifica exactamente a una persona que pertenecía al período,\n // prevalece sobre un nombre abreviado que pueda coincidir con un perfil\n // histórico distinto (caso real: Suyapa Palma).\n const holderDecision = holderName\n ? precomputedDecision(holderName)\n : null;\n\n if (\n holderDecision?.found &&\n holderDecision.employee?.overlaps_period === true\n ) {\n return {\n ...holderDecision,\n matched_by:\n `${holderDecision.matched_by || 'precomputed_name'}_holder_in_period`,\n };\n }\n\n const names = Array.from(\n new Set(\n [\n holderName,\n fileName,\n ...linkedPayrollNames.map(clean),\n ].filter(Boolean)\n )\n );\n\n const decisions = names\n .map(precomputedDecision)\n .filter(Boolean);\n\n if (!decisions.length) {\n return {\n found: false,\n reason: 'no_confident_name_match',\n };\n }\n\n // Antes de declarar conflicto, descartar perfiles históricos que no\n // pertenecen al período cuando existe una única persona vigente/en período.\n const inPeriodByKey = new Map();\n\n for (const decision of decisions) {\n if (decision.employee?.overlaps_period !== true) continue;\n\n const current =\n inPeriodByKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n inPeriodByKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const inPeriodRanked =\n Array.from(inPeriodByKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (inPeriodRanked.length === 1) {\n return {\n ...inPeriodRanked[0],\n matched_by:\n `${inPeriodRanked[0].matched_by || 'precomputed_name'}_unique_in_period`,\n };\n }\n\n if (\n inPeriodRanked.length >= 2 &&\n (\n inPeriodRanked[0].confidence -\n inPeriodRanked[1].confidence\n ) >= 0.08\n ) {\n return inPeriodRanked[0];\n }\n\n const byKey = new Map();\n\n for (const decision of decisions) {\n const current =\n byKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n byKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const ranked =\n Array.from(byKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (ranked.length === 1) {\n return ranked[0];\n }\n\n if (\n (\n ranked[0].confidence -\n ranked[1].confidence\n ) >= 0.08\n ) {\n return ranked[0];\n }\n\n return {\n found: false,\n reason: 'conflicting_precomputed_name_matches',\n candidates: ranked.slice(0, 3),\n };\n}\n\nconst usedBankKeys = new Set();\nconst matchedPayrollIndexes = new Set();\nconst matchedPairs = [];\nconst bankIndexByKey = new Map();\n\n// Índice por centavos para que la pasada principal no compare cada fila de\n// nómina contra todas las transacciones del banco. Esto preserva exactamente\n// la lógica original: la primera pasada ya exigía el mismo monto al centavo.\nconst bankIndexesByAmountCents = new Map();\n\nfunction amountCents(value) {\n return Math.round(roundMoney(value) * 100);\n}\n\nbankRows.forEach((row, index) => {\n const key = bankRowKey(row, index);\n bankIndexByKey.set(key, index);\n\n const amountKey = `${clean(row.currency || 'HNL').toUpperCase()}|${amountCents(row.amount)}`;\n const bucket = bankIndexesByAmountCents.get(amountKey) || [];\n bucket.push(index);\n bankIndexesByAmountCents.set(amountKey, bucket);\n});\n\nfunction availableNameCandidates(payroll, requireExactAmount) {\n const candidates = [];\n\n // La pasada exacta solo inspecciona filas con el mismo importe. La segunda\n // pasada conserva el barrido completo y el mismo criterio de persona fuerte\n // + menor diferencia que tenía el workflow validado.\n let candidateIndexes = null;\n\n if (requireExactAmount) {\n const currency =\n clean(payroll.currency || 'HNL')\n .toUpperCase();\n\n const center =\n amountCents(payroll.payroll_amount);\n\n const indexSet = new Set();\n\n for (\n let delta = -MONEY_TOLERANCE_CENTS;\n delta <= MONEY_TOLERANCE_CENTS;\n delta++\n ) {\n const bucket =\n bankIndexesByAmountCents.get(\n `${currency}|${center + delta}`\n ) || [];\n\n for (const bankIndex of bucket) {\n indexSet.add(bankIndex);\n }\n }\n\n candidateIndexes =\n Array.from(indexSet);\n }\n\n const inspectBank = (bankIndex) => {\n const bank = bankRows[bankIndex];\n if (!bank) return;\n\n const key = bankRowKey(bank, bankIndex);\n if (usedBankKeys.has(key)) return;\n if (clean(payroll.currency || 'HNL').toUpperCase() !== clean(bank.currency || 'HNL').toUpperCase()) return;\n\n if (requireExactAmount && !sameCent(payroll.payroll_amount, bank.amount)) return;\n\n const periodScore = bankCandidatePeriodScore(payroll, bank);\n if (periodScore < 0) return;\n\n const score = bestNameScore(payroll.employee_name, bank);\n if (score <= 0) return;\n\n candidates.push({\n bank,\n bankIndex,\n key,\n score,\n periodScore,\n amountDiff: Math.abs(moneyDiff(payroll.payroll_amount, bank.amount)),\n });\n };\n\n if (candidateIndexes) {\n for (const bankIndex of candidateIndexes) {\n inspectBank(bankIndex);\n }\n } else {\n for (let bankIndex = 0; bankIndex < bankRows.length; bankIndex++) {\n inspectBank(bankIndex);\n }\n }\n\n candidates.sort((a, b) => {\n if (b.periodScore !== a.periodScore) return b.periodScore - a.periodScore;\n if (b.score !== a.score) return b.score - a.score;\n if (a.amountDiff !== b.amountDiff) return a.amountDiff - b.amountDiff;\n return a.bankIndex - b.bankIndex;\n });\n\n return candidates;\n}\n\n// Primera pasada: persona + monto exacto. Es la evidencia más fuerte y evita\n// que extraordinarias/vacaciones de una misma persona se mezclen entre sí.\npayrollRows.forEach((payroll, payrollIndex) => {\n const candidates = availableNameCandidates(payroll, true);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\n// Segunda pasada: persona fuerte aunque el monto sea distinto. Solo se toma\n// una contraparte bancaria por fila de nómina y se prioriza la menor diferencia.\npayrollRows.forEach((payroll, payrollIndex) => {\n if (matchedPayrollIndexes.has(payrollIndex)) return;\n const candidates = availableNameCandidates(payroll, false);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\nconst linkedPayrollNamesByBankKey = new Map();\nfor (const pair of matchedPairs) {\n const current = linkedPayrollNamesByBankKey.get(pair.key) || [];\n current.push(pair.payroll.employee_name);\n linkedPayrollNamesByBankKey.set(pair.key, current);\n}\n\n// Filas bancarias del período de Honduras.\n// Toda fila emparejada entra siempre. Para una fila no emparejada se usa la\n// fecha/concepto del plano para evitar arrastrar accidentalmente otra quincena.\nconst includedBankRows = [];\nconst excludedBankRows = [];\n\nfor (let index = 0; index < bankRows.length; index++) {\n const row = bankRows[index];\n const key = bankRowKey(row, index);\n\n if (usedBankKeys.has(key)) {\n includedBankRows.push({ row, key, index, inclusion: 'matched_payroll' });\n continue;\n }\n\n const periodInfo = periodInfoForBankRow(row);\n if (periodInfo.known && !periodInfo.inPeriod) {\n excludedBankRows.push({ row, key, reason: 'outside_selected_payroll_period' });\n continue;\n }\n\n includedBankRows.push({ row, key, index, inclusion: 'unmatched_current_period' });\n}\n\nconst includedBankKeySet = new Set(includedBankRows.map((entry) => entry.key));\n\nconst coreRows = [];\nconst wrongAccountGroups = new Map();\nfor (const pair of matchedPairs) {\n if (!includedBankKeySet.has(pair.key)) continue;\n const payrollAmount = roundMoney(pair.payroll.payroll_amount);\n const bankAmount = roundMoney(pair.bank.amount);\n const difference = moneyDiff(payrollAmount, bankAmount);\n const currency = clean(pair.payroll.currency || pair.bank.currency || 'HNL').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAccount = normalizeAccount(pair.payroll.account || pair.payroll.payroll_account);\n const bankAccount = normalizeAccount(pair.bank.account);\n const accountMismatch = Boolean(payrollAccount && bankAccount && payrollAccount !== bankAccount);\n if (accountMismatch) {\n const groupKey = `${normalizeName(pair.payroll.employee_name)}|${currency}|${payrollAccount}|${bankAccount}`;\n const current = wrongAccountGroups.get(groupKey) || {\n id: `hn_wrong_account_${wrongAccountGroups.size + 1}`,\n employee: pair.payroll.employee_name,\n employee_name: pair.payroll.employee_name,\n employeeNumber: pair.payroll.employee_number || '', employee_number: pair.payroll.employee_number || '',\n account: bankAccount, payrollAccount, payroll_account: payrollAccount, bankAccount, bank_account: bankAccount,\n currency, payrollAmount:0, payroll_amount:0, bankAmount:0, bank_amount:0, difference:0,\n status:'Pendiente revisión', category:'posible_cuenta_mal_digitada', subcategory:'nombre_y_monto_coinciden_cuenta_diferente',\n observation:`El nombre y el monto coinciden, pero la cuenta de nómina (${payrollAccount}) es diferente a la cuenta del banco (${bankAccount}).`,\n source_rows:[], source_sheets:new Set(), source_files:new Set(), shipment_numbers:new Set(), plan_numbers:new Set(),\n bank_name_file: pair.bank.bank_name_file || '', bank_account_holder: pair.bank.bank_account_holder || ''\n };\n current.payrollAmount=roundMoney(current.payrollAmount+payrollAmount); current.payroll_amount=current.payrollAmount;\n current.bankAmount=roundMoney(current.bankAmount+bankAmount); current.bank_amount=current.bankAmount; current.difference=moneyDiff(current.payrollAmount,current.bankAmount);\n current.source_rows.push(pair.payroll); if(pair.payroll.source_sheet)current.source_sheets.add(pair.payroll.source_sheet); if(pair.bank.source_file)current.source_files.add(pair.bank.source_file); if(pair.bank.shipment_number)current.shipment_numbers.add(pair.bank.shipment_number); if(pair.bank.plan_number)current.plan_numbers.add(pair.bank.plan_number);\n wrongAccountGroups.set(groupKey,current); continue;\n }\n const exact = sameCent(payrollAmount, bankAmount);\n coreRows.push({id:`hn_match_${pair.payrollIndex+1}_${pair.bankIndex+1}`,employee:pair.payroll.employee_name,employee_name:pair.payroll.employee_name,employeeNumber:pair.payroll.employee_number||'',employee_number:pair.payroll.employee_number||'',account:bankAccount||payrollAccount,payrollAccount:payrollAccount,payroll_account:payrollAccount,bankAccount:bankAccount,bank_account:bankAccount,currency,payrollAmount,payroll_amount:payrollAmount,bankAmount,bank_amount:bankAmount,difference,status:exact?'Coincide':'Pendiente revisión',category:exact?'coincidencia':'discrepancia',subcategory:exact?'persona_cuenta_y_monto_exactos':'persona_y_cuenta_coinciden_monto_diferente',observation:exact?'Coincidencia de persona, cuenta y monto.':difference>0?'Se pagó de menos.':'Se pagó de más.',source_kind:pair.payroll.source_kind||'',source_sheet:pair.payroll.source_sheet||'',source_file:pair.bank.source_file||'',shipment_number:pair.bank.shipment_number||'',plan_number:pair.bank.plan_number||'',plan_label:pair.bank.plan_label||'',reference:pair.bank.reference||'',bank_name_file:pair.bank.bank_name_file||'',bank_account_holder:pair.bank.bank_account_holder||''});\n}\nfor (let payrollIndex=0; payrollIndex({...row,source_sheets:Array.from(row.source_sheets),source_files:Array.from(row.source_files),shipment_numbers:Array.from(row.shipment_numbers),plan_numbers:Array.from(row.plan_numbers),source_sheet:Array.from(row.source_sheets).join(' / '),source_file:Array.from(row.source_files).join(' / '),shipment_number:Array.from(row.shipment_numbers).join(' / '),plan_number:Array.from(row.plan_numbers).join(' / ')}));\ncoreRows.push(...wrongAccountRows);\n\n// Banco sin Nómina se conserva a nivel de TRANSACCIÓN.\n// Una misma persona/cuenta puede tener más de un pago bancario legítimamente\n// separado; agruparlos reduce artificialmente el número de hallazgos.\nconst bankWithoutPayrollRows = [];\n\nfor (const entry of includedBankRows) {\n if (usedBankKeys.has(entry.key)) continue;\n\n const row = entry.row;\n const displayName =\n bankDisplayName(row);\n\n const rowCurrency =\n clean(row.currency || 'HNL')\n .toUpperCase() === 'USD'\n ? 'USD'\n : 'HNL';\n\n const bankAmount =\n roundMoney(row.amount || 0);\n\n bankWithoutPayrollRows.push({\n id:\n `hn_bank_without_payroll_${entry.index + 1}`,\n employee: displayName,\n employee_name: displayName,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount,\n bank_amount: bankAmount,\n difference:\n roundMoney(0 - bankAmount),\n status: 'Pendiente revisión',\n category: 'banco_sin_nomina',\n subcategory:\n 'pago_banco_sin_registro_nomina',\n observation:\n 'Pago bancario sin registro equivalente en las planillas cargadas.',\n bank_name_file:\n row.bank_name_file || '',\n bank_account_holder:\n row.bank_account_holder || '',\n source_file:\n row.source_file || '',\n shipment_number:\n row.shipment_number || '',\n plan_number:\n row.plan_number || '',\n reference:\n row.reference || '',\n row_number:\n row.row_number || null,\n source_rows: [row],\n });\n}\n\ncoreRows.push(...bankWithoutPayrollRows);\n\nconst nameDifferenceRows = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n if (!clean(row.bank_name_file) || !clean(row.bank_account_holder)) continue;\n if (bankNameLooksConsistent(row.bank_name_file, row.bank_account_holder)) continue;\n nameDifferenceRows.push({\n id: `hn_bank_name_difference_${entry.index + 1}`,\n employee: row.bank_name_file,\n employee_name: row.bank_name_file,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount: roundMoney(row.amount),\n bank_amount: roundMoney(row.amount),\n difference: 0,\n status: 'Pendiente revisión',\n category: 'diferencia_nombre_banco',\n subcategory: 'nombre_archivo_vs_cuentahabiente',\n observation: `El Nombre en Archivo (${row.bank_name_file}) no coincide con el Nombre del Cuentahabiente (${row.bank_account_holder}).`,\n bank_name_file: row.bank_name_file,\n bank_account_holder: row.bank_account_holder,\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\n\nconst bankWithoutBambooMap = new Map();\nconst bambooMatchDetails = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const linkedNames = linkedPayrollNamesByBankKey.get(entry.key) || [];\n const decision = resolveBamboo(row, linkedNames);\n bambooMatchDetails.push({\n bank_row_key: entry.key,\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n account: row.account || '',\n amount: row.amount || 0,\n linked_payroll_names: linkedNames,\n ...decision,\n });\n if (decision.found) continue;\n\n const displayName = bankDisplayName(row);\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n const groupingKey = `${normalizeAccount(row.account)}|${normalizeName(displayName)}|${rowCurrency}`;\n const current = bankWithoutBambooMap.get(groupingKey) || {\n id: `hn_bank_without_bamboo_${bankWithoutBambooMap.size + 1}`,\n bank_name_file: row.bank_name_file || displayName,\n bank_account_holder: row.bank_account_holder || '',\n employee_name: row.bank_name_file || displayName,\n account: row.account || '',\n currency: rowCurrency,\n bank_amount: 0,\n amount: 0,\n status: 'Pendiente revisión',\n category: 'banco_sin_bamboo',\n shipment_numbers: new Set(),\n references: new Set(),\n source_files: new Set(),\n source_rows: [],\n };\n current.bank_amount = roundMoney(current.bank_amount + Number(row.amount || 0));\n current.amount = current.bank_amount;\n if (row.shipment_number) current.shipment_numbers.add(row.shipment_number);\n if (row.reference) current.references.add(row.reference);\n if (row.source_file) current.source_files.add(row.source_file);\n current.source_rows.push(row);\n bankWithoutBambooMap.set(groupingKey, current);\n}\n\nconst bankWithoutBamboo = Array.from(bankWithoutBambooMap.values()).map((row) => ({\n ...row,\n shipment_numbers: Array.from(row.shipment_numbers),\n shipment_number: Array.from(row.shipment_numbers).join(' / '),\n references: Array.from(row.references),\n source_files: Array.from(row.source_files),\n source_file: Array.from(row.source_files).join(' / '),\n difference: roundMoney(0 - row.bank_amount),\n}));\n\nfunction priority(row) {\n const category = clean(row.category).toLowerCase();\n if (category === 'posible_cuenta_mal_digitada') return 1;\n if (category === 'discrepancia') return 2;\n if (category === 'banco_sin_nomina') return 3;\n if (category === 'diferencia_nombre_banco') return 5;\n if (category === 'coincidencia') return 99;\n return 50;\n}\n\nconst rowsFinales = [...coreRows, ...nameDifferenceRows].sort((a, b) => {\n const p = priority(a) - priority(b);\n if (p !== 0) return p;\n return clean(a.employee_name).localeCompare(clean(b.employee_name), 'es');\n});\n\nconst coincidencias = coreRows.filter((row) => row.category === 'coincidencia').length;\nconst discrepancias = coreRows.filter((row) => row.category === 'discrepancia').length;\nconst bancoSinNomina = bankWithoutPayrollRows.length;\nconst diferenciasNombreBanco = nameDifferenceRows.length;\nconst posiblesCuentasMalDigitadas = wrongAccountRows.length;\nconst pendientes = discrepancias + bancoSinNomina + diferenciasNombreBanco;\nconst totalNominaByCurrency = {HNL:0,USD:0};\nfor(const row of payrollRows){const c=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalNominaByCurrency[c]=roundMoney(totalNominaByCurrency[c]+Number(row.payroll_amount||0));}\nconst totalBancoByCurrency = {HNL:0,USD:0};\nfor(const entry of includedBankRows){const c=clean(entry.row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalBancoByCurrency[c]=roundMoney(totalBancoByCurrency[c]+Number(entry.row.amount||0));}\nconst diferenciaByCurrency={HNL:moneyDiff(totalNominaByCurrency.HNL,totalBancoByCurrency.HNL),USD:moneyDiff(totalNominaByCurrency.USD,totalBancoByCurrency.USD)};\nconst totalNomina=totalNominaByCurrency.HNL || totalNominaByCurrency.USD;\nconst totalBanco=totalBancoByCurrency.HNL || totalBancoByCurrency.USD;\nconst diferenciaTotal=totalNominaByCurrency.HNL||totalBancoByCurrency.HNL?diferenciaByCurrency.HNL:diferenciaByCurrency.USD;\n\nconst bambooSummaryCompact = { ...(data.bamboo || {}) };\ndelete bambooSummaryCompact.employees;\n\nreturn [{\n json: {\n ok: true,\n stage: 'cruce_nomina_banco_honduras',\n errors: [],\n metadata: data.metadata || {},\n summary: {\n coincidencias,\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo: bankWithoutBamboo.length,\n nominaSinCuenta: 0,\n diferenciasNombreBanco,\n posiblesCuentasMalDigitadas,\n pendientes,\n filasNominaValidas: Number(data.payroll?.base_rows_count || data.payroll?.valid_rows_count || 0),\n filasNominaSinCuenta: 0,\n suplementosPotenciales: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaAplicados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAplicados: 0,\n suplementosNominaAdjuntados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAdjuntados: Number(data.payroll?.unattached_supplements_count || 0),\n reconciliacionesExactasFinales: coincidencias,\n cuentasNominaAgrupadas: 0,\n transaccionesBanco: includedBankRows.length,\n cuentasBancoAgrupadas: new Set(includedBankRows.map((entry) => normalizeAccount(entry.row.account)).filter(Boolean)).size,\n empleadosBambooHN: Number(data.bamboo?.honduras_count || 0),\n empleadosBambooEnPeriodo: Number(data.bamboo?.active_in_period_count || 0),\n bambooPaginasDescargadas: Number(data.bamboo?.pages_fetched || 0),\n bambooEmpleadosEsperados: Number(data.bamboo?.expected_total || 0),\n bambooDescargaCompleta: Boolean(data.bamboo?.fetch_complete),\n bambooValidacionDisponible: bambooValidationAvailable,\n totalNominaBase: roundMoney(data.payroll?.base_total || 0),\n totalSuplementosAplicados: roundMoney(data.payroll?.supplements_total || 0),\n totalNomina,\n totalBanco,\n diferenciaTotal,\n totalNominaPorMoneda: totalNominaByCurrency,\n totalBancoPorMoneda: totalBancoByCurrency,\n diferenciaTotalPorMoneda: diferenciaByCurrency,\n },\n rows: rowsFinales,\n bankWithoutBamboo,\n nameDifferences: nameDifferenceRows,\n bambooSummary: bambooSummaryCompact,\n reportUrl: null,\n debug: {\n matched_pairs_count: matchedPairs.length,\n included_bank_rows_count: includedBankRows.length,\n excluded_bank_rows_count: excludedBankRows.length,\n excluded_bank_rows_preview: excludedBankRows.slice(0, 100),\n validated_bamboo_aliases_loaded: validatedBambooAliases.length,\n bamboo_matches: bambooMatchDetails,\n bamboo_validation_available: bambooValidationAvailable,\n banco_sin_bamboo: bankWithoutBamboo,\n payroll_preview: payrollRows.slice(0, 20),\n bank_included_preview: includedBankRows.slice(0, 20).map((entry) => entry.row),\n },\n },\n}];\n" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -261,7 +261,7 @@ 1328, 512 ], - "id": "6f3dcbf7-8940-45f7-b3dd-c9b96553d8da", + "id": "c061625b-f317-41d2-b10a-dd0bec96269c", "name": "Cruzar Nómina vs Banco" }, { @@ -274,7 +274,7 @@ 1568, 512 ], - "id": "9b692b9e-bc13-465b-8c91-804d3909752e", + "id": "d27e1baa-53ec-4862-9467-8b708916955b", "name": "Preparar Google Sheet" }, { @@ -320,7 +320,7 @@ 1808, 512 ], - "id": "6f9b4cad-7771-4db4-b3a2-2e62d304147b", + "id": "c9c2472c-8e08-47dc-814c-9f878e11730e", "name": "Buscar reporte existente Supabase" }, { @@ -340,7 +340,7 @@ 2048, 512 ], - "id": "c971dc31-ead1-4f97-82c6-40f8f421bd5c", + "id": "398d6e91-0f62-4d90-a493-a507d7ee9d8a", "name": "Crear Google Sheet", "retryOnFail": true, "credentials": { @@ -365,7 +365,7 @@ 2288, 512 ], - "id": "abe06c36-f6cd-490d-a88b-4363c7d9211f", + "id": "ce89510d-5f8f-4378-a548-f98297279add", "name": "Leer Google Sheet actual", "retryOnFail": true, "credentials": { @@ -385,7 +385,7 @@ 2528, 512 ], - "id": "e185a0db-f278-48df-a45f-bd8954d97531", + "id": "4fab55a2-a11e-4fda-ae21-93147793a8ee", "name": "Preparar actualización Google Sheet" }, { @@ -407,7 +407,7 @@ 2768, 512 ], - "id": "b7c10e3f-e8ce-4c1c-b258-a4bc0f3ee2fb", + "id": "cc3b923a-6c4d-47f0-902e-98f6123947b9", "name": "Actualizar estructura Google Sheet", "retryOnFail": true, "credentials": { @@ -436,7 +436,7 @@ 3008, 512 ], - "id": "9f6ad41c-5ac9-4555-921a-ecd8f122b045", + "id": "407c9c50-6e67-4e06-ad8f-def748f51f5b", "name": "Limpiar Google Sheet", "retryOnFail": true, "credentials": { @@ -465,7 +465,7 @@ 3248, 512 ], - "id": "972a7c61-0537-4aad-8a9c-781fc4d2749d", + "id": "e9262d82-fea5-46b8-a46c-e44503058b3d", "name": "Escribir Google Sheet", "retryOnFail": true, "credentials": { @@ -494,7 +494,7 @@ 3488, 512 ], - "id": "85899c0a-f31a-4911-8d91-554d0da24e78", + "id": "eeff3ec4-35a7-4643-8e44-8ea5644247e1", "name": "Formatear Google Sheet", "retryOnFail": true, "credentials": { @@ -514,7 +514,7 @@ 3728, 512 ], - "id": "0508be37-3d2a-42c2-b7d5-be42098d813a", + "id": "df55c597-5e31-4237-8eac-cd2598993e20", "name": "Preparar permisos Google Sheet" }, { @@ -534,7 +534,7 @@ 3968, 512 ], - "id": "8e29fb34-a901-4bf2-82c0-d4d9c81e35ce", + "id": "083436a6-f137-4679-aa9c-658cece3301b", "name": "Compartir Google Sheet", "credentials": { "googleOAuth2Api": { @@ -554,7 +554,7 @@ 4208, 512 ], - "id": "dca4a992-cb01-42eb-b64c-b6f0c1440c9e", + "id": "a2397022-6257-4617-88e4-1a5859428e2c", "name": "Preparar histórico Supabase" }, { @@ -593,7 +593,7 @@ 4448, 512 ], - "id": "272ca2cf-cf2a-4531-bca3-c9f92753c614", + "id": "0a1e75d6-ec4e-454d-9eb9-34ef27517ea8", "name": "Insertar histórico Supabase", "onError": "continueRegularOutput" }, @@ -607,7 +607,7 @@ 4688, 432 ], - "id": "fd91d3a5-d496-4f08-945a-dac6d3ec8161", + "id": "6776e4cf-01e3-4ece-878a-3b48cef3e2ca", "name": "Preparar respuesta final" }, { @@ -632,12 +632,12 @@ 4928, 432 ], - "id": "edfd725c-d971-4b46-bf64-8f29ea09f7af", + "id": "1f4078d3-7294-4173-8a97-416b2c83f7e9", "name": "Respond to Webhook" }, { "parameters": { - "jsCode": "const prepared = $('Preparar histórico Supabase').first().json || {};\nconst payload = prepared.supabasePayload || {};\nconst metadata = payload.metadata || {};\nconst summary = payload.summary || {};\nconst reportUpdated = prepared.reportUpdated === true;\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatGeneratedAt(date) {\n const timeZone = 'America/Tegucigalpa';\n\n const dateLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(date);\n\n const timeLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n }).format(date);\n\n const hour24Raw = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour: '2-digit',\n hour12: false,\n }).format(date);\n\n const hour24 =\n Number(String(hour24Raw).replace(/\\D/g, '')) % 24;\n\n const hourArticle =\n hour24 === 1 || hour24 === 13\n ? 'a la'\n : 'a las';\n\n return `${dateLabel} ${hourArticle} ${timeLabel}`;\n}\n\nfunction metricCard(label, value, accent) {\n return `\n \n \n \n \n \n ${escapeHtml(label)}\n \n\n \n ${numberValue(value).toLocaleString('en-US')}\n \n \n \n \n \n `;\n}\n\nconst countryCode = 'HN';\nconst countryName = 'Honduras';\n\nconst periodLabel =\n payload.period_label ||\n metadata.period_label ||\n [\n payload.year || metadata.year,\n payload.month || metadata.month,\n payload.period_type || metadata.period_type,\n ]\n .filter(Boolean)\n .join(' · ') ||\n 'Período no especificado';\n\nconst executedByName =\n payload.ejecutado_por_nombre ||\n metadata.requested_by_name ||\n 'Usuario GLM';\n\nconst executedByEmail =\n payload.ejecutado_por_email ||\n metadata.requested_by_email ||\n '';\n\nconst reportUrl =\n payload.report_url ||\n prepared.reportUrl ||\n '';\n\nconst discrepancias = numberValue(\n payload.discrepancias ??\n summary.discrepancias\n);\n\nconst bancoSinNomina = numberValue(\n payload.banco_sin_nomina ??\n summary.bancoSinNomina\n);\n\nconst bancoSinBamboo = numberValue(\n payload.banco_sin_bamboo ??\n summary.bancoSinBamboo\n);\n\nconst diferenciasNombreBanco = numberValue(\n metadata.diferencias_nombre_banco ??\n summary.diferenciasNombreBanco\n);\n\nconst nominaSinCuenta = numberValue(\n payload.nomina_sin_cuenta ??\n summary.nominaSinCuenta\n);\n\nconst coincidencias = numberValue(\n payload.coincidencias ??\n summary.coincidencias\n);\n\nconst generatedAt = new Date();\nconst generatedAtLabel =\n formatGeneratedAt(generatedAt);\n\nconst statusLabel =\n String(payload.estado || '').toLowerCase() === 'resuelto'\n ? 'Resuelto'\n : 'Pendiente de revisión';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp';\n\nconst subject = reportUpdated\n ? `Reporte actualizado - Cruce de Cuentas ${countryName} - ${periodLabel}`\n : `Nuevo reporte generado - Cruce de Cuentas ${countryName} - ${periodLabel}`;\n\n/*\n * Todos son destinatarios directos.\n * Nadie se envía en CC.\n */\n\nconst to = [\n 'iaracena@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n].join(',');\n\n\n// Paleta oficial GLM.\nconst metricCards = [\n metricCard(\n 'Discrepancias',\n discrepancias,\n '#FF6A13'\n ),\n metricCard(\n 'Banco sin nómina',\n bancoSinNomina,\n '#FF8C00'\n ),\n metricCard(\n 'Banco sin Bamboo',\n bancoSinBamboo,\n '#E87722'\n ),\n metricCard(\n 'Diferencias nombre banco',\n diferenciasNombreBanco,\n '#5B7F95'\n ),\n metricCard(\n 'Nómina sin cuenta',\n nominaSinCuenta,\n '#4F758B'\n ),\n].join('');\n\nconst htmlBody = `\n\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n  \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n ${reportUpdated ? 'REPORTE ACTUALIZADO' : 'REPORTE GENERADO'}\n \n \n \n \n\n \n\n \n \n\n \n \n \n\n \n\n \n Cruce de Cuentas · ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n\n \n \n\n \n \n\n \n \n \n\n \n ${reportUpdated ? 'Se actualizó el reporte existente del Portal de Cruce de Cuentas GLM conservando las resoluciones previamente registradas que continúan correspondiendo a los mismos casos.' : 'Se completó una nueva ejecución del Portal de Cruce de Cuentas GLM. Este es el resumen del reporte generado.'}\n \n\n \n \n\n \n \n \n\n \n \n ${metricCards}\n \n \n\n \n \n\n \n \n \n\n \n\n \n\n \n\n \n Ejecutado por\n \n\n \n ${escapeHtml(executedByName)}\n \n\n \n ${escapeHtml(\n executedByEmail ||\n 'Correo no disponible'\n )}\n \n\n \n\n \n\n \n País\n \n\n \n \n\n \n \n \n\n \n\n \n ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(\n countryCode || '—'\n )}\n \n\n \n\n \n \n\n \n\n \n\n \n\n \n\n \n Período\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n ${escapeHtml(generatedAtLabel)}\n \n\n \n\n \n\n \n Estado inicial\n \n\n \n ${escapeHtml(statusLabel)}\n \n\n \n Coincidencias:\n ${coincidencias.toLocaleString('en-US')}\n \n\n \n\n \n\n \n\n \n \n\n \n ${\n reportUrl\n ? `\n \n \n\n \n Abrir reporte en Google Sheets\n \n\n \n \n `\n : ''\n }\n\n \n \n \n\n \n\n \n Notificación automática · Portal Cruce de Cuentas GLM\n
\n Los casos de revisión deben gestionarse directamente en el reporte generado.\n \n\n \n \n\n \n\n \n \n \n\n\n\n`;\n\nreturn [\n {\n json: {\n to,\n subject,\n htmlBody,\n\n notification: {\n country: countryCode,\n countryName,\n flagUrl,\n periodLabel,\n executedByName,\n executedByEmail,\n generatedAt: generatedAt.toISOString(),\n reportUrl,\n\n metrics: {\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo,\n diferenciasNombreBanco,\n nominaSinCuenta,\n coincidencias,\n },\n },\n },\n },\n];" + "jsCode": "const prepared = $('Preparar histórico Supabase').first().json || {};\nconst payload = prepared.supabasePayload || {};\nconst metadata = payload.metadata || {};\nconst summary = payload.summary || {};\nconst reportUpdated = prepared.reportUpdated === true;\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatGeneratedAt(date) {\n const timeZone = 'America/Tegucigalpa';\n\n const dateLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(date);\n\n const timeLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n }).format(date);\n\n const hour24Raw = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour: '2-digit',\n hour12: false,\n }).format(date);\n\n const hour24 =\n Number(String(hour24Raw).replace(/\\D/g, '')) % 24;\n\n const hourArticle =\n hour24 === 1 || hour24 === 13\n ? 'a la'\n : 'a las';\n\n return `${dateLabel} ${hourArticle} ${timeLabel}`;\n}\n\nfunction metricCard(label, value, accent) {\n return `\n \n \n \n \n \n ${escapeHtml(label)}\n \n\n \n ${numberValue(value).toLocaleString('en-US')}\n \n \n \n \n \n `;\n}\n\nconst countryCode = 'HN';\nconst countryName = 'Honduras';\n\nconst periodLabel =\n payload.period_label ||\n metadata.period_label ||\n [\n payload.year || metadata.year,\n payload.month || metadata.month,\n payload.period_type || metadata.period_type,\n ]\n .filter(Boolean)\n .join(' · ') ||\n 'Período no especificado';\n\nconst executedByName =\n payload.ejecutado_por_nombre ||\n metadata.requested_by_name ||\n 'Usuario GLM';\n\nconst executedByEmail =\n payload.ejecutado_por_email ||\n metadata.requested_by_email ||\n '';\n\nconst reportUrl =\n payload.report_url ||\n prepared.reportUrl ||\n '';\n\nconst discrepancias = numberValue(\n payload.discrepancias ??\n summary.discrepancias\n);\n\nconst bancoSinNomina = numberValue(\n payload.banco_sin_nomina ??\n summary.bancoSinNomina\n);\n\nconst bancoSinBamboo = numberValue(\n payload.banco_sin_bamboo ??\n summary.bancoSinBamboo\n);\n\nconst diferenciasNombreBanco = numberValue(\n metadata.diferencias_nombre_banco ??\n summary.diferenciasNombreBanco\n);\n\nconst nominaSinCuenta = numberValue(\n payload.nomina_sin_cuenta ??\n summary.nominaSinCuenta\n);\n\nconst coincidencias = numberValue(\n payload.coincidencias ??\n summary.coincidencias\n);\n\nconst generatedAt = new Date();\nconst generatedAtLabel =\n formatGeneratedAt(generatedAt);\n\nconst statusLabel =\n String(payload.estado || '').toLowerCase() === 'resuelto'\n ? 'Resuelto'\n : 'Pendiente de revisión';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp';\n\nconst subject = reportUpdated\n ? `Reporte actualizado - Cruce de Cuentas ${countryName} - ${periodLabel}`\n : `Nuevo reporte generado - Cruce de Cuentas ${countryName} - ${periodLabel}`;\n\n/*\n * Todos son destinatarios directos.\n * Nadie se envía en CC.\n */\n\nconst to = [\n 'iaracena@gomezleemarketing.com',\n].join(',');\n\n\n// Paleta oficial GLM.\nconst metricCards = [\n metricCard(\n 'Discrepancias',\n discrepancias,\n '#FF6A13'\n ),\n metricCard(\n 'Banco sin nómina',\n bancoSinNomina,\n '#FF8C00'\n ),\n metricCard(\n 'Banco sin Bamboo',\n bancoSinBamboo,\n '#E87722'\n ),\n metricCard(\n 'Diferencias nombre banco',\n diferenciasNombreBanco,\n '#5B7F95'\n ),\n metricCard(\n 'Nómina sin cuenta',\n nominaSinCuenta,\n '#4F758B'\n ),\n].join('');\n\nconst htmlBody = `\n\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n  \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n ${reportUpdated ? 'REPORTE ACTUALIZADO' : 'REPORTE GENERADO'}\n \n \n \n \n\n \n\n \n \n\n \n \n \n\n \n\n \n Cruce de Cuentas · ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n\n \n \n\n \n \n\n \n \n \n\n \n ${reportUpdated ? 'Se actualizó el reporte existente del Portal de Cruce de Cuentas GLM conservando las resoluciones previamente registradas que continúan correspondiendo a los mismos casos.' : 'Se completó una nueva ejecución del Portal de Cruce de Cuentas GLM. Este es el resumen del reporte generado.'}\n \n\n \n \n\n \n \n \n\n \n \n ${metricCards}\n \n \n\n \n \n\n \n \n \n\n \n\n \n\n \n\n \n Ejecutado por\n \n\n \n ${escapeHtml(executedByName)}\n \n\n \n ${escapeHtml(\n executedByEmail ||\n 'Correo no disponible'\n )}\n \n\n \n\n \n\n \n País\n \n\n \n \n\n \n \n \n\n \n\n \n ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(\n countryCode || '—'\n )}\n \n\n \n\n \n \n\n \n\n \n\n \n\n \n\n \n Período\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n ${escapeHtml(generatedAtLabel)}\n \n\n \n\n \n\n \n Estado inicial\n \n\n \n ${escapeHtml(statusLabel)}\n \n\n \n Coincidencias:\n ${coincidencias.toLocaleString('en-US')}\n \n\n \n\n \n\n \n\n \n \n\n \n ${\n reportUrl\n ? `\n \n \n\n \n Abrir reporte en Google Sheets\n \n\n \n \n `\n : ''\n }\n\n \n \n \n\n \n\n \n Notificación automática · Portal Cruce de Cuentas GLM\n
\n Los casos de revisión deben gestionarse directamente en el reporte generado.\n \n\n \n \n\n \n\n \n \n \n\n\n\n`;\n\nreturn [\n {\n json: {\n to,\n subject,\n htmlBody,\n\n notification: {\n country: countryCode,\n countryName,\n flagUrl,\n periodLabel,\n executedByName,\n executedByEmail,\n generatedAt: generatedAt.toISOString(),\n reportUrl,\n\n metrics: {\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo,\n diferenciasNombreBanco,\n nominaSinCuenta,\n coincidencias,\n },\n },\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -645,7 +645,7 @@ 4688, 672 ], - "id": "a067ffc0-607f-4094-be4b-19bbd95f1171", + "id": "e3ee2bf6-38a8-4e38-8f13-b706aa667650", "name": "Preparar correo reporte generado" }, { @@ -663,7 +663,7 @@ 4928, 672 ], - "id": "c309bd25-b751-46a5-a2fd-fbc4442cb7c0", + "id": "f29d8f4e-5156-41a6-ae07-874de0e8bd83", "name": "Enviar correo reporte generado", "webhookId": "4bcdbd7b-503d-46ac-aeaf-9207f59f01e7", "credentials": { @@ -676,7 +676,7 @@ }, { "parameters": { - "content": "# 🇭🇳 CRUCE DE CUENTAS — HONDURAS\n\nFlujo principal para una nómina quincenal de Honduras + todos los planos bancarios BAC del período + BambooHR.\n\n- Monedas: HNL y USD.\n- BambooHR: `onlyCurrent=false`, filtrado por país/localidad Honduras.\n- Q1: días 1–15. Q2: días 16–último día real del mes.\n- Los meses/años se calculan dinámicamente, incluidos años bisiestos.\n- La nómina usa las hojas operativas validadas de GLM Honduras y omite hojas auxiliares/copias para evitar duplicados.", + "content": "# 🇭🇳 CRUCE DE CUENTAS — HONDURAS\n\nFlujo principal para una nómina quincenal de Honduras + todos los planos bancarios BAC del período + BambooHR.\n\n- Monedas: HNL y USD.\n- BambooHR: `onlyCurrent=false`, filtrado por país/localidad Honduras.\n- Q1: días 1–15. Q2: días 16–último día real del mes.\n- Los meses/años se calculan dinámicamente, incluidos años bisiestos.\n- La nómina usa las hojas operativas validadas de GLM Honduras y omite hojas auxiliares/copias para evitar duplicados.\n\n\n### Correcciones de conciliación HN · 08-09-2026\n- Tolerancia monetaria operativa: ±0.05 por redondeos bancarios.\n- Banco sin Nómina se mantiene por transacción, sin agrupar pagos distintos.\n- Nombre en archivo truncado por un carácter al final no genera falso positivo.\n- BambooHR prioriza el nombre legal del cuentahabiente y perfiles vigentes en el período.\n- La cuenta mal digitada se determina por la cuenta real de la nómina vs. la cuenta real del banco.\n", "height": 260, "width": 520 }, @@ -686,7 +686,7 @@ -1088, 80 ], - "id": "bfd68985-f8c4-4311-bc99-dd4f67042115", + "id": "8a0379c3-9a89-4428-a42d-53f18a7b3250", "name": "📌 HONDURAS · V1" }, { @@ -701,7 +701,7 @@ 0, 0 ], - "id": "91b14935-3200-4f6a-b926-6299f3a6141b", + "id": "8640e03f-2128-4eb4-aae8-a2ee3ec221d3", "name": "📌 REGLA BANCO HN" }, { @@ -716,12 +716,12 @@ -608, 928 ], - "id": "62ec2b77-ecfb-42a1-984c-874f84fd77ba", + "id": "ce7cb681-290e-404b-a3de-fa879e57fb2e", "name": "📌 ARCHIVOS NÓMINA HN" }, { "parameters": { - "content": "# 📊 SALIDA\n\nConserva la misma estructura de reportes de GT/TT/PA/SV/HN, reutiliza el Google Sheet del mismo período si existe, actualiza Supabase y envía correo tanto en creación como en actualización.\n\nLos valores se escriben en Google Sheets con `RAW` para preservar las cuentas bancarias como texto exacto, incluyendo ceros iniciales y evitando notación científica.", + "content": "# 📊 SALIDA\n\nConserva la misma estructura de reportes de GT/TT/PA/SV, reutiliza el Google Sheet del mismo período si existe, actualiza Supabase y envía correo tanto en creación como en actualización.\n\nLos valores se escriben en Google Sheets con `RAW` para preservar las cuentas bancarias como texto exacto, incluyendo ceros iniciales y evitando notación científica.", "height": 250, "width": 720 }, @@ -731,7 +731,7 @@ 3120, 832 ], - "id": "70cdc5fd-3337-43dd-950d-64f68b311936", + "id": "cfcb9b0f-32a1-4859-81ee-910ba6aa89dc", "name": "📌 REPORTES" } ], @@ -1055,9 +1055,10 @@ "executionOrder": "v1", "binaryMode": "separate" }, - "versionId": "a4e7c2cc-6784-43db-a676-1288cf3f718e", + "versionId": "e9ce02ee-af02-439b-8679-30525f6173ba", "meta": { "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" }, + "id": "fu6dsueqDQax1c43", "tags": [] } \ No newline at end of file diff --git a/Portal de Verificaci├│n de N├│mina - Puerto Rico.json b/Portal de Verificaci├│n de N├│mina - Puerto Rico.json new file mode 100644 index 0000000..c0d421a --- /dev/null +++ b/Portal de Verificaci├│n de N├│mina - Puerto Rico.json @@ -0,0 +1,1196 @@ +{ + "name": "Portal de Verificación de Nómina - Puerto Rico", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "nominapr-bamboo-test", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + -2800, + 5200 + ], + "id": "e07fef4a-5007-452d-b75e-975757344c59", + "name": "Webhook", + "webhookId": "00132835-9daf-4f36-95ee-233ff6f7b691" + }, + { + "parameters": { + "jsCode": "const item = $input.first();\nconst body = item.json.body || {};\nconst binary = item.binary || {};\n\nlet metadata = {};\ntry {\n metadata = typeof body.metadata === 'string'\n ? JSON.parse(body.metadata)\n : body.metadata || {};\n} catch (error) {\n metadata = {};\n}\n\nconst binaryKeys = Object.keys(binary);\n\nfunction naturalBinarySort(left, right) {\n return String(left).localeCompare(String(right), undefined, {\n numeric: true,\n sensitivity: 'base',\n });\n}\n\nfunction extensionOf(file) {\n const explicit = String(file?.fileExtension || '').toLowerCase().replace(/^\\./, '');\n if (explicit) return explicit;\n const name = String(file?.fileName || '').toLowerCase();\n const match = name.match(/\\.([a-z0-9]+)$/i);\n return match ? match[1] : '';\n}\n\nconst numberedPayrollKeys = binaryKeys\n .filter((key) => /^payroll_file_\\d+$/i.test(key))\n .sort(naturalBinarySort);\nconst repeatedPayrollKeys = binaryKeys\n .filter((key) => /^payroll_files(?:_?\\d+)?$/i.test(key))\n .sort(naturalBinarySort);\nlet payrollKeys = numberedPayrollKeys.length ? numberedPayrollKeys : repeatedPayrollKeys;\nif (payrollKeys.length === 0 && binary.payroll_file) payrollKeys = ['payroll_file'];\n\nconst numberedBankKeys = binaryKeys\n .filter((key) => /^bank_file_\\d+$/i.test(key))\n .sort(naturalBinarySort);\nconst repeatedBankKeys = binaryKeys\n .filter((key) => /^bank_files(?:_?\\d+)?$/i.test(key))\n .sort(naturalBinarySort);\nlet bankKeys = numberedBankKeys.length ? numberedBankKeys : repeatedBankKeys;\nif (bankKeys.length === 0 && binary.bank_file) bankKeys = ['bank_file'];\n\nconst payrollFiles = payrollKeys.map((key) => ({\n binary_key: key,\n file_name: binary[key]?.fileName || key,\n file_extension: extensionOf(binary[key]),\n mime_type: binary[key]?.mimeType || '',\n file_size: binary[key]?.fileSize || null,\n}));\n\nconst bankFiles = bankKeys.map((key) => ({\n binary_key: key,\n file_name: binary[key]?.fileName || key,\n file_extension: extensionOf(binary[key]),\n mime_type: binary[key]?.mimeType || '',\n file_size: binary[key]?.fileSize || null,\n}));\n\nconst receivedCountry = String(metadata.country || '').trim().toUpperCase();\nconst errors = [];\n\nif (receivedCountry !== 'PR') errors.push('El país recibido no es Puerto Rico.');\nif (!metadata.year) errors.push('No se recibió el año del cruce.');\nif (!metadata.month) errors.push('No se recibió el mes del cruce.');\nif (!metadata.period_type) errors.push('No se recibió el tipo de quincena.');\nif (!metadata.period_start || !metadata.period_end) errors.push('No se recibió el período calculado.');\nif (payrollFiles.length === 0) errors.push('No se recibió el archivo Excel de nómina de Puerto Rico.');\nif (bankFiles.length === 0) errors.push('No se recibió el PDF de pagos de Banco Popular de Puerto Rico.');\n\nconst badPayroll = payrollFiles.filter((file) => !['xlsx', 'xls', 'xlsm'].includes(file.file_extension));\nif (badPayroll.length) {\n errors.push(`Puerto Rico requiere nómina Excel (.xlsx, .xls o .xlsm). Archivo(s) inválido(s): ${badPayroll.map((f) => f.file_name).join(', ')}.`);\n}\nconst badBank = bankFiles.filter((file) => file.file_extension !== 'pdf');\nif (badBank.length) {\n errors.push(`Puerto Rico requiere el Transaction Detail Report de Banco Popular en PDF. Archivo(s) inválido(s): ${badBank.map((f) => f.file_name).join(', ')}.`);\n}\n\nconst mainPayroll = payrollFiles[0] || null;\nconst normalizedMetadata = {\n ...metadata,\n country: 'PR',\n country_name: 'Puerto Rico',\n currency: 'USD',\n source_app: metadata.source_app || 'portal-cruce-cuentas-glm',\n payroll_file_name: metadata.payroll_file_name || mainPayroll?.file_name || '',\n payroll_file_names: Array.isArray(metadata.payroll_file_names) && metadata.payroll_file_names.length\n ? metadata.payroll_file_names\n : payrollFiles.map((file) => file.file_name),\n bank_file_names: Array.isArray(metadata.bank_file_names) && metadata.bank_file_names.length\n ? metadata.bank_file_names\n : bankFiles.map((file) => file.file_name),\n};\n\nreturn [{\n json: {\n ok: errors.length === 0,\n stage: 'entrada_pr_recibida',\n errors,\n metadata: normalizedMetadata,\n payroll_files: payrollFiles,\n bank_files: bankFiles,\n summary: {\n payroll_files_count: payrollFiles.length,\n bank_files_count: bankFiles.length,\n },\n },\n binary,\n}];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2560, + 5200 + ], + "id": "1753550c-cf8b-48eb-ba58-0a8642163242", + "name": "Preparar entrada app" + }, + { + "parameters": { + "jsCode": "const input = $input.first();\nconst base = input.json || {};\nconst binary = input.binary || {};\nconst configuredFiles = Array.isArray(base.bank_files) ? base.bank_files : [];\nconst outputs = [];\n\nfor (const fileInfo of configuredFiles) {\n const key = String(fileInfo.binary_key || '');\n const file = binary[key];\n if (!key || !file) continue;\n outputs.push({\n json: {\n source_file: String(file.fileName || fileInfo.file_name || key),\n source_binary_key: key,\n source_kind: 'bppr_pdf',\n currency: 'USD',\n },\n binary: { data: file },\n });\n}\n\nif (!outputs.length) {\n throw new Error('Puerto Rico: no se pudo preparar el PDF de Banco Popular.');\n}\nreturn outputs;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2080, + 4960 + ], + "id": "eba2da3b-8ca3-49bd-9679-a61a1270c8f8", + "name": "Preparar PDF banco PR" + }, + { + "parameters": { + "jsCode": "const input = $input.first();\nconst base = input.json || {};\nconst binary = input.binary || {};\nconst configuredFiles = Array.isArray(base.payroll_files) ? base.payroll_files : [];\nconst SHEETS = [\n ['Whirlpool', 1, 'USD'],\n ['Personal 2', 1, 'USD'],\n ['Personal 3', 1, 'USD'],\n];\nconst outputs = [];\nfor (const fileInfo of configuredFiles) {\n const key = String(fileInfo.binary_key || '');\n const file = binary[key];\n if (!key || !file) continue;\n const fileName = String(file.fileName || fileInfo.file_name || key);\n for (const [sheetName, headerRow, currency] of SHEETS) {\n outputs.push({\n json: {\n source_file: fileName,\n source_binary_key: key,\n source_kind: 'quincenal',\n payroll_group: 'puerto_rico',\n sheet_name: sheetName,\n header_row: headerRow,\n range: `A${headerRow}:CA2000`,\n currency,\n },\n binary: { data: file },\n });\n }\n}\nif (!outputs.length) {\n throw new Error('Puerto Rico: no se pudo preparar el archivo Excel de nómina.');\n}\nreturn outputs;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2080, + 5440 + ], + "id": "2b1028fb-bee3-4b82-bb5c-78c534959b20", + "name": "Preparar archivos nómina PR" + }, + { + "parameters": { + "operation": "xlsx", + "options": { + "headerRow": true, + "range": "={{ $json.range }}", + "sheetName": "={{ $json.sheet_name }}" + } + }, + "type": "n8n-nodes-base.extractFromFile", + "typeVersion": 1.1, + "position": [ + -1840, + 5440 + ], + "id": "ec028b3b-a794-462a-a33b-afb839928069", + "name": "Extract - Archivo Nómina PR", + "retryOnFail": false, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const items = $input.all();\nconst base = $('Preparar entrada app').first().json || {};\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\uFEFF/g, '').replace(/\\u00A0/g, ' ').replace(/\\s+/g, ' ').trim();\n}\nfunction normalize(value) {\n return clean(value).toLowerCase().normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').replace(/[^a-z0-9 #]/g, ' ').replace(/\\s+/g, ' ').trim();\n}\nfunction parseMoney(value) {\n if (typeof value === 'number') return Number.isFinite(value) ? value : 0;\n const parsed = Number.parseFloat(String(value ?? '').replace(/USD/gi, '').replace(/\\$/g, '').replace(/,/g, '').replace(/\\s+/g, '').trim());\n return Number.isFinite(parsed) ? parsed : 0;\n}\nfunction roundMoney(value) { return Math.round((Number(value) || 0) * 100) / 100; }\nfunction normalizeAccount(value) {\n const raw = clean(value);\n if (!raw || /[A-Za-zÀ-ÿ]/.test(raw)) return '';\n if (typeof value === 'number' && Number.isFinite(value)) return String(Math.round(value));\n if (/^[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)[eE][+-]?\\d+$/.test(raw)) {\n const number = Number(raw);\n if (Number.isFinite(number)) return String(Math.round(number));\n }\n const account = raw.replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n return account.length >= 5 && !/^0+$/.test(account) ? account : '';\n}\nfunction getValue(row, keys) {\n for (const key of keys) if (row[key] !== undefined && row[key] !== null && row[key] !== '') return row[key];\n const actual = Object.keys(row || {});\n for (const expected of keys) {\n const normalizedExpected = normalize(expected);\n const key = actual.find((candidate) => normalize(candidate) === normalizedExpected);\n if (key) {\n const value = row[key];\n if (value !== undefined && value !== null && value !== '') return value;\n }\n }\n return '';\n}\nfunction validEmployeeName(value) {\n const name = clean(value);\n const normalized = normalize(name);\n if (!name || /^[\\d.,\\s-]+$/.test(name)) return false;\n const bad = ['total', 'subtotal', 'total general', 'gran total', 'nombre', 'empleado', 'colaborador'];\n return !bad.some((term) => normalized === term || normalized.startsWith(`${term} `));\n}\nfunction sourceMeta(index) {\n try { return $('Preparar archivos nómina PR').itemMatching(index).json || {}; }\n catch { return {}; }\n}\n\nconst rows = [];\nconst ignored = [];\nconst counters = new Map();\nconst summaries = new Map();\n\nitems.forEach((item, index) => {\n const source = item.json || {};\n const linked = sourceMeta(index);\n const sourceFile = clean(linked.source_file || '');\n const sourceSheet = clean(linked.sheet_name || '');\n const currency = 'USD';\n const key = `${sourceFile}|${sourceSheet}`;\n const dataIndex = (counters.get(key) || 0) + 1;\n counters.set(key, dataIndex);\n const rowNumber = Number(linked.header_row || 1) + dataIndex;\n\n const employeeName = clean(getValue(source, ['NOMBRE', 'Nombre', 'Nombre del Empleado', 'NOMBRE DEL EMPLEADO']));\n const account = normalizeAccount(getValue(source, ['Cuenta', 'CUENTA', 'Cuenta Banco', 'CUENTA BANCO', 'CUENTA BANCARIA']));\n const amount = roundMoney(parseMoney(getValue(source, ['NETO A PAGAR', 'Neto a Pagar', 'Total a Pagar', 'TOTAL A PAGAR', 'Total Neto'])));\n\n const summary = summaries.get(key) || {\n source_file: sourceFile,\n source_sheet: sourceSheet,\n currency,\n extracted_rows_count: 0,\n accepted_rows_count: 0,\n ignored_rows_count: 0,\n total_amount: 0,\n };\n summary.extracted_rows_count += 1;\n\n if (!validEmployeeName(employeeName) || amount <= 0 || amount > 5000000) {\n summary.ignored_rows_count += 1;\n summaries.set(key, summary);\n if (employeeName || amount) ignored.push({ source_file: sourceFile, source_sheet: sourceSheet, row_number: rowNumber, employee_name: employeeName, account, payroll_amount: amount, currency });\n return;\n }\n\n rows.push({\n payroll_row_key: `PR_PAYROLL_${index + 1}_${rowNumber}`,\n row_number: rowNumber,\n employee_name: employeeName,\n employee_number: '',\n account,\n payroll_account: account,\n payroll_amount: amount,\n currency,\n source_file: sourceFile,\n source_kind: 'quincenal',\n source_sheet: sourceSheet,\n payroll_group: 'puerto_rico',\n is_main_payroll: true,\n raw: source,\n });\n summary.accepted_rows_count += 1;\n summary.total_amount = roundMoney(summary.total_amount + amount);\n summaries.set(key, summary);\n});\n\nif (!rows.length) {\n throw new Error('Puerto Rico: no se encontró ninguna fila válida en las hojas Whirlpool, Personal 2 y Personal 3.');\n}\nconst totalAmount = roundMoney(rows.reduce((sum, row) => sum + row.payroll_amount, 0));\nreturn [{\n json: {\n ...base,\n stage: 'nomina_pr_normalizada',\n payroll: {\n source: 'nomina_puerto_rico_usd',\n files_count: Number(base.summary?.payroll_files_count || 0),\n file_summaries: Array.from(summaries.values()),\n raw_rows_count: items.length,\n rows_count: rows.length,\n valid_rows_count: rows.length,\n base_rows_count: rows.length,\n supplement_rows_count: 0,\n attached_supplements_count: 0,\n unattached_supplements_count: 0,\n no_account_rows_count: rows.filter((row) => !row.account).length,\n ignored_rows_count: ignored.length,\n base_total: totalAmount,\n supplements_total: 0,\n total_amount: totalAmount,\n totals_by_currency: { USD: totalAmount },\n rows,\n no_account_rows: [],\n grouped_by_account: [],\n potential_supplements: [],\n },\n debug_payroll: {\n file_summaries: Array.from(summaries.values()),\n ignored_rows_preview: ignored.slice(0, 100),\n },\n },\n}];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1600, + 5440 + ], + "id": "1e1a261f-8711-49db-9640-aa64f3540fdd", + "name": "Normalizar Nómina PR" + }, + { + "parameters": { + "mode": "combine", + "combineBy": "combineByPosition", + "options": {} + }, + "type": "n8n-nodes-base.merge", + "typeVersion": 3.2, + "position": [ + -1320, + 5200 + ], + "id": "349328dc-e99b-499a-b79b-bbf62918ba68", + "name": "Merge Banco + Nómina PR" + }, + { + "parameters": { + "method": "POST", + "url": "https://glm.bamboohr.com/api/v1/reports/custom?format=JSON&onlyCurrent=false", + "authentication": "genericCredentialType", + "genericAuthType": "httpBasicAuth", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Accept", + "value": "application/json" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": { + "title": "Información de BambooHR - Cruce de Cuentas Puerto Rico", + "fields": [ + "firstName", + "middleName", + "lastName", + "displayName", + "department", + "division", + "location", + "customPosicion-Cliente", + "hireDate", + "originalHireDate", + "status", + "terminationDate", + "employeeNumber", + "country", + "workEmail", + "homeEmail", + "bestEmail" + ] + }, + "options": { + "response": { + "response": { + "responseFormat": "json" + } + }, + "timeout": 300000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + -1080, + 5040 + ], + "id": "396dcc5d-eb94-4f6d-a234-9adaa99baa7b", + "name": "HTTP - Empleados BambooHR PR", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 3000, + "credentials": { + "httpBasicAuth": { + "id": "7VrpNZ2jBLmiJ35q", + "name": "BambooHR GLM Full Access" + } + } + }, + { + "parameters": { + "jsCode": "const inputItems = $input.all();\nconst base = $('Preparar entrada app').first().json || {};\nconst reconciliationData = $('Merge Banco + Nómina PR').first().json || {};\nconst metadata = base.metadata || {};\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los',\n 'y', 'e', 'el', 'da', 'do',\n 'dos', 'das', 'van', 'von',\n]);\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalize(value) {\n return clean(value)\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction unique(values) {\n const result = [];\n const seen = new Set();\n\n for (const value of values) {\n const cleaned = clean(value);\n\n if (!cleaned || seen.has(cleaned)) {\n continue;\n }\n\n seen.add(cleaned);\n result.push(cleaned);\n }\n\n return result;\n}\n\nfunction nameTokens(value) {\n return normalize(value)\n .split(' ')\n .filter(\n (token) =>\n token.length > 1 &&\n !IGNORED_NAME_TOKENS.has(token)\n );\n}\n\nfunction uniqueNameTokens(value) {\n return Array.from(\n new Set(nameTokens(value))\n );\n}\n\nfunction parseDate(value) {\n const raw = clean(value);\n if (!raw || raw === '0000-00-00') return null;\n\n const direct = raw.match(\n /^(\\d{4})-(\\d{2})-(\\d{2})/\n );\n\n if (direct) {\n return `${direct[1]}-${direct[2]}-${direct[3]}`;\n }\n\n const date = new Date(raw);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toISOString().slice(0, 10);\n}\n\nfunction parseBoolean(value) {\n if (typeof value === 'boolean') return value;\n\n return [\n 'true', 'yes', 'si', 'sí', '1', 'y',\n ].includes(normalize(value));\n}\n\nfunction isTargetCountry(employee) {\n const country = normalize(employee.country);\n const location = normalize(\n employee.location ||\n employee.jobInformationLocation ||\n employee.jobLocation\n );\n\n return (\n country === 'pr' ||\n country.includes('puerto rico') ||\n location === 'pr' ||\n location.includes('puerto rico')\n );\n}\n\nfunction overlapsPeriod(\n hireDate,\n terminationDate,\n periodStart,\n periodEnd\n) {\n if (!periodStart || !periodEnd) return false;\n\n const hiredBeforeEnd =\n !hireDate || hireDate <= periodEnd;\n\n const notTerminatedBeforeStart =\n !terminationDate ||\n terminationDate >= periodStart;\n\n return hiredBeforeEnd && notTerminatedBeforeStart;\n}\n\nfunction collectPageObjects(value, pages) {\n if (!value) return;\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n collectPageObjects(entry, pages);\n }\n return;\n }\n\n if (typeof value !== 'object') return;\n\n if (value.body && typeof value.body === 'object') {\n collectPageObjects(value.body, pages);\n return;\n }\n\n if (\n Array.isArray(value.data) ||\n Array.isArray(value.employees)\n ) {\n pages.push(value);\n return;\n }\n\n if (value.json && typeof value.json === 'object') {\n collectPageObjects(value.json, pages);\n }\n}\n\nfunction profileContains(\n leftTokens,\n leftTokenSet,\n rightTokens,\n rightTokenSet\n) {\n if (\n leftTokens.length < 3 ||\n rightTokens.length < 3\n ) {\n return false;\n }\n\n const leftInsideRight =\n leftTokens.every((token) =>\n rightTokenSet.has(token)\n );\n\n if (leftInsideRight) return true;\n\n return rightTokens.every((token) =>\n leftTokenSet.has(token)\n );\n}\n\nfunction employeeKey(employee) {\n return (\n employee.bamboo_id ||\n employee.employee_number ||\n normalize(employee.full_name)\n );\n}\n\nconst pageObjects = [];\n\nfor (const item of inputItems) {\n collectPageObjects(item.json, pageObjects);\n}\n\nconst employeeMap = new Map();\nlet expectedTotal = 0;\nlet restrictedFields = 0;\n\nfor (const page of pageObjects) {\n const pageEmployees =\n Array.isArray(page.data)\n ? page.data\n : Array.isArray(page.employees)\n ? page.employees\n : [];\n\n const pageTotal = Number(\n page.meta?.total ||\n page.total ||\n 0\n );\n\n if (Number.isFinite(pageTotal)) {\n expectedTotal = Math.max(\n expectedTotal,\n pageTotal\n );\n }\n\n for (const employee of pageEmployees) {\n const key =\n clean(employee.employeeId || employee.id) ||\n clean(employee.employeeNumber) ||\n clean(employee.bestEmail).toLowerCase() ||\n [\n clean(employee.firstName),\n clean(employee.middleName),\n clean(employee.lastName),\n ].filter(Boolean).join('|').toLowerCase();\n\n if (!key) continue;\n\n employeeMap.set(key, employee);\n\n restrictedFields += Array.isArray(\n employee._restrictedFields\n )\n ? employee._restrictedFields.length\n : 0;\n }\n}\n\nconst rawEmployees = Array.from(\n employeeMap.values()\n);\n\nconst periodStart = clean(metadata.period_start);\nconst periodEnd = clean(metadata.period_end);\n\nconst targetEmployees = [];\nconst outsideRecords = [];\nlet normalizedEmployeesCount = 0;\n\nfor (const employee of rawEmployees) {\n const firstName = clean(employee.firstName);\n const middleName = clean(employee.middleName);\n const lastName = clean(employee.lastName);\n const preferredName = clean(\n employee.preferredName\n );\n\n const constructedFullName = [\n firstName,\n middleName,\n lastName,\n ].filter(Boolean).join(' ');\n\n const aliases = unique([\n employee.displayName,\n employee.fullName1,\n employee.fullName2,\n employee.fullName3,\n employee.fullName4,\n employee.fullName5,\n constructedFullName,\n [preferredName, lastName]\n .filter(Boolean)\n .join(' '),\n [firstName, lastName]\n .filter(Boolean)\n .join(' '),\n ]);\n\n const hireDate = parseDate(\n employee.hireDate ||\n employee.originalHireDate\n );\n\n const terminationDate = parseDate(\n employee.terminationDate\n );\n\n const status = clean(\n employee.status ||\n employee.employmentStatus ||\n employee.employmentHistoryStatus\n );\n\n const employeeNumber = clean(\n employee.employeeNumber ||\n employee.employee_number\n );\n\n const normalizedEmployee = {\n bamboo_id: clean(\n employee.employeeId ||\n employee.id\n ),\n employee_number: employeeNumber,\n first_name: firstName,\n middle_name: middleName,\n last_name: lastName,\n preferred_name: preferredName,\n full_name:\n clean(employee.displayName) ||\n clean(employee.fullName1) ||\n constructedFullName,\n aliases,\n normalized_aliases:\n aliases.map(normalize).filter(Boolean),\n status,\n hire_date: hireDate,\n termination_date: terminationDate,\n location: clean(\n employee.location ||\n employee.jobInformationLocation ||\n employee.jobLocation\n ),\n country: clean(employee.country),\n include_in_payroll:\n parseBoolean(employee.includeInPayroll),\n work_email:\n clean(employee.workEmail).toLowerCase(),\n home_email:\n clean(employee.homeEmail).toLowerCase(),\n best_email: clean(\n employee.bestEmail ||\n employee.workEmail ||\n employee.homeEmail\n ).toLowerCase(),\n exists_in_bamboo: true,\n overlaps_period: overlapsPeriod(\n hireDate,\n terminationDate,\n periodStart,\n periodEnd\n ),\n };\n\n normalizedEmployeesCount += 1;\n\n if (isTargetCountry(normalizedEmployee)) {\n targetEmployees.push({\n ...normalizedEmployee,\n validation_eligible: true,\n validation_scope:\n 'puerto_rico_country_or_location',\n });\n continue;\n }\n\n const aliasProfiles = aliases\n .map((rawAlias) => {\n const tokens =\n uniqueNameTokens(rawAlias);\n\n return {\n raw: rawAlias,\n tokens,\n token_set: new Set(tokens),\n };\n })\n .filter(\n (profile) =>\n profile.tokens.length >= 3\n );\n\n const searchTokens = new Set();\n\n for (const profile of aliasProfiles) {\n for (const token of profile.tokens) {\n searchTokens.add(token);\n }\n }\n\n outsideRecords.push({\n employee: normalizedEmployee,\n alias_profiles: aliasProfiles,\n search_tokens: searchTokens,\n });\n}\n\nconst relevantNameMap = new Map();\n\nfunction addRelevantName(value) {\n const cleaned = clean(value);\n const normalized = normalize(cleaned);\n\n if (!normalized) return;\n\n const tokens = uniqueNameTokens(cleaned);\n const current =\n relevantNameMap.get(normalized);\n\n if (\n !current ||\n tokens.length > current.tokens.length\n ) {\n relevantNameMap.set(\n normalized,\n {\n raw: cleaned,\n tokens,\n token_set: new Set(tokens),\n }\n );\n }\n}\n\nfor (const row of reconciliationData.bank?.rows || []) {\n addRelevantName(row.bank_name_file);\n addRelevantName(row.bank_account_holder);\n addRelevantName(row.participant_name);\n}\n\nfor (\n const row of\n reconciliationData.bank?.grouped_by_account || []\n) {\n addRelevantName(row.bank_name_file);\n addRelevantName(row.bank_account_holder);\n\n for (const name of row.bank_name_files || []) {\n addRelevantName(name);\n }\n\n for (\n const name of\n row.bank_account_holders || []\n ) {\n addRelevantName(name);\n }\n}\n\nfor (const row of [\n ...(reconciliationData.payroll?.rows || []),\n ...(reconciliationData.payroll?.grouped_by_account || []),\n ...(reconciliationData.payroll?.no_account_rows || []),\n]) {\n addRelevantName(\n row.employee_name ||\n row.employee ||\n ''\n );\n}\n\n/*\n * Índice invertido para rescatar perfiles con país/localidad incorrectos.\n *\n * La versión anterior comparaba cada nombre relevante contra todos los\n * empleados fuera de Puerto Rico y todas sus variantes de nombre. Con casi\n * 10,000 perfiles, eso provocaba millones de normalizaciones y bloqueaba el\n * task runner. Aquí cada palabra apunta directamente a los pocos empleados\n * que la contienen; luego solo se revisan candidatos con al menos tres\n * palabras compartidas.\n */\nconst outsideTokenIndex = new Map();\n\nfor (\n let index = 0;\n index < outsideRecords.length;\n index++\n) {\n for (\n const token of\n outsideRecords[index].search_tokens\n ) {\n let bucket =\n outsideTokenIndex.get(token);\n\n if (!bucket) {\n bucket = [];\n outsideTokenIndex.set(\n token,\n bucket\n );\n }\n\n bucket.push(index);\n }\n}\n\nconst targetExactAliasSet = new Set();\n\nfor (const employee of targetEmployees) {\n for (\n const normalizedAlias of\n employee.normalized_aliases || []\n ) {\n if (normalizedAlias) {\n targetExactAliasSet.add(\n normalizedAlias\n );\n }\n }\n}\n\nlet contextualOutsideSkippedByTargetExact = 0;\nconst contextualOutsideMap = new Map();\n\nfor (\n const relevantProfile of\n relevantNameMap.values()\n) {\n if (relevantProfile.tokens.length < 3) {\n continue;\n }\n\n /*\n * Prioridad absoluta al país objetivo:\n *\n * Cuando el nombre completo recibido existe exactamente en Puerto Rico,\n * no se incorpora un perfil externo cuyo nombre más corto esté contenido\n * dentro de ese mismo texto. Esto evita conflictos entre dos personas\n * distintas, por ejemplo:\n *\n * - NOMBRE COMPLETO · Honduras\n * - un perfil con nombre más corto · otro país\n *\n * El rescate fuera del país permanece activo cuando no existe una\n * coincidencia exacta entre los perfiles de Puerto Rico.\n */\n if (\n targetExactAliasSet.has(\n normalize(relevantProfile.raw)\n )\n ) {\n contextualOutsideSkippedByTargetExact += 1;\n continue;\n }\n\n const sharedTokenCounts = new Map();\n\n for (const token of relevantProfile.tokens) {\n for (\n const outsideIndex of\n outsideTokenIndex.get(token) || []\n ) {\n sharedTokenCounts.set(\n outsideIndex,\n (\n sharedTokenCounts.get(\n outsideIndex\n ) || 0\n ) + 1\n );\n }\n }\n\n const uniqueMatches = new Map();\n\n for (\n const [\n outsideIndex,\n sharedTokenCount,\n ] of sharedTokenCounts\n ) {\n if (sharedTokenCount < 3) {\n continue;\n }\n\n const record =\n outsideRecords[outsideIndex];\n\n const matches =\n record.alias_profiles.some(\n (aliasProfile) =>\n profileContains(\n relevantProfile.tokens,\n relevantProfile.token_set,\n aliasProfile.tokens,\n aliasProfile.token_set\n )\n );\n\n if (!matches) continue;\n\n const key =\n employeeKey(record.employee);\n\n if (key) {\n uniqueMatches.set(\n key,\n record.employee\n );\n }\n }\n\n /*\n * Solo se rescata un perfil fuera del país cuando un nombre informativo\n * identifica exactamente a una única persona. Así se corrigen localidades\n * erróneas sin convertir nombres comunes en falsos positivos.\n */\n if (uniqueMatches.size !== 1) {\n continue;\n }\n\n const employee =\n uniqueMatches.values().next().value;\n\n const key = employeeKey(employee);\n\n if (!key) continue;\n\n contextualOutsideMap.set(key, {\n ...employee,\n validation_eligible: true,\n validation_scope:\n 'outside_country_unique_informative_name',\n });\n}\n\nconst validationEmployeeMap = new Map();\n\nfor (const employee of [\n ...targetEmployees,\n ...contextualOutsideMap.values(),\n]) {\n const key = employeeKey(employee);\n\n if (key) {\n validationEmployeeMap.set(\n key,\n employee\n );\n }\n}\n\nconst validationEmployees =\n Array.from(\n validationEmployeeMap.values()\n );\n\n\n/*\n * Resolución previa de nombres contra BambooHR.\n *\n * Cada nombre distinto recibido desde banco y nómina se resuelve una sola\n * vez, usando índices de alias y palabras. El resultado queda disponible\n * para el nodo de cruce mediante resolved_name_matches.\n */\nconst CONFIRMED_BAMBOO_NAME_ALIASES = new Map([]);\n\nfunction relevantEntryRaw(entry) {\n if (typeof entry === 'string') return clean(entry);\n return clean(entry?.raw || entry?.name || '');\n}\n\nfunction bambooResolutionEmployeeKey(employee) {\n return (\n clean(employee.bamboo_id) ||\n clean(employee.employee_number) ||\n normalize(employee.full_name)\n );\n}\n\nfunction bambooResolutionEditDistance(left, right) {\n const a = String(left || '');\n const b = String(right || '');\n\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n\n let previous = Array.from(\n { length: b.length + 1 },\n (_, index) => index\n );\n\n for (let row = 1; row <= a.length; row++) {\n const current = [row];\n\n for (let column = 1; column <= b.length; column++) {\n const cost =\n a[row - 1] === b[column - 1]\n ? 0\n : 1;\n\n current[column] = Math.min(\n current[column - 1] + 1,\n previous[column] + 1,\n previous[column - 1] + cost\n );\n }\n\n previous = current;\n }\n\n return previous[b.length];\n}\nfunction bambooResolutionTokenSimilarity(left, right) {\n const a = String(left || '');\n const b = String(right || '');\n\n if (!a || !b) return 0;\n if (a === b) return 1;\n\n const minimumLength = Math.min(\n a.length,\n b.length\n );\n\n const maximumLength = Math.max(\n a.length,\n b.length\n );\n\n const distance =\n bambooResolutionEditDistance(a, b);\n\n if (\n minimumLength >= 4 &&\n distance <= 1\n ) {\n return Math.max(\n 0.90,\n 1 - distance / maximumLength\n );\n }\n\n if (\n minimumLength >= 6 &&\n distance <= 2\n ) {\n return Math.max(\n 0.82,\n 1 - distance / maximumLength\n );\n }\n\n const prefixOrSuffix =\n a.startsWith(b) ||\n b.startsWith(a) ||\n a.endsWith(b) ||\n b.endsWith(a);\n\n if (\n prefixOrSuffix &&\n minimumLength >= 4\n ) {\n return Math.max(\n 0.78,\n minimumLength / maximumLength\n );\n }\n\n return 0;\n}\n\nfunction bambooResolutionAliasDetails(\n queryName,\n aliasProfile\n) {\n const queryWords = Array.from(\n new Set(nameTokens(queryName))\n );\n\n const aliasWords =\n aliasProfile.words;\n\n if (\n queryWords.length < 2 ||\n aliasWords.length < 2\n ) {\n return null;\n }\n\n const aliasWordSet =\n aliasProfile.word_set;\n\n const queryWordSet =\n new Set(queryWords);\n\n const queryInsideAlias =\n queryWords.every((word) =>\n aliasWordSet.has(word)\n );\n\n const aliasInsideQuery =\n aliasWords.every((word) =>\n queryWordSet.has(word)\n );\n\n const usedAliasIndexes = new Set();\n const usedQueryIndexes = new Set();\n const similarities = new Array(\n queryWords.length\n ).fill(0);\n\n let exactMatches = 0;\n\n for (\n let queryIndex = 0;\n queryIndex < queryWords.length;\n queryIndex++\n ) {\n const aliasIndex =\n aliasWords.findIndex(\n (aliasWord, currentAliasIndex) =>\n !usedAliasIndexes.has(\n currentAliasIndex\n ) &&\n aliasWord ===\n queryWords[queryIndex]\n );\n\n if (aliasIndex < 0) continue;\n\n usedQueryIndexes.add(queryIndex);\n usedAliasIndexes.add(aliasIndex);\n similarities[queryIndex] = 1;\n exactMatches += 1;\n }\n\n const remainingQueryIndexes =\n queryWords\n .map((word, index) => ({\n word,\n index,\n }))\n .filter((entry) =>\n !usedQueryIndexes.has(entry.index)\n )\n .sort((left, right) =>\n right.word.length -\n left.word.length\n );\n\n for (const queryEntry of remainingQueryIndexes) {\n let bestSimilarity = 0;\n let bestAliasIndex = -1;\n\n for (\n let aliasIndex = 0;\n aliasIndex < aliasWords.length;\n aliasIndex++\n ) {\n if (\n usedAliasIndexes.has(\n aliasIndex\n )\n ) {\n continue;\n }\n\n const similarity =\n bambooResolutionTokenSimilarity(\n queryEntry.word,\n aliasWords[aliasIndex]\n );\n\n if (similarity > bestSimilarity) {\n bestSimilarity = similarity;\n bestAliasIndex = aliasIndex;\n }\n }\n\n if (\n bestAliasIndex >= 0 &&\n bestSimilarity >= 0.78\n ) {\n usedAliasIndexes.add(\n bestAliasIndex\n );\n similarities[queryEntry.index] =\n bestSimilarity;\n }\n }\n\n const matchedTokens =\n similarities.filter(\n (value) => value >= 0.78\n ).length;\n\n const queryCoverage =\n similarities.reduce(\n (sum, value) => sum + value,\n 0\n ) / queryWords.length;\n\n const aliasCoverage =\n matchedTokens /\n aliasWords.length;\n\n const lengthBalance =\n Math.min(\n queryWords.length,\n aliasWords.length\n ) /\n Math.max(\n queryWords.length,\n aliasWords.length\n );\n\n const score =\n queryCoverage * 0.65 +\n aliasCoverage * 0.20 +\n (\n exactMatches /\n queryWords.length\n ) * 0.10 +\n lengthBalance * 0.05;\n\n return {\n score,\n exact_matches: exactMatches,\n matched_tokens: matchedTokens,\n query_tokens:\n queryWords.length,\n alias_tokens:\n aliasWords.length,\n query_coverage:\n queryCoverage,\n alias_coverage:\n aliasCoverage,\n containment:\n queryInsideAlias ||\n aliasInsideQuery,\n };\n}\n\nconst bambooResolutionProfiles =\n validationEmployees.map(\n (employee, employeeIndex) => {\n const aliases = [];\n const seenAliases = new Set();\n\n for (\n const rawAlias of\n employee.aliases || []\n ) {\n const normalizedAlias =\n normalize(rawAlias);\n\n if (\n !normalizedAlias ||\n seenAliases.has(\n normalizedAlias\n )\n ) {\n continue;\n }\n\n seenAliases.add(\n normalizedAlias\n );\n\n const words = Array.from(\n new Set(nameTokens(rawAlias))\n );\n\n if (!words.length) continue;\n\n aliases.push({\n raw: clean(rawAlias),\n normalized:\n normalizedAlias,\n words,\n word_set:\n new Set(words),\n });\n }\n\n return {\n employee,\n employee_index:\n employeeIndex,\n employee_key:\n bambooResolutionEmployeeKey(\n employee\n ),\n aliases,\n };\n }\n );\n\nconst bambooResolutionExactAliasSets =\n new Map();\n\nconst bambooResolutionTokenSets =\n new Map();\n\nconst bambooResolutionTokenShapeSets =\n new Map();\n\nfor (\n let employeeIndex = 0;\n employeeIndex <\n bambooResolutionProfiles.length;\n employeeIndex++\n) {\n const profile =\n bambooResolutionProfiles[\n employeeIndex\n ];\n\n for (const alias of profile.aliases) {\n let exactSet =\n bambooResolutionExactAliasSets\n .get(alias.normalized);\n\n if (!exactSet) {\n exactSet = new Set();\n bambooResolutionExactAliasSets\n .set(\n alias.normalized,\n exactSet\n );\n }\n\n exactSet.add(employeeIndex);\n\n for (const token of alias.words) {\n if (token.length < 3) continue;\n\n let tokenSet =\n bambooResolutionTokenSets\n .get(token);\n\n if (!tokenSet) {\n tokenSet = new Set();\n bambooResolutionTokenSets\n .set(token, tokenSet);\n }\n\n tokenSet.add(employeeIndex);\n\n const tokenShape =\n `${token[0]}:${token.length}`;\n\n let shapeSet =\n bambooResolutionTokenShapeSets\n .get(tokenShape);\n\n if (!shapeSet) {\n shapeSet = new Set();\n bambooResolutionTokenShapeSets\n .set(\n tokenShape,\n shapeSet\n );\n }\n\n shapeSet.add(employeeIndex);\n }\n }\n}\n\nconst bambooResolutionExactAliasMap =\n new Map();\n\nfor (\n const [alias, indexes] of\n bambooResolutionExactAliasSets\n) {\n bambooResolutionExactAliasMap.set(\n alias,\n Array.from(indexes)\n );\n}\n\nfunction bambooResolutionDecision(\n queryName\n) {\n const rawQuery = clean(queryName);\n const normalizedQuery =\n normalize(rawQuery);\n\n const queryWords = Array.from(\n new Set(nameTokens(rawQuery))\n );\n\n if (\n !normalizedQuery ||\n queryWords.length < 2\n ) {\n return {\n found: false,\n matched_by: null,\n confidence: 0,\n reason:\n 'insufficient_name_tokens',\n };\n }\n\n const confirmedCanonical =\n CONFIRMED_BAMBOO_NAME_ALIASES\n .get(normalizedQuery);\n\n if (confirmedCanonical) {\n const confirmedIndexes =\n bambooResolutionExactAliasMap\n .get(confirmedCanonical) || [];\n\n if (confirmedIndexes.length === 1) {\n const employeeIndex =\n confirmedIndexes[0];\n\n return {\n found: true,\n matched_by:\n 'confirmed_alias_catalog',\n confidence: 1,\n employee_index:\n employeeIndex,\n employee_key:\n bambooResolutionProfiles[\n employeeIndex\n ].employee_key,\n query_name:\n rawQuery,\n bamboo_alias:\n bambooResolutionProfiles[\n employeeIndex\n ].aliases.find(\n (alias) =>\n alias.normalized ===\n confirmedCanonical\n )?.raw ||\n bambooResolutionProfiles[\n employeeIndex\n ].employee.full_name ||\n '',\n };\n }\n }\n\n const exactIndexes =\n bambooResolutionExactAliasMap\n .get(normalizedQuery) || [];\n\n if (exactIndexes.length === 1) {\n const employeeIndex =\n exactIndexes[0];\n\n return {\n found: true,\n matched_by:\n 'exact_precomputed_name',\n confidence: 1,\n employee_index:\n employeeIndex,\n employee_key:\n bambooResolutionProfiles[\n employeeIndex\n ].employee_key,\n query_name:\n rawQuery,\n bamboo_alias:\n bambooResolutionProfiles[\n employeeIndex\n ].aliases.find(\n (alias) =>\n alias.normalized ===\n normalizedQuery\n )?.raw ||\n bambooResolutionProfiles[\n employeeIndex\n ].employee.full_name ||\n '',\n };\n }\n\n const candidateVotes = new Map();\n\n function addCandidateVotes(\n indexes,\n weight\n ) {\n for (const employeeIndex of indexes) {\n candidateVotes.set(\n employeeIndex,\n (\n candidateVotes.get(\n employeeIndex\n ) || 0\n ) + weight\n );\n }\n }\n\n for (const token of queryWords) {\n addCandidateVotes(\n bambooResolutionTokenSets\n .get(token) || [],\n 4\n );\n\n for (\n let lengthOffset = -2;\n lengthOffset <= 2;\n lengthOffset++\n ) {\n const candidateLength =\n token.length + lengthOffset;\n\n if (candidateLength < 3) {\n continue;\n }\n\n addCandidateVotes(\n bambooResolutionTokenShapeSets\n .get(\n `${token[0]}:${candidateLength}`\n ) || [],\n 1\n );\n }\n }\n\n const candidateIndexes =\n Array.from(\n candidateVotes.entries()\n )\n .sort((left, right) =>\n right[1] - left[1]\n )\n .slice(0, 120)\n .map(([employeeIndex]) =>\n employeeIndex\n );\n\n const rankedCandidates = [];\n\n for (\n const employeeIndex of\n candidateIndexes\n ) {\n const profile =\n bambooResolutionProfiles[\n employeeIndex\n ];\n\n let bestDetails = null;\n let bestAlias = '';\n\n for (const alias of profile.aliases) {\n const details =\n bambooResolutionAliasDetails(\n rawQuery,\n alias\n );\n\n if (\n details &&\n (\n !bestDetails ||\n details.score >\n bestDetails.score\n )\n ) {\n bestDetails = details;\n bestAlias = alias.raw;\n }\n }\n\n if (!bestDetails) continue;\n\n rankedCandidates.push({\n employee_index:\n employeeIndex,\n employee_key:\n profile.employee_key,\n details:\n bestDetails,\n bamboo_alias:\n bestAlias,\n });\n }\n\n rankedCandidates.sort(\n (left, right) => {\n if (\n right.details.score !==\n left.details.score\n ) {\n return (\n right.details.score -\n left.details.score\n );\n }\n\n if (\n right.details.exact_matches !==\n left.details.exact_matches\n ) {\n return (\n right.details.exact_matches -\n left.details.exact_matches\n );\n }\n\n return (\n right.details.query_coverage -\n left.details.query_coverage\n );\n }\n );\n\n const best =\n rankedCandidates[0] || null;\n\n const second =\n rankedCandidates[1] || null;\n\n const margin =\n best\n ? best.details.score -\n (\n second?.details.score ||\n 0\n )\n : 0;\n\n const details =\n best?.details || null;\n\n const exactContainment =\n Boolean(\n details?.containment &&\n details.exact_matches >= 2\n );\n\n const strongTwoTokenName =\n Boolean(\n details &&\n details.query_tokens === 2 &&\n details.matched_tokens === 2 &&\n details.exact_matches >= 1 &&\n details.query_coverage >= 0.90 &&\n details.score >= 0.88\n );\n\n const strongLongName =\n Boolean(\n details &&\n details.query_tokens >= 3 &&\n details.matched_tokens >=\n Math.min(\n 3,\n details.query_tokens\n ) &&\n details.exact_matches >= 2 &&\n details.query_coverage >= 0.85 &&\n details.score >= 0.84\n );\n\n const acceptableMargin =\n !second ||\n margin >= (\n exactContainment\n ? 0.04\n : 0.06\n ) ||\n (\n details?.exact_matches || 0\n ) >\n (\n second?.details\n ?.exact_matches || 0\n );\n\n if (\n best &&\n acceptableMargin &&\n (\n exactContainment ||\n strongTwoTokenName ||\n strongLongName\n )\n ) {\n return {\n found: true,\n matched_by:\n exactContainment\n ? 'unique_precomputed_containment'\n : 'strong_precomputed_fuzzy_name',\n confidence:\n Math.min(\n 1,\n details.score\n ),\n employee_index:\n best.employee_index,\n employee_key:\n best.employee_key,\n query_name:\n rawQuery,\n bamboo_alias:\n best.bamboo_alias,\n margin,\n exact_matches:\n details.exact_matches,\n matched_tokens:\n details.matched_tokens,\n };\n }\n\n return {\n found: false,\n matched_by: null,\n confidence:\n details?.score || 0,\n reason:\n best\n ? (\n acceptableMargin\n ? 'insufficient_name_evidence'\n : 'ambiguous_name'\n )\n : 'no_candidate',\n best_candidate:\n best\n ? {\n employee_index:\n best.employee_index,\n employee_key:\n best.employee_key,\n bamboo_alias:\n best.bamboo_alias,\n score:\n best.details.score,\n }\n : null,\n second_candidate:\n second\n ? {\n employee_index:\n second.employee_index,\n employee_key:\n second.employee_key,\n bamboo_alias:\n second.bamboo_alias,\n score:\n second.details.score,\n }\n : null,\n };\n}\n\nconst resolvedNameMatches = {};\nlet resolvedNameMatchesFound = 0;\n\nfor (\n const [\n normalizedRelevantName,\n relevantEntry,\n ] of relevantNameMap\n) {\n const rawRelevantName =\n relevantEntryRaw(relevantEntry);\n\n const decision =\n bambooResolutionDecision(\n rawRelevantName\n );\n\n resolvedNameMatches[\n normalizedRelevantName\n ] = decision;\n\n if (decision.found) {\n resolvedNameMatchesFound += 1;\n }\n}\n\n\nconst fetchedEmployeesCount =\n rawEmployees.length;\n\nconst fetchComplete =\n expectedTotal > 0\n ? fetchedEmployeesCount >= expectedTotal\n : (\n pageObjects.length > 0 &&\n !pageObjects.some(\n (page) =>\n Boolean(\n page?._links?.next?.href\n )\n )\n );\n\nconst errors = [];\n\nif (!pageObjects.length) {\n errors.push(\n 'BambooHR no devolvió páginas de empleados.'\n );\n}\n\nif (!fetchedEmployeesCount) {\n errors.push(\n 'BambooHR no devolvió empleados.'\n );\n}\n\nif (\n expectedTotal > 0 &&\n fetchedEmployeesCount < expectedTotal\n) {\n errors.push(\n `La descarga de BambooHR quedó incompleta: ` +\n `${fetchedEmployeesCount} de ${expectedTotal} empleados.`\n );\n}\n\nif (!targetEmployees.length) {\n errors.push(\n 'No se encontraron empleados de Puerto Rico en BambooHR.'\n );\n}\n\nreturn [\n {\n json: {\n ...base,\n ok:\n Boolean(base.ok ?? true) &&\n errors.length === 0,\n stage:\n errors.length === 0\n ? 'bamboohr_pr_normalizado'\n : 'bamboohr_pr_incompleto',\n errors: [\n ...(Array.isArray(base.errors)\n ? base.errors\n : []),\n ...errors,\n ],\n bamboo: {\n source:\n 'bamboohr_custom_report_only_current_false',\n period_start: periodStart,\n period_end: periodEnd,\n pages_fetched: pageObjects.length,\n expected_total: expectedTotal,\n raw_employees_count:\n fetchedEmployeesCount,\n employees_count:\n normalizedEmployeesCount,\n puerto_rico_count:\n targetEmployees.length,\n active_in_period_count:\n targetEmployees.filter(\n (employee) =>\n employee.overlaps_period\n ).length,\n active_status_count:\n targetEmployees.filter(\n (employee) =>\n normalize(employee.status) ===\n 'active'\n ).length,\n contextual_outside_country_count:\n contextualOutsideMap.size,\n contextual_outside_skipped_by_target_exact_count:\n contextualOutsideSkippedByTargetExact,\n validation_candidates_count:\n validationEmployees.length,\n resolved_name_matches:\n resolvedNameMatches,\n resolved_name_matches_count:\n Object.keys(\n resolvedNameMatches\n ).length,\n resolved_name_matches_found:\n resolvedNameMatchesFound,\n name_resolution_strategy:\n 'precomputed_indexed_fuzzy_matching_with_target_exact_precedence',\n fetch_complete: fetchComplete,\n validation_available:\n fetchComplete &&\n validationEmployees.length > 0,\n validation_rule:\n 'Target country/location first; outside-country contextual rescue only when no exact target alias exists',\n performance_strategy:\n 'precomputed_alias_tokens_and_inverted_index',\n employees:\n validationEmployees,\n restricted_fields:\n restrictedFields,\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -840, + 5040 + ], + "id": "c9609568-0db8-42c7-8629-601841c2c3d7", + "name": "Normalizar BambooHR PR" + }, + { + "parameters": { + "mode": "combine", + "combineBy": "combineByPosition", + "options": {} + }, + "type": "n8n-nodes-base.merge", + "typeVersion": 3.2, + "position": [ + -600, + 5200 + ], + "id": "0d44d9e2-bf49-400c-873c-c9adb8bcbbd3", + "name": "Merge - Agregar BambooHR PR" + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/cruce_cuentas_bamboo_aliases_activos", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": { + "p_pais": "PR" + }, + "options": { + "response": { + "response": { + "responseFormat": "json" + } + }, + "timeout": 30000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + -360, + 5200 + ], + "id": "b8e487d1-2745-4fce-a4ae-00a1ceb44bab", + "name": "Obtener alias BambooHR Supabase", + "retryOnFail": true, + "maxTries": 2, + "waitBetweenTries": 1000, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const data = $('Merge - Agregar BambooHR PR').first().json || {};\nconst aliasResponse = $input.first().json || {};\nconst validatedBambooAliases = Array.isArray(aliasResponse.aliases)\n ? aliasResponse.aliases\n : Array.isArray(aliasResponse)\n ? aliasResponse\n : [];\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst normalizedNameCache = new Map();\nconst nameWordsCache = new Map();\nconst nameMatchScoreCache = new Map();\n\nfunction normalizeName(value) {\n const raw = clean(value);\n if (normalizedNameCache.has(raw)) {\n return normalizedNameCache.get(raw);\n }\n\n const normalized = raw\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n normalizedNameCache.set(raw, normalized);\n return normalized;\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n}\n\nfunction normalizeEmployeeNumber(value) {\n return String(value ?? '')\n .replace(/\\u00A0/g, '')\n .replace(/\\D/g, '')\n .trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction moneyDiff(payroll, bank) {\n return roundMoney((Number(payroll) || 0) - (Number(bank) || 0));\n}\n\nconst MONEY_TOLERANCE = 0.01;\nconst MONEY_TOLERANCE_CENTS = Math.round(MONEY_TOLERANCE * 100);\n\nfunction sameCent(a, b) {\n return Math.abs(moneyDiff(a, b)) <= MONEY_TOLERANCE + 1e-9;\n}\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los', 'y', 'e', 'el',\n 'da', 'do', 'dos', 'das', 'van', 'von',\n]);\n\nfunction nameWords(value) {\n const normalized = normalizeName(value);\n if (nameWordsCache.has(normalized)) {\n return nameWordsCache.get(normalized);\n }\n\n const words = normalized\n .split(' ')\n .filter((word) => word.length > 1 && !IGNORED_NAME_TOKENS.has(word));\n\n nameWordsCache.set(normalized, words);\n return words;\n}\n\nfunction editDistance(a, b) {\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n let previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n const current = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);\n }\n previous = current;\n }\n return previous[b.length];\n}\n\nfunction tokenMatches(a, b) {\n if (a === b) return true;\n const minLength = Math.min(a.length, b.length);\n if (minLength >= 8 && editDistance(a, b) <= 2) return true;\n if (minLength >= 5 && editDistance(a, b) <= 1) return true;\n return false;\n}\n\nfunction samePersonName(a, b) {\n const normalizedA = normalizeName(a);\n const normalizedB = normalizeName(b);\n if (!normalizedA || !normalizedB) return false;\n if (normalizedA === normalizedB) return true;\n\n const wordsA = nameWords(a);\n const wordsB = nameWords(b);\n if (!wordsA.length || !wordsB.length) return false;\n\n const usedB = new Set();\n let matches = 0;\n for (const wordA of wordsA) {\n const matchIndex = wordsB.findIndex((wordB, index) =>\n !usedB.has(index) && tokenMatches(wordA, wordB)\n );\n if (matchIndex >= 0) {\n usedB.add(matchIndex);\n matches += 1;\n }\n }\n\n const smallerLength = Math.min(wordsA.length, wordsB.length);\n const ratio = matches / smallerLength;\n if (smallerLength <= 2) return matches === smallerLength && matches >= 2;\n return matches >= 2 && ratio >= 0.6;\n}\n\nfunction bankNameLooksConsistent(fileName, holderName) {\n const fileWords = nameWords(fileName);\n const holderWords = nameWords(holderName);\n\n if (!fileWords.length || !holderWords.length) return true;\n\n const used = new Set();\n\n for (const fileWord of fileWords) {\n let matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord === fileWord\n );\n\n // Algunos planos de Puerto Rico recortan el último carácter del apellido\n // por longitud de campo (ej.: ALVARAD -> ALVARADO). Eso no debe\n // convertirse en una diferencia de nombre. No se aceptan sustituciones\n // ortográficas (ej.: GONZALES -> GONZALEZ), porque sí requieren revisión.\n if (matchIndex < 0) {\n matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord.startsWith(fileWord) &&\n (\n (fileWord.length <= 3) ||\n (\n fileWord.length >= 4 &&\n holderWord.length === fileWord.length + 1\n )\n )\n );\n }\n\n if (matchIndex < 0) return false;\n used.add(matchIndex);\n }\n\n return true;\n}\n\nfunction nameMatchScore(a, b) {\n const na = normalizeName(a);\n const nb = normalizeName(b);\n if (!na || !nb) return 0;\n\n const cacheKey = na <= nb ? `${na}\\u0000${nb}` : `${nb}\\u0000${na}`;\n if (nameMatchScoreCache.has(cacheKey)) {\n return nameMatchScoreCache.get(cacheKey);\n }\n\n if (na === nb) {\n nameMatchScoreCache.set(cacheKey, 1);\n return 1;\n }\n\n const wa = nameWords(a);\n const wb = nameWords(b);\n if (!samePersonName(a, b)) {\n nameMatchScoreCache.set(cacheKey, 0);\n return 0;\n }\n\n const setA = new Set(wa);\n const setB = new Set(wb);\n let exact = 0;\n for (const w of setA) if (setB.has(w)) exact += 1;\n const min = Math.max(1, Math.min(setA.size, setB.size));\n const score = Math.min(\n 0.99,\n 0.75 + (exact / min) * 0.20 + (wa.length === wb.length ? 0.04 : 0)\n );\n\n nameMatchScoreCache.set(cacheKey, score);\n return score;\n}\n\nfunction bankNames(row) {\n return [row.bank_name_file, row.bank_account_holder]\n .map(clean)\n .filter(Boolean);\n}\n\nfunction bestNameScore(payrollName, bankRow) {\n return Math.max(0, ...bankNames(bankRow).map((name) => nameMatchScore(payrollName, name)));\n}\n\nfunction bankDisplayName(row) {\n return clean(row.bank_name_file) || clean(row.bank_account_holder) || 'Pago bancario';\n}\n\nfunction bankRowKey(row, index) {\n return clean(row.bank_row_key) || `${clean(row.source_file)}|${row.row_number || index + 1}`;\n}\n\n\nconst metadata = data.metadata || {};\n\nfunction parseBankDate(value, sourceFile = '') {\n const raw = clean(value);\n let match = raw.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (match) {\n return {\n year: Number(match[3]),\n month: Number(match[2]),\n day: Number(match[1]),\n };\n }\n\n match = raw.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})/);\n if (match) {\n return {\n year: Number(match[1]),\n month: Number(match[2]),\n day: Number(match[3]),\n };\n }\n\n // Respaldo por el patrón habitual del nombre: _DDMMYY / _DDMMYYYY.\n const fileMatch = clean(sourceFile).match(/_(\\d{2})(\\d{2})(\\d{2}|\\d{4})(?:\\D|$)/);\n if (!fileMatch) return null;\n\n const yearPart = Number(fileMatch[3]);\n return {\n year: fileMatch[3].length === 2 ? 2000 + yearPart : yearPart,\n month: Number(fileMatch[2]),\n day: Number(fileMatch[1]),\n };\n}\n\nfunction selectedPeriodKind() {\n const type = clean(metadata.period_type).toLowerCase();\n if (type === 'quincena_15') return 'q15';\n if (type === 'quincena_30') return 'q30';\n return 'unknown';\n}\n\nfunction conceptPeriodHint(row) {\n const concept = normalizeName(row.concept || '');\n if (!concept) return 'unknown';\n\n if (\n /(?:^| )1(?:ra|er|era)? quincena(?: |$)/.test(concept) ||\n concept.includes('primera quincena') ||\n concept.includes('quincena 1ra') ||\n concept.includes('quincena 1')\n ) {\n return 'q15';\n }\n\n if (\n /(?:^| )2(?:da|do|nda)? quincena(?: |$)/.test(concept) ||\n concept.includes('segunda quincena') ||\n concept.includes('quincena 2da') ||\n concept.includes('quincena 2') ||\n concept.includes('fin de mes')\n ) {\n return 'q30';\n }\n\n return 'unknown';\n}\n\nfunction periodInfoForBankRow(row) {\n const selected = selectedPeriodKind();\n const rowDate = parseBankDate(row.application_date, row.source_file);\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n const hint = conceptPeriodHint(row);\n\n if (selected === 'unknown') {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (hint !== 'unknown' && hint === selected) {\n return { known: true, selected, inPeriod: true, affinity: 4, rowDate, hint };\n }\n\n if (!rowDate || !selectedYear || !selectedMonth) {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (rowDate.year !== selectedYear || rowDate.month !== selectedMonth) {\n return { known: true, selected, inPeriod: false, affinity: 0, rowDate, hint };\n }\n\n const inHalf = selected === 'q15'\n ? rowDate.day <= 15\n : rowDate.day >= 16;\n\n return {\n known: true,\n selected,\n inPeriod: inHalf,\n affinity: inHalf ? 3 : 0,\n rowDate,\n hint,\n };\n}\n\nfunction isSupplementPayroll(payroll) {\n return clean(payroll?.source_kind).toLowerCase() !== 'quincenal';\n}\n\nfunction bankCandidatePeriodScore(payroll, bank) {\n const info = periodInfoForBankRow(bank);\n if (!info.known || info.inPeriod) return info.affinity || 1;\n\n // Una extraordinaria/vacación/XIII Mes puede pagarse fuera de la mitad\n // estricta del mes. Se permite como respaldo, siempre detrás de cualquier\n // candidato del período seleccionado.\n if (isSupplementPayroll(payroll) && info.rowDate) {\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n if (info.rowDate.year === selectedYear && info.rowDate.month === selectedMonth) {\n return 0.5;\n }\n }\n\n return -1;\n}\n\nconst payrollRows = Array.isArray(data.payroll?.rows) ? data.payroll.rows : [];\nconst bankRows = Array.isArray(data.bank?.rows) ? data.bank.rows : [];\n\n// Guardia de seguridad: nunca generar un reporte \"válido\" cuando el request\n// traía archivos pero el parser no consiguió ninguna fila. Esto evita que\n// todas las personas de nómina aparezcan falsamente como discrepancias.\nif (payrollRows.length === 0) {\n throw new Error(\n 'Puerto Rico: no se pudo leer ninguna fila válida de nómina. ' +\n 'Revisa los archivo Excel de Puerto Rico y las hojas Whirlpool, Personal 2 y Personal 3.'\n );\n}\n\nif (bankRows.length === 0) {\n const detectedFiles = Number(data.bank?.files_count || data.summary?.bank_files_count || 0);\n const invalidFiles = Array.isArray(data.bank?.file_summaries)\n ? data.bank.file_summaries.filter((file) => file?.ok === false)\n : [];\n\n throw new Error(\n 'Puerto Rico: no se pudo leer ninguna transacción bancaria. ' +\n `Archivos bancarios detectados: ${detectedFiles}. ` +\n (invalidFiles.length\n ? `Archivos con error de formato: ${invalidFiles.map((file) => file.file_name).join(', ')}.`\n : 'Verifica que el PDF sea el Transaction Detail Report de Banco Popular de Puerto Rico.')\n );\n}\n\nconst bambooEmployees = Array.isArray(data.bamboo?.employees) ? data.bamboo.employees : [];\nconst resolvedNameMatches = data.bamboo?.resolved_name_matches || {};\nconst bambooValidationAvailable = data.bamboo?.fetch_complete === true &&\n data.bamboo?.validation_available === true && bambooEmployees.length > 0;\n\nconst bambooByEmployeeNumber = new Map();\nconst bambooById = new Map();\nconst bambooByExactName = new Map();\nfor (const employee of bambooEmployees) {\n const number = normalizeEmployeeNumber(\n employee.employee_number || employee.employeeNumber\n );\n const id = clean(employee.bamboo_id || employee.id);\n if (number) {\n const arr = bambooByEmployeeNumber.get(number) || [];\n arr.push(employee);\n bambooByEmployeeNumber.set(number, arr);\n }\n if (id) bambooById.set(id, employee);\n for (const alias of employee.aliases || [employee.full_name]) {\n const normalized = normalizeName(alias);\n if (!normalized) continue;\n const arr = bambooByExactName.get(normalized) || [];\n arr.push(employee);\n bambooByExactName.set(normalized, arr);\n }\n}\n\nfunction employeeKey(employee) {\n return clean(employee?.bamboo_id || employee?.id) ||\n clean(employee?.employee_number || employee?.employeeNumber) ||\n normalizeName(employee?.full_name || employee?.displayName);\n}\n\nfunction validatedAliasDecision(row) {\n if (!validatedBambooAliases.length) return null;\n const rowAccount = normalizeAccount(row.account);\n const names = bankNames(row).map(normalizeName).filter(Boolean);\n const candidates = validatedBambooAliases.filter((alias) => {\n const country = clean(alias.pais || alias.country).toUpperCase();\n if (country && country !== 'PR') return false;\n const source = normalizeName(alias.nombre_origen || alias.source_name || alias.alias || '');\n if (!source || !names.includes(source)) return false;\n const aliasAccount = normalizeAccount(alias.cuenta_bancaria || alias.cuenta_banco || alias.bank_account || '');\n return !aliasAccount || !rowAccount || aliasAccount === rowAccount;\n });\n if (candidates.length !== 1) return null;\n const alias = candidates[0];\n let employee = null;\n const employeeNumber = normalizeEmployeeNumber(\n alias.bamboo_employee_number || ''\n );\n const bambooId = clean(alias.bamboo_employee_id || '');\n const bambooName = normalizeName(alias.bamboo_nombre || '');\n if (employeeNumber && (bambooByEmployeeNumber.get(employeeNumber) || []).length === 1) {\n employee = bambooByEmployeeNumber.get(employeeNumber)[0];\n } else if (bambooId && bambooById.has(bambooId)) {\n employee = bambooById.get(bambooId);\n } else if (bambooName && (bambooByExactName.get(bambooName) || []).length === 1) {\n employee = bambooByExactName.get(bambooName)[0];\n }\n if (!employee) return null;\n return {\n found: true,\n matched_by: 'validated_bamboo_alias',\n confidence: 1,\n employee,\n employee_key: employeeKey(employee),\n bamboo_alias: clean(alias.bamboo_nombre) || employee.full_name || '',\n };\n}\n\nfunction precomputedDecision(name) {\n const normalized = normalizeName(name);\n if (!normalized) return null;\n const decision = resolvedNameMatches[normalized];\n if (!decision || decision.found !== true) return null;\n const index = Number(decision.employee_index);\n const employee = Number.isInteger(index) && index >= 0 ? bambooEmployees[index] : null;\n const key = clean(decision.employee_key) || employeeKey(employee);\n if (!key) return null;\n return {\n found: true,\n matched_by: decision.matched_by || 'precomputed_name',\n confidence: Number(decision.confidence || 0),\n employee,\n employee_key: key,\n bamboo_alias: clean(decision.bamboo_alias) || employee?.full_name || '',\n query_name: clean(name),\n };\n}\n\nfunction resolveBamboo(\n row,\n linkedPayrollNames = [],\n linkedPayrollEmployeeNumbers = []\n) {\n if (!bambooValidationAvailable) {\n return {\n found: false,\n reason: 'validation_unavailable',\n };\n }\n\n const validated = validatedAliasDecision(row);\n if (validated) return validated;\n\n /*\n * Puerto Rico: si existe un identificador fuerte procedente de nómina se puede usar como respaldo para determinar\n * si el pago pertenece a una persona existente en BambooHR.\n *\n * Se normalizan guiones/espacios tanto en banco como en BambooHR:\n * 0611198400414 <-> 0611-1984-00414\n *\n * Como respaldo, cuando la fila bancaria ya fue conciliada con nómina,\n * también se usa el Employee Number/Identidad de esa fila de nómina.\n * Solo se acepta el identificador cuando apunta a UNA sola persona.\n */\n const referenceEmployeeNumbers = Array.from(\n new Set(\n [\n row.reference,\n row.participant_id,\n row.employee_number,\n row.employeeNumber,\n ...linkedPayrollEmployeeNumbers,\n ]\n .flatMap((value) =>\n String(value ?? '').match(/\\d{6,20}/g) || []\n )\n .map(normalizeEmployeeNumber)\n .filter((value) => value.length >= 6)\n )\n );\n\n const employeesByStrongIdentifier = new Map();\n\n for (const employeeNumber of referenceEmployeeNumbers) {\n const matches =\n bambooByEmployeeNumber.get(employeeNumber) || [];\n\n if (matches.length !== 1) continue;\n\n const employee = matches[0];\n const key = employeeKey(employee);\n\n if (key) {\n employeesByStrongIdentifier.set(\n key,\n {\n employee,\n employee_number: employeeNumber,\n }\n );\n }\n }\n\n if (employeesByStrongIdentifier.size === 1) {\n const resolved =\n employeesByStrongIdentifier.values().next().value;\n\n return {\n found: true,\n matched_by: 'bank_or_payroll_employee_number',\n confidence: 1,\n employee: resolved.employee,\n employee_key: employeeKey(resolved.employee),\n bamboo_alias:\n clean(resolved.employee.full_name) ||\n clean(resolved.employee.displayName) ||\n '',\n matched_employee_number:\n resolved.employee_number,\n };\n }\n\n const holderName = clean(row.bank_account_holder);\n const fileName = clean(row.bank_name_file);\n\n // El nombre legal del cuentahabiente es la evidencia bancaria más fuerte.\n // Si identifica exactamente a una persona que pertenecía al período,\n // prevalece sobre un nombre abreviado que pueda coincidir con un perfil\n // histórico distinto (caso real: Suyapa Palma).\n const holderDecision = holderName\n ? precomputedDecision(holderName)\n : null;\n\n const holderMatchedBy =\n clean(holderDecision?.matched_by).toLowerCase();\n\n const holderIsExactUnique =\n holderDecision?.found === true &&\n (\n holderDecision.confidence >= 0.999 ||\n holderMatchedBy.includes('exact_precomputed_name') ||\n holderMatchedBy.includes('confirmed_alias_catalog')\n );\n\n // \"Banco sin Bamboo\" valida existencia en BambooHR, no que la persona\n // siguiera activa durante toda la quincena. Un finiquito/pago final puede\n // ocurrir después de la fecha de terminación. Por eso un nombre legal\n // completo y único del cuentahabiente es suficiente evidencia.\n if (holderIsExactUnique) {\n return {\n ...holderDecision,\n matched_by:\n `${holderDecision.matched_by || 'precomputed_name'}_holder_exact`,\n };\n }\n\n if (\n holderDecision?.found &&\n holderDecision.employee?.overlaps_period === true\n ) {\n return {\n ...holderDecision,\n matched_by:\n `${holderDecision.matched_by || 'precomputed_name'}_holder_in_period`,\n };\n }\n\n const names = Array.from(\n new Set(\n [\n holderName,\n fileName,\n ...linkedPayrollNames.map(clean),\n ].filter(Boolean)\n )\n );\n\n const decisions = names\n .map(precomputedDecision)\n .filter(Boolean);\n\n if (!decisions.length) {\n return {\n found: false,\n reason: 'no_confident_name_match',\n };\n }\n\n // Antes de declarar conflicto, descartar perfiles históricos que no\n // pertenecen al período cuando existe una única persona vigente/en período.\n const inPeriodByKey = new Map();\n\n for (const decision of decisions) {\n if (decision.employee?.overlaps_period !== true) continue;\n\n const current =\n inPeriodByKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n inPeriodByKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const inPeriodRanked =\n Array.from(inPeriodByKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (inPeriodRanked.length === 1) {\n return {\n ...inPeriodRanked[0],\n matched_by:\n `${inPeriodRanked[0].matched_by || 'precomputed_name'}_unique_in_period`,\n };\n }\n\n if (\n inPeriodRanked.length >= 2 &&\n (\n inPeriodRanked[0].confidence -\n inPeriodRanked[1].confidence\n ) >= 0.08\n ) {\n return inPeriodRanked[0];\n }\n\n const byKey = new Map();\n\n for (const decision of decisions) {\n const current =\n byKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n byKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const ranked =\n Array.from(byKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (ranked.length === 1) {\n return ranked[0];\n }\n\n if (\n (\n ranked[0].confidence -\n ranked[1].confidence\n ) >= 0.08\n ) {\n return ranked[0];\n }\n\n return {\n found: false,\n reason: 'conflicting_precomputed_name_matches',\n candidates: ranked.slice(0, 3),\n };\n}\n\nconst usedBankKeys = new Set();\nconst matchedPayrollIndexes = new Set();\nconst matchedPairs = [];\nconst bankIndexByKey = new Map();\n\n// Índice por centavos para que la pasada principal no compare cada fila de\n// nómina contra todas las transacciones del banco. Esto preserva exactamente\n// la lógica original: la primera pasada ya exigía el mismo monto al centavo.\nconst bankIndexesByAmountCents = new Map();\n\nfunction amountCents(value) {\n return Math.round(roundMoney(value) * 100);\n}\n\nbankRows.forEach((row, index) => {\n const key = bankRowKey(row, index);\n bankIndexByKey.set(key, index);\n\n const amountKey = `${clean(row.currency || 'USD').toUpperCase()}|${amountCents(row.amount)}`;\n const bucket = bankIndexesByAmountCents.get(amountKey) || [];\n bucket.push(index);\n bankIndexesByAmountCents.set(amountKey, bucket);\n});\n\nfunction availableNameCandidates(payroll, requireExactAmount) {\n const candidates = [];\n\n // La pasada exacta solo inspecciona filas con el mismo importe. La segunda\n // pasada conserva el barrido completo y el mismo criterio de persona fuerte\n // + menor diferencia que tenía el workflow validado.\n let candidateIndexes = null;\n\n if (requireExactAmount) {\n const currency =\n clean(payroll.currency || 'USD')\n .toUpperCase();\n\n const center =\n amountCents(payroll.payroll_amount);\n\n const indexSet = new Set();\n\n for (\n let delta = -MONEY_TOLERANCE_CENTS;\n delta <= MONEY_TOLERANCE_CENTS;\n delta++\n ) {\n const bucket =\n bankIndexesByAmountCents.get(\n `${currency}|${center + delta}`\n ) || [];\n\n for (const bankIndex of bucket) {\n indexSet.add(bankIndex);\n }\n }\n\n candidateIndexes =\n Array.from(indexSet);\n }\n\n const inspectBank = (bankIndex) => {\n const bank = bankRows[bankIndex];\n if (!bank) return;\n\n const key = bankRowKey(bank, bankIndex);\n if (usedBankKeys.has(key)) return;\n if (clean(payroll.currency || 'USD').toUpperCase() !== clean(bank.currency || 'HNL').toUpperCase()) return;\n\n if (requireExactAmount && !sameCent(payroll.payroll_amount, bank.amount)) return;\n\n const periodScore = bankCandidatePeriodScore(payroll, bank);\n if (periodScore < 0) return;\n\n const score = bestNameScore(payroll.employee_name, bank);\n if (score <= 0) return;\n\n candidates.push({\n bank,\n bankIndex,\n key,\n score,\n periodScore,\n amountDiff: Math.abs(moneyDiff(payroll.payroll_amount, bank.amount)),\n });\n };\n\n if (candidateIndexes) {\n for (const bankIndex of candidateIndexes) {\n inspectBank(bankIndex);\n }\n } else {\n for (let bankIndex = 0; bankIndex < bankRows.length; bankIndex++) {\n inspectBank(bankIndex);\n }\n }\n\n candidates.sort((a, b) => {\n if (b.periodScore !== a.periodScore) return b.periodScore - a.periodScore;\n if (b.score !== a.score) return b.score - a.score;\n if (a.amountDiff !== b.amountDiff) return a.amountDiff - b.amountDiff;\n return a.bankIndex - b.bankIndex;\n });\n\n return candidates;\n}\n\n// Primera pasada: persona + monto exacto. Es la evidencia más fuerte y evita\n// que extraordinarias/vacaciones de una misma persona se mezclen entre sí.\npayrollRows.forEach((payroll, payrollIndex) => {\n const candidates = availableNameCandidates(payroll, true);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\n// Segunda pasada: persona fuerte aunque el monto sea distinto. Solo se toma\n// una contraparte bancaria por fila de nómina y se prioriza la menor diferencia.\npayrollRows.forEach((payroll, payrollIndex) => {\n if (matchedPayrollIndexes.has(payrollIndex)) return;\n const candidates = availableNameCandidates(payroll, false);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\nconst linkedPayrollNamesByBankKey = new Map();\nconst linkedPayrollEmployeeNumbersByBankKey = new Map();\n\nfor (const pair of matchedPairs) {\n const currentNames =\n linkedPayrollNamesByBankKey.get(pair.key) || [];\n\n currentNames.push(pair.payroll.employee_name);\n\n linkedPayrollNamesByBankKey.set(\n pair.key,\n currentNames\n );\n\n const employeeNumber =\n normalizeEmployeeNumber(\n pair.payroll.employee_number ||\n pair.payroll.employeeNumber ||\n ''\n );\n\n if (employeeNumber) {\n const currentNumbers =\n linkedPayrollEmployeeNumbersByBankKey.get(\n pair.key\n ) || [];\n\n currentNumbers.push(employeeNumber);\n\n linkedPayrollEmployeeNumbersByBankKey.set(\n pair.key,\n currentNumbers\n );\n }\n}\n\n// Filas bancarias del período de Puerto Rico.\n// Toda fila emparejada entra siempre. Para una fila no emparejada se usa la\n// fecha/concepto del plano para evitar arrastrar accidentalmente otra quincena.\nconst includedBankRows = [];\nconst excludedBankRows = [];\n\nfor (let index = 0; index < bankRows.length; index++) {\n const row = bankRows[index];\n const key = bankRowKey(row, index);\n\n if (usedBankKeys.has(key)) {\n includedBankRows.push({ row, key, index, inclusion: 'matched_payroll' });\n continue;\n }\n\n const periodInfo = periodInfoForBankRow(row);\n if (periodInfo.known && !periodInfo.inPeriod) {\n excludedBankRows.push({ row, key, reason: 'outside_selected_payroll_period' });\n continue;\n }\n\n includedBankRows.push({ row, key, index, inclusion: 'unmatched_current_period' });\n}\n\nconst includedBankKeySet = new Set(includedBankRows.map((entry) => entry.key));\n\nconst coreRows = [];\nconst wrongAccountGroups = new Map();\nfor (const pair of matchedPairs) {\n if (!includedBankKeySet.has(pair.key)) continue;\n const payrollAmount = roundMoney(pair.payroll.payroll_amount);\n const bankAmount = roundMoney(pair.bank.amount);\n const difference = moneyDiff(payrollAmount, bankAmount);\n const currency = clean(pair.payroll.currency || pair.bank.currency || 'USD').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAccount = normalizeAccount(pair.payroll.account || pair.payroll.payroll_account);\n const bankAccount = normalizeAccount(pair.bank.account);\n const accountMismatch = Boolean(payrollAccount && bankAccount && payrollAccount !== bankAccount);\n if (accountMismatch) {\n const groupKey = `${normalizeName(pair.payroll.employee_name)}|${currency}|${payrollAccount}|${bankAccount}`;\n const current = wrongAccountGroups.get(groupKey) || {\n id: `pr_wrong_account_${wrongAccountGroups.size + 1}`,\n employee: pair.payroll.employee_name,\n employee_name: pair.payroll.employee_name,\n employeeNumber: pair.payroll.employee_number || '', employee_number: pair.payroll.employee_number || '',\n account: bankAccount, payrollAccount, payroll_account: payrollAccount, bankAccount, bank_account: bankAccount,\n currency, payrollAmount:0, payroll_amount:0, bankAmount:0, bank_amount:0, difference:0,\n status:'Pendiente revisión', category:'posible_cuenta_mal_digitada', subcategory:'nombre_y_monto_coinciden_cuenta_diferente',\n observation:`El nombre y el monto coinciden, pero la cuenta de nómina (${payrollAccount}) es diferente a la cuenta del banco (${bankAccount}).`,\n source_rows:[], source_sheets:new Set(), source_files:new Set(), shipment_numbers:new Set(), plan_numbers:new Set(),\n bank_name_file: pair.bank.bank_name_file || '', bank_account_holder: pair.bank.bank_account_holder || ''\n };\n current.payrollAmount=roundMoney(current.payrollAmount+payrollAmount); current.payroll_amount=current.payrollAmount;\n current.bankAmount=roundMoney(current.bankAmount+bankAmount); current.bank_amount=current.bankAmount; current.difference=moneyDiff(current.payrollAmount,current.bankAmount);\n current.source_rows.push(pair.payroll); if(pair.payroll.source_sheet)current.source_sheets.add(pair.payroll.source_sheet); if(pair.bank.source_file)current.source_files.add(pair.bank.source_file); if(pair.bank.shipment_number)current.shipment_numbers.add(pair.bank.shipment_number); if(pair.bank.plan_number)current.plan_numbers.add(pair.bank.plan_number);\n wrongAccountGroups.set(groupKey,current); continue;\n }\n const exact = sameCent(payrollAmount, bankAmount);\n coreRows.push({id:`pr_match_${pair.payrollIndex+1}_${pair.bankIndex+1}`,employee:pair.payroll.employee_name,employee_name:pair.payroll.employee_name,employeeNumber:pair.payroll.employee_number||'',employee_number:pair.payroll.employee_number||'',account:bankAccount||payrollAccount,payrollAccount:payrollAccount,payroll_account:payrollAccount,bankAccount:bankAccount,bank_account:bankAccount,currency,payrollAmount,payroll_amount:payrollAmount,bankAmount,bank_amount:bankAmount,difference,status:exact?'Coincide':'Pendiente revisión',category:exact?'coincidencia':'discrepancia',subcategory:exact?'persona_cuenta_y_monto_exactos':'persona_y_cuenta_coinciden_monto_diferente',observation:exact?'Coincidencia de persona, cuenta y monto.':difference>0?'Se pagó de menos.':'Se pagó de más.',source_kind:pair.payroll.source_kind||'',source_sheet:pair.payroll.source_sheet||'',source_file:pair.bank.source_file||'',shipment_number:pair.bank.shipment_number||'',plan_number:pair.bank.plan_number||'',plan_label:pair.bank.plan_label||'',reference:pair.bank.reference||'',bank_name_file:pair.bank.bank_name_file||'',bank_account_holder:pair.bank.bank_account_holder||''});\n}\nfor (let payrollIndex=0; payrollIndex({...row,source_sheets:Array.from(row.source_sheets),source_files:Array.from(row.source_files),shipment_numbers:Array.from(row.shipment_numbers),plan_numbers:Array.from(row.plan_numbers),source_sheet:Array.from(row.source_sheets).join(' / '),source_file:Array.from(row.source_files).join(' / '),shipment_number:Array.from(row.shipment_numbers).join(' / '),plan_number:Array.from(row.plan_numbers).join(' / ')}));\ncoreRows.push(...wrongAccountRows);\n\n// Banco sin Nómina se conserva a nivel de TRANSACCIÓN.\n// Una misma persona/cuenta puede tener más de un pago bancario legítimamente\n// separado; agruparlos reduce artificialmente el número de hallazgos.\nconst bankWithoutPayrollRows = [];\n\nfor (const entry of includedBankRows) {\n if (usedBankKeys.has(entry.key)) continue;\n\n const row = entry.row;\n const displayName =\n bankDisplayName(row);\n\n const rowCurrency =\n clean(row.currency || 'USD')\n .toUpperCase() === 'USD'\n ? 'USD'\n : 'HNL';\n\n const bankAmount =\n roundMoney(row.amount || 0);\n\n bankWithoutPayrollRows.push({\n id:\n `pr_bank_without_payroll_${entry.index + 1}`,\n employee: displayName,\n employee_name: displayName,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount,\n bank_amount: bankAmount,\n difference:\n roundMoney(0 - bankAmount),\n status: 'Pendiente revisión',\n category: 'banco_sin_nomina',\n subcategory:\n 'pago_banco_sin_registro_nomina',\n observation:\n 'Pago bancario sin registro equivalente en las planillas cargadas.',\n bank_name_file:\n row.bank_name_file || '',\n bank_account_holder:\n row.bank_account_holder || '',\n source_file:\n row.source_file || '',\n shipment_number:\n row.shipment_number || '',\n plan_number:\n row.plan_number || '',\n reference:\n row.reference || '',\n row_number:\n row.row_number || null,\n source_rows: [row],\n });\n}\n\ncoreRows.push(...bankWithoutPayrollRows);\n\nconst nameDifferenceRows = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const rowCurrency=clean(row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';\n if (!clean(row.bank_name_file) || !clean(row.bank_account_holder)) continue;\n if (bankNameLooksConsistent(row.bank_name_file, row.bank_account_holder)) continue;\n nameDifferenceRows.push({\n id: `pr_bank_name_difference_${entry.index + 1}`,\n employee: row.bank_name_file,\n employee_name: row.bank_name_file,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount: roundMoney(row.amount),\n bank_amount: roundMoney(row.amount),\n difference: 0,\n status: 'Pendiente revisión',\n category: 'diferencia_nombre_banco',\n subcategory: 'nombre_archivo_vs_cuentahabiente',\n observation: `El Nombre en Archivo (${row.bank_name_file}) no coincide con el Nombre del Cuentahabiente (${row.bank_account_holder}).`,\n bank_name_file: row.bank_name_file,\n bank_account_holder: row.bank_account_holder,\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\n\nconst bankWithoutBambooMap = new Map();\nconst bambooMatchDetails = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const linkedNames =\n linkedPayrollNamesByBankKey.get(entry.key) || [];\n\n const linkedEmployeeNumbers =\n linkedPayrollEmployeeNumbersByBankKey.get(\n entry.key\n ) || [];\n\n const decision = resolveBamboo(\n row,\n linkedNames,\n linkedEmployeeNumbers\n );\n bambooMatchDetails.push({\n bank_row_key: entry.key,\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n account: row.account || '',\n amount: row.amount || 0,\n linked_payroll_names: linkedNames,\n ...decision,\n });\n if (decision.found) continue;\n\n const displayName = bankDisplayName(row);\n const rowCurrency=clean(row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';\n const groupingKey = `${normalizeAccount(row.account)}|${normalizeName(displayName)}|${rowCurrency}`;\n const current = bankWithoutBambooMap.get(groupingKey) || {\n id: `pr_bank_without_bamboo_${bankWithoutBambooMap.size + 1}`,\n bank_name_file: row.bank_name_file || displayName,\n bank_account_holder: row.bank_account_holder || '',\n employee_name: row.bank_name_file || displayName,\n account: row.account || '',\n currency: rowCurrency,\n bank_amount: 0,\n amount: 0,\n status: 'Pendiente revisión',\n category: 'banco_sin_bamboo',\n shipment_numbers: new Set(),\n references: new Set(),\n source_files: new Set(),\n source_rows: [],\n };\n current.bank_amount = roundMoney(current.bank_amount + Number(row.amount || 0));\n current.amount = current.bank_amount;\n if (row.shipment_number) current.shipment_numbers.add(row.shipment_number);\n if (row.reference) current.references.add(row.reference);\n if (row.source_file) current.source_files.add(row.source_file);\n current.source_rows.push(row);\n bankWithoutBambooMap.set(groupingKey, current);\n}\n\nconst bankWithoutBamboo = Array.from(bankWithoutBambooMap.values()).map((row) => ({\n ...row,\n shipment_numbers: Array.from(row.shipment_numbers),\n shipment_number: Array.from(row.shipment_numbers).join(' / '),\n references: Array.from(row.references),\n source_files: Array.from(row.source_files),\n source_file: Array.from(row.source_files).join(' / '),\n difference: roundMoney(0 - row.bank_amount),\n}));\n\nfunction priority(row) {\n const category = clean(row.category).toLowerCase();\n if (category === 'posible_cuenta_mal_digitada') return 1;\n if (category === 'discrepancia') return 2;\n if (category === 'banco_sin_nomina') return 3;\n if (category === 'diferencia_nombre_banco') return 5;\n if (category === 'coincidencia') return 99;\n return 50;\n}\n\nconst rowsFinales = [...coreRows, ...nameDifferenceRows].sort((a, b) => {\n const p = priority(a) - priority(b);\n if (p !== 0) return p;\n return clean(a.employee_name).localeCompare(clean(b.employee_name), 'es');\n});\n\nconst coincidencias = coreRows.filter((row) => row.category === 'coincidencia').length;\nconst discrepancias = coreRows.filter((row) => row.category === 'discrepancia').length;\nconst bancoSinNomina = bankWithoutPayrollRows.length;\nconst diferenciasNombreBanco = nameDifferenceRows.length;\nconst posiblesCuentasMalDigitadas = wrongAccountRows.length;\nconst pendientes = discrepancias + bancoSinNomina + diferenciasNombreBanco;\nconst totalNominaByCurrency = {HNL:0,USD:0};\nfor(const row of payrollRows){const c=clean(row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';totalNominaByCurrency[c]=roundMoney(totalNominaByCurrency[c]+Number(row.payroll_amount||0));}\nconst totalBancoByCurrency = {HNL:0,USD:0};\nfor(const entry of includedBankRows){const c=clean(entry.row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';totalBancoByCurrency[c]=roundMoney(totalBancoByCurrency[c]+Number(entry.row.amount||0));}\nconst diferenciaByCurrency={HNL:moneyDiff(totalNominaByCurrency.HNL,totalBancoByCurrency.HNL),USD:moneyDiff(totalNominaByCurrency.USD,totalBancoByCurrency.USD)};\nconst totalNomina=totalNominaByCurrency.HNL || totalNominaByCurrency.USD;\nconst totalBanco=totalBancoByCurrency.HNL || totalBancoByCurrency.USD;\nconst diferenciaTotal=totalNominaByCurrency.HNL||totalBancoByCurrency.HNL?diferenciaByCurrency.HNL:diferenciaByCurrency.USD;\n\nconst bambooSummaryCompact = { ...(data.bamboo || {}) };\ndelete bambooSummaryCompact.employees;\n\nreturn [{\n json: {\n ok: true,\n stage: 'cruce_nomina_banco_puerto_rico',\n errors: [],\n metadata: data.metadata || {},\n summary: {\n coincidencias,\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo: bankWithoutBamboo.length,\n nominaSinCuenta: 0,\n diferenciasNombreBanco,\n posiblesCuentasMalDigitadas,\n pendientes,\n filasNominaValidas: Number(data.payroll?.base_rows_count || data.payroll?.valid_rows_count || 0),\n filasNominaSinCuenta: 0,\n suplementosPotenciales: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaAplicados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAplicados: 0,\n suplementosNominaAdjuntados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAdjuntados: Number(data.payroll?.unattached_supplements_count || 0),\n reconciliacionesExactasFinales: coincidencias,\n cuentasNominaAgrupadas: 0,\n transaccionesBanco: includedBankRows.length,\n cuentasBancoAgrupadas: new Set(includedBankRows.map((entry) => normalizeAccount(entry.row.account)).filter(Boolean)).size,\n empleadosBambooPR: Number(data.bamboo?.puerto_rico_count || 0),\n empleadosBambooEnPeriodo: Number(data.bamboo?.active_in_period_count || 0),\n bambooPaginasDescargadas: Number(data.bamboo?.pages_fetched || 0),\n bambooEmpleadosEsperados: Number(data.bamboo?.expected_total || 0),\n bambooDescargaCompleta: Boolean(data.bamboo?.fetch_complete),\n bambooValidacionDisponible: bambooValidationAvailable,\n totalNominaBase: roundMoney(data.payroll?.base_total || 0),\n totalSuplementosAplicados: roundMoney(data.payroll?.supplements_total || 0),\n totalNomina,\n totalBanco,\n diferenciaTotal,\n totalNominaPorMoneda: totalNominaByCurrency,\n totalBancoPorMoneda: totalBancoByCurrency,\n diferenciaTotalPorMoneda: diferenciaByCurrency,\n },\n rows: rowsFinales,\n bankWithoutBamboo,\n nameDifferences: nameDifferenceRows,\n bambooSummary: bambooSummaryCompact,\n reportUrl: null,\n debug: {\n matched_pairs_count: matchedPairs.length,\n included_bank_rows_count: includedBankRows.length,\n excluded_bank_rows_count: excludedBankRows.length,\n excluded_bank_rows_preview: excludedBankRows.slice(0, 100),\n validated_bamboo_aliases_loaded: validatedBambooAliases.length,\n bamboo_matches: bambooMatchDetails,\n bamboo_validation_available: bambooValidationAvailable,\n banco_sin_bamboo: bankWithoutBamboo,\n payroll_preview: payrollRows.slice(0, 20),\n bank_included_preview: includedBankRows.slice(0, 20).map((entry) => entry.row),\n },\n },\n}];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -120, + 5200 + ], + "id": "f7befa53-9b77-44d3-b0fb-e456328163e5", + "name": "Cruzar Nómina vs Banco" + }, + { + "parameters": { + "jsCode": "const data = $input.first().json || {};\n\nfunction normalizeText(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction readableCategory(value) {\n const map = {\n coincidencia: 'Coincidencia',\n discrepancia: 'Discrepancia',\n posible_cuenta_mal_digitada: 'Posible cuenta mal digitada',\n banco_sin_nomina: 'Banco sin nómina',\n banco_sin_bamboo: 'Banco sin Bamboo',\n nomina_sin_cuenta: 'Nómina sin cuenta',\n diferencia_nombre_banco: 'Diferencia nombre banco',\n };\n\n return map[value] || normalizeText(value).replace(/_/g, ' ');\n}\n\nfunction firstValue(value) {\n if (Array.isArray(value)) {\n return value.filter(Boolean).join(' / ');\n }\n\n return normalizeText(value);\n}\n\nfunction formatPeriodEnd(value) {\n const raw = normalizeText(value);\n\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(raw)) {\n return raw;\n }\n\n const [year, month, day] = raw.split('-');\n const monthNames = {\n '01': 'ene',\n '02': 'feb',\n '03': 'mar',\n '04': 'abr',\n '05': 'may',\n '06': 'jun',\n '07': 'jul',\n '08': 'ago',\n '09': 'sep',\n '10': 'oct',\n '11': 'nov',\n '12': 'dic',\n };\n\n return `${day}-${monthNames[month] || month}-${year}`;\n}\n\nfunction mainReportSense(row, difference) {\n const category = normalizeText(row.category).toLowerCase();\n const subcategory = normalizeText(row.subcategory).toLowerCase();\n\n if (category === 'posible_cuenta_mal_digitada') {\n return 'Revisar cuenta';\n }\n\n if (\n subcategory === 'nomina_con_cuenta_sin_pago_banco' ||\n (Number(row.bank_amount ?? row.bankAmount ?? 0) === 0 &&\n Number(row.payroll_amount ?? row.payrollAmount ?? 0) > 0)\n ) {\n return 'No aparece pagado en banco';\n }\n\n if (difference > 0) {\n return 'Se pagó de menos';\n }\n\n if (difference < 0) {\n return 'Se pagó de más';\n }\n\n return 'Revisar';\n}\n\nconst metadata = data.metadata || {};\nconst summary = data.summary || {};\nconst rows = Array.isArray(data.rows) ? data.rows : [];\nconst bankWithoutBamboo = Array.isArray(data.bankWithoutBamboo)\n ? data.bankWithoutBamboo\n : [];\n\nconst periodLabel =\n metadata.period_label ||\n `${metadata.year || ''}-${metadata.month || ''}-${metadata.period_type || ''}`;\n\nconst spreadsheetTitle =\n `Cruce de Cuentas GLM PR - ${periodLabel}`;\n\nconst mainReportSubtitle =\n `Diferencias de Monto Nómina vs. Banco · Puerto Rico · ${\n formatPeriodEnd(metadata.period_end || '')\n }`;\n\nconst bancoSinBambooSubtitle =\n `Pagos en banco sin empleado identificado en BambooHR · Puerto Rico · ${\n formatPeriodEnd(metadata.period_end || '')\n }`;\n\nconst cuentaMalDigitadaSubtitle =\n `Cuenta Mal Digitada en Nómina · Puerto Rico · ${\n formatPeriodEnd(metadata.period_end || '')\n }`;\n\nconst cuentaMalDigitadaCases = rows.filter(\n (row) => row.category === 'posible_cuenta_mal_digitada'\n);\n\nconst hasCuentaMalDigitada =\n cuentaMalDigitadaCases.length > 0;\n\nconst sheetIds = {\n nominaVsBanco: 101,\n bancoSinNomina: 102,\n bancoSinBamboo: 103,\n diferenciasNombreBanco: 104,\n cuentaMalDigitada: 105,\n resumen: 106,\n};\n\nconst sheetTitles = {\n nominaVsBanco: '01 Nómina vs Banco',\n bancoSinNomina: '02 Banco sin Nómina',\n bancoSinBamboo: '03 Banco sin Bamboo',\n diferenciasNombreBanco: '04 Diferencias nombre banco',\n cuentaMalDigitada: '05 Cuenta Mal Digitada',\n resumen: hasCuentaMalDigitada\n ? '06 Resumen'\n : '05 Resumen',\n};\n\nconst nominaVsBancoHeader = [\n '#',\n 'Empleado',\n 'Cuenta',\n 'Monto en Nómina',\n 'Monto en Banco',\n 'Diferencia',\n 'Sentido',\n 'Estado',\n 'Resolución',\n];\n\nconst mainReportRows = rows\n .filter((row) => row.category === 'discrepancia')\n .map((row, index) => {\n const payrollAmount = roundMoney(\n row.payroll_amount ?? row.payrollAmount ?? 0\n );\n const bankAmount = roundMoney(\n row.bank_amount ?? row.bankAmount ?? 0\n );\n const difference = roundMoney(\n row.difference ?? (payrollAmount - bankAmount)\n );\n\n return [\n index + 1,\n normalizeText(row.employee_name || row.employee || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.payroll_account ||\n row.payrollAccount ||\n row.account ||\n ''\n ),\n payrollAmount,\n bankAmount,\n difference,\n mainReportSense(row, difference),\n normalizeText(row.status || 'Riesgo').toUpperCase(),\n '',\n ];\n });\n\nconst nominaVsBancoValues = [\n [\n 'GOMEZLEE MARKETING',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n [\n mainReportSubtitle,\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n ['', '', '', '', '', '', '', '', ''],\n nominaVsBancoHeader,\n ...mainReportRows,\n];\n\nconst bancoSinNominaHeader = [\n 'Empleado Banco',\n 'Cuenta Banco',\n 'Moneda',\n 'Monto Banco',\n 'Estado',\n 'Observación',\n 'Resolución',\n];\n\nconst bancoSinNominaRows = rows\n .filter((row) => row.category === 'banco_sin_nomina')\n .map((row) => [\n normalizeText(row.employee_name || row.employee || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n ),\n normalizeText(row.currency || 'USD'),\n roundMoney(row.bank_amount ?? row.bankAmount ?? 0),\n normalizeText(row.status || ''),\n normalizeText(row.observation || ''),\n '',\n ]);\n\nconst bancoSinBambooHeader = [\n '#',\n 'Nombre en banco',\n 'Nombre del cuentahabiente',\n 'Cuenta',\n 'Monto en banco',\n 'Número de envío',\n 'Estado',\n 'Resolución',\n];\n\nconst bancoSinBambooRows = bankWithoutBamboo.map((row, index) => [\n index + 1,\n normalizeText(row.bank_name_file || row.employee_name || ''),\n normalizeText(row.bank_account_holder || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n ),\n roundMoney(row.bank_amount ?? row.bankAmount ?? 0),\n firstValue(row.shipment_numbers || row.shipment_number || ''),\n 'PENDIENTE REVISIÓN',\n '',\n]);\n\nconst bancoSinBambooValues = [\n [\n 'GOMEZLEE MARKETING',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n [\n bancoSinBambooSubtitle,\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n ['', '', '', '', '', '', '', ''],\n bancoSinBambooHeader,\n ...bancoSinBambooRows,\n];\n\nconst diferenciasNombreHeader = [\n 'Nombre en Archivo',\n 'Nombre del Cuentahabiente',\n 'Cuenta Destino',\n 'Moneda',\n 'Monto',\n 'Número de envío',\n 'Número de plan',\n 'Archivo',\n 'Estado',\n 'Observación',\n 'Resolución',\n];\n\nconst diferenciasNombreRows = rows\n .filter((row) => row.category === 'diferencia_nombre_banco')\n .map((row) => [\n normalizeText(row.bank_name_file || row.employee_name || ''),\n normalizeText(row.bank_account_holder || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n ),\n normalizeText(row.currency || 'USD'),\n roundMoney(row.bank_amount ?? row.bankAmount ?? 0),\n normalizeText(row.shipment_number || ''),\n normalizeText(row.plan_number || ''),\n normalizeText(row.source_file || ''),\n normalizeText(row.status || ''),\n normalizeText(row.observation || ''),\n '',\n ]);\n\n\nfunction accountValues(value) {\n const values = Array.isArray(value)\n ? value\n : String(value ?? '')\n .split(/\\s*(?:\\/|;|,|\\by\\b)\\s*/i);\n\n return values\n .map((item) =>\n String(item ?? '')\n .replace(/\\.0$/g, '')\n .replace(/\\D/g, '')\n .trim()\n )\n .filter((item) =>\n item.length >= 7 &&\n !/^0+$/.test(item)\n );\n}\n\nfunction payrollAccountsForWrongAccount(row) {\n const candidates = [\n row.payroll_account,\n row.payrollAccount,\n ...(Array.isArray(row.source_rows)\n ? row.source_rows.flatMap((sourceRow) => [\n sourceRow.account,\n sourceRow.payroll_account,\n sourceRow.payrollAccount,\n ])\n : []),\n ];\n\n return Array.from(new Set(\n candidates.flatMap(accountValues)\n ));\n}\n\nfunction bankAccountForWrongAccount(row) {\n return firstValue(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n );\n}\n\nfunction wrongAccountTotalStatus(row) {\n const currency = normalizeText(row.currency || 'USD').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAmount = roundMoney(\n row.payroll_amount ?? row.payrollAmount ?? 0\n );\n const bankAmount = roundMoney(\n row.bank_amount ?? row.bankAmount ?? 0\n );\n const difference = roundMoney(\n payrollAmount - bankAmount\n );\n\n if (Math.abs(difference) <= 0.02) {\n return 'El total de nómina coincide con el total del banco.';\n }\n\n if (difference > 0) {\n return (\n `El total de nómina supera el total del banco por ` +\n `${currency} ${Math.abs(difference).toLocaleString('en-US', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}.`\n );\n }\n\n return (\n `El total pagado por el banco supera el total de nómina por ` +\n `${currency} ${Math.abs(difference).toLocaleString('en-US', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}.`\n );\n}\n\nfunction wrongAccountFinding(row) {\n const existing = normalizeText(row.observation || '');\n\n if (existing) {\n return existing;\n }\n\n const payrollAccounts =\n payrollAccountsForWrongAccount(row);\n const bankAccount =\n bankAccountForWrongAccount(row);\n\n return (\n `El empleado presenta una posible inconsistencia entre ` +\n `la cuenta registrada en nómina ` +\n `(${payrollAccounts.join(' y ') || 'sin cuenta identificada'}) ` +\n `y la cuenta utilizada por el banco ` +\n `(${bankAccount || 'sin cuenta identificada'}).`\n );\n}\n\nconst cuentaMalDigitadaHeader = [\n '#',\n 'Campo',\n 'Detalle',\n 'Resolución',\n];\n\nconst cuentaMalDigitadaRows = [];\n\nfor (\n let index = 0;\n index < cuentaMalDigitadaCases.length;\n index++\n) {\n const row = cuentaMalDigitadaCases[index];\n const payrollAccounts =\n payrollAccountsForWrongAccount(row);\n const bankAccount =\n bankAccountForWrongAccount(row);\n\n const fields = [\n [\n 'Empleado',\n normalizeText(\n row.employee_name ||\n row.employee ||\n ''\n ),\n ],\n [\n 'Cuentas registradas en las hojas de nómina',\n payrollAccounts.join(' y ') ||\n 'No se identificó una cuenta válida en la nómina.',\n ],\n [\n 'Cuenta utilizada por el banco',\n bankAccount ||\n 'No se identificó una cuenta válida en el banco.',\n ],\n [\n 'Estado del total',\n wrongAccountTotalStatus(row),\n ],\n [\n 'Hallazgo',\n wrongAccountFinding(row),\n ],\n [\n 'Clasificación',\n 'Posible cuenta mal digitada — revisar y unificar la cuenta en las hojas de nómina.',\n ],\n ];\n\n fields.forEach((field, fieldIndex) => {\n cuentaMalDigitadaRows.push([\n fieldIndex === 0 ? index + 1 : '',\n field[0],\n field[1],\n '',\n ]);\n });\n}\n\nconst cuentaMalDigitadaValues = [\n [\n 'GOMEZLEE MARKETING',\n '',\n '',\n '',\n ],\n [\n cuentaMalDigitadaSubtitle,\n '',\n '',\n '',\n ],\n ['', '', '', ''],\n cuentaMalDigitadaHeader,\n ...cuentaMalDigitadaRows,\n];\n\nconst resumenHeader = ['Indicador', 'Valor'];\nconst resumenRows = [\n ['Período', periodLabel],\n ['Coincidencias', Number(summary.coincidencias || 0)],\n ['Discrepancias', Number(summary.discrepancias || 0)],\n ['Banco sin nómina', Number(summary.bancoSinNomina || 0)],\n ['Banco sin Bamboo', Number(summary.bancoSinBamboo || 0)],\n ['Nómina sin cuenta', Number(summary.nominaSinCuenta || 0)],\n [\n 'Diferencias nombre banco',\n Number(summary.diferenciasNombreBanco || 0),\n ],\n [\n 'Posibles cuentas mal digitadas',\n Number(summary.posiblesCuentasMalDigitadas || 0),\n ],\n ['Pendientes cruce principal', Number(summary.pendientes || 0)],\n [\n 'Empleados BambooHR Puerto Rico',\n Number(summary.empleadosBambooPR || 0),\n ],\n [\n 'Empleados BambooHR en el período',\n Number(summary.empleadosBambooEnPeriodo || 0),\n ],\n ['Filas válidas de nómina', Number(summary.filasNominaValidas || 0)],\n [\n 'Suplementos adjuntados',\n Number(summary.suplementosNominaAdjuntados || 0),\n ],\n [\n 'Suplementos sin coincidencia',\n Number(summary.suplementosNominaNoAdjuntados || 0),\n ],\n ['Transacciones bancarias', Number(summary.transaccionesBanco || 0)],\n ['Total nómina', `USD ${roundMoney(summary.totalNominaPorMoneda?.USD || summary.totalNomina || 0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`],\n ['Total banco', `USD ${roundMoney(summary.totalBancoPorMoneda?.USD || summary.totalBanco || 0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`],\n ['Diferencia total', `USD ${roundMoney(summary.diferenciaTotalPorMoneda?.USD || summary.diferenciaTotal || 0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`],\n];\n\nfunction buildSheetValues(header, bodyRows) {\n return [header, ...bodyRows];\n}\n\nconst valueData = [\n {\n range: `'${sheetTitles.nominaVsBanco}'!A1:I`,\n values: nominaVsBancoValues,\n },\n {\n range: `'${sheetTitles.bancoSinNomina}'!A1:G`,\n values: buildSheetValues(\n bancoSinNominaHeader,\n bancoSinNominaRows\n ),\n },\n {\n range: `'${sheetTitles.bancoSinBamboo}'!A1:H`,\n values: bancoSinBambooValues,\n },\n {\n range: `'${sheetTitles.diferenciasNombreBanco}'!A1:K`,\n values: buildSheetValues(\n diferenciasNombreHeader,\n diferenciasNombreRows\n ),\n },\n ...(hasCuentaMalDigitada\n ? [\n {\n range:\n `'${sheetTitles.cuentaMalDigitada}'!A1:D`,\n values: cuentaMalDigitadaValues,\n },\n ]\n : []),\n {\n range: `'${sheetTitles.resumen}'!A1:B`,\n values: buildSheetValues(\n resumenHeader,\n resumenRows\n ),\n },\n];\n\nfunction headerFormatRequest(\n sheetId,\n endColumnIndex,\n startRowIndex = 0\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex: startRowIndex + 1,\n startColumnIndex: 0,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 0.29,\n green: 0.49,\n blue: 0.58,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 1,\n green: 1,\n blue: 1,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction freezeHeaderRequest(sheetId, frozenRowCount = 1) {\n return {\n updateSheetProperties: {\n properties: {\n sheetId,\n gridProperties: {\n frozenRowCount,\n },\n },\n fields: 'gridProperties.frozenRowCount',\n },\n };\n}\n\nfunction autoResizeRequest(sheetId, endColumnIndex) {\n return {\n autoResizeDimensions: {\n dimensions: {\n sheetId,\n dimension: 'COLUMNS',\n startIndex: 0,\n endIndex: endColumnIndex,\n },\n },\n };\n}\n\nfunction moneyFormatRequest(\n sheetId,\n startColumnIndex,\n endColumnIndex,\n startRowIndex = 1,\n pattern = '#,##0.00',\n endRowIndex = null\n) {\n const range = {\n sheetId,\n startRowIndex,\n startColumnIndex,\n endColumnIndex,\n };\n\n if (Number.isInteger(endRowIndex)) {\n range.endRowIndex = endRowIndex;\n }\n\n return {\n repeatCell: {\n range,\n cell: {\n userEnteredFormat: {\n numberFormat: {\n type: 'NUMBER',\n pattern,\n },\n },\n },\n fields: 'userEnteredFormat.numberFormat',\n },\n };\n}\n\nfunction basicFilterRequest(\n sheetId,\n endColumnIndex,\n startRowIndex = 0,\n endRowIndex = null\n) {\n const range = {\n sheetId,\n startRowIndex,\n startColumnIndex: 0,\n endColumnIndex,\n };\n\n if (Number.isInteger(endRowIndex)) {\n range.endRowIndex = endRowIndex;\n }\n\n return {\n setBasicFilter: {\n filter: {\n range,\n },\n },\n };\n}\n\nfunction mergeRowRequest(\n sheetId,\n rowIndex,\n endColumnIndex\n) {\n return {\n mergeCells: {\n range: {\n sheetId,\n startRowIndex: rowIndex,\n endRowIndex: rowIndex + 1,\n startColumnIndex: 0,\n endColumnIndex,\n },\n mergeType: 'MERGE_ALL',\n },\n };\n}\n\nfunction titleRowFormatRequest(\n sheetId,\n rowIndex,\n endColumnIndex,\n options = {}\n) {\n const {\n fontSize = 12,\n bold = true,\n italic = false,\n horizontalAlignment = 'LEFT',\n } = options;\n\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: rowIndex,\n endRowIndex: rowIndex + 1,\n startColumnIndex: 0,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 0.29,\n green: 0.49,\n blue: 0.58,\n },\n textFormat: {\n bold,\n italic,\n fontSize,\n foregroundColor: {\n red: 1,\n green: 1,\n blue: 1,\n },\n },\n horizontalAlignment,\n verticalAlignment: 'MIDDLE',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment)',\n },\n };\n}\n\nfunction columnWidthRequest(\n sheetId,\n startIndex,\n endIndex,\n pixelSize\n) {\n return {\n updateDimensionProperties: {\n range: {\n sheetId,\n dimension: 'COLUMNS',\n startIndex,\n endIndex,\n },\n properties: {\n pixelSize,\n },\n fields: 'pixelSize',\n },\n };\n}\n\nfunction rowHeightRequest(\n sheetId,\n startIndex,\n endIndex,\n pixelSize\n) {\n return {\n updateDimensionProperties: {\n range: {\n sheetId,\n dimension: 'ROWS',\n startIndex,\n endIndex,\n },\n properties: {\n pixelSize,\n },\n fields: 'pixelSize',\n },\n };\n}\n\nfunction bodyAlignmentRequest(\n sheetId,\n startColumnIndex,\n endColumnIndex,\n horizontalAlignment,\n endRowIndex\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: 4,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n horizontalAlignment,\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction statusFormatRequest(sheetId, endRowIndex) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: 4,\n endRowIndex,\n startColumnIndex: 7,\n endColumnIndex: 8,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 1,\n green: 0.92,\n blue: 0.92,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.85,\n green: 0.08,\n blue: 0.08,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment)',\n },\n };\n}\n\nfunction bambooStatusFormatRequest(\n sheetId,\n endRowIndex\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: 4,\n endRowIndex,\n startColumnIndex: 6,\n endColumnIndex: 7,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 1,\n green: 0.97,\n blue: 0.82,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.70,\n green: 0.30,\n blue: 0.00,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\n\nfunction mergeVerticalRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n columnIndex\n) {\n return {\n mergeCells: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex: columnIndex,\n endColumnIndex: columnIndex + 1,\n },\n mergeType: 'MERGE_ALL',\n },\n };\n}\n\nfunction rangeFormatRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n userEnteredFormat\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat,\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction conditionalDifferenceRequest(\n sheetId,\n formula,\n backgroundColor,\n textColor,\n ranges,\n index\n) {\n return {\n addConditionalFormatRule: {\n index,\n rule: {\n ranges,\n booleanRule: {\n condition: {\n type: 'CUSTOM_FORMULA',\n values: [\n {\n userEnteredValue: formula,\n },\n ],\n },\n format: {\n backgroundColor,\n textFormat: {\n bold: true,\n foregroundColor: textColor,\n },\n },\n },\n },\n },\n };\n}\n\nfunction borderRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n endColumnIndex\n) {\n const border = {\n style: 'SOLID',\n color: {\n red: 0.72,\n green: 0.82,\n blue: 0.76,\n },\n };\n\n return {\n updateBorders: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex: 0,\n endColumnIndex,\n },\n top: border,\n bottom: border,\n left: border,\n right: border,\n innerHorizontal: border,\n innerVertical: border,\n },\n };\n}\n\nconst mainReportEndRow = Math.max(\n 4 + mainReportRows.length,\n 4\n);\n\nconst bancoSinBambooEndRow = Math.max(\n 4 + bancoSinBambooRows.length,\n 4\n);\n\nconst mainConditionalRanges = mainReportRows.length\n ? [\n {\n sheetId: sheetIds.nominaVsBanco,\n startRowIndex: 4,\n endRowIndex: mainReportEndRow,\n startColumnIndex: 5,\n endColumnIndex: 7,\n },\n ]\n : [];\n\nconst mainDataFormatRequests = mainReportRows.length\n ? [\n moneyFormatRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 4,\n 'Q#,##0.00;[Red](Q#,##0.00)',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 0,\n 1,\n 'CENTER',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 'LEFT',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 2,\n 3,\n 'CENTER',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 'RIGHT',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 6,\n 8,\n 'CENTER',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 8,\n 9,\n 'LEFT',\n mainReportEndRow\n ),\n statusFormatRequest(\n sheetIds.nominaVsBanco,\n mainReportEndRow\n ),\n conditionalDifferenceRequest(\n sheetIds.nominaVsBanco,\n '=$F5>0',\n {\n red: 1,\n green: 0.97,\n blue: 0.82,\n },\n {\n red: 0.25,\n green: 0.25,\n blue: 0.25,\n },\n mainConditionalRanges,\n 0\n ),\n conditionalDifferenceRequest(\n sheetIds.nominaVsBanco,\n '=$F5<0',\n {\n red: 1,\n green: 0.89,\n blue: 0.89,\n },\n {\n red: 0.9,\n green: 0.05,\n blue: 0.05,\n },\n mainConditionalRanges,\n 1\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 4,\n mainReportEndRow,\n 28\n ),\n ]\n : [];\n\n\nconst cuentaMalDigitadaEndRow = Math.max(\n 4 + cuentaMalDigitadaRows.length,\n 4\n);\n\nconst cuentaMalDigitadaCaseRequests =\n hasCuentaMalDigitada\n ? cuentaMalDigitadaCases.flatMap(\n (_, caseIndex) => {\n const startRowIndex =\n 4 + caseIndex * 6;\n const endRowIndex =\n startRowIndex + 6;\n\n return [\n mergeVerticalRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex,\n endRowIndex,\n 0\n ),\n mergeVerticalRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex,\n endRowIndex,\n 3\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex,\n startRowIndex + 4,\n 34\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 4,\n startRowIndex + 5,\n 76\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 5,\n endRowIndex,\n 54\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 4,\n startRowIndex + 5,\n 2,\n 3,\n {\n backgroundColor: {\n red: 0.97,\n green: 0.97,\n blue: 0.97,\n },\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 5,\n endRowIndex,\n 2,\n 3,\n {\n backgroundColor: {\n red: 1,\n green: 0.97,\n blue: 0.82,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.55,\n green: 0.30,\n blue: 0.00,\n },\n },\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n ];\n }\n )\n : [];\n\nconst cuentaMalDigitadaFormatRequests =\n hasCuentaMalDigitada\n ? [\n mergeRowRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 4\n ),\n mergeRowRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 4\n ),\n titleRowFormatRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 4,\n {\n fontSize: 12,\n bold: true,\n italic: false,\n horizontalAlignment: 'LEFT',\n }\n ),\n titleRowFormatRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 4,\n {\n fontSize: 10,\n bold: false,\n italic: true,\n horizontalAlignment: 'LEFT',\n }\n ),\n headerFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n 3\n ),\n freezeHeaderRequest(\n sheetIds.cuentaMalDigitada,\n 4\n ),\n borderRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n cuentaMalDigitadaEndRow,\n 4\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 1,\n 30\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 2,\n 26\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n 40\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 0,\n 1,\n {\n backgroundColor: {\n red: 0.91,\n green: 0.95,\n blue: 0.99,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.20,\n green: 0.36,\n blue: 0.45,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 1,\n 2,\n {\n backgroundColor: {\n red: 0.93,\n green: 0.97,\n blue: 0.90,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.20,\n green: 0.36,\n blue: 0.45,\n },\n },\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 2,\n 3,\n {\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 3,\n 4,\n {\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 1,\n 48\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 2,\n 285\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 2,\n 3,\n 520\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n 260\n ),\n ...cuentaMalDigitadaCaseRequests,\n ]\n : [];\n\n\nfunction wrapRangeRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction autoResizeRowsRequest(\n sheetId,\n startIndex,\n endIndex\n) {\n return {\n autoResizeDimensions: {\n dimensions: {\n sheetId,\n dimension: 'ROWS',\n startIndex,\n endIndex,\n },\n },\n };\n}\n\nconst formatRequests = [\n mergeRowRequest(sheetIds.nominaVsBanco, 0, 9),\n mergeRowRequest(sheetIds.nominaVsBanco, 1, 9),\n titleRowFormatRequest(\n sheetIds.nominaVsBanco,\n 0,\n 9,\n {\n fontSize: 12,\n bold: true,\n italic: false,\n horizontalAlignment: 'LEFT',\n }\n ),\n titleRowFormatRequest(\n sheetIds.nominaVsBanco,\n 1,\n 9,\n {\n fontSize: 10,\n bold: false,\n italic: true,\n horizontalAlignment: 'LEFT',\n }\n ),\n headerFormatRequest(\n sheetIds.nominaVsBanco,\n 9,\n 3\n ),\n freezeHeaderRequest(\n sheetIds.nominaVsBanco,\n 4\n ),\n basicFilterRequest(\n sheetIds.nominaVsBanco,\n 9,\n 3,\n mainReportEndRow\n ),\n borderRequest(\n sheetIds.nominaVsBanco,\n 3,\n mainReportEndRow,\n 9\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 0,\n 1,\n 30\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 26\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 3,\n 4,\n 42\n ),\n ...mainDataFormatRequests,\n\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 0,\n 1,\n 48\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 260\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 2,\n 3,\n 130\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 130\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 6,\n 7,\n 155\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 7,\n 8,\n 110\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 8,\n 9,\n 260\n ),\n\n headerFormatRequest(sheetIds.bancoSinNomina, 7),\n freezeHeaderRequest(sheetIds.bancoSinNomina),\n autoResizeRequest(sheetIds.bancoSinNomina, 7),\n moneyFormatRequest(sheetIds.bancoSinNomina, 3, 4),\n basicFilterRequest(sheetIds.bancoSinNomina, 7),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 6,\n 7,\n 260\n ),\n\n mergeRowRequest(sheetIds.bancoSinBamboo, 0, 8),\n mergeRowRequest(sheetIds.bancoSinBamboo, 1, 8),\n titleRowFormatRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 8,\n {\n fontSize: 12,\n bold: true,\n italic: false,\n horizontalAlignment: 'LEFT',\n }\n ),\n titleRowFormatRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 8,\n {\n fontSize: 10,\n bold: false,\n italic: true,\n horizontalAlignment: 'LEFT',\n }\n ),\n headerFormatRequest(\n sheetIds.bancoSinBamboo,\n 8,\n 3\n ),\n freezeHeaderRequest(\n sheetIds.bancoSinBamboo,\n 4\n ),\n basicFilterRequest(\n sheetIds.bancoSinBamboo,\n 8,\n 3,\n bancoSinBambooEndRow\n ),\n borderRequest(\n sheetIds.bancoSinBamboo,\n 3,\n bancoSinBambooEndRow,\n 8\n ),\n rowHeightRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 1,\n 30\n ),\n rowHeightRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 2,\n 26\n ),\n rowHeightRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 42\n ),\n moneyFormatRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 4,\n 'Q#,##0.00',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 1,\n 'CENTER',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 3,\n 'LEFT',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 'CENTER',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 'RIGHT',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 5,\n 7,\n 'CENTER',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 7,\n 8,\n 'LEFT',\n bancoSinBambooEndRow\n ),\n bambooStatusFormatRequest(\n sheetIds.bancoSinBamboo,\n bancoSinBambooEndRow\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 1,\n 48\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 2,\n 220\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 2,\n 3,\n 270\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 135\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 130\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 5,\n 6,\n 110\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 6,\n 7,\n 140\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 7,\n 8,\n 260\n ),\n\n headerFormatRequest(\n sheetIds.diferenciasNombreBanco,\n 11\n ),\n freezeHeaderRequest(\n sheetIds.diferenciasNombreBanco\n ),\n autoResizeRequest(\n sheetIds.diferenciasNombreBanco,\n 11\n ),\n moneyFormatRequest(\n sheetIds.diferenciasNombreBanco,\n 4,\n 5\n ),\n basicFilterRequest(\n sheetIds.diferenciasNombreBanco,\n 11\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 10,\n 11,\n 260\n ),\n\n ...cuentaMalDigitadaFormatRequests,\n\n headerFormatRequest(sheetIds.resumen, 2),\n freezeHeaderRequest(sheetIds.resumen),\n autoResizeRequest(sheetIds.resumen, 2),\n moneyFormatRequest(sheetIds.resumen, 1, 2),\n];\n\n\nconst bancoSinNominaEndRow =\n 1 + bancoSinNominaRows.length;\n\nconst diferenciasNombreEndRow =\n 1 + diferenciasNombreRows.length;\n\nconst resumenReadabilityEndRow =\n 1 + resumenRows.length;\n\n/*\n * Ajuste final de legibilidad.\n *\n * Se ejecuta al final para que los anchos definitivos ya estén aplicados\n * cuando Google Sheets calcule automáticamente la altura de cada fila.\n * Así, cualquier texto largo queda envuelto y visible sin que el usuario\n * tenga que expandir columnas o filas manualmente.\n */\nformatRequests.push(\n // 01 Nómina vs Banco\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 300\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 2,\n 3,\n 150\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 145\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 6,\n 7,\n 220\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 7,\n 8,\n 140\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 8,\n 9,\n 320\n ),\n ...(mainReportRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.nominaVsBanco,\n 4,\n mainReportEndRow,\n 0,\n 9\n ),\n autoResizeRowsRequest(\n sheetIds.nominaVsBanco,\n 4,\n mainReportEndRow\n ),\n ]\n : []),\n\n // 02 Banco sin Nómina\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 0,\n 1,\n 320\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 1,\n 2,\n 165\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 2,\n 3,\n 95\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 3,\n 4,\n 140\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 4,\n 5,\n 170\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 5,\n 6,\n 560\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 6,\n 7,\n 320\n ),\n autoResizeRowsRequest(\n sheetIds.bancoSinNomina,\n 0,\n 1\n ),\n ...(bancoSinNominaRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.bancoSinNomina,\n 1,\n bancoSinNominaEndRow,\n 0,\n 7\n ),\n autoResizeRowsRequest(\n sheetIds.bancoSinNomina,\n 1,\n bancoSinNominaEndRow\n ),\n ]\n : []),\n\n // 03 Banco sin Bamboo\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 2,\n 300\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 2,\n 3,\n 330\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 150\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 145\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 5,\n 6,\n 150\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 6,\n 7,\n 170\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 7,\n 8,\n 320\n ),\n ...(bancoSinBambooRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.bancoSinBamboo,\n 4,\n bancoSinBambooEndRow,\n 0,\n 8\n ),\n autoResizeRowsRequest(\n sheetIds.bancoSinBamboo,\n 4,\n bancoSinBambooEndRow\n ),\n ]\n : []),\n\n // 04 Diferencias nombre banco\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 0,\n 1,\n 300\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 1,\n 2,\n 330\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 2,\n 3,\n 155\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 3,\n 4,\n 95\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 4,\n 5,\n 140\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 5,\n 7,\n 150\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 7,\n 8,\n 260\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 8,\n 9,\n 170\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 9,\n 10,\n 650\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 10,\n 11,\n 320\n ),\n autoResizeRowsRequest(\n sheetIds.diferenciasNombreBanco,\n 0,\n 1\n ),\n ...(diferenciasNombreRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.diferenciasNombreBanco,\n 1,\n diferenciasNombreEndRow,\n 0,\n 11\n ),\n autoResizeRowsRequest(\n sheetIds.diferenciasNombreBanco,\n 1,\n diferenciasNombreEndRow\n ),\n ]\n : []),\n\n // 05 Cuenta Mal Digitada\n ...(hasCuentaMalDigitada\n ? [\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 2,\n 300\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 2,\n 3,\n 600\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n 320\n ),\n ]\n : []),\n\n // Resumen\n columnWidthRequest(\n sheetIds.resumen,\n 0,\n 1,\n 380\n ),\n columnWidthRequest(\n sheetIds.resumen,\n 1,\n 2,\n 320\n ),\n autoResizeRowsRequest(\n sheetIds.resumen,\n 0,\n 1\n ),\n ...(resumenRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.resumen,\n 1,\n resumenReadabilityEndRow,\n 0,\n 2\n ),\n autoResizeRowsRequest(\n sheetIds.resumen,\n 1,\n resumenReadabilityEndRow\n ),\n ]\n : [])\n);\n\n\n\n/*\n * Protección de integridad del reporte.\n *\n * La hoja completa queda protegida para impedir que los usuarios con acceso\n * al reporte modifiquen datos calculados, nombres, cuentas, montos, estados,\n * observaciones o cualquier otro resultado del cruce.\n *\n * Únicamente las celdas de datos de la columna \"Resolución\" quedan fuera de\n * la protección para que puedan completarse después de recibir el reporte.\n * Los encabezados y las filas fuera de la tabla permanecen protegidos.\n *\n * El propietario de la hoja conserva siempre el control. Además, el usuario\n * administrativo iaracena@gomezleemarketing.com queda registrado como editor explícito del\n * rango protegido para mantenimiento de IT.\n */\nfunction protectedSheetRequest(\n sheetId,\n description,\n resolutionColumnIndex = null,\n resolutionStartRowIndex = null,\n resolutionRowsCount = 0\n) {\n const protectedRange = {\n range: {\n sheetId,\n },\n description,\n warningOnly: false,\n editors: {\n users: [\n 'iaracena@gomezleemarketing.com',\n ],\n domainUsersCanEdit: false,\n },\n };\n\n if (\n Number.isInteger(resolutionColumnIndex) &&\n Number.isInteger(resolutionStartRowIndex) &&\n Number(resolutionRowsCount) > 0\n ) {\n protectedRange.unprotectedRanges = [\n {\n sheetId,\n startRowIndex:\n resolutionStartRowIndex,\n endRowIndex:\n resolutionStartRowIndex +\n Number(resolutionRowsCount),\n startColumnIndex:\n resolutionColumnIndex,\n endColumnIndex:\n resolutionColumnIndex + 1,\n },\n ];\n }\n\n return {\n addProtectedRange: {\n protectedRange,\n },\n };\n}\n\n\nfunction protectedResolutionRequest(\n sheetId,\n resolutionColumnIndex,\n resolutionStartRowIndex,\n resolutionRowsCount\n) {\n if (\n !Number.isInteger(resolutionColumnIndex) ||\n !Number.isInteger(resolutionStartRowIndex) ||\n Number(resolutionRowsCount) <= 0\n ) {\n return null;\n }\n\n return {\n addProtectedRange: {\n protectedRange: {\n range: {\n sheetId,\n startRowIndex:\n resolutionStartRowIndex,\n endRowIndex:\n resolutionStartRowIndex +\n Number(resolutionRowsCount),\n startColumnIndex:\n resolutionColumnIndex,\n endColumnIndex:\n resolutionColumnIndex + 1,\n },\n description:\n 'Protección GLM — Resolución. Solo Viviana Páramo y Liliana Benítez pueden editar.',\n warningOnly: false,\n editors: {\n users: [\n 'vparamo@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n ],\n domainUsersCanEdit: false,\n },\n },\n },\n };\n}\n\nfunction addResolutionProtection(\n sheetId,\n resolutionColumnIndex,\n resolutionStartRowIndex,\n resolutionRowsCount\n) {\n const request =\n protectedResolutionRequest(\n sheetId,\n resolutionColumnIndex,\n resolutionStartRowIndex,\n resolutionRowsCount\n );\n\n if (request) {\n formatRequests.push(request);\n }\n}\n\n\nformatRequests.push(\n protectedSheetRequest(\n sheetIds.nominaVsBanco,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 8,\n 4,\n mainReportRows.length\n ),\n protectedSheetRequest(\n sheetIds.bancoSinNomina,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 6,\n 1,\n bancoSinNominaRows.length\n ),\n protectedSheetRequest(\n sheetIds.bancoSinBamboo,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 7,\n 4,\n bancoSinBambooRows.length\n ),\n protectedSheetRequest(\n sheetIds.diferenciasNombreBanco,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 10,\n 1,\n diferenciasNombreRows.length\n ),\n ...(hasCuentaMalDigitada\n ? [\n protectedSheetRequest(\n sheetIds.cuentaMalDigitada,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 3,\n 4,\n cuentaMalDigitadaRows.length\n ),\n ]\n : []),\n protectedSheetRequest(\n sheetIds.resumen,\n 'Protección GLM — hoja de resumen de solo lectura.'\n )\n);\n\n\naddResolutionProtection(\n sheetIds.nominaVsBanco,\n 8,\n 4,\n mainReportRows.length\n);\naddResolutionProtection(\n sheetIds.bancoSinNomina,\n 6,\n 1,\n bancoSinNominaRows.length\n);\naddResolutionProtection(\n sheetIds.bancoSinBamboo,\n 7,\n 4,\n bancoSinBambooRows.length\n);\naddResolutionProtection(\n sheetIds.diferenciasNombreBanco,\n 10,\n 1,\n diferenciasNombreRows.length\n);\nif (hasCuentaMalDigitada) {\n addResolutionProtection(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n cuentaMalDigitadaRows.length\n );\n}\n\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'preparar_google_sheet',\n metadata,\n summary,\n spreadsheetTitle,\n sheetIds,\n sheetTitles,\n createSpreadsheetBody: {\n properties: {\n title: spreadsheetTitle,\n },\n sheets: [\n {\n properties: {\n sheetId: sheetIds.nominaVsBanco,\n title: sheetTitles.nominaVsBanco,\n },\n },\n {\n properties: {\n sheetId: sheetIds.bancoSinNomina,\n title: sheetTitles.bancoSinNomina,\n },\n },\n {\n properties: {\n sheetId: sheetIds.bancoSinBamboo,\n title: sheetTitles.bancoSinBamboo,\n },\n },\n {\n properties: {\n sheetId:\n sheetIds.diferenciasNombreBanco,\n title:\n sheetTitles.diferenciasNombreBanco,\n },\n },\n ...(hasCuentaMalDigitada\n ? [\n {\n properties: {\n sheetId:\n sheetIds.cuentaMalDigitada,\n title:\n sheetTitles.cuentaMalDigitada,\n },\n },\n ]\n : []),\n {\n properties: {\n sheetId: sheetIds.resumen,\n title: sheetTitles.resumen,\n },\n },\n ],\n },\n valueBatchBody: {\n // RAW conserva los números de cuenta como texto exacto (incluidos\n // ceros iniciales) y mantiene los montos numéricos porque ya se\n // envían como números en el payload.\n valueInputOption: 'RAW',\n data: valueData,\n },\n formatBatchBody: {\n requests: formatRequests,\n },\n originalResponse: data,\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 120, + 5200 + ], + "id": "ffde494e-1ffa-41e6-a76d-3322f684dec7", + "name": "Preparar Google Sheet" + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/cruce_cuentas_get_reporte_periodo", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { p_country: $('Preparar Google Sheet').first().json.metadata.country || 'PR', p_year: Number($('Preparar Google Sheet').first().json.metadata.year || 0), p_month: Number($('Preparar Google Sheet').first().json.metadata.month || 0), p_period_type: $('Preparar Google Sheet').first().json.metadata.period_type || '' } }}", + "options": { + "response": { + "response": { + "responseFormat": "json" + } + }, + "timeout": 30000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 360, + 5200 + ], + "id": "81fd24de-a18d-4efd-b030-c8f193673745", + "name": "Buscar reporte existente Supabase" + }, + { + "parameters": { + "method": "POST", + "url": "={{ (() => { const lookup = $('Buscar reporte existente Supabase').first().json || {}; const report = lookup.report || {}; const id = String(report.spreadsheet_id || '').trim(); return (lookup.found === true && id) ? 'https://sheets.googleapis.com/v4/spreadsheets/' + id + ':batchUpdate' : 'https://sheets.googleapis.com/v4/spreadsheets'; })() }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{\n(() => {\n const prepared = $('Preparar Google Sheet').first().json || {};\n const lookup = $('Buscar reporte existente Supabase').first().json || {};\n const report = lookup.report || {};\n const existingId = String(report.spreadsheet_id || '').trim();\n\n if (lookup.found === true && existingId) {\n return {\n requests: [\n {\n updateSpreadsheetProperties: {\n properties: {\n title: prepared.spreadsheetTitle || prepared.createSpreadsheetBody?.properties?.title || 'Cruce de Cuentas GLM',\n },\n fields: 'title',\n },\n },\n ],\n };\n }\n\n const createBody = prepared.createSpreadsheetBody || {};\n\n if (!Array.isArray(createBody.sheets) || createBody.sheets.length === 0) {\n throw new Error('Preparar Google Sheet no devolvió las hojas que deben crearse.');\n }\n\n return {\n properties: {\n ...(createBody.properties || {}),\n timeZone: 'America/Puerto_Rico',\n },\n sheets: createBody.sheets.map((sheet) => ({\n properties: {\n ...(sheet.properties || {}),\n gridProperties: {\n ...((sheet.properties || {}).gridProperties || {}),\n frozenRowCount: 1,\n },\n },\n })),\n };\n})()\n}}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 600, + 5200 + ], + "id": "170ec328-0a9f-401b-9852-ca472d6ac14d", + "name": "Crear Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "eHseMeH39kRcXgOF", + "name": "Google account 2" + } + } + }, + { + "parameters": { + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Crear Google Sheet').first().json.spreadsheetId + '?includeGridData=true' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 840, + 5200 + ], + "id": "5d4d04ba-ee83-4a46-a419-f028729bb7bc", + "name": "Leer Google Sheet actual", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "jsCode": "const prepared =\n $('Preparar Google Sheet').first().json || {};\n\nconst lookup =\n $('Buscar reporte existente Supabase').first().json || {};\n\nconst resolvedSheet =\n $('Crear Google Sheet').first().json || {};\n\nconst spreadsheet =\n $input.first().json || {};\n\nconst report =\n lookup && typeof lookup.report === 'object' && lookup.report\n ? lookup.report\n : {};\n\nconst historyExists =\n lookup.found === true &&\n Boolean(report.id);\n\nconst reusingSheet =\n historyExists &&\n Boolean(\n String(\n report.spreadsheet_id ||\n ''\n ).trim()\n );\n\nconst spreadsheetId =\n String(\n resolvedSheet.spreadsheetId ||\n report.spreadsheet_id ||\n spreadsheet.spreadsheetId ||\n ''\n ).trim();\n\nif (!spreadsheetId) {\n throw new Error(\n 'No se pudo resolver el spreadsheetId del reporte.'\n );\n}\n\nfunction normalizeText(value) {\n return String(value ?? '')\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '')\n .replace(/\\.0$/g, '')\n .replace(/\\D/g, '')\n .trim();\n}\n\nfunction cellValue(cell) {\n if (!cell || typeof cell !== 'object') {\n return '';\n }\n\n const entered =\n cell.userEnteredValue || {};\n\n if (\n entered.stringValue !== undefined\n ) {\n return String(entered.stringValue);\n }\n\n if (\n entered.numberValue !== undefined\n ) {\n return String(entered.numberValue);\n }\n\n if (\n entered.boolValue !== undefined\n ) {\n return entered.boolValue\n ? 'TRUE'\n : 'FALSE';\n }\n\n if (\n entered.formulaValue !== undefined\n ) {\n return String(\n cell.formattedValue ??\n entered.formulaValue\n );\n }\n\n return String(\n cell.formattedValue ?? ''\n );\n}\n\nfunction sheetMatrix(sheet) {\n const matrix = [];\n const blocks =\n Array.isArray(sheet?.data)\n ? sheet.data\n : [];\n\n for (const block of blocks) {\n const startRow =\n Number(block.startRow || 0);\n const rowData =\n Array.isArray(block.rowData)\n ? block.rowData\n : [];\n\n for (\n let rowOffset = 0;\n rowOffset < rowData.length;\n rowOffset++\n ) {\n const rowIndex =\n startRow + rowOffset;\n\n const cells =\n Array.isArray(\n rowData[rowOffset]?.values\n )\n ? rowData[rowOffset].values\n : [];\n\n matrix[rowIndex] =\n cells.map(cellValue);\n }\n }\n\n return matrix;\n}\n\nfunction findHeaderInfo(matrix) {\n for (\n let rowIndex = 0;\n rowIndex < matrix.length;\n rowIndex++\n ) {\n const row =\n Array.isArray(matrix[rowIndex])\n ? matrix[rowIndex]\n : [];\n\n const normalized =\n row.map(normalizeText);\n\n const resolutionIndex =\n normalized.findIndex(\n (value) =>\n value === 'resolucion'\n );\n\n if (resolutionIndex >= 0) {\n return {\n rowIndex,\n header: row,\n normalized,\n resolutionIndex,\n };\n }\n }\n\n return null;\n}\n\nfunction indexMatching(\n normalizedHeader,\n candidates\n) {\n for (const candidate of candidates) {\n const normalizedCandidate =\n normalizeText(candidate);\n\n const exact =\n normalizedHeader.findIndex(\n (header) =>\n header === normalizedCandidate\n );\n\n if (exact >= 0) {\n return exact;\n }\n }\n\n for (const candidate of candidates) {\n const normalizedCandidate =\n normalizeText(candidate);\n\n const partial =\n normalizedHeader.findIndex(\n (header) =>\n header.includes(\n normalizedCandidate\n )\n );\n\n if (partial >= 0) {\n return partial;\n }\n }\n\n return -1;\n}\n\nfunction firstAt(row, indexes) {\n for (const index of indexes) {\n if (\n index >= 0 &&\n String(row[index] ?? '').trim()\n ) {\n return String(row[index]);\n }\n }\n\n return '';\n}\n\nconst ids =\n prepared.sheetIds || {};\n\nfunction rowIdentity(\n sheetId,\n row,\n normalizedHeader\n) {\n const accountIndex =\n indexMatching(\n normalizedHeader,\n [\n 'Cuenta',\n 'Cuenta Banco',\n 'Cuenta Destino',\n ]\n );\n\n const account =\n accountIndex >= 0\n ? normalizeAccount(\n row[accountIndex]\n )\n : '';\n\n if (account) {\n return `account:${account}`;\n }\n\n if (\n Number(sheetId) ===\n Number(ids.nominaVsBanco)\n ) {\n const employeeIndex =\n indexMatching(\n normalizedHeader,\n ['Empleado', 'Nombre en nómina']\n );\n\n return `employee:${normalizeText(\n firstAt(\n row,\n [employeeIndex]\n )\n )}`;\n }\n\n if (\n Number(sheetId) ===\n Number(ids.bancoSinNomina)\n ) {\n const nameIndex =\n indexMatching(\n normalizedHeader,\n [\n 'Empleado Banco',\n 'Nombre en banco',\n ]\n );\n\n const currencyIndex =\n indexMatching(\n normalizedHeader,\n ['Moneda']\n );\n\n return [\n 'bank-no-payroll',\n normalizeText(\n firstAt(row, [nameIndex])\n ),\n normalizeText(\n firstAt(row, [currencyIndex])\n ),\n ].join(':');\n }\n\n if (\n Number(sheetId) ===\n Number(ids.bancoSinBamboo)\n ) {\n const nameIndex =\n indexMatching(\n normalizedHeader,\n ['Nombre en banco']\n );\n\n const holderIndex =\n indexMatching(\n normalizedHeader,\n ['Nombre del cuentahabiente']\n );\n\n return [\n 'bank-no-bamboo',\n normalizeText(\n firstAt(row, [nameIndex])\n ),\n normalizeText(\n firstAt(row, [holderIndex])\n ),\n ].join(':');\n }\n\n if (\n Number(sheetId) ===\n Number(ids.diferenciasNombreBanco)\n ) {\n const nameOne =\n indexMatching(\n normalizedHeader,\n [\n 'Nombre en Archivo',\n 'Nombre en nómina',\n ]\n );\n\n const nameTwo =\n indexMatching(\n normalizedHeader,\n [\n 'Nombre del Cuentahabiente',\n 'Nombre en banco',\n ]\n );\n\n return [\n 'name-difference',\n normalizeText(\n firstAt(row, [nameOne])\n ),\n normalizeText(\n firstAt(row, [nameTwo])\n ),\n ].join(':');\n }\n\n return '';\n}\n\nfunction collectRegularResolutions(\n sheetId,\n matrix\n) {\n const result = new Map();\n const info =\n findHeaderInfo(matrix);\n\n if (!info) {\n return result;\n }\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex < matrix.length;\n rowIndex++\n ) {\n const row =\n Array.isArray(matrix[rowIndex])\n ? matrix[rowIndex]\n : [];\n\n const resolution =\n String(\n row[\n info.resolutionIndex\n ] ?? ''\n ).trim();\n\n if (!resolution) {\n continue;\n }\n\n const key =\n rowIdentity(\n sheetId,\n row,\n info.normalized\n );\n\n if (key) {\n result.set(\n key,\n resolution\n );\n }\n }\n\n return result;\n}\n\nfunction collectWrongAccountResolutions(\n matrix\n) {\n const result = new Map();\n const info =\n findHeaderInfo(matrix);\n\n if (!info) {\n return result;\n }\n\n const fieldIndex =\n indexMatching(\n info.normalized,\n ['Campo']\n );\n\n const detailIndex =\n indexMatching(\n info.normalized,\n ['Detalle']\n );\n\n let currentEmployee = '';\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex < matrix.length;\n rowIndex++\n ) {\n const row =\n Array.isArray(matrix[rowIndex])\n ? matrix[rowIndex]\n : [];\n\n const field =\n normalizeText(\n row[fieldIndex] ?? ''\n );\n\n const detail =\n String(\n row[detailIndex] ?? ''\n );\n\n if (field === 'empleado') {\n currentEmployee =\n normalizeText(detail);\n }\n\n const resolution =\n String(\n row[\n info.resolutionIndex\n ] ?? ''\n ).trim();\n\n if (\n resolution &&\n currentEmployee &&\n field\n ) {\n result.set(\n `${currentEmployee}|${field}`,\n resolution\n );\n }\n }\n\n return result;\n}\n\nconst existingSheets =\n Array.isArray(spreadsheet.sheets)\n ? spreadsheet.sheets\n : [];\n\nconst sheetById =\n new Map();\n\nfor (const sheet of existingSheets) {\n const sheetId =\n sheet?.properties?.sheetId;\n\n if (\n sheetId !== null &&\n sheetId !== undefined\n ) {\n sheetById.set(\n Number(sheetId),\n sheet\n );\n }\n}\n\nconst resolutionMaps =\n new Map();\n\nif (reusingSheet) {\n for (\n const [key, rawId]\n of Object.entries(ids)\n ) {\n const sheetId =\n Number(rawId);\n\n const sheet =\n sheetById.get(sheetId);\n\n if (!sheet) {\n continue;\n }\n\n const matrix =\n sheetMatrix(sheet);\n\n const map =\n key === 'cuentaMalDigitada'\n ? collectWrongAccountResolutions(\n matrix\n )\n : collectRegularResolutions(\n sheetId,\n matrix\n );\n\n resolutionMaps.set(\n sheetId,\n map\n );\n }\n}\n\nfunction titleFromRange(range) {\n const match =\n String(range || '')\n .match(/^'([^']+)'!/);\n\n return match?.[1] || '';\n}\n\nconst desiredTitles =\n prepared.sheetTitles || {};\n\nconst titleToId =\n new Map();\n\nfor (\n const [key, title]\n of Object.entries(desiredTitles)\n) {\n if (\n ids[key] !== undefined\n ) {\n titleToId.set(\n String(title),\n Number(ids[key])\n );\n }\n}\n\nlet preservedResolutionsCount = 0;\n\nconst valueBatchBody =\n JSON.parse(\n JSON.stringify(\n prepared.valueBatchBody || {\n valueInputOption:\n 'USER_ENTERED',\n data: [],\n }\n )\n );\n\nfor (\n const dataEntry\n of valueBatchBody.data || []\n) {\n const title =\n titleFromRange(\n dataEntry.range\n );\n\n const sheetId =\n titleToId.get(title);\n\n if (\n sheetId === undefined ||\n !Array.isArray(\n dataEntry.values\n )\n ) {\n continue;\n }\n\n const info =\n findHeaderInfo(\n dataEntry.values\n );\n\n if (!info) {\n continue;\n }\n\n const oldMap =\n resolutionMaps.get(\n Number(sheetId)\n ) || new Map();\n\n if (\n Number(sheetId) ===\n Number(ids.cuentaMalDigitada)\n ) {\n const fieldIndex =\n indexMatching(\n info.normalized,\n ['Campo']\n );\n\n const detailIndex =\n indexMatching(\n info.normalized,\n ['Detalle']\n );\n\n let currentEmployee = '';\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex <\n dataEntry.values.length;\n rowIndex++\n ) {\n const row =\n dataEntry.values[\n rowIndex\n ];\n\n const field =\n normalizeText(\n row[fieldIndex] ?? ''\n );\n\n const detail =\n String(\n row[detailIndex] ?? ''\n );\n\n if (field === 'empleado') {\n currentEmployee =\n normalizeText(detail);\n }\n\n const key =\n currentEmployee && field\n ? `${currentEmployee}|${field}`\n : '';\n\n if (\n key &&\n oldMap.has(key)\n ) {\n while (\n row.length <=\n info.resolutionIndex\n ) {\n row.push('');\n }\n\n row[\n info.resolutionIndex\n ] = oldMap.get(key);\n\n preservedResolutionsCount++;\n }\n }\n\n continue;\n }\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex <\n dataEntry.values.length;\n rowIndex++\n ) {\n const row =\n dataEntry.values[\n rowIndex\n ];\n\n const key =\n rowIdentity(\n sheetId,\n row,\n info.normalized\n );\n\n if (\n key &&\n oldMap.has(key)\n ) {\n while (\n row.length <=\n info.resolutionIndex\n ) {\n row.push('');\n }\n\n row[\n info.resolutionIndex\n ] = oldMap.get(key);\n\n preservedResolutionsCount++;\n }\n }\n}\n\nconst desiredSheets =\n Array.isArray(\n prepared\n .createSpreadsheetBody\n ?.sheets\n )\n ? prepared\n .createSpreadsheetBody\n .sheets\n : [];\n\nconst existingIds =\n new Set(\n existingSheets\n .map(\n (sheet) =>\n Number(\n sheet?.properties\n ?.sheetId\n )\n )\n .filter(\n Number.isFinite\n )\n );\n\nconst desiredIds =\n new Set(\n desiredSheets\n .map(\n (sheet) =>\n Number(\n sheet?.properties\n ?.sheetId\n )\n )\n .filter(\n Number.isFinite\n )\n );\n\nconst newlyAddedSheetIds =\n new Set();\n\nconst structureRequests = [\n {\n updateSpreadsheetProperties: {\n properties: {\n title:\n prepared.spreadsheetTitle ||\n prepared\n .createSpreadsheetBody\n ?.properties?.title ||\n spreadsheet.properties\n ?.title ||\n 'Cruce de Cuentas GLM',\n },\n fields: 'title',\n },\n },\n];\n\nif (reusingSheet) {\n const optionalId =\n Number(\n ids.cuentaMalDigitada\n );\n\n if (\n Number.isFinite(optionalId) &&\n existingIds.has(optionalId) &&\n !desiredIds.has(optionalId)\n ) {\n structureRequests.push({\n deleteSheet: {\n sheetId: optionalId,\n },\n });\n\n existingIds.delete(\n optionalId\n );\n }\n\n for (\n let desiredIndex = 0;\n desiredIndex <\n desiredSheets.length;\n desiredIndex++\n ) {\n const desired =\n desiredSheets[\n desiredIndex\n ];\n\n const props =\n desired.properties || {};\n\n const desiredId =\n Number(props.sheetId);\n\n if (\n !Number.isFinite(\n desiredId\n )\n ) {\n continue;\n }\n\n const existing =\n existingSheets.find(\n (sheet) =>\n Number(\n sheet?.properties\n ?.sheetId\n ) === desiredId\n );\n\n if (!existing) {\n const sameTitle =\n existingSheets.find(\n (sheet) =>\n String(\n sheet?.properties\n ?.title || ''\n ) ===\n String(\n props.title || ''\n )\n );\n\n if (\n sameTitle &&\n Number(\n sameTitle\n ?.properties\n ?.sheetId\n ) !== desiredId\n ) {\n structureRequests.push({\n deleteSheet: {\n sheetId:\n Number(\n sameTitle\n .properties\n .sheetId\n ),\n },\n });\n }\n\n structureRequests.push({\n addSheet: {\n properties: {\n ...props,\n // El índice es 0-based. Así \"05 Cuenta Mal Digitada\"\n // vuelve exactamente antes de \"06 Resumen\" cuando reaparece.\n index:\n desiredIndex,\n gridProperties: {\n ...(props\n .gridProperties ||\n {}),\n frozenRowCount: 1,\n },\n },\n },\n });\n\n newlyAddedSheetIds.add(\n desiredId\n );\n\n continue;\n }\n\n const currentTitle =\n String(\n existing.properties\n ?.title || ''\n );\n\n const desiredTitle =\n String(\n props.title || ''\n );\n\n const currentIndex =\n Number(\n existing.properties\n ?.index\n );\n\n const titleNeedsUpdate =\n Boolean(\n desiredTitle &&\n currentTitle !==\n desiredTitle\n );\n\n const indexNeedsUpdate =\n Number.isFinite(\n currentIndex\n ) &&\n currentIndex !==\n desiredIndex;\n\n if (\n titleNeedsUpdate ||\n indexNeedsUpdate\n ) {\n const properties = {\n sheetId:\n desiredId,\n };\n\n const fields = [];\n\n if (titleNeedsUpdate) {\n properties.title =\n desiredTitle;\n fields.push('title');\n }\n\n if (indexNeedsUpdate) {\n properties.index =\n desiredIndex;\n fields.push('index');\n }\n\n structureRequests.push({\n updateSheetProperties: {\n properties,\n fields:\n fields.join(','),\n },\n });\n }\n }\n}\n\nconst originalFormatRequests =\n Array.isArray(\n prepared\n .formatBatchBody\n ?.requests\n )\n ? prepared\n .formatBatchBody\n .requests\n : [];\n\n/*\n * IMPORTANTE AL REUTILIZAR UN REPORTE:\n *\n * \"Actualizar estructura Google Sheet\" se ejecuta ANTES de\n * \"Formatear Google Sheet\". Cuando la estructura elimina una hoja\n * (por ejemplo, \"05 Cuenta Mal Digitada\" deja de ser necesaria),\n * Google elimina automáticamente también los ProtectedRange de esa hoja.\n *\n * Por eso NO debemos intentar borrar después esos protectedRangeId en el\n * batch de formato, porque ya dejaron de existir y Google responde:\n * \"Invalid requests[x].deleteProtectedRange: No protected range with id\".\n *\n * Se identifican aquí todas las hojas que serán eliminadas por el batch\n * de estructura y se excluyen sus protecciones del batch de formato.\n */\nconst sheetIdsScheduledForDeletion =\n new Set(\n structureRequests\n .map((request) =>\n Number(\n request?.deleteSheet?.sheetId\n )\n )\n .filter(Number.isFinite)\n );\n\nconst formatRequests = [];\nconst protectedRangeIdsScheduled =\n new Set();\n\nif (reusingSheet) {\n for (\n const sheet\n of existingSheets\n ) {\n const currentSheetId =\n Number(\n sheet?.properties?.sheetId\n );\n\n // Si esta hoja será eliminada en el paso de estructura, sus\n // protecciones también desaparecerán automáticamente.\n if (\n Number.isFinite(currentSheetId) &&\n sheetIdsScheduledForDeletion.has(\n currentSheetId\n )\n ) {\n continue;\n }\n\n const protections =\n Array.isArray(\n sheet.protectedRanges\n )\n ? sheet.protectedRanges\n : [];\n\n for (\n const protection\n of protections\n ) {\n const description =\n String(\n protection.description ||\n ''\n );\n\n const protectedRangeId =\n Number(\n protection\n .protectedRangeId\n );\n\n if (\n description.startsWith(\n 'Protección GLM'\n ) &&\n Number.isFinite(\n protectedRangeId\n ) &&\n !protectedRangeIdsScheduled.has(\n protectedRangeId\n )\n ) {\n protectedRangeIdsScheduled.add(\n protectedRangeId\n );\n\n formatRequests.push({\n deleteProtectedRange: {\n protectedRangeId,\n },\n });\n }\n }\n }\n}\n\n\n/*\n * Limpieza visual al reutilizar un reporte.\n *\n * values:batchClear elimina el CONTENIDO de las filas antiguas, pero Google\n * Sheets conserva su formato. Si una ejecución anterior tenía cientos de\n * discrepancias y la nueva tiene pocas, podían quedar celdas vacías con el\n * fondo rojo/amarillo aplicado por la ejecución anterior.\n *\n * La corrección es deliberadamente limitada: solamente se limpia el\n * background de las filas que quedan DESPUÉS de la última fila escrita por\n * la nueva ejecución. No se toca ninguna fila vigente, encabezado,\n * Resolución preservada, protección, borde, ancho, fórmula ni lógica del\n * cruce.\n */\nif (reusingSheet) {\n for (\n const dataEntry\n of valueBatchBody.data || []\n ) {\n const title =\n titleFromRange(\n dataEntry.range\n );\n\n const currentSheetId =\n Number(\n titleToId.get(title)\n );\n\n if (\n !Number.isFinite(\n currentSheetId\n ) ||\n sheetIdsScheduledForDeletion.has(\n currentSheetId\n ) ||\n newlyAddedSheetIds.has(\n currentSheetId\n )\n ) {\n continue;\n }\n\n const existingSheet =\n sheetById.get(\n currentSheetId\n );\n\n if (!existingSheet) {\n continue;\n }\n\n const currentValues =\n Array.isArray(\n dataEntry.values\n )\n ? dataEntry.values\n : [];\n\n const writtenRowCount =\n currentValues.length;\n\n const writtenColumnCount =\n currentValues.reduce(\n (maximum, row) =>\n Math.max(\n maximum,\n Array.isArray(row)\n ? row.length\n : 0\n ),\n 0\n );\n\n const gridRowCount =\n Number(\n existingSheet\n ?.properties\n ?.gridProperties\n ?.rowCount || 0\n );\n\n if (\n writtenRowCount <= 0 ||\n writtenColumnCount <= 0 ||\n !Number.isFinite(\n gridRowCount\n ) ||\n gridRowCount <=\n writtenRowCount\n ) {\n continue;\n }\n\n formatRequests.push({\n repeatCell: {\n range: {\n sheetId:\n currentSheetId,\n startRowIndex:\n writtenRowCount,\n endRowIndex:\n gridRowCount,\n startColumnIndex: 0,\n endColumnIndex:\n writtenColumnCount,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 1,\n green: 1,\n blue: 1,\n },\n },\n },\n fields:\n 'userEnteredFormat.backgroundColor',\n },\n });\n }\n}\n\nfor (\n const request\n of originalFormatRequests\n) {\n if (\n reusingSheet &&\n request.mergeCells\n ) {\n const mergeSheetId =\n Number(\n request\n .mergeCells\n ?.range\n ?.sheetId\n );\n\n if (\n !newlyAddedSheetIds.has(\n mergeSheetId\n )\n ) {\n continue;\n }\n }\n\n formatRequests.push(\n request\n );\n}\n\nconst clearRanges =\n (valueBatchBody.data || [])\n .map(\n (entry) =>\n entry.range\n )\n .filter(Boolean);\n\nreturn [\n {\n json: {\n ok: true,\n stage:\n 'google_sheet_destino_preparado',\n spreadsheetId,\n reportUrl:\n `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit`,\n historyExists,\n reportUpdated:\n historyExists,\n reusingSheet,\n existingHistoricalId:\n report.id\n ? String(report.id)\n : '',\n reportIdentity:\n String(\n report.report_identity ||\n ''\n ),\n previousReport:\n report,\n preservedResolutionsCount,\n valueBatchBody,\n structureBatchBody: {\n requests:\n structureRequests,\n },\n clearBatchBody: {\n ranges: clearRanges,\n },\n formatBatchBody: {\n requests:\n formatRequests,\n },\n originalResponse:\n prepared.originalResponse ||\n {},\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1080, + 5200 + ], + "id": "81decb9b-a5ca-4c4b-836f-41a1389cf472", + "name": "Preparar actualización Google Sheet" + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + ':batchUpdate' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.structureBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1320, + 5200 + ], + "id": "973e9e0e-a044-4870-8f41-1c02f1ff5412", + "name": "Actualizar estructura Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + '/values:batchClear' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.clearBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1560, + 5200 + ], + "id": "174a00f9-d50b-439a-ae7f-844d3c5cc53e", + "name": "Limpiar Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + '/values:batchUpdate' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.valueBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1800, + 5200 + ], + "id": "7731ef3c-018c-4a73-b4c2-11247e0142b8", + "name": "Escribir Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + ':batchUpdate' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.formatBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 2040, + 5200 + ], + "id": "0e27f441-b958-44ad-8f90-b33449c42d03", + "name": "Formatear Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "jsCode": "const context =\n $('Preparar actualización Google Sheet').first().json || {};\n\nconst spreadsheetId =\n context.spreadsheetId;\n\nif (!spreadsheetId) {\n throw new Error(\n 'No se recibió spreadsheetId para configurar permisos.'\n );\n}\n\nconst allowedEmails = [\n 'iaracena@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com'\n];\n\nreturn allowedEmails.map((email) => ({\n json: {\n spreadsheetId,\n email,\n permissionBody: {\n type: 'user',\n role: 'writer',\n emailAddress: email,\n },\n reportUpdated:\n context.reportUpdated === true,\n },\n}));" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2280, + 5200 + ], + "id": "24e9bed0-49df-488f-8b07-d9027c62bf9f", + "name": "Preparar permisos Google Sheet" + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://www.googleapis.com/drive/v3/files/' + $json.spreadsheetId + '/permissions?sendNotificationEmail=false' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $json.permissionBody }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 2520, + 5200 + ], + "id": "88b2fb70-5a7b-4343-bc45-b84fb664b21b", + "name": "Compartir Google Sheet", + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const cruce = $('Cruzar Nómina vs Banco').first().json || {};\nconst sheetContext =\n $('Preparar actualización Google Sheet').first().json || {};\n\nconst metadata = cruce.metadata || {};\nconst summary = cruce.summary || {};\nconst debug = cruce.debug || {};\n\nconst spreadsheetId = sheetContext.spreadsheetId || cruce.spreadsheetId || '';\nconst reportUrl =\n sheetContext.reportUrl ||\n (spreadsheetId ? `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit` : null);\n\nfunction toNumber(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction buildPeriodKey(periodMetadata) {\n const country = periodMetadata.country || 'PR';\n const year = periodMetadata.year || '';\n const month = String(periodMetadata.month || '').padStart(2, '0');\n const periodType = periodMetadata.period_type || 'periodo';\n return `${country}-${year}-${month}-${periodType}`;\n}\n\nconst discrepancias = toNumber(summary.discrepancias);\nconst bancoSinNomina = toNumber(summary.bancoSinNomina);\nconst nominaSinCuenta = toNumber(summary.nominaSinCuenta);\nconst diferenciasNombreBanco =\n toNumber(summary.diferenciasNombreBanco);\nconst bancoSinBamboo =\n toNumber(summary.bancoSinBamboo);\nconst pendientes = toNumber(summary.pendientes) || (\n discrepancias +\n bancoSinNomina +\n nominaSinCuenta +\n diferenciasNombreBanco\n);\n\n// Banco sin Bamboo es una revisión independiente del cruce principal.\nconst requiereRevision =\n pendientes > 0 || bancoSinBamboo > 0;\n\n// La app final solo manejará Pendiente revisión y Resuelto.\nconst estado = requiereRevision\n ? 'pendiente_revision'\n : 'resuelto';\n\nconst reportIdentity = [\n 'PR',\n String(metadata.year || ''),\n String(metadata.month || '').padStart(2, '0'),\n String(metadata.period_type || '').trim().toLowerCase(),\n].join('-');\n\nconst reportUpdated =\n sheetContext.reportUpdated === true;\n\nconst preservedResolutionsCount =\n Number(\n sheetContext.preservedResolutionsCount || 0\n );\n\nconst payload = {\n source_app: metadata.source_app || 'cruce-cuentas-glm-puerto-rico',\n country: metadata.country || 'PR',\n country_name: metadata.country_name || 'Puerto Rico',\n client_request_id: metadata.client_request_id || null,\n report_identity: reportIdentity,\n\n year: toNumber(metadata.year),\n month: toNumber(metadata.month),\n period_type: metadata.period_type || '',\n period_label: metadata.period_label || '',\n period_start: metadata.period_start || null,\n period_end: metadata.period_end || null,\n period_key: buildPeriodKey(metadata),\n\n payroll_file_name: metadata.payroll_file_name || '',\n bank_file_names: metadata.bank_file_names || [],\n\n coincidencias: toNumber(summary.coincidencias),\n discrepancias,\n\n banco_sin_bamboo: bancoSinBamboo,\n detalle_banco_sin_bamboo:\n Array.isArray(cruce.bankWithoutBamboo)\n ? cruce.bankWithoutBamboo\n : [],\n banco_sin_nomina: bancoSinNomina,\n nomina_sin_cuenta: nominaSinCuenta,\n nomina_sin_bamboo: 0,\n bamboo_sin_nomina: 0,\n\n filas_nomina_validas: toNumber(summary.filasNominaValidas),\n cuentas_nomina_agrupadas: toNumber(summary.cuentasNominaAgrupadas),\n transacciones_banco: toNumber(summary.transaccionesBanco),\n cuentas_banco_agrupadas: toNumber(summary.cuentasBancoAgrupadas),\n\n total_nomina: toNumber(summary.totalNomina),\n total_banco: toNumber(summary.totalBanco),\n diferencia_total: toNumber(summary.diferenciaTotal),\n\n report_url: reportUrl,\n spreadsheet_id: spreadsheetId,\n estado,\n\n ejecutado_por_nombre: metadata.requested_by_name || 'Usuario GLM',\n ejecutado_por_email: metadata.requested_by_email || '',\n\n metadata: {\n ...metadata,\n report_identity: reportIdentity,\n report_updated: reportUpdated,\n preserved_resolutions_count: preservedResolutionsCount,\n diferencias_nombre_banco:\n diferenciasNombreBanco,\n banco_sin_bamboo:\n bancoSinBamboo,\n pendientes_cruce_principal:\n pendientes,\n requiere_revision:\n requiereRevision,\n },\n summary,\n debug: {\n sheet_summaries: debug.sheet_summaries || [],\n attached_supplements: debug.attached_supplements || [],\n unattached_supplements: debug.unattached_supplements || [],\n bank_name_differences_preview:\n debug.bank_name_differences_preview || [],\n bamboo_matches:\n debug.bamboo_matches || [],\n bamboo_excluded_payments:\n debug.bamboo_excluded_payments || [],\n banco_sin_bamboo:\n cruce.bankWithoutBamboo || [],\n },\n};\n\n\nif (\n reportUpdated &&\n estado !== 'resuelto'\n) {\n payload.resuelto_por_nombre = null;\n payload.resuelto_por_email = null;\n payload.resuelto_en = null;\n payload.comentario_resolucion = null;\n}\n\nreturn [\n {\n json: {\n ...cruce,\n reportUpdated,\n preservedResolutionsCount,\n existingHistoricalId: sheetContext.existingHistoricalId || '',\n reportIdentity,\n supabaseTable: 'cruces_cuentas_gt_reportes',\n supabasePayload: payload,\n reportUrl,\n spreadsheetId,\n },\n },\n];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2760, + 5200 + ], + "id": "0534fec7-fb94-4893-9e50-9a1afdfb843f", + "name": "Preparar histórico Supabase" + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?on_conflict=report_identity", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "resolution=merge-duplicates,return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $json.supabasePayload }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 3000, + 5200 + ], + "id": "c87f31ff-6179-42c7-904c-e2fc684685f9", + "name": "Insertar histórico Supabase", + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const prepared = $('Preparar Google Sheet').first().json || {};\nconst sheetContext = $('Preparar actualización Google Sheet').first().json || {};\n\nconst original =\n prepared.originalResponse ||\n prepared.original_response ||\n prepared.response ||\n {};\n\nconst spreadsheetId = sheetContext.spreadsheetId || '';\nconst reportUrl =\n sheetContext.reportUrl ||\n (spreadsheetId ? `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit` : null);\n\n\nfunction collectHistoricalObjects(value, output) {\n if (Array.isArray(value)) {\n for (const item of value) collectHistoricalObjects(item, output);\n return;\n }\n\n if (!value || typeof value !== 'object') return;\n\n output.push(value);\n\n if (Array.isArray(value.data)) {\n for (const item of value.data) collectHistoricalObjects(item, output);\n }\n}\n\nconst historicalObjects = [];\ntry {\n for (const item of $items('Insertar histórico Supabase')) {\n collectHistoricalObjects(item.json, historicalObjects);\n }\n} catch (error) {\n // Si Supabase no devolvió representación, el reporte sigue respondiendo\n // igual que antes; únicamente executionId quedará vacío.\n}\n\nconst insertedHistoricalReport =\n historicalObjects.find((item) => item && item.id) || null;\n\nconst executionId = insertedHistoricalReport?.id\n ? String(insertedHistoricalReport.id)\n : '';\n\nreturn [\n {\n json: {\n ok: original.ok ?? true,\n message: reportUrl\n ? (sheetContext.reportUpdated === true\n ? `Reporte existente actualizado correctamente. Se conservaron ${Number(sheetContext.preservedResolutionsCount || 0)} resolución(es) previamente registradas.`\n : 'Cruce procesado correctamente. Google Sheet generado.')\n : 'Cruce procesado correctamente, pero no se recibió URL del Google Sheet.',\n stage: reportUrl ? 'cruce_completado_con_reporte' : 'cruce_completado_sin_reporte',\n errors: original.errors || [],\n metadata: original.metadata || {},\n summary: original.summary || {},\n rows: original.rows || [],\n bankWithoutBamboo:\n original.bankWithoutBamboo || [],\n bambooSummary:\n original.bambooSummary || {},\n reportUrl,\n executionId: executionId || undefined,\n reportUpdated: sheetContext.reportUpdated === true,\n reusingSheet: sheetContext.reusingSheet === true,\n preservedResolutionsCount: Number(sheetContext.preservedResolutionsCount || 0),\n clientRequestId: original.metadata?.client_request_id || '',\n googleSheet: {\n spreadsheetId,\n spreadsheetUrl: reportUrl,\n },\n debug: {\n rows_returned: Array.isArray(original.rows) ? original.rows.length : 0,\n coincidencias: original.summary?.coincidencias ?? 0,\n discrepancias: original.summary?.discrepancias ?? 0,\n bancoSinBamboo:\n original.summary?.bancoSinBamboo ?? 0,\n bancoSinBambooRows:\n Array.isArray(original.bankWithoutBamboo)\n ? original.bankWithoutBamboo.length\n : 0,\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3240, + 5120 + ], + "id": "aaa3a012-5f5f-412d-93fb-890f7093b906", + "name": "Preparar respuesta final" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{\n(() => {\n const data = $json || {};\n\n const original =\n data.originalResponse ||\n data.original_response ||\n data.response ||\n data.cruceResponse ||\n data.cruce_response ||\n data;\n\n const summary = original.summary || data.summary || {};\n const rows = original.rows || data.rows || [];\n const bankWithoutBamboo =\n original.bankWithoutBamboo ||\n data.bankWithoutBamboo ||\n [];\n const bambooSummary =\n original.bambooSummary ||\n data.bambooSummary ||\n {};\n\n const reportUrl =\n data.reportUrl ||\n data.report_url ||\n data.googleSheetUrl ||\n data.google_sheet_url ||\n data.spreadsheetUrl ||\n data.spreadsheet_url ||\n original.reportUrl ||\n original.report_url ||\n null;\n\n return {\n ok: original.ok ?? data.ok ?? true,\n message: original.message || data.message || (reportUrl\n ? 'Cruce procesado correctamente. Google Sheet generado.'\n : 'Cruce procesado correctamente.'),\n stage: reportUrl ? 'cruce_completado_con_reporte' : 'cruce_completado',\n errors: original.errors || data.errors || [],\n metadata: original.metadata || data.metadata || {},\n summary,\n rows,\n bankWithoutBamboo,\n bambooSummary,\n reportUrl,\n executionId: original.executionId || data.executionId || null,\n reportUpdated: original.reportUpdated ?? data.reportUpdated ?? false,\n reusingSheet: original.reusingSheet ?? data.reusingSheet ?? false,\n preservedResolutionsCount: Number(original.preservedResolutionsCount ?? data.preservedResolutionsCount ?? 0),\n clientRequestId: original.clientRequestId || data.clientRequestId || original.metadata?.client_request_id || data.metadata?.client_request_id || null,\n debug: {\n source_stage: data.stage || null,\n rows_returned:\n Array.isArray(rows) ? rows.length : 0,\n banco_sin_bamboo_rows:\n Array.isArray(bankWithoutBamboo)\n ? bankWithoutBamboo.length\n : 0,\n report_url_found: Boolean(reportUrl),\n },\n };\n})()\n}}", + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 3480, + 5120 + ], + "id": "3278f6df-9a9a-443d-a8a5-5267cf38286c", + "name": "Respond to Webhook" + }, + { + "parameters": { + "jsCode": "const prepared = $('Preparar histórico Supabase').first().json || {};\nconst payload = prepared.supabasePayload || {};\nconst metadata = payload.metadata || {};\nconst summary = payload.summary || {};\nconst reportUpdated = prepared.reportUpdated === true;\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatGeneratedAt(date) {\n const timeZone = 'America/Puerto_Rico';\n\n const dateLabel = new Intl.DateTimeFormat('es-PR', {\n timeZone,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(date);\n\n const timeLabel = new Intl.DateTimeFormat('es-PR', {\n timeZone,\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n }).format(date);\n\n const hour24Raw = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour: '2-digit',\n hour12: false,\n }).format(date);\n\n const hour24 =\n Number(String(hour24Raw).replace(/\\D/g, '')) % 24;\n\n const hourArticle =\n hour24 === 1 || hour24 === 13\n ? 'a la'\n : 'a las';\n\n return `${dateLabel} ${hourArticle} ${timeLabel}`;\n}\n\nfunction metricCard(label, value, accent) {\n return `\n \n \n \n \n \n ${escapeHtml(label)}\n \n\n \n ${numberValue(value).toLocaleString('en-US')}\n \n \n \n \n \n `;\n}\n\nconst countryCode = 'PR';\nconst countryName = 'Puerto Rico';\n\nconst periodLabel =\n payload.period_label ||\n metadata.period_label ||\n [\n payload.year || metadata.year,\n payload.month || metadata.month,\n payload.period_type || metadata.period_type,\n ]\n .filter(Boolean)\n .join(' · ') ||\n 'Período no especificado';\n\nconst executedByName =\n payload.ejecutado_por_nombre ||\n metadata.requested_by_name ||\n 'Usuario GLM';\n\nconst executedByEmail =\n payload.ejecutado_por_email ||\n metadata.requested_by_email ||\n '';\n\nconst reportUrl =\n payload.report_url ||\n prepared.reportUrl ||\n '';\n\nconst discrepancias = numberValue(\n payload.discrepancias ??\n summary.discrepancias\n);\n\nconst bancoSinNomina = numberValue(\n payload.banco_sin_nomina ??\n summary.bancoSinNomina\n);\n\nconst bancoSinBamboo = numberValue(\n payload.banco_sin_bamboo ??\n summary.bancoSinBamboo\n);\n\nconst diferenciasNombreBanco = numberValue(\n metadata.diferencias_nombre_banco ??\n summary.diferenciasNombreBanco\n);\n\nconst nominaSinCuenta = numberValue(\n payload.nomina_sin_cuenta ??\n summary.nominaSinCuenta\n);\n\nconst coincidencias = numberValue(\n payload.coincidencias ??\n summary.coincidencias\n);\n\nconst generatedAt = new Date();\nconst generatedAtLabel =\n formatGeneratedAt(generatedAt);\n\nconst statusLabel =\n String(payload.estado || '').toLowerCase() === 'resuelto'\n ? 'Resuelto'\n : 'Pendiente de revisión';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/bandera-nacional-tela-puerto-rico_113767-1759.avif';\n\nconst subject = reportUpdated\n ? `Reporte actualizado - Cruce de Cuentas ${countryName} - ${periodLabel}`\n : `Nuevo reporte generado - Cruce de Cuentas ${countryName} - ${periodLabel}`;\n\n/*\n * Todos son destinatarios directos.\n * Nadie se envía en CC.\n */\n\nconst to = [\n 'iaracena@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n].join(',');\n\n\n// Paleta oficial GLM.\nconst metricCards = [\n metricCard(\n 'Discrepancias',\n discrepancias,\n '#FF6A13'\n ),\n metricCard(\n 'Banco sin nómina',\n bancoSinNomina,\n '#FF8C00'\n ),\n metricCard(\n 'Banco sin Bamboo',\n bancoSinBamboo,\n '#E87722'\n ),\n metricCard(\n 'Diferencias nombre banco',\n diferenciasNombreBanco,\n '#5B7F95'\n ),\n metricCard(\n 'Nómina sin cuenta',\n nominaSinCuenta,\n '#4F758B'\n ),\n].join('');\n\nconst htmlBody = `\n\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n  \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n ${reportUpdated ? 'REPORTE ACTUALIZADO' : 'REPORTE GENERADO'}\n \n \n \n \n\n \n\n \n \n\n \n \n \n\n \n\n \n Cruce de Cuentas · ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n\n \n \n\n \n \n\n \n \n \n\n \n ${reportUpdated ? 'Se actualizó el reporte existente del Portal de Cruce de Cuentas GLM conservando las resoluciones previamente registradas que continúan correspondiendo a los mismos casos.' : 'Se completó una nueva ejecución del Portal de Cruce de Cuentas GLM. Este es el resumen del reporte generado.'}\n \n\n \n \n\n \n \n \n\n \n \n ${metricCards}\n \n \n\n \n \n\n \n \n \n\n \n\n \n\n \n\n \n Ejecutado por\n \n\n \n ${escapeHtml(executedByName)}\n \n\n \n ${escapeHtml(\n executedByEmail ||\n 'Correo no disponible'\n )}\n \n\n \n\n \n\n \n País\n \n\n \n \n\n \n \n \n\n \n\n \n ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(\n countryCode || '—'\n )}\n \n\n \n\n \n \n\n \n\n \n\n \n\n \n\n \n Período\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n ${escapeHtml(generatedAtLabel)}\n \n\n \n\n \n\n \n Estado inicial\n \n\n \n ${escapeHtml(statusLabel)}\n \n\n \n Coincidencias:\n ${coincidencias.toLocaleString('en-US')}\n \n\n \n\n \n\n \n\n \n \n\n \n ${\n reportUrl\n ? `\n \n \n\n \n Abrir reporte en Google Sheets\n \n\n \n \n `\n : ''\n }\n\n \n \n \n\n \n\n \n Notificación automática · Portal Cruce de Cuentas GLM\n
\n Los casos de revisión deben gestionarse directamente en el reporte generado.\n \n\n \n \n\n \n\n \n \n \n\n\n\n`;\n\nreturn [\n {\n json: {\n to,\n subject,\n htmlBody,\n\n notification: {\n country: countryCode,\n countryName,\n flagUrl,\n periodLabel,\n executedByName,\n executedByEmail,\n generatedAt: generatedAt.toISOString(),\n reportUrl,\n\n metrics: {\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo,\n diferenciasNombreBanco,\n nominaSinCuenta,\n coincidencias,\n },\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3240, + 5360 + ], + "id": "6875ed54-851a-44b3-b998-25735b5464dd", + "name": "Preparar correo reporte generado" + }, + { + "parameters": { + "sendTo": "={{$json.to}}", + "subject": "={{$json.subject}}", + "message": "={{ $json.htmlBody }}", + "options": { + "appendAttribution": false + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 3480, + 5360 + ], + "id": "442d0a94-4369-4bfd-8d8e-dbb9f77e4df3", + "name": "Enviar correo reporte generado", + "webhookId": "4bcdbd7b-503d-46ac-aeaf-9207f59f01e7", + "credentials": { + "gmailOAuth2": { + "id": "UDcO1FLJqA453V2D", + "name": "Gmail account 3" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "content": "# 🇭🇳 CRUCE DE CUENTAS — PUERTO RICO\n\nFlujo principal para una nómina quincenal de Puerto Rico + todos los planos bancarios BAC del período + BambooHR.\n\n- Monedas: PRL y USD.\n- BambooHR: `onlyCurrent=false`, filtrado por país/localidad Puerto Rico.\n- Q1: días 1–15. Q2: días 16–último día real del mes.\n- Los meses/años se calculan dinámicamente, incluidos años bisiestos.\n- La nómina usa las hojas operativas validadas de GLM Puerto Rico y omite hojas auxiliares/copias para evitar duplicados.\n\n\n### Correcciones de conciliación PR · 08-09-2026\n- Tolerancia monetaria operativa: ±0.05 por redondeos bancarios.\n- Banco sin Nómina se mantiene por transacción, sin agrupar pagos distintos.\n- Nombre en archivo truncado por un carácter al final no genera falso positivo.\n- BambooHR prioriza el nombre legal del cuentahabiente y perfiles vigentes en el período.\n- La cuenta mal digitada se determina por la cuenta real de la nómina vs. la cuenta real del banco.\n", + "height": 260, + "width": 520 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -2800, + 4768 + ], + "id": "ebbfb113-b7a1-42ff-bedb-bb02ad43ac33", + "name": "📌 PUERTO RICO · V1" + }, + { + "parameters": { + "content": "# 🏦 REGLA DE PLANOS — PUERTO RICO\n\nSe procesan todos los PDF BPPR `Consulta Detalle de Envío_HON_...` cargados. Se reconocen planes AR94 (PRL) y AR95US (USD), además de la moneda informada dentro del archivo.\n\nEl período se filtra por la fecha de aplicación de cada transacción: Q1 = 1–15 y Q2 = 16–fin de mes. El cruce se realiza por persona, cuenta, moneda y monto, con una segunda pasada controlada para detectar diferencias o posibles cuentas mal digitadas.", + "height": 300, + "width": 700 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -1712, + 4688 + ], + "id": "3f985792-728c-4f1a-baae-18e963312f62", + "name": "📌 REGLA BANCO PR" + }, + { + "parameters": { + "content": "# 📄 ARCHIVO DE NÓMINA — PUERTO RICO\n\nLa app recibe un solo Excel por quincena, por ejemplo `Nomina 7.1 2026 GLM PR.xlsx` o `Nomina 7.2 2026 GLM PR.xlsx`.\n\nEl flujo extrae únicamente las hojas de pago validadas (Colgate, POP Helper, Motorola, Nestlé, Whirlpool, FF Regional y GLM) y normaliza `NOMBRE`, `Identidad`, `# de Cuenta Bac` y `NETO A PAGAR` / `Total a Pagar`. Las hojas de copia/auxiliares no se contabilizan.", + "height": 240, + "width": 620 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -2320, + 5616 + ], + "id": "c1e22489-fa4a-49f8-984d-fa2c497620de", + "name": "📌 ARCHIVOS NÓMINA PR" + }, + { + "parameters": { + "content": "# 📊 SALIDA\n\nConserva la misma estructura de reportes de GT/TT/PA/SV, reutiliza el Google Sheet del mismo período si existe, actualiza Supabase y envía correo tanto en creación como en actualización.\n\nLos valores se escriben en Google Sheets con `RAW` para preservar las cuentas bancarias como texto exacto, incluyendo ceros iniciales y evitando notación científica.", + "height": 250, + "width": 720 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 1672, + 5520 + ], + "id": "dd6b181f-198e-4a8a-a363-a0db9d5741b5", + "name": "📌 REPORTES" + }, + { + "parameters": { + "operation": "pdf", + "options": { + "joinPages": true, + "keepSource": "json" + } + }, + "type": "n8n-nodes-base.extractFromFile", + "typeVersion": 1.1, + "position": [ + -1840, + 4960 + ], + "id": "d9f7fced-ae32-4065-87fb-9f86c8c286f2", + "name": "Extract - PDF Banco PR" + }, + { + "parameters": { + "jsCode": "const items = $input.all();\nconst base = $('Preparar entrada app').first().json || {};\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\r/g, '')\n .replace(/[ \\t]+/g, ' ')\n .trim();\n}\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\D/g, '').trim();\n}\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\nfunction parseMoney(value) {\n const parsed = Number.parseFloat(String(value ?? '').replace(/[$,\\s]/g, ''));\n return Number.isFinite(parsed) ? parsed : 0;\n}\nfunction toIsoDate(raw) {\n const value = clean(raw);\n let match = value.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/);\n if (match) return `${match[1]}-${String(match[2]).padStart(2, '0')}-${String(match[3]).padStart(2, '0')}`;\n match = value.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{2}|\\d{4})$/);\n if (!match) return '';\n const year = match[3].length === 2 ? 2000 + Number(match[3]) : Number(match[3]);\n return `${year}-${String(match[1]).padStart(2, '0')}-${String(match[2]).padStart(2, '0')}`;\n}\nfunction cleanNameFragment(value) {\n let text = clean(value).replace(/\\f/g, ' ');\n if (!text) return '';\n const normalized = text.toLowerCase();\n const noise = [\n 'transaction details:', 'transaction details', 'name', 'id', 'number',\n 'amount d/c bank id account#', 'acct', 'type', 'trace # addenda',\n 'batch status', 'extracted entry class: directdeposit', 'entry class: directdeposit',\n 'company', 'company name:', 'name:', 'effective', 'date:', 'template',\n 'release date', 'and time:', 'extract file', 'total # credits:',\n 'total # debits:', 'total $ offset credit:', 'subtotal # credits:',\n 'grand totals:', 'grand total # credits:'\n ];\n if (noise.some((entry) => normalized === entry || normalized.startsWith(entry))) return '';\n if (/^[\\d\\-\\s]+$/.test(text)) return '';\n text = text.replace(/\\s*\\d[\\d-]{2,}\\s*$/, '').trim();\n if (!text || /^[\\d\\-\\s]+$/.test(text)) return '';\n return text;\n}\nfunction sourceMeta(index) {\n try { return $('Preparar PDF banco PR').itemMatching(index).json || {}; }\n catch { return {}; }\n}\nfunction templateLabel(templateId) {\n const id = clean(templateId).toUpperCase();\n if (id === 'MOTOP') return 'Motorola Payroll';\n if (id === 'APPLE PAYR') return 'APPLE PAYR';\n if (id === 'PAYROLLV2') return 'PayrollV2';\n return clean(templateId) || 'BPPR';\n}\nfunction expectedTotals(text) {\n const grandCount = text.match(/Grand Total # Credits:\\s*(\\d+)/i);\n const grandAmount = text.match(/Grand Total Credit Amt:\\s*\\$([\\d,]+\\.\\d{2})/i);\n if (grandCount && grandAmount) {\n return { count: Number(grandCount[1]), amount: roundMoney(parseMoney(grandAmount[1])), source: 'grand_total' };\n }\n let count = 0;\n let amount = 0;\n const regex = /(?:^|\\n)Total # Credits:\\s*(\\d+)[^\\n]*Total Credit Amt:\\s*\\$([\\d,]+\\.\\d{2})/gim;\n for (const match of text.matchAll(regex)) {\n count += Number(match[1] || 0);\n amount = roundMoney(amount + parseMoney(match[2]));\n }\n return count > 0 ? { count, amount, source: 'batch_totals' } : null;\n}\nfunction effectiveDateFromText(text) {\n const subtotal = text.match(/Subtotals? for Effective Date:\\s*(\\d{4}-\\d{2}-\\d{2})/i);\n if (subtotal) return toIsoDate(subtotal[1]);\n const raw = text.match(/Effective\\s*(?:\\n|\\s)*Date:\\s*(?:\\n|\\s)*(\\d{1,2}\\/\\d{1,2}\\/\\d{2,4})/i);\n if (raw) return toIsoDate(raw[1]);\n return '';\n}\nfunction inSelectedPeriod(dateValue) {\n const start = clean(base.metadata?.period_start);\n const end = clean(base.metadata?.period_end);\n if (!dateValue || !start || !end) return true;\n return dateValue >= start && dateValue <= end;\n}\n\nconst allRows = [];\nconst fileSummaries = [];\nconst seenTransactions = new Set();\n\nfor (let itemIndex = 0; itemIndex < items.length; itemIndex++) {\n const item = items[itemIndex] || {};\n const linked = sourceMeta(itemIndex);\n const sourceFile = clean(linked.source_file || item.json?.source_file || `Banco PR ${itemIndex + 1}.pdf`);\n const text = String(item.json?.text ?? item.json?.data ?? item.json?.content ?? '');\n if (!text.trim()) {\n throw new Error(`Puerto Rico: no se pudo extraer texto del PDF bancario ${sourceFile}.`);\n }\n\n const effectiveDate = effectiveDateFromText(text);\n if (!effectiveDate) {\n throw new Error(`Puerto Rico: no se encontró la Effective Date en ${sourceFile}.`);\n }\n if (!inSelectedPeriod(effectiveDate)) {\n throw new Error(`Puerto Rico: ${sourceFile} tiene Effective Date ${effectiveDate}, fuera del período seleccionado ${base.metadata?.period_start || ''} a ${base.metadata?.period_end || ''}.`);\n }\n\n const expected = expectedTotals(text);\n const lines = text.split(/\\n+/).map((line) => clean(line)).filter(Boolean);\n let currentTemplate = '';\n let pendingLines = [];\n const rowsFromFile = [];\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex];\n const templateMatch = line.match(/^Template ID:\\s*(.+)$/i);\n if (templateMatch) {\n currentTemplate = clean(templateMatch[1]);\n pendingLines = [];\n continue;\n }\n\n if (/^(?:Total|Subtotal|Grand Total)/i.test(line)) {\n pendingLines = [];\n continue;\n }\n\n const tx = line.match(/^(.*?)\\$([\\d,]+\\.\\d{2})\\s+([CD])\\s+(\\d{6,12})\\s+([0-9-]{5,25})\\s+(Checking|Savings|CHK)\\s+(\\d{6,30})(?:\\s+(.*))?$/i);\n if (!tx) {\n const fragment = cleanNameFragment(line);\n if (fragment) pendingLines.push(fragment);\n if (pendingLines.length > 8) pendingLines = pendingLines.slice(-8);\n continue;\n }\n\n let prefix = clean(tx[1]);\n let idNumber = '';\n const idAtEnd = prefix.match(/(\\d[\\d-]{5,})\\s*$/);\n if (idAtEnd) {\n idNumber = normalizeAccount(idAtEnd[1]);\n prefix = clean(prefix.slice(0, idAtEnd.index));\n }\n\n const debitCredit = String(tx[3]).toUpperCase();\n if (debitCredit !== 'C') {\n pendingLines = [];\n continue;\n }\n\n const prefixName = cleanNameFragment(prefix);\n const nameParts = [...pendingLines, prefixName].filter(Boolean);\n const name = clean(nameParts.join(' '));\n const amount = roundMoney(parseMoney(tx[2]));\n const bankId = normalizeAccount(tx[4]);\n const account = normalizeAccount(tx[5]);\n const accountType = clean(tx[6]);\n const trace = normalizeAccount(tx[7]);\n const addenda = clean(tx[8]);\n const templateId = currentTemplate || 'BPPR';\n const duplicateKey = `${effectiveDate}|${trace}|${account}|${amount.toFixed(2)}`;\n\n if (seenTransactions.has(duplicateKey)) {\n throw new Error(`Puerto Rico: se detectó una transacción bancaria duplicada (${trace}) al consolidar los PDF cargados.`);\n }\n seenTransactions.add(duplicateKey);\n\n const row = {\n source_file: sourceFile,\n row_number: lineIndex + 1,\n bank_row_key: `PR|${sourceFile}|${templateId}|${trace}|${lineIndex + 1}`,\n account,\n raw_account: account,\n account_is_valid: account.length >= 5,\n reference: '',\n participant_id: '',\n bank_id_number: idNumber,\n bank_id: bankId,\n shipment_number: trace,\n plan_number: templateId,\n plan_label: templateLabel(templateId),\n template_id: templateId,\n bank_name_file: name || `Pago BPPR ${trace}`,\n bank_account_holder: '',\n amount,\n currency: 'USD',\n application_date: effectiveDate,\n concept: '',\n addenda,\n account_type: accountType,\n status: 'C',\n };\n rowsFromFile.push(row);\n allRows.push(row);\n pendingLines = [];\n }\n\n const parsedTotal = roundMoney(rowsFromFile.reduce((sum, row) => sum + row.amount, 0));\n if (expected) {\n if (rowsFromFile.length !== expected.count) {\n throw new Error(`Puerto Rico: ${sourceFile} reporta ${expected.count} créditos, pero se pudieron leer ${rowsFromFile.length}. No se generará un reporte incompleto.`);\n }\n if (Math.abs(parsedTotal - expected.amount) > 0.01) {\n throw new Error(`Puerto Rico: el total leído de ${sourceFile} (USD ${parsedTotal.toFixed(2)}) no coincide con el total del PDF (USD ${expected.amount.toFixed(2)}).`);\n }\n }\n\n const batchSummaryMap = new Map();\n for (const row of rowsFromFile) {\n const key = row.template_id;\n const current = batchSummaryMap.get(key) || { template_id: key, template_name: row.plan_label, rows_count: 0, total_amount: 0 };\n current.rows_count += 1;\n current.total_amount = roundMoney(current.total_amount + row.amount);\n batchSummaryMap.set(key, current);\n }\n\n fileSummaries.push({\n file_name: sourceFile,\n ok: true,\n effective_date: effectiveDate,\n rows_count: rowsFromFile.length,\n total_amount: parsedTotal,\n currency: 'USD',\n expected_count: expected?.count ?? null,\n expected_total: expected?.amount ?? null,\n expected_source: expected?.source ?? null,\n batches: Array.from(batchSummaryMap.values()),\n });\n}\n\nif (!allRows.length) {\n throw new Error('Puerto Rico: no se encontró ninguna transacción bancaria válida en los PDF cargados.');\n}\n\nconst totalAmount = roundMoney(allRows.reduce((sum, row) => sum + row.amount, 0));\nreturn [{\n json: {\n ...base,\n stage: 'banco_pr_normalizado',\n bank: {\n source: 'bppr_batch_summary_pdf',\n files_count: fileSummaries.length,\n file_summaries: fileSummaries,\n rows_count: allRows.length,\n valid_rows_count: allRows.length,\n total_amount: totalAmount,\n totals_by_currency: { USD: totalAmount },\n rows: allRows,\n },\n debug_bank: {\n file_summaries: fileSummaries,\n parsed_rows_preview: allRows.slice(0, 100),\n },\n },\n}];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1600, + 4960 + ], + "id": "aaead29e-f149-443f-80ab-7d6bad0a2b6f", + "name": "Parsear PDF banco PR" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "ed579987-a9c3-404e-a548-8c66fae47745", + "leftValue": "={{ $json.ok }}", + "rightValue": "", + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + -2320, + 5200 + ], + "id": "e48a9f66-61b5-4880-8f51-4032744e9f8b", + "name": "¿Entrada PR válida?" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, stage: $json.stage || 'entrada_pr_invalida', message: 'No fue posible iniciar el cruce de Puerto Rico.', errors: Array.isArray($json.errors) ? $json.errors : ['Solicitud inválida.'] } }}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + -2080, + 5720 + ], + "id": "9a663a01-3ad7-4904-a2ac-239c9c6a2ef8", + "name": "Responder error entrada PR" + } + ], + "pinData": {}, + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "Preparar entrada app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar entrada app": { + "main": [ + [ + { + "node": "¿Entrada PR válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Entrada PR válida?": { + "main": [ + [ + { + "node": "Preparar PDF banco PR", + "type": "main", + "index": 0 + }, + { + "node": "Preparar archivos nómina PR", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder error entrada PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar PDF banco PR": { + "main": [ + [ + { + "node": "Extract - PDF Banco PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract - PDF Banco PR": { + "main": [ + [ + { + "node": "Parsear PDF banco PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parsear PDF banco PR": { + "main": [ + [ + { + "node": "Merge Banco + Nómina PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar archivos nómina PR": { + "main": [ + [ + { + "node": "Extract - Archivo Nómina PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract - Archivo Nómina PR": { + "main": [ + [ + { + "node": "Normalizar Nómina PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar Nómina PR": { + "main": [ + [ + { + "node": "Merge Banco + Nómina PR", + "type": "main", + "index": 1 + } + ] + ] + }, + "Merge Banco + Nómina PR": { + "main": [ + [ + { + "node": "HTTP - Empleados BambooHR PR", + "type": "main", + "index": 0 + }, + { + "node": "Merge - Agregar BambooHR PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "HTTP - Empleados BambooHR PR": { + "main": [ + [ + { + "node": "Normalizar BambooHR PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar BambooHR PR": { + "main": [ + [ + { + "node": "Merge - Agregar BambooHR PR", + "type": "main", + "index": 1 + } + ] + ] + }, + "Merge - Agregar BambooHR PR": { + "main": [ + [ + { + "node": "Obtener alias BambooHR Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Obtener alias BambooHR Supabase": { + "main": [ + [ + { + "node": "Cruzar Nómina vs Banco", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cruzar Nómina vs Banco": { + "main": [ + [ + { + "node": "Preparar Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar Google Sheet": { + "main": [ + [ + { + "node": "Buscar reporte existente Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Buscar reporte existente Supabase": { + "main": [ + [ + { + "node": "Crear Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Crear Google Sheet": { + "main": [ + [ + { + "node": "Leer Google Sheet actual", + "type": "main", + "index": 0 + } + ] + ] + }, + "Leer Google Sheet actual": { + "main": [ + [ + { + "node": "Preparar actualización Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar actualización Google Sheet": { + "main": [ + [ + { + "node": "Actualizar estructura Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Actualizar estructura Google Sheet": { + "main": [ + [ + { + "node": "Limpiar Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Limpiar Google Sheet": { + "main": [ + [ + { + "node": "Escribir Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Escribir Google Sheet": { + "main": [ + [ + { + "node": "Formatear Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Formatear Google Sheet": { + "main": [ + [ + { + "node": "Preparar permisos Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar permisos Google Sheet": { + "main": [ + [ + { + "node": "Compartir Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Compartir Google Sheet": { + "main": [ + [ + { + "node": "Preparar histórico Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar histórico Supabase": { + "main": [ + [ + { + "node": "Insertar histórico Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Insertar histórico Supabase": { + "main": [ + [ + { + "node": "Preparar respuesta final", + "type": "main", + "index": 0 + }, + { + "node": "Preparar correo reporte generado", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar respuesta final": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar correo reporte generado": { + "main": [ + [ + { + "node": "Enviar correo reporte generado", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "1a174b21-9f77-443a-9616-ef028022f4d8", + "meta": { + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "tags": [] +} diff --git a/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV.json b/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV.json deleted file mode 100644 index 1d1d2ca..0000000 --- a/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV.json +++ /dev/null @@ -1,510 +0,0 @@ -{ - "name": "Portal de Verificación de Nómina - Reportar corrección BambooHR a RRHH - GT TT PA SV", - "nodes": [ - { - "parameters": { - "httpMethod": "POST", - "path": "cruce-cuentas-bamboo-correccion", - "responseMode": "responseNode", - "options": {} - }, - "type": "n8n-nodes-base.webhook", - "typeVersion": 2.1, - "position": [ - 512, - 1232 - ], - "id": "0224eae5-7a42-44cb-9690-979802f07e6f", - "name": "Webhook", - "webhookId": "2e0a0f53-605b-4470-b41e-c028b9ff03eb" - }, - { - "parameters": { - "jsCode": "const incoming = $input.first().json || {};\nconst rawBody = incoming.body ?? incoming;\n\nlet body = rawBody;\n\nif (rawBody && typeof rawBody === 'object' && typeof rawBody.payload === 'string') {\n try {\n body = JSON.parse(rawBody.payload);\n } catch (error) {\n throw new Error('El payload recibido no contiene JSON válido.');\n }\n}\n\nif (!body || typeof body !== 'object') {\n throw new Error('No se recibió una solicitud válida.');\n}\n\nconst accessToken = String(\n (rawBody && typeof rawBody === 'object' ? rawBody.access_token : '') ||\n body.access_token ||\n ''\n).trim();\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction normalizeName(value) {\n return clean(value)\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst country = clean(body.country).toUpperCase();\nif (!['GT', 'TT', 'PA', 'SV'].includes(country)) {\n throw new Error('País no permitido para reportar correcciones BambooHR.');\n}\n\nif (!accessToken) {\n throw new Error('No se recibió una sesión autenticada de Supabase.');\n}\n\nif (body.confirmed_same_person !== true) {\n throw new Error('La revisión manual debe confirmar que se trata de la misma persona.');\n}\n\nconst sourceNames = Array.from(new Set([\n ...(Array.isArray(body.source_names) ? body.source_names : []),\n body.source_name,\n body.bank_name_file,\n body.bank_account_holder,\n]\n .map(clean)\n .filter(Boolean)\n .filter((value) => normalizeName(value))));\n\nif (!sourceNames.length) {\n throw new Error('No se recibió el nombre detectado en Banco sin BambooHR.');\n}\n\nconst bambooName = clean(body.bamboo_name);\nif (!bambooName) {\n throw new Error('Debes indicar el nombre correcto en BambooHR.');\n}\n\nreturn [{\n json: {\n accessToken,\n request: {\n country,\n countryName: ({ GT: 'Guatemala', TT: 'Trinidad y Tobago', PA: 'Panamá', SV: 'El Salvador' })[country],\n sourceName: clean(body.source_name) || sourceNames[0],\n sourceNames,\n bankNameFile: clean(body.bank_name_file),\n bankAccountHolder: clean(body.bank_account_holder),\n bankAccount: clean(body.bank_account),\n bankAmount: Number.isFinite(Number(body.bank_amount)) ? Number(body.bank_amount) : null,\n currency: clean(body.currency) || ({ GT: 'GTQ', TT: 'TTD', PA: 'USD', SV: 'USD' })[country],\n bambooName,\n bambooEmployeeNumber: clean(body.bamboo_employee_number),\n reason: clean(body.reason) || 'Corrección de identidad en BambooHR',\n comment: clean(body.comment),\n executionId: clean(body.execution_id),\n reportUrl: clean(body.report_url),\n periodLabel: clean(body.period_label),\n periodStart: clean(body.period_start),\n periodEnd: clean(body.period_end),\n requestedByNameClaimed: clean(body.requested_by_name),\n requestedByEmailClaimed: clean(body.requested_by_email).toLowerCase(),\n },\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 752, - 1232 - ], - "id": "114aaba8-ca39-4e55-9e59-ec92f7abfa84", - "name": "Preparar solicitud" - }, - { - "parameters": { - "method": "POST", - "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/cruce_cuentas_mi_acceso", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE" - }, - { - "name": "Authorization", - "value": "={{ 'Bearer ' + $('Preparar solicitud').first().json.accessToken }}" - }, - { - "name": "Content-Type", - "value": "application/json" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": {}, - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 992, - 1232 - ], - "id": "d12539bc-5be4-4be1-b1d6-4cddfad8f32f", - "name": "Validar acceso Supabase", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "jsCode": "const raw = $input.first().json || {};\nconst access = Array.isArray(raw) ? (raw[0] || {}) : raw;\nconst prepared = $('Preparar solicitud').first().json || {};\nconst request = prepared.request || {};\n\nconst email = String(access.email || '').trim().toLowerCase();\nconst countries = Array.isArray(access.paises)\n ? access.paises.map((value) => String(value || '').trim().toUpperCase()).filter(Boolean)\n : [];\n\nif (\n access.ok !== true ||\n access.autenticado !== true ||\n access.autorizado !== true ||\n access.activo !== true ||\n access.acceso_app !== true ||\n !email\n) {\n throw new Error(access.motivo || 'El usuario no tiene acceso autorizado al Cruce de Cuentas.');\n}\n\nif (countries.length && !countries.includes(request.country)) {\n throw new Error(`El usuario autenticado no tiene acceso al país ${request.country}.`);\n}\n\nreturn [{\n json: {\n ...request,\n requestedByName: String(access.nombre || request.requestedByNameClaimed || email).trim(),\n requestedByEmail: email,\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1232, - 1232 - ], - "id": "4ef140fa-293e-4684-87b5-70e710dbe732", - "name": "Validar usuario" - }, - { - "parameters": { - "method": "POST", - "url": "https://glm.bamboohr.com/api/v1/reports/custom?format=JSON&onlyCurrent=false", - "authentication": "genericCredentialType", - "genericAuthType": "httpBasicAuth", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "Accept", - "value": "application/json" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": { - "title": "Validación manual - Corrección identidad Cruce de Cuentas", - "fields": [ - "firstName", - "middleName", - "lastName", - "displayName", - "location", - "status", - "employeeNumber", - "country" - ] - }, - "options": { - "response": { - "response": { - "responseFormat": "json" - } - }, - "timeout": 300000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 1472, - 1232 - ], - "id": "c84ad074-c790-4317-81ef-27dc6ed32706", - "name": "Consultar empleado BambooHR", - "retryOnFail": true, - "maxTries": 3, - "waitBetweenTries": 3000, - "credentials": { - "httpBasicAuth": { - "id": "7VrpNZ2jBLmiJ35q", - "name": "BambooHR GLM Full Access" - } - } - }, - { - "parameters": { - "jsCode": "const request = $('Validar usuario').first().json || {};\nconst raw = $input.first().json || {};\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction normalizeName(value) {\n return clean(value)\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeIdentifier(value) {\n return clean(value).toUpperCase().replace(/[^A-Z0-9]/g, '');\n}\n\nfunction countryMatches(employee) {\n const location = normalizeName(\n employee.location ||\n employee.jobInformationLocation ||\n employee.jobLocation ||\n ''\n );\n const country = normalizeName(employee.country || '');\n const combined = `${location} ${country}`.trim();\n\n if (request.country === 'GT') {\n return combined.includes('guatemala');\n }\n\n if (request.country === 'PA') {\n return combined.includes('panama');\n }\n\n if (request.country === 'SV') {\n return combined.includes('el salvador') || combined.includes('salvador');\n }\n\n return combined.includes('trinidad') || combined.includes('tobago');\n}\n\nfunction employeeAliases(employee) {\n const first = clean(employee.firstName || employee.first_name);\n const middle = clean(employee.middleName || employee.middle_name);\n const last = clean(employee.lastName || employee.last_name);\n const display = clean(employee.displayName || employee.fullName || employee.full_name);\n\n return Array.from(new Set([\n display,\n [first, middle, last].filter(Boolean).join(' '),\n [first, last].filter(Boolean).join(' '),\n ].map(clean).filter(Boolean)));\n}\n\nconst employees = Array.isArray(raw.employees)\n ? raw.employees\n : Array.isArray(raw.data?.employees)\n ? raw.data.employees\n : [];\n\nif (!employees.length) {\n throw new Error('BambooHR no devolvió empleados para validar la corrección.');\n}\n\nconst candidates = employees.filter(countryMatches);\nif (!candidates.length) {\n throw new Error(`No se encontraron empleados de ${request.countryName} en BambooHR.`);\n}\n\nconst requestedNumber = normalizeIdentifier(request.bambooEmployeeNumber);\nlet matches = [];\nlet matchedBy = '';\n\nif (requestedNumber) {\n matches = candidates.filter((employee) =>\n normalizeIdentifier(employee.employeeNumber || employee.employee_number) === requestedNumber\n );\n matchedBy = 'employee_number';\n}\n\nif (!matches.length) {\n const targetName = normalizeName(request.bambooName);\n matches = candidates.filter((employee) =>\n employeeAliases(employee).some((alias) => normalizeName(alias) === targetName)\n );\n matchedBy = 'exact_bamboo_name';\n}\n\nif (matches.length !== 1) {\n if (matches.length > 1) {\n throw new Error('La identificación en BambooHR es ambigua. Indica el Employee Number para seleccionar una sola persona.');\n }\n\n throw new Error(\n 'No se encontró exactamente ese empleado en BambooHR para el país seleccionado. Revisa el nombre o indica el Employee Number.'\n );\n}\n\nconst employee = matches[0];\nconst aliases = employeeAliases(employee);\nconst actualName = clean(employee.displayName) || aliases[0] || request.bambooName;\nconst actualEmployeeNumber = clean(employee.employeeNumber || employee.employee_number);\nconst bambooId = clean(employee.employeeId || employee.id);\nconst now = new Date().toISOString();\n\nconst recipientsByCountry = {\n GT: ['haguirre@gomezleemarketing.com'],\n TT: ['lnicomejack@gomezleemarketing.com'],\n PA: ['dpenalba@gomezleemarketing.com'],\n SV: ['cmalta@gomezleemarketing.com'],\n};\nconst recipients = recipientsByCountry[request.country] || [];\n\nconst aliasPayload = request.sourceNames.map((sourceName) => ({\n pais: request.country,\n nombre_origen: sourceName,\n nombre_origen_normalizado: normalizeName(sourceName),\n cuenta_bancaria: String(request.bankAccount || '').replace(/[^0-9A-Za-z]+/g, ''),\n bamboo_employee_number: actualEmployeeNumber || null,\n bamboo_employee_id: bambooId || null,\n bamboo_nombre: actualName,\n motivo: request.reason,\n comentario: request.comment || null,\n activo: true,\n validado_por_email: request.requestedByEmail,\n validado_por_nombre: request.requestedByName,\n last_reported_at: now,\n updated_at: now,\n}));\n\nreturn [{\n json: {\n request,\n matchedBy,\n employee: {\n bambooId,\n employeeNumber: actualEmployeeNumber,\n name: actualName,\n status: clean(employee.status),\n location: clean(employee.location),\n },\n recipients,\n aliasPayload,\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1712, - 1232 - ], - "id": "188d62e3-f420-4446-8c87-3e13d4435ddd", - "name": "Resolver empleado BambooHR" - }, - { - "parameters": { - "method": "POST", - "url": "https://dbit.digitalcompass.agency/rest/v1/cruce_cuentas_bamboo_aliases?on_conflict=pais,nombre_origen_normalizado,cuenta_bancaria", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "Prefer", - "value": "resolution=merge-duplicates,return=representation" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={{ $('Resolver empleado BambooHR').first().json.aliasPayload }}", - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 1952, - 1232 - ], - "id": "bfe12fe6-b92b-405f-882d-5200a5877c78", - "name": "Guardar alias Supabase", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "jsCode": "const resolved = $('Resolver empleado BambooHR').first().json || {};\nconst request = resolved.request || {};\nconst employee = resolved.employee || {};\nconst aliasResponseItems = $input.all().map((item) => item.json || {});\n\nfunction flattenRows(items) {\n const rows = [];\n for (const item of items) {\n if (Array.isArray(item)) rows.push(...item);\n else if (Array.isArray(item.data)) rows.push(...item.data);\n else rows.push(item);\n }\n return rows.filter((row) => row && typeof row === 'object');\n}\n\nconst aliasRows = flattenRows(aliasResponseItems);\nconst aliasIds = aliasRows\n .map((row) => row.id)\n .filter(Boolean);\n\nconst requestPayload = {\n pais: request.country,\n nombre_origen_principal: request.sourceName,\n nombres_origen: request.sourceNames,\n cuenta_bancaria: request.bankAccount || null,\n monto_banco: request.bankAmount,\n moneda: request.currency,\n bamboo_employee_number: employee.employeeNumber || null,\n bamboo_employee_id: employee.bambooId || null,\n bamboo_nombre: employee.name,\n motivo: request.reason,\n comentario: request.comment || null,\n execution_id: request.executionId || null,\n report_url: request.reportUrl || null,\n period_label: request.periodLabel || null,\n period_start: request.periodStart || null,\n period_end: request.periodEnd || null,\n solicitado_por_email: request.requestedByEmail,\n solicitado_por_nombre: request.requestedByName,\n destinatarios_rrhh: resolved.recipients || [],\n alias_ids: aliasIds,\n estado: 'pendiente_notificacion',\n correo_enviado: false,\n};\n\nreturn [{ json: { requestPayload, aliasIds } }];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 2192, - 1232 - ], - "id": "7207bfa1-c774-4dd9-8923-6f3e1494845d", - "name": "Preparar auditoría" - }, - { - "parameters": { - "method": "POST", - "url": "https://dbit.digitalcompass.agency/rest/v1/cruce_cuentas_bamboo_correcciones", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "Prefer", - "value": "return=representation" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={{ $json.requestPayload }}", - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 2432, - 1232 - ], - "id": "296a7fdf-22e8-4256-a3fc-4de184eb3589", - "name": "Guardar solicitud Supabase", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "jsCode": "const resolved = $('Resolver empleado BambooHR').first().json || {};\nconst request = resolved.request || {};\nconst employee = resolved.employee || {};\nconst auditInput = $input.all().map((item) => item.json || {});\n\nfunction firstObject(value) {\n if (Array.isArray(value)) return value[0] || {};\n if (value?.data && Array.isArray(value.data)) return value.data[0] || {};\n return value || {};\n}\n\nconst auditRow = firstObject(auditInput[0]);\nconst requestId = String(auditRow.id || '');\nif (!requestId) {\n throw new Error('Supabase no devolvió el ID de la solicitud de corrección.');\n}\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nconst logoUrl = 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\nconst flagUrl = request.country === 'TT'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Trinidad_and_Tobago.svg.webp'\n : request.country === 'PA'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Panama.svg'\n : request.country === 'SV'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_El_Salvador.svg.webp'\n : 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Guatemala.svg.webp';\n\nconst to = (resolved.recipients || []).join(',');\nconst subject = `Corrección requerida en BambooHR - ${request.countryName} - ${request.sourceName}`;\n\nconst reportButton = request.reportUrl\n ? `Abrir reporte de Cruce de Cuentas`\n : '';\n\nconst htmlBody = `\n\n\n\n\n
\n\n\n
 
\n\n\n\n
\"GomezLee\"Bandera
\n
\n
Corrección de identidad · BambooHR
\n

Revisión solicitada para ${escapeHtml(request.countryName)}

\n

Durante la revisión manual de Banco sin BambooHR se confirmó que el pago corresponde a una persona existente en BambooHR, pero su nombre no está permitiendo identificarla correctamente en el cruce.

\n\n\n\n\n\n\n\n\n
Nombre detectado${escapeHtml(request.sourceName)}
Empleado en BambooHR${escapeHtml(employee.name)}
Employee Number${escapeHtml(employee.employeeNumber || 'No disponible')}
Motivo${escapeHtml(request.reason)}
Período${escapeHtml(request.periodLabel || 'No especificado')}
Reportado por${escapeHtml(request.requestedByName)} · ${escapeHtml(request.requestedByEmail)}
\n\n${request.comment ? `
Comentario: ${escapeHtml(request.comment)}
` : ''}\n\n

Por favor, revisen y corrijan el nombre o la información correspondiente en BambooHR. La equivalencia validada ya quedó registrada en el Cruce de Cuentas para evitar que este mismo falso positivo se repita mientras se realiza la corrección.

\n\n${reportButton ? `
${reportButton}
` : ''}\n\n
\n
Notificación automática · Portal Cruce de Cuentas GLM · Uso interno
\n
\n
\n`;\n\nreturn [{\n json: {\n requestId,\n to,\n subject,\n htmlBody,\n country: request.country,\n countryName: request.countryName,\n aliasCount: Array.isArray(resolved.aliasPayload) ? resolved.aliasPayload.length : 0,\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 2672, - 1232 - ], - "id": "4cda98d3-2fa1-4685-9a9e-d3200ec99d43", - "name": "Preparar correo RRHH" - }, - { - "parameters": { - "sendTo": "={{$json.to}}", - "subject": "={{$json.subject}}", - "message": "={{$json.htmlBody}}", - "options": { - "appendAttribution": false - } - }, - "type": "n8n-nodes-base.gmail", - "typeVersion": 2.2, - "position": [ - 2912, - 1232 - ], - "id": "4bbb64e6-01f8-4899-96c1-aaa5acee33f3", - "name": "Enviar correo RRHH", - "retryOnFail": true, - "maxTries": 3, - "waitBetweenTries": 2000, - "webhookId": "8fd723dd-b333-4db8-9c43-2f5a9d86f86e", - "credentials": { - "gmailOAuth2": { - "id": "UDcO1FLJqA453V2D", - "name": "Gmail account 3" - } - } - }, - { - "parameters": { - "method": "PATCH", - "url": "={{ 'https://dbit.digitalcompass.agency/rest/v1/cruce_cuentas_bamboo_correcciones?id=eq.' + $('Preparar correo RRHH').first().json.requestId }}", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "Prefer", - "value": "return=minimal" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={{ { correo_enviado: true, correo_enviado_at: new Date().toISOString(), estado: 'notificado_rrhh' } }}", - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 3152, - 1232 - ], - "id": "5e5ff653-0888-4470-9c04-4eb07e68c0e2", - "name": "Marcar correo enviado", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "respondWith": "json", - "responseBody": "={{ { ok: true, message: 'Caso validado, equivalencia registrada y correo enviado a RRHH.', country: $('Preparar correo RRHH').first().json.country, countryName: $('Preparar correo RRHH').first().json.countryName, aliasCount: $('Preparar correo RRHH').first().json.aliasCount, requestId: $('Preparar correo RRHH').first().json.requestId } }}", - "options": { - "responseCode": 200, - "responseHeaders": { - "entries": [ - { - "name": "Content-Type", - "value": "application/json" - } - ] - } - } - }, - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.5, - "position": [ - 3392, - 1232 - ], - "id": "67d862b9-df06-44d6-9654-2e59c630a51d", - "name": "Respond to Webhook" - } - ], - "pinData": {}, - "connections": { - "Webhook": { - "main": [ - [ - { - "node": "Preparar solicitud", - "type": "main", - "index": 0 - } - ] - ] - }, - "Preparar solicitud": { - "main": [ - [ - { - "node": "Validar acceso Supabase", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validar acceso Supabase": { - "main": [ - [ - { - "node": "Validar usuario", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validar usuario": { - "main": [ - [ - { - "node": "Consultar empleado BambooHR", - "type": "main", - "index": 0 - } - ] - ] - }, - "Consultar empleado BambooHR": { - "main": [ - [ - { - "node": "Resolver empleado BambooHR", - "type": "main", - "index": 0 - } - ] - ] - }, - "Resolver empleado BambooHR": { - "main": [ - [ - { - "node": "Guardar alias Supabase", - "type": "main", - "index": 0 - } - ] - ] - }, - "Guardar alias Supabase": { - "main": [ - [ - { - "node": "Preparar auditoría", - "type": "main", - "index": 0 - } - ] - ] - }, - "Preparar auditoría": { - "main": [ - [ - { - "node": "Guardar solicitud Supabase", - "type": "main", - "index": 0 - } - ] - ] - }, - "Guardar solicitud Supabase": { - "main": [ - [ - { - "node": "Preparar correo RRHH", - "type": "main", - "index": 0 - } - ] - ] - }, - "Preparar correo RRHH": { - "main": [ - [ - { - "node": "Enviar correo RRHH", - "type": "main", - "index": 0 - } - ] - ] - }, - "Enviar correo RRHH": { - "main": [ - [ - { - "node": "Marcar correo enviado", - "type": "main", - "index": 0 - } - ] - ] - }, - "Marcar correo enviado": { - "main": [ - [ - { - "node": "Respond to Webhook", - "type": "main", - "index": 0 - } - ] - ] - } - }, - "active": false, - "settings": { - "executionOrder": "v1", - "binaryMode": "separate" - }, - "tags": [] -} \ No newline at end of file diff --git a/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json b/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json new file mode 100644 index 0000000..f32fed8 --- /dev/null +++ b/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json @@ -0,0 +1,347 @@ +{ + "name": "Portal de Verificación de Nómina Puerto Rico - Envío de Reporte", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "cruce-cuentas-pr-marcar-resuelto", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + 784, + 592 + ], + "id": "67ea20ff-7e47-4605-be07-b9c1fea50d78", + "name": "Webhook Marcar Resuelto", + "webhookId": "9b76b71d-729d-4559-a6d8-4321986e4e5d" + }, + { + "parameters": { + "jsCode": "const body = $input.first().json.body || $input.first().json || {};\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nconst reportId = clean(body.reportId || body.id);\nconst comentarioResolucion = clean(body.comentarioResolucion || body.comentario || '');\nconst resueltoPorNombre = clean(body.resueltoPorNombre || body.userName || 'Usuario GLM');\nconst resueltoPorEmail = clean(body.resueltoPorEmail || body.userEmail || '');\n\nconst errors = [];\n\nif (!reportId) {\n errors.push('No se recibió el ID del reporte.');\n}\n\nif (!comentarioResolucion) {\n errors.push('Debe indicar un comentario de resolución.');\n}\n\nif (comentarioResolucion.length < 10) {\n errors.push('El comentario de resolución debe ser más descriptivo.');\n}\n\nif (errors.length > 0) {\n return [\n {\n json: {\n ok: false,\n stage: 'validacion_resolucion',\n errors,\n reportId,\n },\n },\n ];\n}\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'resolucion_validada',\n reportId,\n comentarioResolucion,\n resueltoPorNombre,\n resueltoPorEmail,\n resolvedAt: new Date().toISOString(),\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 992, + 592 + ], + "id": "57d081f4-b2ff-4272-b660-84b71692c0b8", + "name": "Validar resolución" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "c89884c6-de96-4b08-bd20-54644bb95bc7", + "leftValue": "={{ $json.ok }}", + "rightValue": "", + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + 1200, + 592 + ], + "id": "0e91368c-172f-44bf-bf5f-0c1e4e4842e7", + "name": "¿Solicitud válida?" + }, + { + "parameters": { + "method": "PATCH", + "url": "={{ 'https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?id=eq.' + encodeURIComponent($json.reportId) + '&country=eq.PR' }}", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{\n {\n estado: 'resuelto',\n resuelto_por_nombre: $json.resueltoPorNombre,\n resuelto_por_email: $json.resueltoPorEmail,\n resuelto_en: $json.resolvedAt,\n comentario_resolucion: $json.comentarioResolucion,\n updated_at: $json.resolvedAt\n }\n}}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1536, + 480 + ], + "id": "13114fe0-2945-432d-ac06-02b46682b9c0", + "name": "Actualizar reporte Supabase" + }, + { + "parameters": { + "jsCode": "const input = $input.first().json || {};\nconst report = Array.isArray(input) ? input[0] : input;\n\nif (!report || !report.id) {\n throw new Error(\n 'No se encontró un reporte de Puerto Rico con el ID recibido.'\n );\n}\n\nif (String(report.country || '').toUpperCase() !== 'PR') {\n throw new Error(\n 'El reporte localizado no pertenece a Puerto Rico.'\n );\n}\nconst original = $('Validar resolución').first().json || {};\n\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nconst periodo = report.period_label || 'Período no especificado';\nconst reportUrl = report.report_url || '';\n\nconst discrepancias = numberValue(report.discrepancias);\nconst bancoSinNomina = numberValue(report.banco_sin_nomina);\nconst bancoSinBamboo = numberValue(report.banco_sin_bamboo);\nconst nominaSinCuenta = numberValue(report.nomina_sin_cuenta);\n\nconst resolvedAt = original.resolvedAt || new Date().toISOString();\n\nconst resolvedDateLabel = new Date(resolvedAt).toLocaleString('es-PR', {\n timeZone: 'America/Puerto_Rico',\n dateStyle: 'long',\n timeStyle: 'short',\n});\n\nconst resolvedByName =\n original.resueltoPorNombre || 'Usuario GLM';\n\nconst resolvedByEmail =\n original.resueltoPorEmail || '';\n\nconst resolutionComment =\n original.comentarioResolucion || 'Sin comentario registrado.';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/bandera-nacional-tela-puerto-rico_113767-1759.avif';\n\nconst subject =\n `Reporte resuelto - Cruce de Cuentas Puerto Rico - ${periodo}`;\n\nconst textBody = `\nHola equipo,\n\nSe marcó como resuelto un reporte de Cruce de Cuentas Puerto Rico.\n\nPeríodo:\n${periodo}\n\nResuelto por:\n${resolvedByName}${resolvedByEmail ? ` (${resolvedByEmail})` : ''}\n\nFecha/hora:\n${resolvedDateLabel}\n\nResumen:\n- Discrepancias: ${discrepancias}\n- Banco sin nómina: ${bancoSinNomina}\n- Banco sin Bamboo: ${bancoSinBamboo}\n- Nómina sin cuenta: ${nominaSinCuenta}\n\nComentario:\n${resolutionComment}\n\nGoogle Sheet:\n${reportUrl || 'No disponible'}\n\nSaludos,\nPortal Cruce de Cuentas GLM\n`.trim();\n\nconst htmlBody = `\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n \n \n\n \n  \n \n\n \n \n
\n Cruce de Cuentas Puerto Rico\n \"Bandera\n
\n\n

\n Reporte marcado como resuelto\n

\n \n \n\n \n \n

\n Hola equipo,\n

\n\n

\n El siguiente reporte fue revisado y marcado como\n resuelto.\n

\n \n \n\n \n \n \n \n \n Período\n \n\n \n ${escapeHtml(periodo)}\n \n \n\n \n \n Resuelto por\n \n\n \n ${escapeHtml(resolvedByName)}\n ${\n resolvedByEmail\n ? `
${escapeHtml(resolvedByEmail)}`\n : ''\n }\n \n \n\n \n \n Fecha y hora\n \n\n \n ${escapeHtml(resolvedDateLabel)}\n \n \n \n \n \n\n \n \n
\n Resumen del reporte\n
\n \n \n\n \n \n \n \n \n
\n ${discrepancias}\n
\n\n
\n Discrepancias\n
\n \n\n \n
\n ${bancoSinBamboo}\n
\n\n
\n Banco sin Bamboo\n
\n \n\n \n
\n ${bancoSinNomina}\n
\n\n
\n Banco sin nómina\n
\n \n\n \n
\n ${nominaSinCuenta}\n
\n\n
\n Nómina sin cuenta\n
\n \n \n \n \n \n\n \n \n
\n Comentario de resolución\n
\n \n \n\n \n \n
\n ${escapeHtml(resolutionComment).replace(/\\n/g, '
')}\n
\n \n \n\n ${\n reportUrl\n ? `\n \n \n \n Abrir Google Sheet\n \n \n \n `\n : ''\n }\n\n \n \n Mensaje automático del Portal Cruce de Cuentas GLM.\n \n \n\n \n \n \n \n\n\n\n`.trim();\n\nreturn [\n {\n json: {\n to: [\n 'iaracena@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n ].join(','),\n subject,\n textBody,\n htmlBody,\n report,\n resolution: original,\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1744, + 480 + ], + "id": "f756f935-f1ab-4c87-b46a-eb159ce95cef", + "name": "Preparar correo equipo PR" + }, + { + "parameters": { + "sendTo": "={{$json.to}}", + "subject": "={{$json.subject}}", + "message": "={{ $json.htmlBody }}", + "options": { + "appendAttribution": false, + "ccList": "={{ $json.cc }}" + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 1952, + 480 + ], + "id": "127fa51c-cc53-41c9-8c5f-6c0cd65e8ca9", + "name": "Enviar correo equipo PR", + "webhookId": "ad819366-b163-4e58-86de-a7cd158630b1", + "credentials": { + "gmailOAuth2": { + "id": "UDcO1FLJqA453V2D", + "name": "Gmail account 3" + } + } + }, + { + "parameters": { + "jsCode": "const original = $('Validar resolución').first().json || {};\nconst updatedResponse = $('Actualizar reporte Supabase').first().json || {};\nconst updatedReport = Array.isArray(updatedResponse) ? updatedResponse[0] : updatedResponse;\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'reporte_resuelto',\n message: 'Reporte marcado como resuelto correctamente.',\n report: {\n id: original.reportId,\n estado: 'resuelto',\n estadoLabel: 'RESUELTO',\n country: 'PR',\n countryName: 'Puerto Rico',\n resueltoPorNombre: original.resueltoPorNombre,\n resueltoPorEmail: original.resueltoPorEmail,\n resueltoEn: original.resolvedAt,\n comentarioResolucion: original.comentarioResolucion,\n reportUrl: updatedReport.report_url || '',\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2160, + 480 + ], + "id": "7f19e2dd-b8a2-427c-83d3-345d18fbf267", + "name": "Preparar respuesta resuelto" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "name": "Access-Control-Allow-Methods", + "value": "GET,POST,OPTIONS" + }, + { + "name": "Access-Control-Allow-Headers", + "value": "Content-Type, Authorization" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 2368, + 480 + ], + "id": "ebb6b338-b872-4167-acea-85b9e235cb38", + "name": "Responder resuelto app" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "name": "Access-Control-Allow-Methods", + "value": "GET,POST,OPTIONS" + }, + { + "name": "Access-Control-Allow-Headers", + "value": "Content-Type, Authorization" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 1408, + 688 + ], + "id": "cf478ef5-e94d-4fce-afe6-2a5a2b16c673", + "name": "Responder error resolución" + }, + { + "parameters": { + "content": "# ✅ CIERRE DE REPORTE — PUERTO RICO\n\nRecibe desde el Portal de Verificación de Nóminas la solicitud para marcar un reporte de Puerto Rico como resuelto.\n\n## Validación inicial\n\nEl Webhook recibe la información enviada por la aplicación y valida datos como:\n\n- Identificador del reporte.\n- Usuario que realizó el cierre.\n- Estado solicitado.\n- Comentario o detalle de resolución.\n- Datos necesarios para localizar el registro en Supabase.\n\nLa solicitud solamente continúa cuando contiene información válida y suficiente.\n\n## Actualización del reporte\n\nCuando la solicitud es correcta:\n\n1. Localiza el reporte correspondiente en Supabase.\n2. Cambia su estado a RESUELTO.\n3. Registra quién realizó la resolución.\n4. Guarda la fecha y hora del cierre.\n5. Conserva el comentario o información de seguimiento disponible.\n\nSupabase es la fuente oficial del estado del reporte dentro del portal.\n\n## Notificación al equipo de Puerto Rico\n\nDespués de actualizar el registro:\n\n1. Se prepara un correo para el equipo responsable de Puerto Rico.\n2. Se incluyen los datos principales del reporte.\n3. Se informa que el caso fue marcado como resuelto.\n4. Se identifica al usuario que realizó el cierre.\n5. Se envía el correo mediante Gmail.\n\nLa notificación permite que el equipo conozca el cambio sin tener que revisar manualmente el portal.\n\n## Respuesta a la aplicación\n\nCuando el proceso termina correctamente:\n\n- Se construye una respuesta de éxito.\n- Se confirma que el reporte fue actualizado.\n- Se devuelve el nuevo estado a la aplicación.\n- El portal puede actualizar inmediatamente su interfaz.\n\n## Ruta de error\n\nCuando la solicitud es inválida:\n\n- No se modifica Supabase.\n- No se envía el correo al equipo de Puerto Rico.\n- Se devuelve una respuesta de error a la aplicación.\n- Se informa que faltan datos o que la solicitud no pudo procesarse.\n\n## Reglas\n\n- No marcar un reporte como resuelto sin un identificador válido.\n- No enviar la notificación antes de confirmar la actualización en Supabase.\n- No declarar éxito cuando la base de datos no fue actualizada.\n- Toda respuesta del Webhook debe cerrarse correctamente para evitar solicitudes abiertas.\n- La ruta de error debe terminar sin realizar cambios parciales.", + "height": 1264, + "width": 2624, + "color": "#28765F" + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "e8c96878-0be9-4c70-bea5-e445119cad84", + "name": "Sticky Note" + } + ], + "pinData": {}, + "connections": { + "Webhook Marcar Resuelto": { + "main": [ + [ + { + "node": "Validar resolución", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar resolución": { + "main": [ + [ + { + "node": "¿Solicitud válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Solicitud válida?": { + "main": [ + [ + { + "node": "Actualizar reporte Supabase", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder error resolución", + "type": "main", + "index": 0 + } + ] + ] + }, + "Actualizar reporte Supabase": { + "main": [ + [ + { + "node": "Preparar correo equipo PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar respuesta resuelto": { + "main": [ + [ + { + "node": "Responder resuelto app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar correo equipo PR": { + "main": [ + [ + { + "node": "Enviar correo equipo PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Enviar correo equipo PR": { + "main": [ + [ + { + "node": "Preparar respuesta resuelto", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "b51451ef-379a-479a-88b4-cfcd3e183c20", + "meta": { + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "tags": [] +} diff --git a/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json b/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json new file mode 100644 index 0000000..c5b7fa1 --- /dev/null +++ b/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json @@ -0,0 +1,163 @@ +{ + "name": "Portal de Verificación de Nómina Puerto Rico - Históricos", + "nodes": [ + { + "parameters": { + "path": "cruce-cuentas-pr-historicos", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + 304, + 272 + ], + "id": "dff3b1cf-ebbe-4525-b367-0479d76a102d", + "name": "Webhook Históricos PR", + "webhookId": "24baab72-bc55-439d-96db-94780a3dffdf" + }, + { + "parameters": { + "jsCode": "const items = $input.all();\n\nfunction formatDate(value) {\n if (!value) return '';\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return String(value).slice(0, 10);\n return date.toISOString().slice(0, 10);\n}\n\nfunction toNumber(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction normalizeStatus(value, discrepancias) {\n const status = String(value || '').toLowerCase();\n\n if (status === 'resuelto') return 'resuelto';\n if (status === 'procesado' && toNumber(discrepancias) === 0) return 'procesado';\n\n return 'pendiente_revision';\n}\n\nfunction statusLabel(status) {\n if (status === 'resuelto') return 'RESUELTO';\n if (status === 'procesado') return 'PROCESADO';\n return 'PENDIENTE REVISIÓN';\n}\n\nconst reports = items.map((item) => {\n const row = item.json || {};\n\n const discrepancias = toNumber(row.discrepancias);\n const bancoSinNomina = toNumber(row.banco_sin_nomina);\n const nominaSinCuenta = toNumber(row.nomina_sin_cuenta);\n const bancoSinBamboo = toNumber(row.banco_sin_bamboo);\n const nominaSinBamboo = toNumber(row.nomina_sin_bamboo);\n const bambooSinNomina = toNumber(row.bamboo_sin_nomina);\n\n const totalPendientes =\n discrepancias +\n bancoSinNomina +\n nominaSinCuenta +\n bancoSinBamboo +\n nominaSinBamboo +\n bambooSinNomina;\n\n const estado = normalizeStatus(row.estado, totalPendientes);\n\n return {\n id: row.id,\n\n fecha: formatDate(row.period_end || row.ejecutado_en || row.created_at),\n fechaEjecucion: row.ejecutado_en || row.created_at,\n\n periodo: row.period_label || '',\n year: row.year,\n month: row.month,\n periodType: row.period_type,\n periodStart: row.period_start,\n periodEnd: row.period_end,\n\n archivoNomina: row.payroll_file_name || '',\n archivosBanco: row.bank_file_names || [],\n\n coincidencias: toNumber(row.coincidencias),\n discrepancias,\n bancoSinNomina,\n nominaSinCuenta,\n bancoSinBamboo,\n nominaSinBamboo,\n bambooSinNomina,\n totalPendientes,\n\n totalNomina: toNumber(row.total_nomina),\n totalBanco: toNumber(row.total_banco),\n diferenciaTotal: toNumber(row.diferencia_total),\n\n estado,\n estadoLabel: statusLabel(estado),\n\n reportUrl: row.report_url || '',\n spreadsheetId: row.spreadsheet_id || '',\n\n ejecutadoPorNombre: row.ejecutado_por_nombre || '',\n ejecutadoPorEmail: row.ejecutado_por_email || '',\n\n resueltoPorNombre: row.resuelto_por_nombre || '',\n resueltoPorEmail: row.resuelto_por_email || '',\n resueltoEn: row.resuelto_en || null,\n comentarioResolucion: row.comentario_resolucion || '',\n };\n});\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'historicos_pr_listos',\n count: reports.length,\n reports,\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 720, + 272 + ], + "id": "9c4a3deb-631f-409f-b3ac-a86c75514f14", + "name": "Normalizar históricos para app" + }, + { + "parameters": { + "url": "https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?select=*&country=eq.PR&order=ejecutado_en.desc&limit=100", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 512, + 272 + ], + "id": "70248bd5-e8ff-4ca5-9770-3a68ca418a12", + "name": "Consultar históricos Supabase" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": { + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "name": "Access-Control-Allow-Methods", + "value": "GET,POST,OPTIONS" + }, + { + "name": "Access-Control-Allow-Headers", + "value": "Content-Type, Authorization" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 944, + 272 + ], + "id": "65b35ac9-9272-4abf-86e4-c9330322c8ab", + "name": "Responder históricos app" + }, + { + "parameters": { + "content": "# 📚 HISTÓRICOS DE VERIFICACIÓN — PUERTO RICO\n\nEntrega a la aplicación el historial de reportes procesados para Puerto Rico.\n\n- Consulta únicamente registros con `country = PR`.\n- Supabase continúa usando la tabla histórica compartida `cruces_cuentas_gt_reportes`; el nombre de la tabla es legado y funciona como tabla multi-país.\n- Devuelve la misma estructura de históricos utilizada por los demás países.\n- Este webhook sirve como respaldo; la app intenta primero la RPC paginada de Supabase.", + "height": 496, + "width": 1360, + "color": 6 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "8c0b0a43-8bfe-443b-8795-06d79512ca64", + "name": "Sticky Note" + } + ], + "pinData": {}, + "connections": { + "Consultar históricos Supabase": { + "main": [ + [ + { + "node": "Normalizar históricos para app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar históricos para app": { + "main": [ + [ + { + "node": "Responder históricos app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook Históricos PR": { + "main": [ + [ + { + "node": "Consultar históricos Supabase", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "ffb6f922-6c37-498e-9dec-0011deddb49e", + "meta": { + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "tags": [] +} diff --git a/SUPABASE_Puerto_Rico_READY.sql b/SUPABASE_Puerto_Rico_READY.sql new file mode 100644 index 0000000..e498918 --- /dev/null +++ b/SUPABASE_Puerto_Rico_READY.sql @@ -0,0 +1,311 @@ +-- ============================================================ +-- CRUCE DE CUENTAS GLM · HABILITAR PUERTO RICO (PR) +-- GT + TT + PA + SV + HN + PR +-- ============================================================ +-- Ejecutar una sola vez en el SQL Editor del Supabase empresarial. +-- Es idempotente respecto a PR: conserva datos existentes y puede +-- volver a ejecutarse si se interrumpe antes del COMMIT. + +begin; + +-- 1) Validar tablas requeridas y ampliar constraints de país. +do $$ +declare r record; +begin + if to_regclass('public.cruce_cuentas_bamboo_aliases') is null then raise exception 'No existe public.cruce_cuentas_bamboo_aliases.'; end if; + if to_regclass('public.cruce_cuentas_bamboo_correcciones') is null then raise exception 'No existe public.cruce_cuentas_bamboo_correcciones.'; end if; + if to_regclass('public.cruces_cuentas_gt_reportes') is null then raise exception 'No existe public.cruces_cuentas_gt_reportes.'; end if; + if to_regclass('public.cruce_cuentas_usuarios_autorizados') is null then raise exception 'No existe public.cruce_cuentas_usuarios_autorizados.'; end if; + + -- Alias BambooHR. + for r in + select conname + from pg_constraint + where conrelid='public.cruce_cuentas_bamboo_aliases'::regclass + and contype='c' + and pg_get_constraintdef(oid) ilike '%pais%' + and pg_get_constraintdef(oid) ilike '%GT%' + and pg_get_constraintdef(oid) not ilike '%PR%' + loop + execute format('alter table public.cruce_cuentas_bamboo_aliases drop constraint %I',r.conname); + end loop; + alter table public.cruce_cuentas_bamboo_aliases drop constraint if exists ck_cruce_cuentas_bamboo_aliases_pais; + alter table public.cruce_cuentas_bamboo_aliases + add constraint ck_cruce_cuentas_bamboo_aliases_pais + check (upper(trim(pais)) in ('GT','TT','PA','SV','HN','PR')); + + -- Solicitudes/correcciones BambooHR. + for r in + select conname + from pg_constraint + where conrelid='public.cruce_cuentas_bamboo_correcciones'::regclass + and contype='c' + and pg_get_constraintdef(oid) ilike '%pais%' + and pg_get_constraintdef(oid) ilike '%GT%' + and pg_get_constraintdef(oid) not ilike '%PR%' + loop + execute format('alter table public.cruce_cuentas_bamboo_correcciones drop constraint %I',r.conname); + end loop; + alter table public.cruce_cuentas_bamboo_correcciones drop constraint if exists ck_cruce_cuentas_bamboo_correcciones_pais; + alter table public.cruce_cuentas_bamboo_correcciones + add constraint ck_cruce_cuentas_bamboo_correcciones_pais + check (upper(trim(pais)) in ('GT','TT','PA','SV','HN','PR')); + + -- Históricos multi-país (el nombre de la tabla es legado). + for r in + select conname + from pg_constraint + where conrelid='public.cruces_cuentas_gt_reportes'::regclass + and contype='c' + and pg_get_constraintdef(oid) ilike '%country%' + and pg_get_constraintdef(oid) ilike '%GT%' + and pg_get_constraintdef(oid) not ilike '%PR%' + loop + execute format('alter table public.cruces_cuentas_gt_reportes drop constraint %I',r.conname); + end loop; + alter table public.cruces_cuentas_gt_reportes drop constraint if exists ck_cruces_cuentas_reportes_country; + alter table public.cruces_cuentas_gt_reportes + add constraint ck_cruces_cuentas_reportes_country + check (country is null or upper(trim(country)) in ('GT','TT','PA','SV','HN','PR')); + + -- Accesos: text[] de países habilitados. + alter table public.cruce_cuentas_usuarios_autorizados drop constraint if exists cruce_cuentas_paises_validos_check; + alter table public.cruce_cuentas_usuarios_autorizados + add constraint cruce_cuentas_paises_validos_check + check (paises is null or paises <@ array['GT','TT','PA','SV','HN','PR']::text[]); +end; +$$; + +-- 2) Dar PR a todos los usuarios actualmente autorizados, preservando permisos. +update public.cruce_cuentas_usuarios_autorizados +set paises = case + when paises is null then array['GT','TT','PA','SV','HN','PR']::text[] + when not ('PR'=any(paises)) then array_append(paises,'PR') + else paises +end, +updated_at = now(); + +-- 3) Alias BambooHR activos: habilitar PR en la función compartida. +create or replace function public.cruce_cuentas_bamboo_aliases_activos(p_pais text) +returns jsonb +language sql +stable +security definer +set search_path=public +as $$ + select jsonb_build_object( + 'ok',true, + 'pais',upper(trim(coalesce(p_pais,''))), + 'aliases',coalesce( + jsonb_agg( + jsonb_build_object( + 'id',a.id, + 'pais',a.pais, + 'nombre_origen',a.nombre_origen, + 'nombre_origen_normalizado',a.nombre_origen_normalizado, + 'cuenta_bancaria',a.cuenta_bancaria, + 'bamboo_employee_number',a.bamboo_employee_number, + 'bamboo_employee_id',a.bamboo_employee_id, + 'bamboo_nombre',a.bamboo_nombre, + 'motivo',a.motivo, + 'activo',a.activo + ) order by a.updated_at desc + ) filter (where a.id is not null), + '[]'::jsonb + ) + ) + from public.cruce_cuentas_bamboo_aliases a + where a.activo=true + and a.pais=upper(trim(coalesce(p_pais,''))) + and upper(trim(coalesce(p_pais,''))) in ('GT','TT','PA','SV','HN','PR'); +$$; +revoke all on function public.cruce_cuentas_bamboo_aliases_activos(text) from public,anon,authenticated; +grant execute on function public.cruce_cuentas_bamboo_aliases_activos(text) to service_role; + +-- 4) Históricos paginados multi-país, incluyendo PR. +create index if not exists idx_cruces_cuentas_reportes_country_created + on public.cruces_cuentas_gt_reportes(country,created_at desc); +create index if not exists idx_cruces_cuentas_reportes_country_status_created + on public.cruces_cuentas_gt_reportes(country,estado,created_at desc); + +create or replace function public.cruce_cuentas_get_historicos( + p_country text, + p_page integer default 1, + p_page_size integer default 10, + p_search text default null, + p_status text default null +) +returns jsonb +language plpgsql +security definer +set search_path=public +as $$ +declare + v_country text := upper(trim(coalesce(p_country,''))); + v_page integer := greatest(coalesce(p_page,1),1); + v_page_size integer := least(greatest(coalesce(p_page_size,10),1),100); + v_offset integer; + v_search text := nullif(trim(coalesce(p_search,'')),''); + v_status text := nullif(trim(coalesce(p_status,'')),''); + v_total bigint; + v_filtered_total bigint; + v_reports jsonb; +begin + if v_country not in ('GT','TT','PA','SV','HN','PR') then + raise exception 'País no permitido: %',v_country; + end if; + + v_offset := (v_page-1)*v_page_size; + + select count(*) into v_total + from public.cruces_cuentas_gt_reportes r + where upper(coalesce(r.country,'GT'))=v_country; + + select count(*) into v_filtered_total + from public.cruces_cuentas_gt_reportes r + where upper(coalesce(r.country,'GT'))=v_country + and (v_status is null or r.estado=v_status) + and ( + v_search is null + or coalesce(r.period_label,'') ilike '%'||v_search||'%' + or coalesce(r.payroll_file_name,'') ilike '%'||v_search||'%' + or coalesce(r.ejecutado_por_nombre,'') ilike '%'||v_search||'%' + or coalesce(r.resuelto_por_nombre,'') ilike '%'||v_search||'%' + or r.id::text ilike '%'||v_search||'%' + ); + + select coalesce( + jsonb_agg((to_jsonb(q)-'detalle_banco_sin_bamboo') order by q.created_at desc), + '[]'::jsonb + ) into v_reports + from ( + select r.* + from public.cruces_cuentas_gt_reportes r + where upper(coalesce(r.country,'GT'))=v_country + and (v_status is null or r.estado=v_status) + and ( + v_search is null + or coalesce(r.period_label,'') ilike '%'||v_search||'%' + or coalesce(r.payroll_file_name,'') ilike '%'||v_search||'%' + or coalesce(r.ejecutado_por_nombre,'') ilike '%'||v_search||'%' + or coalesce(r.resuelto_por_nombre,'') ilike '%'||v_search||'%' + or r.id::text ilike '%'||v_search||'%' + ) + order by r.created_at desc + limit v_page_size offset v_offset + ) q; + + return jsonb_build_object( + 'ok',true, + 'country',v_country, + 'page',v_page, + 'page_size',v_page_size, + 'total',v_total, + 'filtered_total',v_filtered_total, + 'total_pages',greatest(ceil(v_filtered_total::numeric/v_page_size)::integer,1), + 'reports',v_reports + ); +end; +$$; +revoke all on function public.cruce_cuentas_get_historicos(text,integer,integer,text,text) from public,anon; +grant execute on function public.cruce_cuentas_get_historicos(text,integer,integer,text,text) to authenticated; + +-- 5) Detalle Banco sin BambooHR de históricos, incluyendo PR. +create or replace function public.cruce_cuentas_get_banco_sin_bamboo_reporte( + p_report_id text, + p_country text +) +returns jsonb +language plpgsql +stable +security definer +set search_path=public +as $$ +declare + v_country text := upper(trim(coalesce(p_country,''))); + v_report public.cruces_cuentas_gt_reportes%rowtype; + v_corrections jsonb := '[]'::jsonb; +begin + if auth.uid() is null then raise exception 'No autenticado.'; end if; + if v_country not in ('GT','TT','PA','SV','HN','PR') then raise exception 'País no permitido: %',v_country; end if; + + select r.* into v_report + from public.cruces_cuentas_gt_reportes r + where r.id::text=trim(coalesce(p_report_id,'')) + and upper(coalesce(r.country,'GT'))=v_country + limit 1; + + if not found then + return jsonb_build_object('ok',false,'message','No se encontró el reporte solicitado.'); + end if; + + select coalesce( + jsonb_agg( + jsonb_build_object( + 'id',c.id, + 'pais',c.pais, + 'nombre_origen_principal',c.nombre_origen_principal, + 'nombres_origen',c.nombres_origen, + 'cuenta_bancaria',c.cuenta_bancaria, + 'bamboo_employee_number',c.bamboo_employee_number, + 'bamboo_nombre',c.bamboo_nombre, + 'estado',c.estado, + 'correo_enviado',c.correo_enviado, + 'correo_enviado_at',c.correo_enviado_at, + 'created_at',c.created_at + ) order by c.created_at desc + ), + '[]'::jsonb + ) into v_corrections + from public.cruce_cuentas_bamboo_correcciones c + where c.pais=v_country + and ( + c.execution_id=v_report.id::text + or ( + nullif(trim(coalesce(c.execution_id,'')),'') is null + and nullif(trim(coalesce(c.report_url,'')),'') is not null + and c.report_url=v_report.report_url + ) + ); + + return jsonb_build_object( + 'ok',true, + 'report',jsonb_build_object( + 'id',v_report.id, + 'country',v_report.country, + 'period_label',v_report.period_label, + 'period_start',v_report.period_start, + 'period_end',v_report.period_end, + 'report_url',v_report.report_url, + 'banco_sin_bamboo',v_report.banco_sin_bamboo + ), + 'cases',coalesce(v_report.detalle_banco_sin_bamboo,'[]'::jsonb), + 'corrections',v_corrections + ); +end; +$$; +revoke all on function public.cruce_cuentas_get_banco_sin_bamboo_reporte(text,text) from public,anon; +grant execute on function public.cruce_cuentas_get_banco_sin_bamboo_reporte(text,text) to authenticated; + +commit; + +-- ============================================================ +-- VALIDACIÓN (solo lectura) +-- ============================================================ +select email,nombre,paises +from public.cruce_cuentas_usuarios_autorizados +order by email; + +select conname, pg_get_constraintdef(oid) as definicion +from pg_constraint +where conrelid in ( + 'public.cruce_cuentas_bamboo_aliases'::regclass, + 'public.cruce_cuentas_bamboo_correcciones'::regclass, + 'public.cruces_cuentas_gt_reportes'::regclass, + 'public.cruce_cuentas_usuarios_autorizados'::regclass +) + and contype='c' + and (pg_get_constraintdef(oid) ilike '%pais%' or pg_get_constraintdef(oid) ilike '%country%') +order by conname; + +select public.cruce_cuentas_get_historicos('PR',1,10,null,null) as historicos_pr; diff --git a/VALIDACION_PUERTO_RICO_FINAL.md b/VALIDACION_PUERTO_RICO_FINAL.md new file mode 100644 index 0000000..f115ca0 --- /dev/null +++ b/VALIDACION_PUERTO_RICO_FINAL.md @@ -0,0 +1,18 @@ +# Validación final · Puerto Rico + +Fecha: 10-09-2026 + +Este paquete parte de la versión `GT-TT-PA-SV-HN-BONO14-FINAL-READY-CORREGIDO` y agrega Puerto Rico sin sustituir la autenticación ni la lógica histórica existente. + +Validaciones realizadas: +- `src/main.tsx` y el árbol completo `src/` están incluidos. +- `App.tsx`, `Login.tsx`, `lib/auth.ts`, `lib/sessionHealth.ts`, `lib/supabase.ts` y `lib/tabSessionState.ts` se conservaron idénticos a la base corregida. +- Puerto Rico está habilitado como `PR`, moneda `USD`, locale `es-PR` y bandera oficial configurada. +- La carga bancaria para Puerto Rico acepta PDF; los demás países conservan CSV. +- Históricos de PR usan `cruce_cuentas_get_historicos` y el fallback `cruce-cuentas-pr-historicos`. +- Resolución de PR usa `cruce-cuentas-pr-marcar-resuelto`. +- El cruce de PR usa `nominapr-bamboo-test`. +- El frontend de correcciones muestra a Noralmy Laureano para Puerto Rico. +- `.env.local` conserva Supabase Auth y agrega únicamente los tres endpoints PR. +- El `dist/` incluido contiene login habilitado, Supabase, históricos y endpoints PR, con cache-buster nuevo. +- Los cinco workflows PR/compartidos incluidos son JSON válidos; sus conexiones apuntan a nodos existentes y los Code Nodes pasan validación sintáctica. diff --git a/dist/assets/index-BmyDbce7.js b/dist/assets/index-BmyDbce7.js deleted file mode 100644 index 730db32..0000000 --- a/dist/assets/index-BmyDbce7.js +++ /dev/null @@ -1,270 +0,0 @@ -(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))a(l);new MutationObserver(l=>{for(const c of l)if(c.type==="childList")for(const u of c.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&a(u)}).observe(document,{childList:!0,subtree:!0});function i(l){const c={};return l.integrity&&(c.integrity=l.integrity),l.referrerPolicy&&(c.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?c.credentials="include":l.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}function a(l){if(l.ep)return;l.ep=!0;const c=i(l);fetch(l.href,c)}})();var sd={exports:{}},qr={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ky;function ZS(){if(Ky)return qr;Ky=1;var n=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function i(a,l,c){var u=null;if(c!==void 0&&(u=""+c),l.key!==void 0&&(u=""+l.key),"key"in l){c={};for(var d in l)d!=="key"&&(c[d]=l[d])}else c=l;return l=c.ref,{$$typeof:n,type:a,key:u,ref:l!==void 0?l:null,props:c}}return qr.Fragment=e,qr.jsx=i,qr.jsxs=i,qr}var Yy;function WS(){return Yy||(Yy=1,sd.exports=ZS()),sd.exports}var g=WS(),id={exports:{}},be={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Xy;function e1(){if(Xy)return be;Xy=1;var n=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),l=Symbol.for("react.profiler"),c=Symbol.for("react.consumer"),u=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),m=Symbol.for("react.suspense"),p=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),x=Symbol.iterator;function _(E){return E===null||typeof E!="object"?null:(E=x&&E[x]||E["@@iterator"],typeof E=="function"?E:null)}var S={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,k={};function C(E,P,X){this.props=E,this.context=P,this.refs=k,this.updater=X||S}C.prototype.isReactComponent={},C.prototype.setState=function(E,P){if(typeof E!="object"&&typeof E!="function"&&E!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,E,P,"setState")},C.prototype.forceUpdate=function(E){this.updater.enqueueForceUpdate(this,E,"forceUpdate")};function M(){}M.prototype=C.prototype;function N(E,P,X){this.props=E,this.context=P,this.refs=k,this.updater=X||S}var z=N.prototype=new M;z.constructor=N,T(z,C.prototype),z.isPureReactComponent=!0;var Y=Array.isArray;function Z(){}var F={H:null,A:null,T:null,S:null},G=Object.prototype.hasOwnProperty;function ne(E,P,X){var ee=X.ref;return{$$typeof:n,type:E,key:P,ref:ee!==void 0?ee:null,props:X}}function Q(E,P){return ne(E.type,P,E.props)}function ce(E){return typeof E=="object"&&E!==null&&E.$$typeof===n}function me(E){var P={"=":"=0",":":"=2"};return"$"+E.replace(/[=:]/g,function(X){return P[X]})}var _e=/\/+/g;function Re(E,P){return typeof E=="object"&&E!==null&&E.key!=null?me(""+E.key):P.toString(36)}function Se(E){switch(E.status){case"fulfilled":return E.value;case"rejected":throw E.reason;default:switch(typeof E.status=="string"?E.then(Z,Z):(E.status="pending",E.then(function(P){E.status==="pending"&&(E.status="fulfilled",E.value=P)},function(P){E.status==="pending"&&(E.status="rejected",E.reason=P)})),E.status){case"fulfilled":return E.value;case"rejected":throw E.reason}}throw E}function V(E,P,X,ee,se){var ue=typeof E;(ue==="undefined"||ue==="boolean")&&(E=null);var ve=!1;if(E===null)ve=!0;else switch(ue){case"bigint":case"string":case"number":ve=!0;break;case"object":switch(E.$$typeof){case n:case e:ve=!0;break;case y:return ve=E._init,V(ve(E._payload),P,X,ee,se)}}if(ve)return se=se(E),ve=ee===""?"."+Re(E,0):ee,Y(se)?(X="",ve!=null&&(X=ve.replace(_e,"$&/")+"/"),V(se,P,X,"",function(fn){return fn})):se!=null&&(ce(se)&&(se=Q(se,X+(se.key==null||E&&E.key===se.key?"":(""+se.key).replace(_e,"$&/")+"/")+ve)),P.push(se)),1;ve=0;var Je=ee===""?".":ee+":";if(Y(E))for(var Ye=0;Ye>>1,ae=V[pe];if(0>>1;pel(X,W))eel(se,X)?(V[pe]=se,V[ee]=W,pe=ee):(V[pe]=X,V[P]=W,pe=P);else if(eel(se,W))V[pe]=se,V[ee]=W,pe=ee;else break e}}return K}function l(V,K){var W=V.sortIndex-K.sortIndex;return W!==0?W:V.id-K.id}if(n.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var c=performance;n.unstable_now=function(){return c.now()}}else{var u=Date,d=u.now();n.unstable_now=function(){return u.now()-d}}var m=[],p=[],y=1,v=null,x=3,_=!1,S=!1,T=!1,k=!1,C=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function z(V){for(var K=i(p);K!==null;){if(K.callback===null)a(p);else if(K.startTime<=V)a(p),K.sortIndex=K.expirationTime,e(m,K);else break;K=i(p)}}function Y(V){if(T=!1,z(V),!S)if(i(m)!==null)S=!0,Z||(Z=!0,me());else{var K=i(p);K!==null&&Se(Y,K.startTime-V)}}var Z=!1,F=-1,G=5,ne=-1;function Q(){return k?!0:!(n.unstable_now()-neV&&Q());){var pe=v.callback;if(typeof pe=="function"){v.callback=null,x=v.priorityLevel;var ae=pe(v.expirationTime<=V);if(V=n.unstable_now(),typeof ae=="function"){v.callback=ae,z(V),K=!0;break t}v===i(m)&&a(m),z(V)}else a(m);v=i(m)}if(v!==null)K=!0;else{var E=i(p);E!==null&&Se(Y,E.startTime-V),K=!1}}break e}finally{v=null,x=W,_=!1}K=void 0}}finally{K?me():Z=!1}}}var me;if(typeof N=="function")me=function(){N(ce)};else if(typeof MessageChannel<"u"){var _e=new MessageChannel,Re=_e.port2;_e.port1.onmessage=ce,me=function(){Re.postMessage(null)}}else me=function(){C(ce,0)};function Se(V,K){F=C(function(){V(n.unstable_now())},K)}n.unstable_IdlePriority=5,n.unstable_ImmediatePriority=1,n.unstable_LowPriority=4,n.unstable_NormalPriority=3,n.unstable_Profiling=null,n.unstable_UserBlockingPriority=2,n.unstable_cancelCallback=function(V){V.callback=null},n.unstable_forceFrameRate=function(V){0>V||125pe?(V.sortIndex=W,e(p,V),i(m)===null&&V===i(p)&&(T?(M(F),F=-1):T=!0,Se(Y,W-pe))):(V.sortIndex=ae,e(m,V),S||_||(S=!0,Z||(Z=!0,me()))),V},n.unstable_shouldYield=Q,n.unstable_wrapCallback=function(V){var K=x;return function(){var W=x;x=K;try{return V.apply(this,arguments)}finally{x=W}}}})(od)),od}var Zy;function n1(){return Zy||(Zy=1,rd.exports=t1()),rd.exports}var ld={exports:{}},zt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Wy;function s1(){if(Wy)return zt;Wy=1;var n=Tf();function e(m){var p="https://react.dev/errors/"+m;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}return n(),ld.exports=s1(),ld.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var t0;function a1(){if(t0)return Fr;t0=1;var n=n1(),e=Tf(),i=i1();function a(t){var s="https://react.dev/errors/"+t;if(1ae||(t.current=pe[ae],pe[ae]=null,ae--)}function X(t,s){ae++,pe[ae]=t.current,t.current=s}var ee=E(null),se=E(null),ue=E(null),ve=E(null);function Je(t,s){switch(X(ue,s),X(se,t),X(ee,null),s.nodeType){case 9:case 11:t=(t=s.documentElement)&&(t=t.namespaceURI)?gy(t):0;break;default:if(t=s.tagName,s=s.namespaceURI)s=gy(s),t=yy(s,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}P(ee),X(ee,t)}function Ye(){P(ee),P(se),P(ue)}function fn(t){t.memoizedState!==null&&X(ve,t);var s=ee.current,r=yy(s,t.type);s!==r&&(X(se,t),X(ee,r))}function As(t){se.current===t&&(P(ee),P(se)),ve.current===t&&(P(ve),Vr._currentValue=W)}var ts,oi;function J(t){if(ts===void 0)try{throw Error()}catch(r){var s=r.stack.trim().match(/\n( *(at )?)/);ts=s&&s[1]||"",oi=-1)":-1h||A[o]!==U[h]){var $=` -`+A[o].replace(" at new "," at ");return t.displayName&&$.includes("")&&($=$.replace("",t.displayName)),$}while(1<=o&&0<=h);break}}}finally{je=!1,Error.prepareStackTrace=r}return(r=t?t.displayName||t.name:"")?J(r):""}function Ne(t,s){switch(t.tag){case 26:case 27:case 5:return J(t.type);case 16:return J("Lazy");case 13:return t.child!==s&&s!==null?J("Suspense Fallback"):J("Suspense");case 19:return J("SuspenseList");case 0:case 15:return Oe(t.type,!1);case 11:return Oe(t.type.render,!1);case 1:return Oe(t.type,!0);case 31:return J("Activity");default:return""}}function ct(t){try{var s="",r=null;do s+=Ne(t,r),r=t,t=t.return;while(t);return s}catch(o){return` -Error generating stack: `+o.message+` -`+o.stack}}var vt=Object.prototype.hasOwnProperty,Pt=n.unstable_scheduleCallback,et=n.unstable_cancelCallback,Ht=n.unstable_shouldYield,Hn=n.unstable_requestPaint,At=n.unstable_now,js=n.unstable_getCurrentPriorityLevel,Gt=n.unstable_ImmediatePriority,tn=n.unstable_UserBlockingPriority,mn=n.unstable_NormalPriority,Ut=n.unstable_LowPriority,Ns=n.unstable_IdlePriority,zi=n.log,xt=n.unstable_setDisableYieldValue,ks=null,Lt=null;function Nn(t){if(typeof zi=="function"&&xt(t),Lt&&typeof Lt.setStrictMode=="function")try{Lt.setStrictMode(ks,t)}catch{}}var wt=Math.clz32?Math.clz32:Kc,ko=Math.log,Gc=Math.LN2;function Kc(t){return t>>>=0,t===0?32:31-(ko(t)/Gc|0)|0}var Vi=256,Pi=262144,Hi=4194304;function ns(t){var s=t&42;if(s!==0)return s;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function li(t,s,r){var o=t.pendingLanes;if(o===0)return 0;var h=0,f=t.suspendedLanes,b=t.pingedLanes;t=t.warmLanes;var w=o&134217727;return w!==0?(o=w&~f,o!==0?h=ns(o):(b&=w,b!==0?h=ns(b):r||(r=w&~t,r!==0&&(h=ns(r))))):(w=o&~f,w!==0?h=ns(w):b!==0?h=ns(b):r||(r=o&~t,r!==0&&(h=ns(r)))),h===0?0:s!==0&&s!==h&&(s&f)===0&&(f=h&-h,r=s&-s,f>=r||f===32&&(r&4194048)!==0)?s:h}function ci(t,s){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&s)===0}function Yc(t,s){switch(t){case 1:case 2:case 4:case 8:case 64:return s+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return s+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Wa(){var t=Hi;return Hi<<=1,(Hi&62914560)===0&&(Hi=4194304),t}function er(t){for(var s=[],r=0;31>r;r++)s.push(t);return s}function ui(t,s){t.pendingLanes|=s,s!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Xc(t,s,r,o,h,f){var b=t.pendingLanes;t.pendingLanes=r,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=r,t.entangledLanes&=r,t.errorRecoveryDisabledLanes&=r,t.shellSuspendCounter=0;var w=t.entanglements,A=t.expirationTimes,U=t.hiddenUpdates;for(r=b&~r;0"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var Gw=/[\n"\\]/g;function gn(t){return t.replace(Gw,function(s){return"\\"+s.charCodeAt(0).toString(16)+" "})}function Qc(t,s,r,o,h,f,b,w){t.name="",b!=null&&typeof b!="function"&&typeof b!="symbol"&&typeof b!="boolean"?t.type=b:t.removeAttribute("type"),s!=null?b==="number"?(s===0&&t.value===""||t.value!=s)&&(t.value=""+pn(s)):t.value!==""+pn(s)&&(t.value=""+pn(s)):b!=="submit"&&b!=="reset"||t.removeAttribute("value"),s!=null?Zc(t,b,pn(s)):r!=null?Zc(t,b,pn(r)):o!=null&&t.removeAttribute("value"),h==null&&f!=null&&(t.defaultChecked=!!f),h!=null&&(t.checked=h&&typeof h!="function"&&typeof h!="symbol"),w!=null&&typeof w!="function"&&typeof w!="symbol"&&typeof w!="boolean"?t.name=""+pn(w):t.removeAttribute("name")}function um(t,s,r,o,h,f,b,w){if(f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"&&(t.type=f),s!=null||r!=null){if(!(f!=="submit"&&f!=="reset"||s!=null)){Jc(t);return}r=r!=null?""+pn(r):"",s=s!=null?""+pn(s):r,w||s===t.value||(t.value=s),t.defaultValue=s}o=o??h,o=typeof o!="function"&&typeof o!="symbol"&&!!o,t.checked=w?t.checked:!!o,t.defaultChecked=!!o,b!=null&&typeof b!="function"&&typeof b!="symbol"&&typeof b!="boolean"&&(t.name=b),Jc(t)}function Zc(t,s,r){s==="number"&&Bo(t.ownerDocument)===t||t.defaultValue===""+r||(t.defaultValue=""+r)}function Ki(t,s,r,o){if(t=t.options,s){s={};for(var h=0;h"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),su=!1;if(as)try{var sr={};Object.defineProperty(sr,"passive",{get:function(){su=!0}}),window.addEventListener("test",sr,sr),window.removeEventListener("test",sr,sr)}catch{su=!1}var Cs=null,iu=null,Lo=null;function ym(){if(Lo)return Lo;var t,s=iu,r=s.length,o,h="value"in Cs?Cs.value:Cs.textContent,f=h.length;for(t=0;t=rr),Sm=" ",Tm=!1;function Em(t,s){switch(t){case"keyup":return x_.indexOf(s.keyCode)!==-1;case"keydown":return s.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Am(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Qi=!1;function __(t,s){switch(t){case"compositionend":return Am(s);case"keypress":return s.which!==32?null:(Tm=!0,Sm);case"textInput":return t=s.data,t===Sm&&Tm?null:t;default:return null}}function S_(t,s){if(Qi)return t==="compositionend"||!cu&&Em(t,s)?(t=ym(),Lo=iu=Cs=null,Qi=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(s.ctrlKey||s.altKey||s.metaKey)||s.ctrlKey&&s.altKey){if(s.char&&1=s)return{node:r,offset:s-t};t=o}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=Mm(r)}}function Um(t,s){return t&&s?t===s?!0:t&&t.nodeType===3?!1:s&&s.nodeType===3?Um(t,s.parentNode):"contains"in t?t.contains(s):t.compareDocumentPosition?!!(t.compareDocumentPosition(s)&16):!1:!1}function Lm(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var s=Bo(t.document);s instanceof t.HTMLIFrameElement;){try{var r=typeof s.contentWindow.location.href=="string"}catch{r=!1}if(r)t=s.contentWindow;else break;s=Bo(t.document)}return s}function du(t){var s=t&&t.nodeName&&t.nodeName.toLowerCase();return s&&(s==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||s==="textarea"||t.contentEditable==="true")}var R_=as&&"documentMode"in document&&11>=document.documentMode,Zi=null,fu=null,ur=null,mu=!1;function zm(t,s,r){var o=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;mu||Zi==null||Zi!==Bo(o)||(o=Zi,"selectionStart"in o&&du(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),ur&&cr(ur,o)||(ur=o,o=Cl(fu,"onSelect"),0>=b,h-=b,$n=1<<32-wt(s)+h|r<we?(Ce=he,he=null):Ce=he.sibling;var Be=L(O,he,B[we],q);if(Be===null){he===null&&(he=Ce);break}t&&he&&Be.alternate===null&&s(O,he),j=f(Be,j,we),Me===null?fe=Be:Me.sibling=Be,Me=Be,he=Ce}if(we===B.length)return r(O,he),De&&os(O,we),fe;if(he===null){for(;wewe?(Ce=he,he=null):Ce=he.sibling;var Zs=L(O,he,Be.value,q);if(Zs===null){he===null&&(he=Ce);break}t&&he&&Zs.alternate===null&&s(O,he),j=f(Zs,j,we),Me===null?fe=Zs:Me.sibling=Zs,Me=Zs,he=Ce}if(Be.done)return r(O,he),De&&os(O,we),fe;if(he===null){for(;!Be.done;we++,Be=B.next())Be=I(O,Be.value,q),Be!==null&&(j=f(Be,j,we),Me===null?fe=Be:Me.sibling=Be,Me=Be);return De&&os(O,we),fe}for(he=o(he);!Be.done;we++,Be=B.next())Be=H(he,O,we,Be.value,q),Be!==null&&(t&&Be.alternate!==null&&he.delete(Be.key===null?we:Be.key),j=f(Be,j,we),Me===null?fe=Be:Me.sibling=Be,Me=Be);return t&&he.forEach(function(QS){return s(O,QS)}),De&&os(O,we),fe}function qe(O,j,B,q){if(typeof B=="object"&&B!==null&&B.type===T&&B.key===null&&(B=B.props.children),typeof B=="object"&&B!==null){switch(B.$$typeof){case _:e:{for(var fe=B.key;j!==null;){if(j.key===fe){if(fe=B.type,fe===T){if(j.tag===7){r(O,j.sibling),q=h(j,B.props.children),q.return=O,O=q;break e}}else if(j.elementType===fe||typeof fe=="object"&&fe!==null&&fe.$$typeof===G&&wi(fe)===j.type){r(O,j.sibling),q=h(j,B.props),gr(q,B),q.return=O,O=q;break e}r(O,j);break}else s(O,j);j=j.sibling}B.type===T?(q=gi(B.props.children,O.mode,q,B.key),q.return=O,O=q):(q=Ko(B.type,B.key,B.props,null,O.mode,q),gr(q,B),q.return=O,O=q)}return b(O);case S:e:{for(fe=B.key;j!==null;){if(j.key===fe)if(j.tag===4&&j.stateNode.containerInfo===B.containerInfo&&j.stateNode.implementation===B.implementation){r(O,j.sibling),q=h(j,B.children||[]),q.return=O,O=q;break e}else{r(O,j);break}else s(O,j);j=j.sibling}q=wu(B,O.mode,q),q.return=O,O=q}return b(O);case G:return B=wi(B),qe(O,j,B,q)}if(Se(B))return re(O,j,B,q);if(me(B)){if(fe=me(B),typeof fe!="function")throw Error(a(150));return B=fe.call(B),ye(O,j,B,q)}if(typeof B.then=="function")return qe(O,j,el(B),q);if(B.$$typeof===N)return qe(O,j,Jo(O,B),q);tl(O,B)}return typeof B=="string"&&B!==""||typeof B=="number"||typeof B=="bigint"?(B=""+B,j!==null&&j.tag===6?(r(O,j.sibling),q=h(j,B),q.return=O,O=q):(r(O,j),q=xu(B,O.mode,q),q.return=O,O=q),b(O)):r(O,j)}return function(O,j,B,q){try{pr=0;var fe=qe(O,j,B,q);return ca=null,fe}catch(he){if(he===la||he===Zo)throw he;var Me=sn(29,he,null,O.mode);return Me.lanes=q,Me.return=O,Me}finally{}}}var Si=op(!0),lp=op(!1),Bs=!1;function Du(t){t.updateQueue={baseState:t.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Mu(t,s){t=t.updateQueue,s.updateQueue===t&&(s.updateQueue={baseState:t.baseState,firstBaseUpdate:t.firstBaseUpdate,lastBaseUpdate:t.lastBaseUpdate,shared:t.shared,callbacks:null})}function Us(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function Ls(t,s,r){var o=t.updateQueue;if(o===null)return null;if(o=o.shared,(Le&2)!==0){var h=o.pending;return h===null?s.next=s:(s.next=h.next,h.next=s),o.pending=s,s=Go(t),Im(t,null,r),s}return Io(t,o,s,r),Go(t)}function yr(t,s,r){if(s=s.updateQueue,s!==null&&(s=s.shared,(r&4194048)!==0)){var o=s.lanes;o&=t.pendingLanes,r|=o,s.lanes=r,Ro(t,r)}}function Bu(t,s){var r=t.updateQueue,o=t.alternate;if(o!==null&&(o=o.updateQueue,r===o)){var h=null,f=null;if(r=r.firstBaseUpdate,r!==null){do{var b={lane:r.lane,tag:r.tag,payload:r.payload,callback:null,next:null};f===null?h=f=b:f=f.next=b,r=r.next}while(r!==null);f===null?h=f=s:f=f.next=s}else h=f=s;r={baseState:o.baseState,firstBaseUpdate:h,lastBaseUpdate:f,shared:o.shared,callbacks:o.callbacks},t.updateQueue=r;return}t=r.lastBaseUpdate,t===null?r.firstBaseUpdate=s:t.next=s,r.lastBaseUpdate=s}var Uu=!1;function br(){if(Uu){var t=oa;if(t!==null)throw t}}function vr(t,s,r,o){Uu=!1;var h=t.updateQueue;Bs=!1;var f=h.firstBaseUpdate,b=h.lastBaseUpdate,w=h.shared.pending;if(w!==null){h.shared.pending=null;var A=w,U=A.next;A.next=null,b===null?f=U:b.next=U,b=A;var $=t.alternate;$!==null&&($=$.updateQueue,w=$.lastBaseUpdate,w!==b&&(w===null?$.firstBaseUpdate=U:w.next=U,$.lastBaseUpdate=A))}if(f!==null){var I=h.baseState;b=0,$=U=A=null,w=f;do{var L=w.lane&-536870913,H=L!==w.lane;if(H?(ke&L)===L:(o&L)===L){L!==0&&L===ra&&(Uu=!0),$!==null&&($=$.next={lane:0,tag:w.tag,payload:w.payload,callback:null,next:null});e:{var re=t,ye=w;L=s;var qe=r;switch(ye.tag){case 1:if(re=ye.payload,typeof re=="function"){I=re.call(qe,I,L);break e}I=re;break e;case 3:re.flags=re.flags&-65537|128;case 0:if(re=ye.payload,L=typeof re=="function"?re.call(qe,I,L):re,L==null)break e;I=v({},I,L);break e;case 2:Bs=!0}}L=w.callback,L!==null&&(t.flags|=64,H&&(t.flags|=8192),H=h.callbacks,H===null?h.callbacks=[L]:H.push(L))}else H={lane:L,tag:w.tag,payload:w.payload,callback:w.callback,next:null},$===null?(U=$=H,A=I):$=$.next=H,b|=L;if(w=w.next,w===null){if(w=h.shared.pending,w===null)break;H=w,w=H.next,H.next=null,h.lastBaseUpdate=H,h.shared.pending=null}}while(!0);$===null&&(A=I),h.baseState=A,h.firstBaseUpdate=U,h.lastBaseUpdate=$,f===null&&(h.shared.lanes=0),$s|=b,t.lanes=b,t.memoizedState=I}}function cp(t,s){if(typeof t!="function")throw Error(a(191,t));t.call(s)}function up(t,s){var r=t.callbacks;if(r!==null)for(t.callbacks=null,t=0;tf?f:8;var b=V.T,w={};V.T=w,th(t,!1,s,r);try{var A=h(),U=V.S;if(U!==null&&U(w,A),A!==null&&typeof A=="object"&&typeof A.then=="function"){var $=P_(A,o);_r(t,s,$,cn(t))}else _r(t,s,o,cn(t))}catch(I){_r(t,s,{then:function(){},status:"rejected",reason:I},cn())}finally{K.p=f,b!==null&&w.types!==null&&(b.types=w.types),V.T=b}}function G_(){}function Wu(t,s,r,o){if(t.tag!==5)throw Error(a(476));var h=$p(t).queue;Hp(t,h,s,W,r===null?G_:function(){return qp(t),r(o)})}function $p(t){var s=t.memoizedState;if(s!==null)return s;s={memoizedState:W,baseState:W,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:hs,lastRenderedState:W},next:null};var r={};return s.next={memoizedState:r,baseState:r,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:hs,lastRenderedState:r},next:null},t.memoizedState=s,t=t.alternate,t!==null&&(t.memoizedState=s),s}function qp(t){var s=$p(t);s.next===null&&(s=t.alternate.memoizedState),_r(t,s.next.queue,{},cn())}function eh(){return Rt(Vr)}function Fp(){return ht().memoizedState}function Ip(){return ht().memoizedState}function K_(t){for(var s=t.return;s!==null;){switch(s.tag){case 24:case 3:var r=cn();t=Us(r);var o=Ls(s,t,r);o!==null&&(Wt(o,s,r),yr(o,s,r)),s={cache:ku()},t.payload=s;return}s=s.return}}function Y_(t,s,r){var o=cn();r={lane:o,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},hl(t)?Kp(s,r):(r=bu(t,s,r,o),r!==null&&(Wt(r,t,o),Yp(r,s,o)))}function Gp(t,s,r){var o=cn();_r(t,s,r,o)}function _r(t,s,r,o){var h={lane:o,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null};if(hl(t))Kp(s,h);else{var f=t.alternate;if(t.lanes===0&&(f===null||f.lanes===0)&&(f=s.lastRenderedReducer,f!==null))try{var b=s.lastRenderedState,w=f(b,r);if(h.hasEagerState=!0,h.eagerState=w,nn(w,b))return Io(t,s,h,0),Ie===null&&Fo(),!1}catch{}finally{}if(r=bu(t,s,h,o),r!==null)return Wt(r,t,o),Yp(r,s,o),!0}return!1}function th(t,s,r,o){if(o={lane:2,revertLane:Dh(),gesture:null,action:o,hasEagerState:!1,eagerState:null,next:null},hl(t)){if(s)throw Error(a(479))}else s=bu(t,r,o,2),s!==null&&Wt(s,t,2)}function hl(t){var s=t.alternate;return t===xe||s!==null&&s===xe}function Kp(t,s){ha=il=!0;var r=t.pending;r===null?s.next=s:(s.next=r.next,r.next=s),t.pending=s}function Yp(t,s,r){if((r&4194048)!==0){var o=s.lanes;o&=t.pendingLanes,r|=o,s.lanes=r,Ro(t,r)}}var Sr={readContext:Rt,use:ol,useCallback:it,useContext:it,useEffect:it,useImperativeHandle:it,useLayoutEffect:it,useInsertionEffect:it,useMemo:it,useReducer:it,useRef:it,useState:it,useDebugValue:it,useDeferredValue:it,useTransition:it,useSyncExternalStore:it,useId:it,useHostTransitionStatus:it,useFormState:it,useActionState:it,useOptimistic:it,useMemoCache:it,useCacheRefresh:it};Sr.useEffectEvent=it;var Xp={readContext:Rt,use:ol,useCallback:function(t,s){return qt().memoizedState=[t,s===void 0?null:s],t},useContext:Rt,useEffect:Op,useImperativeHandle:function(t,s,r){r=r!=null?r.concat([t]):null,cl(4194308,4,Up.bind(null,s,t),r)},useLayoutEffect:function(t,s){return cl(4194308,4,t,s)},useInsertionEffect:function(t,s){cl(4,2,t,s)},useMemo:function(t,s){var r=qt();s=s===void 0?null:s;var o=t();if(Ti){Nn(!0);try{t()}finally{Nn(!1)}}return r.memoizedState=[o,s],o},useReducer:function(t,s,r){var o=qt();if(r!==void 0){var h=r(s);if(Ti){Nn(!0);try{r(s)}finally{Nn(!1)}}}else h=s;return o.memoizedState=o.baseState=h,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:h},o.queue=t,t=t.dispatch=Y_.bind(null,xe,t),[o.memoizedState,t]},useRef:function(t){var s=qt();return t={current:t},s.memoizedState=t},useState:function(t){t=Yu(t);var s=t.queue,r=Gp.bind(null,xe,s);return s.dispatch=r,[t.memoizedState,r]},useDebugValue:Qu,useDeferredValue:function(t,s){var r=qt();return Zu(r,t,s)},useTransition:function(){var t=Yu(!1);return t=Hp.bind(null,xe,t.queue,!0,!1),qt().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,s,r){var o=xe,h=qt();if(De){if(r===void 0)throw Error(a(407));r=r()}else{if(r=s(),Ie===null)throw Error(a(349));(ke&127)!==0||gp(o,s,r)}h.memoizedState=r;var f={value:r,getSnapshot:s};return h.queue=f,Op(bp.bind(null,o,f,t),[t]),o.flags|=2048,fa(9,{destroy:void 0},yp.bind(null,o,f,r,s),null),r},useId:function(){var t=qt(),s=Ie.identifierPrefix;if(De){var r=qn,o=$n;r=(o&~(1<<32-wt(o)-1)).toString(32)+r,s="_"+s+"R_"+r,r=al++,0<\/script>",f=f.removeChild(f.firstChild);break;case"select":f=typeof o.is=="string"?b.createElement("select",{is:o.is}):b.createElement("select"),o.multiple?f.multiple=!0:o.size&&(f.size=o.size);break;default:f=typeof o.is=="string"?b.createElement(h,{is:o.is}):b.createElement(h)}}f[Te]=s,f[tt]=o;e:for(b=s.child;b!==null;){if(b.tag===5||b.tag===6)f.appendChild(b.stateNode);else if(b.tag!==4&&b.tag!==27&&b.child!==null){b.child.return=b,b=b.child;continue}if(b===s)break e;for(;b.sibling===null;){if(b.return===null||b.return===s)break e;b=b.return}b.sibling.return=b.return,b=b.sibling}s.stateNode=f;e:switch(Dt(f,h,o),h){case"button":case"input":case"select":case"textarea":o=!!o.autoFocus;break e;case"img":o=!0;break e;default:o=!1}o&&fs(s)}}return We(s),ph(s,s.type,t===null?null:t.memoizedProps,s.pendingProps,r),null;case 6:if(t&&s.stateNode!=null)t.memoizedProps!==o&&fs(s);else{if(typeof o!="string"&&s.stateNode===null)throw Error(a(166));if(t=ue.current,ia(s)){if(t=s.stateNode,r=s.memoizedProps,o=null,h=Ct,h!==null)switch(h.tag){case 27:case 5:o=h.memoizedProps}t[Te]=s,t=!!(t.nodeValue===r||o!==null&&o.suppressHydrationWarning===!0||my(t.nodeValue,r)),t||Ds(s,!0)}else t=Rl(t).createTextNode(o),t[Te]=s,s.stateNode=t}return We(s),null;case 31:if(r=s.memoizedState,t===null||t.memoizedState!==null){if(o=ia(s),r!==null){if(t===null){if(!o)throw Error(a(318));if(t=s.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(a(557));t[Te]=s}else yi(),(s.flags&128)===0&&(s.memoizedState=null),s.flags|=4;We(s),t=!1}else r=Eu(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=r),t=!0;if(!t)return s.flags&256?(rn(s),s):(rn(s),null);if((s.flags&128)!==0)throw Error(a(558))}return We(s),null;case 13:if(o=s.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(h=ia(s),o!==null&&o.dehydrated!==null){if(t===null){if(!h)throw Error(a(318));if(h=s.memoizedState,h=h!==null?h.dehydrated:null,!h)throw Error(a(317));h[Te]=s}else yi(),(s.flags&128)===0&&(s.memoizedState=null),s.flags|=4;We(s),h=!1}else h=Eu(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=h),h=!0;if(!h)return s.flags&256?(rn(s),s):(rn(s),null)}return rn(s),(s.flags&128)!==0?(s.lanes=r,s):(r=o!==null,t=t!==null&&t.memoizedState!==null,r&&(o=s.child,h=null,o.alternate!==null&&o.alternate.memoizedState!==null&&o.alternate.memoizedState.cachePool!==null&&(h=o.alternate.memoizedState.cachePool.pool),f=null,o.memoizedState!==null&&o.memoizedState.cachePool!==null&&(f=o.memoizedState.cachePool.pool),f!==h&&(o.flags|=2048)),r!==t&&r&&(s.child.flags|=8192),gl(s,s.updateQueue),We(s),null);case 4:return Ye(),t===null&&Lh(s.stateNode.containerInfo),We(s),null;case 10:return cs(s.type),We(s),null;case 19:if(P(ut),o=s.memoizedState,o===null)return We(s),null;if(h=(s.flags&128)!==0,f=o.rendering,f===null)if(h)Er(o,!1);else{if(at!==0||t!==null&&(t.flags&128)!==0)for(t=s.child;t!==null;){if(f=sl(t),f!==null){for(s.flags|=128,Er(o,!1),t=f.updateQueue,s.updateQueue=t,gl(s,t),s.subtreeFlags=0,t=r,r=s.child;r!==null;)Gm(r,t),r=r.sibling;return X(ut,ut.current&1|2),De&&os(s,o.treeForkCount),s.child}t=t.sibling}o.tail!==null&&At()>wl&&(s.flags|=128,h=!0,Er(o,!1),s.lanes=4194304)}else{if(!h)if(t=sl(f),t!==null){if(s.flags|=128,h=!0,t=t.updateQueue,s.updateQueue=t,gl(s,t),Er(o,!0),o.tail===null&&o.tailMode==="hidden"&&!f.alternate&&!De)return We(s),null}else 2*At()-o.renderingStartTime>wl&&r!==536870912&&(s.flags|=128,h=!0,Er(o,!1),s.lanes=4194304);o.isBackwards?(f.sibling=s.child,s.child=f):(t=o.last,t!==null?t.sibling=f:s.child=f,o.last=f)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=At(),t.sibling=null,r=ut.current,X(ut,h?r&1|2:r&1),De&&os(s,o.treeForkCount),t):(We(s),null);case 22:case 23:return rn(s),zu(),o=s.memoizedState!==null,t!==null?t.memoizedState!==null!==o&&(s.flags|=8192):o&&(s.flags|=8192),o?(r&536870912)!==0&&(s.flags&128)===0&&(We(s),s.subtreeFlags&6&&(s.flags|=8192)):We(s),r=s.updateQueue,r!==null&&gl(s,r.retryQueue),r=null,t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),o=null,s.memoizedState!==null&&s.memoizedState.cachePool!==null&&(o=s.memoizedState.cachePool.pool),o!==r&&(s.flags|=2048),t!==null&&P(xi),null;case 24:return r=null,t!==null&&(r=t.memoizedState.cache),s.memoizedState.cache!==r&&(s.flags|=2048),cs(ft),We(s),null;case 25:return null;case 30:return null}throw Error(a(156,s.tag))}function W_(t,s){switch(Su(s),s.tag){case 1:return t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 3:return cs(ft),Ye(),t=s.flags,(t&65536)!==0&&(t&128)===0?(s.flags=t&-65537|128,s):null;case 26:case 27:case 5:return As(s),null;case 31:if(s.memoizedState!==null){if(rn(s),s.alternate===null)throw Error(a(340));yi()}return t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 13:if(rn(s),t=s.memoizedState,t!==null&&t.dehydrated!==null){if(s.alternate===null)throw Error(a(340));yi()}return t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 19:return P(ut),null;case 4:return Ye(),null;case 10:return cs(s.type),null;case 22:case 23:return rn(s),zu(),t!==null&&P(xi),t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 24:return cs(ft),null;case 25:return null;default:return null}}function vg(t,s){switch(Su(s),s.tag){case 3:cs(ft),Ye();break;case 26:case 27:case 5:As(s);break;case 4:Ye();break;case 31:s.memoizedState!==null&&rn(s);break;case 13:rn(s);break;case 19:P(ut);break;case 10:cs(s.type);break;case 22:case 23:rn(s),zu(),t!==null&&P(xi);break;case 24:cs(ft)}}function Ar(t,s){try{var r=s.updateQueue,o=r!==null?r.lastEffect:null;if(o!==null){var h=o.next;r=h;do{if((r.tag&t)===t){o=void 0;var f=r.create,b=r.inst;o=f(),b.destroy=o}r=r.next}while(r!==h)}}catch(w){Pe(s,s.return,w)}}function Ps(t,s,r){try{var o=s.updateQueue,h=o!==null?o.lastEffect:null;if(h!==null){var f=h.next;o=f;do{if((o.tag&t)===t){var b=o.inst,w=b.destroy;if(w!==void 0){b.destroy=void 0,h=s;var A=r,U=w;try{U()}catch($){Pe(h,A,$)}}}o=o.next}while(o!==f)}}catch($){Pe(s,s.return,$)}}function xg(t){var s=t.updateQueue;if(s!==null){var r=t.stateNode;try{up(s,r)}catch(o){Pe(t,t.return,o)}}}function wg(t,s,r){r.props=Ei(t.type,t.memoizedProps),r.state=t.memoizedState;try{r.componentWillUnmount()}catch(o){Pe(t,s,o)}}function jr(t,s){try{var r=t.ref;if(r!==null){switch(t.tag){case 26:case 27:case 5:var o=t.stateNode;break;case 30:o=t.stateNode;break;default:o=t.stateNode}typeof r=="function"?t.refCleanup=r(o):r.current=o}}catch(h){Pe(t,s,h)}}function Fn(t,s){var r=t.ref,o=t.refCleanup;if(r!==null)if(typeof o=="function")try{o()}catch(h){Pe(t,s,h)}finally{t.refCleanup=null,t=t.alternate,t!=null&&(t.refCleanup=null)}else if(typeof r=="function")try{r(null)}catch(h){Pe(t,s,h)}else r.current=null}function _g(t){var s=t.type,r=t.memoizedProps,o=t.stateNode;try{e:switch(s){case"button":case"input":case"select":case"textarea":r.autoFocus&&o.focus();break e;case"img":r.src?o.src=r.src:r.srcSet&&(o.srcset=r.srcSet)}}catch(h){Pe(t,t.return,h)}}function gh(t,s,r){try{var o=t.stateNode;wS(o,t.type,r,s),o[tt]=s}catch(h){Pe(t,t.return,h)}}function Sg(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&Ks(t.type)||t.tag===4}function yh(t){e:for(;;){for(;t.sibling===null;){if(t.return===null||Sg(t.return))return null;t=t.return}for(t.sibling.return=t.return,t=t.sibling;t.tag!==5&&t.tag!==6&&t.tag!==18;){if(t.tag===27&&Ks(t.type)||t.flags&2||t.child===null||t.tag===4)continue e;t.child.return=t,t=t.child}if(!(t.flags&2))return t.stateNode}}function bh(t,s,r){var o=t.tag;if(o===5||o===6)t=t.stateNode,s?(r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r).insertBefore(t,s):(s=r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r,s.appendChild(t),r=r._reactRootContainer,r!=null||s.onclick!==null||(s.onclick=is));else if(o!==4&&(o===27&&Ks(t.type)&&(r=t.stateNode,s=null),t=t.child,t!==null))for(bh(t,s,r),t=t.sibling;t!==null;)bh(t,s,r),t=t.sibling}function yl(t,s,r){var o=t.tag;if(o===5||o===6)t=t.stateNode,s?r.insertBefore(t,s):r.appendChild(t);else if(o!==4&&(o===27&&Ks(t.type)&&(r=t.stateNode),t=t.child,t!==null))for(yl(t,s,r),t=t.sibling;t!==null;)yl(t,s,r),t=t.sibling}function Tg(t){var s=t.stateNode,r=t.memoizedProps;try{for(var o=t.type,h=s.attributes;h.length;)s.removeAttributeNode(h[0]);Dt(s,o,r),s[Te]=t,s[tt]=r}catch(f){Pe(t,t.return,f)}}var ms=!1,gt=!1,vh=!1,Eg=typeof WeakSet=="function"?WeakSet:Set,Nt=null;function eS(t,s){if(t=t.containerInfo,Ph=zl,t=Lm(t),du(t)){if("selectionStart"in t)var r={start:t.selectionStart,end:t.selectionEnd};else e:{r=(r=t.ownerDocument)&&r.defaultView||window;var o=r.getSelection&&r.getSelection();if(o&&o.rangeCount!==0){r=o.anchorNode;var h=o.anchorOffset,f=o.focusNode;o=o.focusOffset;try{r.nodeType,f.nodeType}catch{r=null;break e}var b=0,w=-1,A=-1,U=0,$=0,I=t,L=null;t:for(;;){for(var H;I!==r||h!==0&&I.nodeType!==3||(w=b+h),I!==f||o!==0&&I.nodeType!==3||(A=b+o),I.nodeType===3&&(b+=I.nodeValue.length),(H=I.firstChild)!==null;)L=I,I=H;for(;;){if(I===t)break t;if(L===r&&++U===h&&(w=b),L===f&&++$===o&&(A=b),(H=I.nextSibling)!==null)break;I=L,L=I.parentNode}I=H}r=w===-1||A===-1?null:{start:w,end:A}}else r=null}r=r||{start:0,end:0}}else r=null;for(Hh={focusedElem:t,selectionRange:r},zl=!1,Nt=s;Nt!==null;)if(s=Nt,t=s.child,(s.subtreeFlags&1028)!==0&&t!==null)t.return=s,Nt=t;else for(;Nt!==null;){switch(s=Nt,f=s.alternate,t=s.flags,s.tag){case 0:if((t&4)!==0&&(t=s.updateQueue,t=t!==null?t.events:null,t!==null))for(r=0;r title"))),Dt(f,o,r),f[Te]=t,jt(f),o=f;break e;case"link":var b=Ry("link","href",h).get(o+(r.href||""));if(b){for(var w=0;wqe&&(b=qe,qe=ye,ye=b);var O=Bm(w,ye),j=Bm(w,qe);if(O&&j&&(H.rangeCount!==1||H.anchorNode!==O.node||H.anchorOffset!==O.offset||H.focusNode!==j.node||H.focusOffset!==j.offset)){var B=I.createRange();B.setStart(O.node,O.offset),H.removeAllRanges(),ye>qe?(H.addRange(B),H.extend(j.node,j.offset)):(B.setEnd(j.node,j.offset),H.addRange(B))}}}}for(I=[],H=w;H=H.parentNode;)H.nodeType===1&&I.push({element:H,left:H.scrollLeft,top:H.scrollTop});for(typeof w.focus=="function"&&w.focus(),w=0;wr?32:r,V.T=null,r=Ah,Ah=null;var f=Fs,b=vs;if(St=0,ba=Fs=null,vs=0,(Le&6)!==0)throw Error(a(331));var w=Le;if(Le|=4,Ug(f.current),Dg(f,f.current,b,r),Le=w,Dr(0,!1),Lt&&typeof Lt.onPostCommitFiberRoot=="function")try{Lt.onPostCommitFiberRoot(ks,f)}catch{}return!0}finally{K.p=h,V.T=o,ey(t,s)}}function ny(t,s,r){s=bn(r,s),s=ah(t.stateNode,s,2),t=Ls(t,s,2),t!==null&&(ui(t,2),In(t))}function Pe(t,s,r){if(t.tag===3)ny(t,t,r);else for(;s!==null;){if(s.tag===3){ny(s,t,r);break}else if(s.tag===1){var o=s.stateNode;if(typeof s.type.getDerivedStateFromError=="function"||typeof o.componentDidCatch=="function"&&(qs===null||!qs.has(o))){t=bn(r,t),r=sg(2),o=Ls(s,r,2),o!==null&&(ig(r,o,s,t),ui(o,2),In(o));break}}s=s.return}}function Ch(t,s,r){var o=t.pingCache;if(o===null){o=t.pingCache=new sS;var h=new Set;o.set(s,h)}else h=o.get(s),h===void 0&&(h=new Set,o.set(s,h));h.has(r)||(_h=!0,h.add(r),t=lS.bind(null,t,s,r),s.then(t,t))}function lS(t,s,r){var o=t.pingCache;o!==null&&o.delete(s),t.pingedLanes|=t.suspendedLanes&r,t.warmLanes&=~r,Ie===t&&(ke&r)===r&&(at===4||at===3&&(ke&62914560)===ke&&300>At()-xl?(Le&2)===0&&va(t,0):Sh|=r,ya===ke&&(ya=0)),In(t)}function sy(t,s){s===0&&(s=Wa()),t=pi(t,s),t!==null&&(ui(t,s),In(t))}function cS(t){var s=t.memoizedState,r=0;s!==null&&(r=s.retryLane),sy(t,r)}function uS(t,s){var r=0;switch(t.tag){case 31:case 13:var o=t.stateNode,h=t.memoizedState;h!==null&&(r=h.retryLane);break;case 19:o=t.stateNode;break;case 22:o=t.stateNode._retryCache;break;default:throw Error(a(314))}o!==null&&o.delete(s),sy(t,r)}function hS(t,s){return Pt(t,s)}var jl=null,wa=null,Rh=!1,Nl=!1,Oh=!1,Gs=0;function In(t){t!==wa&&t.next===null&&(wa===null?jl=wa=t:wa=wa.next=t),Nl=!0,Rh||(Rh=!0,fS())}function Dr(t,s){if(!Oh&&Nl){Oh=!0;do for(var r=!1,o=jl;o!==null;){if(t!==0){var h=o.pendingLanes;if(h===0)var f=0;else{var b=o.suspendedLanes,w=o.pingedLanes;f=(1<<31-wt(42|t)+1)-1,f&=h&~(b&~w),f=f&201326741?f&201326741|1:f?f|2:0}f!==0&&(r=!0,oy(o,f))}else f=ke,f=li(o,o===Ie?f:0,o.cancelPendingCommit!==null||o.timeoutHandle!==-1),(f&3)===0||ci(o,f)||(r=!0,oy(o,f));o=o.next}while(r);Oh=!1}}function dS(){iy()}function iy(){Nl=Rh=!1;var t=0;Gs!==0&&SS()&&(t=Gs);for(var s=At(),r=null,o=jl;o!==null;){var h=o.next,f=ay(o,s);f===0?(o.next=null,r===null?jl=h:r.next=h,h===null&&(wa=r)):(r=o,(t!==0||(f&3)!==0)&&(Nl=!0)),o=h}St!==0&&St!==5||Dr(t),Gs!==0&&(Gs=0)}function ay(t,s){for(var r=t.suspendedLanes,o=t.pingedLanes,h=t.expirationTimes,f=t.pendingLanes&-62914561;0w)break;var $=A.transferSize,I=A.initiatorType;$&&py(I)&&(A=A.responseEnd,b+=$*(A"u"?null:document;function jy(t,s,r){var o=_a;if(o&&typeof s=="string"&&s){var h=gn(s);h='link[rel="'+t+'"][href="'+h+'"]',typeof r=="string"&&(h+='[crossorigin="'+r+'"]'),Ay.has(h)||(Ay.add(h),t={rel:t,crossOrigin:r,href:s},o.querySelector(h)===null&&(s=o.createElement("link"),Dt(s,"link",t),jt(s),o.head.appendChild(s)))}}function OS(t){xs.D(t),jy("dns-prefetch",t,null)}function DS(t,s){xs.C(t,s),jy("preconnect",t,s)}function MS(t,s,r){xs.L(t,s,r);var o=_a;if(o&&t&&s){var h='link[rel="preload"][as="'+gn(s)+'"]';s==="image"&&r&&r.imageSrcSet?(h+='[imagesrcset="'+gn(r.imageSrcSet)+'"]',typeof r.imageSizes=="string"&&(h+='[imagesizes="'+gn(r.imageSizes)+'"]')):h+='[href="'+gn(t)+'"]';var f=h;switch(s){case"style":f=Sa(t);break;case"script":f=Ta(t)}Tn.has(f)||(t=v({rel:"preload",href:s==="image"&&r&&r.imageSrcSet?void 0:t,as:s},r),Tn.set(f,t),o.querySelector(h)!==null||s==="style"&&o.querySelector(Lr(f))||s==="script"&&o.querySelector(zr(f))||(s=o.createElement("link"),Dt(s,"link",t),jt(s),o.head.appendChild(s)))}}function BS(t,s){xs.m(t,s);var r=_a;if(r&&t){var o=s&&typeof s.as=="string"?s.as:"script",h='link[rel="modulepreload"][as="'+gn(o)+'"][href="'+gn(t)+'"]',f=h;switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":f=Ta(t)}if(!Tn.has(f)&&(t=v({rel:"modulepreload",href:t},s),Tn.set(f,t),r.querySelector(h)===null)){switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(r.querySelector(zr(f)))return}o=r.createElement("link"),Dt(o,"link",t),jt(o),r.head.appendChild(o)}}}function US(t,s,r){xs.S(t,s,r);var o=_a;if(o&&t){var h=Ii(o).hoistableStyles,f=Sa(t);s=s||"default";var b=h.get(f);if(!b){var w={loading:0,preload:null};if(b=o.querySelector(Lr(f)))w.loading=5;else{t=v({rel:"stylesheet",href:t,"data-precedence":s},r),(r=Tn.get(f))&&Yh(t,r);var A=b=o.createElement("link");jt(A),Dt(A,"link",t),A._p=new Promise(function(U,$){A.onload=U,A.onerror=$}),A.addEventListener("load",function(){w.loading|=1}),A.addEventListener("error",function(){w.loading|=2}),w.loading|=4,Dl(b,s,o)}b={type:"stylesheet",instance:b,count:1,state:w},h.set(f,b)}}}function LS(t,s){xs.X(t,s);var r=_a;if(r&&t){var o=Ii(r).hoistableScripts,h=Ta(t),f=o.get(h);f||(f=r.querySelector(zr(h)),f||(t=v({src:t,async:!0},s),(s=Tn.get(h))&&Xh(t,s),f=r.createElement("script"),jt(f),Dt(f,"link",t),r.head.appendChild(f)),f={type:"script",instance:f,count:1,state:null},o.set(h,f))}}function zS(t,s){xs.M(t,s);var r=_a;if(r&&t){var o=Ii(r).hoistableScripts,h=Ta(t),f=o.get(h);f||(f=r.querySelector(zr(h)),f||(t=v({src:t,async:!0,type:"module"},s),(s=Tn.get(h))&&Xh(t,s),f=r.createElement("script"),jt(f),Dt(f,"link",t),r.head.appendChild(f)),f={type:"script",instance:f,count:1,state:null},o.set(h,f))}}function Ny(t,s,r,o){var h=(h=ue.current)?Ol(h):null;if(!h)throw Error(a(446));switch(t){case"meta":case"title":return null;case"style":return typeof r.precedence=="string"&&typeof r.href=="string"?(s=Sa(r.href),r=Ii(h).hoistableStyles,o=r.get(s),o||(o={type:"style",instance:null,count:0,state:null},r.set(s,o)),o):{type:"void",instance:null,count:0,state:null};case"link":if(r.rel==="stylesheet"&&typeof r.href=="string"&&typeof r.precedence=="string"){t=Sa(r.href);var f=Ii(h).hoistableStyles,b=f.get(t);if(b||(h=h.ownerDocument||h,b={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},f.set(t,b),(f=h.querySelector(Lr(t)))&&!f._p&&(b.instance=f,b.state.loading=5),Tn.has(t)||(r={rel:"preload",as:"style",href:r.href,crossOrigin:r.crossOrigin,integrity:r.integrity,media:r.media,hrefLang:r.hrefLang,referrerPolicy:r.referrerPolicy},Tn.set(t,r),f||VS(h,t,r,b.state))),s&&o===null)throw Error(a(528,""));return b}if(s&&o!==null)throw Error(a(529,""));return null;case"script":return s=r.async,r=r.src,typeof r=="string"&&s&&typeof s!="function"&&typeof s!="symbol"?(s=Ta(r),r=Ii(h).hoistableScripts,o=r.get(s),o||(o={type:"script",instance:null,count:0,state:null},r.set(s,o)),o):{type:"void",instance:null,count:0,state:null};default:throw Error(a(444,t))}}function Sa(t){return'href="'+gn(t)+'"'}function Lr(t){return'link[rel="stylesheet"]['+t+"]"}function ky(t){return v({},t,{"data-precedence":t.precedence,precedence:null})}function VS(t,s,r,o){t.querySelector('link[rel="preload"][as="style"]['+s+"]")?o.loading=1:(s=t.createElement("link"),o.preload=s,s.addEventListener("load",function(){return o.loading|=1}),s.addEventListener("error",function(){return o.loading|=2}),Dt(s,"link",r),jt(s),t.head.appendChild(s))}function Ta(t){return'[src="'+gn(t)+'"]'}function zr(t){return"script[async]"+t}function Cy(t,s,r){if(s.count++,s.instance===null)switch(s.type){case"style":var o=t.querySelector('style[data-href~="'+gn(r.href)+'"]');if(o)return s.instance=o,jt(o),o;var h=v({},r,{"data-href":r.href,"data-precedence":r.precedence,href:null,precedence:null});return o=(t.ownerDocument||t).createElement("style"),jt(o),Dt(o,"style",h),Dl(o,r.precedence,t),s.instance=o;case"stylesheet":h=Sa(r.href);var f=t.querySelector(Lr(h));if(f)return s.state.loading|=4,s.instance=f,jt(f),f;o=ky(r),(h=Tn.get(h))&&Yh(o,h),f=(t.ownerDocument||t).createElement("link"),jt(f);var b=f;return b._p=new Promise(function(w,A){b.onload=w,b.onerror=A}),Dt(f,"link",o),s.state.loading|=4,Dl(f,r.precedence,t),s.instance=f;case"script":return f=Ta(r.src),(h=t.querySelector(zr(f)))?(s.instance=h,jt(h),h):(o=r,(h=Tn.get(f))&&(o=v({},r),Xh(o,h)),t=t.ownerDocument||t,h=t.createElement("script"),jt(h),Dt(h,"link",o),t.head.appendChild(h),s.instance=h);case"void":return null;default:throw Error(a(443,s.type))}else s.type==="stylesheet"&&(s.state.loading&4)===0&&(o=s.instance,s.state.loading|=4,Dl(o,r.precedence,t));return s.instance}function Dl(t,s,r){for(var o=r.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),h=o.length?o[o.length-1]:null,f=h,b=0;b title"):null)}function PS(t,s,r){if(r===1||s.itemProp!=null)return!1;switch(t){case"meta":case"title":return!0;case"style":if(typeof s.precedence!="string"||typeof s.href!="string"||s.href==="")break;return!0;case"link":if(typeof s.rel!="string"||typeof s.href!="string"||s.href===""||s.onLoad||s.onError)break;switch(s.rel){case"stylesheet":return t=s.disabled,typeof s.precedence=="string"&&t==null;default:return!0}case"script":if(s.async&&typeof s.async!="function"&&typeof s.async!="symbol"&&!s.onLoad&&!s.onError&&s.src&&typeof s.src=="string")return!0}return!1}function Dy(t){return!(t.type==="stylesheet"&&(t.state.loading&3)===0)}function HS(t,s,r,o){if(r.type==="stylesheet"&&(typeof o.media!="string"||matchMedia(o.media).matches!==!1)&&(r.state.loading&4)===0){if(r.instance===null){var h=Sa(o.href),f=s.querySelector(Lr(h));if(f){s=f._p,s!==null&&typeof s=="object"&&typeof s.then=="function"&&(t.count++,t=Bl.bind(t),s.then(t,t)),r.state.loading|=4,r.instance=f,jt(f);return}f=s.ownerDocument||s,o=ky(o),(h=Tn.get(h))&&Yh(o,h),f=f.createElement("link"),jt(f);var b=f;b._p=new Promise(function(w,A){b.onload=w,b.onerror=A}),Dt(f,"link",o),r.instance=f}t.stylesheets===null&&(t.stylesheets=new Map),t.stylesheets.set(r,s),(s=r.state.preload)&&(r.state.loading&3)===0&&(t.count++,r=Bl.bind(t),s.addEventListener("load",r),s.addEventListener("error",r))}}var Jh=0;function $S(t,s){return t.stylesheets&&t.count===0&&Ll(t,t.stylesheets),0Jh?50:800)+s);return t.unsuspend=r,function(){t.unsuspend=null,clearTimeout(o),clearTimeout(h)}}:null}function Bl(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Ll(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var Ul=null;function Ll(t,s){t.stylesheets=null,t.unsuspend!==null&&(t.count++,Ul=new Map,s.forEach(qS,t),Ul=null,Bl.call(t))}function qS(t,s){if(!(s.state.loading&4)){var r=Ul.get(t);if(r)var o=r.get(null);else{r=new Map,Ul.set(t,r);for(var h=t.querySelectorAll("link[data-precedence],style[data-precedence]"),f=0;f"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}return n(),ad.exports=a1(),ad.exports}var o1=r1();const Ef=D.createContext({});function Af(n){const e=D.useRef(null);return e.current===null&&(e.current=n()),e.current}const l1=typeof window<"u",jf=l1?D.useLayoutEffect:D.useEffect,Uc=D.createContext(null);function Nf(n,e){n.indexOf(e)===-1&&n.push(e)}function gc(n,e){const i=n.indexOf(e);i>-1&&n.splice(i,1)}const Zn=(n,e,i)=>i>e?e:i{};const ii={},Ev=n=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(n),Av=n=>typeof n=="object"&&n!==null,jv=n=>/^0[^.\s]+$/u.test(n);function Nv(n){let e;return()=>(e===void 0&&(e=n()),e)}const jn=n=>n,To=(...n)=>n.reduce((e,i)=>a=>i(e(a))),lo=(n,e,i)=>{const a=e-n;return a?(i-n)/a:1};class Cf{constructor(){this.subscriptions=[]}add(e){return Nf(this.subscriptions,e),()=>gc(this.subscriptions,e)}notify(e,i,a){const l=this.subscriptions.length;if(l)if(l===1)this.subscriptions[0](e,i,a);else for(let c=0;cn*1e3,An=n=>n/1e3,kv=(n,e)=>e?n*(1e3/e):0,Cv=(n,e,i)=>(((1-3*i+3*e)*n+(3*i-6*e))*n+3*e)*n,c1=1e-7,u1=12;function h1(n,e,i,a,l){let c,u,d=0;do u=e+(i-e)/2,c=Cv(u,a,l)-n,c>0?i=u:e=u;while(Math.abs(c)>c1&&++dh1(c,0,1,n,i);return c=>c===0||c===1?c:Cv(l(c),e,a)}const Rv=n=>e=>e<=.5?n(2*e)/2:(2-n(2*(1-e)))/2,Ov=n=>e=>1-n(1-e),Dv=Eo(.33,1.53,.69,.99),Rf=Ov(Dv),Mv=Rv(Rf),Bv=n=>n>=1?1:(n*=2)<1?.5*Rf(n):.5*(2-Math.pow(2,-10*(n-1))),Of=n=>1-Math.sin(Math.acos(n)),Uv=Ov(Of),Lv=Rv(Of),d1=Eo(.42,0,1,1),f1=Eo(0,0,.58,1),zv=Eo(.42,0,.58,1),m1=n=>Array.isArray(n)&&typeof n[0]!="number",Vv=n=>Array.isArray(n)&&typeof n[0]=="number",p1={linear:jn,easeIn:d1,easeInOut:zv,easeOut:f1,circIn:Of,circInOut:Lv,circOut:Uv,backIn:Rf,backInOut:Mv,backOut:Dv,anticipate:Bv},g1=n=>typeof n=="string",s0=n=>{if(Vv(n)){kf(n.length===4);const[e,i,a,l]=n;return Eo(e,i,a,l)}else if(g1(n))return p1[n];return n},Il=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"];function y1(n){let e=new Set,i=new Set,a=!1,l=!1;const c=new WeakSet;let u={delta:0,timestamp:0,isProcessing:!1};function d(p){c.has(p)&&(m.schedule(p),n()),p(u)}const m={schedule:(p,y=!1,v=!1)=>{const _=v&&a?e:i;return y&&c.add(p),_.add(p),p},cancel:p=>{i.delete(p),c.delete(p)},process:p=>{if(u=p,a){l=!0;return}a=!0;const y=e;e=i,i=y,e.forEach(d),e.clear(),a=!1,l&&(l=!1,m.process(p))}};return m}const b1=40;function Pv(n,e){let i=!1,a=!0;const l={delta:0,timestamp:0,isProcessing:!1},c=()=>i=!0,u=Il.reduce((N,z)=>(N[z]=y1(c),N),{}),{setup:d,read:m,resolveKeyframes:p,preUpdate:y,update:v,preRender:x,render:_,postRender:S}=u,T=()=>{const N=ii.useManualTiming,z=N?l.timestamp:performance.now();i=!1,N||(l.delta=a?1e3/60:Math.max(Math.min(z-l.timestamp,b1),1)),l.timestamp=z,l.isProcessing=!0,d.process(l),m.process(l),p.process(l),y.process(l),v.process(l),x.process(l),_.process(l),S.process(l),l.isProcessing=!1,i&&e&&(a=!1,n(T))},k=()=>{i=!0,a=!0,l.isProcessing||n(T)};return{schedule:Il.reduce((N,z)=>{const Y=u[z];return N[z]=(Z,F=!1,G=!1)=>(i||k(),Y.schedule(Z,F,G)),N},{}),cancel:N=>{for(let z=0;z(oc===void 0&&Ft.set(Mt.isProcessing||ii.useManualTiming?Mt.timestamp:performance.now()),oc),set:n=>{oc=n,queueMicrotask(v1)}},Hv=n=>e=>typeof e=="string"&&e.startsWith(n),$v=Hv("--"),x1=Hv("var(--"),Df=n=>x1(n)?w1.test(n.split("/*")[0].trim()):!1,w1=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;function i0(n){return typeof n!="string"?!1:n.split("/*")[0].includes("var(--")}const Xa={test:n=>typeof n=="number",parse:parseFloat,transform:n=>n},co={...Xa,transform:n=>Zn(0,1,n)},Gl={...Xa,default:1},eo=n=>Math.round(n*1e5)/1e5,Mf=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;function _1(n){return n==null}const S1=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,Bf=(n,e)=>i=>!!(typeof i=="string"&&S1.test(i)&&i.startsWith(n)||e&&!_1(i)&&Object.prototype.hasOwnProperty.call(i,e)),qv=(n,e,i)=>a=>{if(typeof a!="string")return a;const[l,c,u,d]=a.match(Mf);return{[n]:parseFloat(l),[e]:parseFloat(c),[i]:parseFloat(u),alpha:d!==void 0?parseFloat(d):1}},T1=n=>Zn(0,255,n),ud={...Xa,transform:n=>Math.round(T1(n))},Di={test:Bf("rgb","red"),parse:qv("red","green","blue"),transform:({red:n,green:e,blue:i,alpha:a=1})=>"rgba("+ud.transform(n)+", "+ud.transform(e)+", "+ud.transform(i)+", "+eo(co.transform(a))+")"};function E1(n){let e="",i="",a="",l="";return n.length>5?(e=n.substring(1,3),i=n.substring(3,5),a=n.substring(5,7),l=n.substring(7,9)):(e=n.substring(1,2),i=n.substring(2,3),a=n.substring(3,4),l=n.substring(4,5),e+=e,i+=i,a+=a,l+=l),{red:parseInt(e,16),green:parseInt(i,16),blue:parseInt(a,16),alpha:l?parseInt(l,16)/255:1}}const Ld={test:Bf("#"),parse:E1,transform:Di.transform},Ao=n=>({test:e=>typeof e=="string"&&e.endsWith(n)&&e.split(" ").length===1,parse:parseFloat,transform:e=>`${e}${n}`}),_s=Ao("deg"),Qn=Ao("%"),oe=Ao("px"),A1=Ao("vh"),j1=Ao("vw"),a0={...Qn,parse:n=>Qn.parse(n)/100,transform:n=>Qn.transform(n*100)},za={test:Bf("hsl","hue"),parse:qv("hue","saturation","lightness"),transform:({hue:n,saturation:e,lightness:i,alpha:a=1})=>"hsla("+Math.round(n)+", "+Qn.transform(eo(e))+", "+Qn.transform(eo(i))+", "+eo(co.transform(a))+")"},bt={test:n=>Di.test(n)||Ld.test(n)||za.test(n),parse:n=>Di.test(n)?Di.parse(n):za.test(n)?za.parse(n):Ld.parse(n),transform:n=>typeof n=="string"?n:n.hasOwnProperty("red")?Di.transform(n):za.transform(n),getAnimatableNone:n=>{const e=bt.parse(n);return e.alpha=0,bt.transform(e)}},N1=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;function k1(n){var e,i;return isNaN(n)&&typeof n=="string"&&(((e=n.match(Mf))==null?void 0:e.length)||0)+(((i=n.match(N1))==null?void 0:i.length)||0)>0}const Fv="number",Iv="color",C1="var",R1="var(",r0="${}",O1=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Ga(n){const e=n.toString(),i=[],a={color:[],number:[],var:[]},l=[];let c=0;const d=e.replace(O1,m=>(bt.test(m)?(a.color.push(c),l.push(Iv),i.push(bt.parse(m))):m.startsWith(R1)?(a.var.push(c),l.push(C1),i.push(m)):(a.number.push(c),l.push(Fv),i.push(parseFloat(m))),++c,r0)).split(r0);return{values:i,split:d,indexes:a,types:l}}function D1(n){return Ga(n).values}function Gv({split:n,types:e}){const i=n.length;return a=>{let l="";for(let c=0;ctypeof n=="number"?0:bt.test(n)?bt.getAnimatableNone(n):n,U1=(n,e)=>typeof n=="number"?e!=null&&e.trim().endsWith("/")?n:0:B1(n);function L1(n){const e=Ga(n);return Gv(e)(e.values.map((a,l)=>U1(a,e.split[l])))}const Pn={test:k1,parse:D1,createTransformer:M1,getAnimatableNone:L1};function hd(n,e,i){return i<0&&(i+=1),i>1&&(i-=1),i<1/6?n+(e-n)*6*i:i<1/2?e:i<2/3?n+(e-n)*(2/3-i)*6:n}function z1({hue:n,saturation:e,lightness:i,alpha:a}){n/=360,e/=100,i/=100;let l=0,c=0,u=0;if(!e)l=c=u=i;else{const d=i<.5?i*(1+e):i+e-i*e,m=2*i-d;l=hd(m,d,n+1/3),c=hd(m,d,n),u=hd(m,d,n-1/3)}return{red:Math.round(l*255),green:Math.round(c*255),blue:Math.round(u*255),alpha:a}}function yc(n,e){return i=>i>0?e:n}const Ge=(n,e,i)=>n+(e-n)*i,dd=(n,e,i)=>{const a=n*n,l=i*(e*e-a)+a;return l<0?0:Math.sqrt(l)},V1=[Ld,Di,za],P1=n=>V1.find(e=>e.test(n));function o0(n){const e=P1(n);if(!e)return!1;let i=e.parse(n);return e===za&&(i=z1(i)),i}const l0=(n,e)=>{const i=o0(n),a=o0(e);if(!i||!a)return yc(n,e);const l={...i};return c=>(l.red=dd(i.red,a.red,c),l.green=dd(i.green,a.green,c),l.blue=dd(i.blue,a.blue,c),l.alpha=Ge(i.alpha,a.alpha,c),Di.transform(l))},zd=new Set(["none","hidden"]);function H1(n,e){return zd.has(n)?i=>i<=0?n:e:i=>i>=1?e:n}function $1(n,e){return i=>Ge(n,e,i)}function Uf(n){return typeof n=="number"?$1:typeof n=="string"?Df(n)?yc:bt.test(n)?l0:I1:Array.isArray(n)?Kv:typeof n=="object"?bt.test(n)?l0:q1:yc}function Kv(n,e){const i=[...n],a=i.length,l=n.map((c,u)=>Uf(c)(c,e[u]));return c=>{for(let u=0;u{for(const c in a)i[c]=a[c](l);return i}}function F1(n,e){const i=[],a={color:0,var:0,number:0};for(let l=0;l{const i=Pn.createTransformer(e),a=Ga(n),l=Ga(e);return a.indexes.var.length===l.indexes.var.length&&a.indexes.color.length===l.indexes.color.length&&a.indexes.number.length>=l.indexes.number.length?zd.has(n)&&!l.values.length||zd.has(e)&&!a.values.length?H1(n,e):To(Kv(F1(a,l),l.values),i):yc(n,e)};function Yv(n,e,i){return typeof n=="number"&&typeof e=="number"&&typeof i=="number"?Ge(n,e,i):Uf(n)(n,e)}const G1=n=>{const e=({timestamp:i})=>n(i);return{start:(i=!0)=>Ke.update(e,i),stop:()=>ai(e),now:()=>Mt.isProcessing?Mt.timestamp:Ft.now()}},Xv=(n,e,i=10)=>{let a="";const l=Math.max(Math.round(e/i),2);for(let c=0;c=bc?1/0:e}function K1(n,e=100,i){const a=i({...n,keyframes:[0,e]}),l=Math.min(Lf(a),bc);return{type:"keyframes",ease:c=>a.next(l*c).value/e,duration:An(l)}}const ot={stiffness:100,damping:10,mass:1,velocity:0,duration:800,bounce:.3,visualDuration:.3,restSpeed:{granular:.01,default:2},restDelta:{granular:.005,default:.5},minDuration:.01,maxDuration:10,minDamping:.05,maxDamping:1};function Vd(n,e){return n*Math.sqrt(1-e*e)}const Y1=12;function X1(n,e,i){let a=i;for(let l=1;l{const y=p*u,v=y*n,x=y-i,_=Vd(p,u),S=Math.exp(-v);return fd-x/_*S},c=p=>{const v=p*u*n,x=v*i+i,_=Math.pow(u,2)*Math.pow(p,2)*n,S=Math.exp(-v),T=Vd(Math.pow(p,2),u);return(-l(p)+fd>0?-1:1)*((x-_)*S)/T}):(l=p=>{const y=Math.exp(-p*n),v=(p-i)*n+1;return-fd+y*v},c=p=>{const y=Math.exp(-p*n),v=(i-p)*(n*n);return y*v});const d=5/n,m=X1(l,c,d);if(n=dn(n),isNaN(m))return{stiffness:ot.stiffness,damping:ot.damping,duration:n};{const p=Math.pow(m,2)*a;return{stiffness:p,damping:u*2*Math.sqrt(a*p),duration:n}}}const Q1=["duration","bounce"],Z1=["stiffness","damping","mass"];function c0(n,e){return e.some(i=>n[i]!==void 0)}function W1(n){let e={velocity:ot.velocity,stiffness:ot.stiffness,damping:ot.damping,mass:ot.mass,isResolvedFromDuration:!1,...n};if(!c0(n,Z1)&&c0(n,Q1))if(e.velocity=0,n.visualDuration){const i=n.visualDuration,a=2*Math.PI/(i*1.2),l=a*a,c=2*Zn(.05,1,1-(n.bounce||0))*Math.sqrt(l);e={...e,mass:ot.mass,stiffness:l,damping:c}}else{const i=J1({...n,velocity:0});e={...e,...i,mass:ot.mass},e.isResolvedFromDuration=!0}return e}function vc(n=ot.visualDuration,e=ot.bounce){const i=typeof n!="object"?{visualDuration:n,keyframes:[0,1],bounce:e}:n;let{restSpeed:a,restDelta:l}=i;const c=i.keyframes[0],u=i.keyframes[i.keyframes.length-1],d={done:!1,value:c},{stiffness:m,damping:p,mass:y,duration:v,velocity:x,isResolvedFromDuration:_}=W1({...i,velocity:-An(i.velocity||0)}),S=x||0,T=p/(2*Math.sqrt(m*y)),k=u-c,C=An(Math.sqrt(m/y)),M=Math.abs(k)<5;a||(a=M?ot.restSpeed.granular:ot.restSpeed.default),l||(l=M?ot.restDelta.granular:ot.restDelta.default);let N,z,Y,Z,F,G;if(T<1)Y=Vd(C,T),Z=(S+T*C*k)/Y,N=Q=>{const ce=Math.exp(-T*C*Q);return u-ce*(Z*Math.sin(Y*Q)+k*Math.cos(Y*Q))},F=T*C*Z+k*Y,G=T*C*k-Z*Y,z=Q=>Math.exp(-T*C*Q)*(F*Math.sin(Y*Q)+G*Math.cos(Y*Q));else if(T===1){N=ce=>u-Math.exp(-C*ce)*(k+(S+C*k)*ce);const Q=S+C*k;z=ce=>Math.exp(-C*ce)*(C*Q*ce-S)}else{const Q=C*Math.sqrt(T*T-1);N=Re=>{const Se=Math.exp(-T*C*Re),V=Math.min(Q*Re,300);return u-Se*((S+T*C*k)*Math.sinh(V)+Q*k*Math.cosh(V))/Q};const ce=(S+T*C*k)/Q,me=T*C*ce-k*Q,_e=T*C*k-ce*Q;z=Re=>{const Se=Math.exp(-T*C*Re),V=Math.min(Q*Re,300);return Se*(me*Math.sinh(V)+_e*Math.cosh(V))}}const ne={calculatedDuration:_&&v||null,velocity:Q=>dn(z(Q)),next:Q=>{if(!_&&T<1){const me=Math.exp(-T*C*Q),_e=Math.sin(Y*Q),Re=Math.cos(Y*Q),Se=u-me*(Z*_e+k*Re),V=dn(me*(F*_e+G*Re));return d.done=Math.abs(V)<=a&&Math.abs(u-Se)<=l,d.value=d.done?u:Se,d}const ce=N(Q);if(_)d.done=Q>=v;else{const me=dn(z(Q));d.done=Math.abs(me)<=a&&Math.abs(u-ce)<=l}return d.value=d.done?u:ce,d},toString:()=>{const Q=Math.min(Lf(ne),bc),ce=Xv(me=>ne.next(Q*me).value,Q,30);return Q+"ms "+ce},toTransition:()=>{}};return ne}vc.applyToOptions=n=>{const e=K1(n,100,vc);return n.ease=e.ease,n.duration=dn(e.duration),n.type="keyframes",n};const eT=5;function Jv(n,e,i){const a=Math.max(e-eT,0);return kv(i-n(a),e-a)}function Pd({keyframes:n,velocity:e=0,power:i=.8,timeConstant:a=325,bounceDamping:l=10,bounceStiffness:c=500,modifyTarget:u,min:d,max:m,restDelta:p=.5,restSpeed:y}){const v=n[0],x={done:!1,value:v},_=G=>d!==void 0&&Gm,S=G=>d===void 0?m:m===void 0||Math.abs(d-G)-T*Math.exp(-G/a),N=G=>C+M(G),z=G=>{const ne=M(G),Q=N(G);x.done=Math.abs(ne)<=p,x.value=x.done?C:Q};let Y,Z;const F=G=>{_(x.value)&&(Y=G,Z=vc({keyframes:[x.value,S(x.value)],velocity:Jv(N,G,x.value),damping:l,stiffness:c,restDelta:p,restSpeed:y}))};return F(0),{calculatedDuration:null,next:G=>{let ne=!1;return!Z&&Y===void 0&&(ne=!0,z(G),F(G)),Y!==void 0&&G>=Y?Z.next(G-Y):(!ne&&z(G),x)}}}function tT(n,e,i){const a=[],l=i||ii.mix||Yv,c=n.length-1;for(let u=0;ue[0];if(c===2&&e[0]===e[1])return()=>e[1];const u=n[0]===n[1];n[0]>n[c-1]&&(n=[...n].reverse(),e=[...e].reverse());const d=tT(e,a,l),m=d.length,p=y=>{if(u&&y1)for(;vp(Zn(n[0],n[c-1],y)):p}function sT(n,e){const i=n[n.length-1];for(let a=1;a<=e;a++){const l=lo(0,e,a);n.push(Ge(i,1,l))}}function iT(n){const e=[0];return sT(e,n.length-1),e}function aT(n,e){return n.map(i=>i*e)}function rT(n,e){return n.map(()=>e||zv).splice(0,n.length-1)}function to({duration:n=300,keyframes:e,times:i,ease:a="easeInOut"}){const l=m1(a)?a.map(s0):s0(a),c={done:!1,value:e[0]},u=aT(i&&i.length===e.length?i:iT(e),n),d=nT(u,e,{ease:Array.isArray(l)?l:rT(e,l)});return{calculatedDuration:n,next:m=>(c.value=d(m),c.done=m>=n,c)}}const oT=n=>n!==null;function Lc(n,{repeat:e,repeatType:i="loop"},a,l=1){const c=n.filter(oT),d=l<0||e&&i!=="loop"&&e%2===1?0:c.length-1;return!d||a===void 0?c[d]:a}const lT={decay:Pd,inertia:Pd,tween:to,keyframes:to,spring:vc};function Qv(n){typeof n.type=="string"&&(n.type=lT[n.type])}class zf{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(e=>{this.resolve=e})}notifyFinished(){this.resolve()}then(e,i){return this.finished.then(e,i)}}const cT=n=>n/100;class xc extends zf{constructor(e){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.delayState={done:!1,value:void 0},this.stop=()=>{var a,l;const{motionValue:i}=this.options;i&&i.updatedAt!==Ft.now()&&this.tick(Ft.now()),this.isStopped=!0,this.state!=="idle"&&(this.teardown(),(l=(a=this.options).onStop)==null||l.call(a))},this.options=e,this.initAnimation(),this.play(),e.autoplay===!1&&this.pause()}initAnimation(){const{options:e}=this;Qv(e);const{type:i=to,repeat:a=0,repeatDelay:l=0,repeatType:c,velocity:u=0}=e;let{keyframes:d}=e;const m=i||to;m!==to&&typeof d[0]!="number"&&(this.mixKeyframes=To(cT,Yv(d[0],d[1])),d=[0,100]);const p=m({...e,keyframes:d});c==="mirror"&&(this.mirroredGenerator=m({...e,keyframes:[...d].reverse(),velocity:-u})),p.calculatedDuration===null&&(p.calculatedDuration=Lf(p));const{calculatedDuration:y}=p;this.calculatedDuration=y,this.resolvedDuration=y+l,this.totalDuration=this.resolvedDuration*(a+1)-l,this.generator=p}updateTime(e){const i=Math.round(e-this.startTime)*this.playbackSpeed;this.holdTime!==null?this.currentTime=this.holdTime:this.currentTime=i}tick(e,i=!1){const{generator:a,totalDuration:l,mixKeyframes:c,mirroredGenerator:u,resolvedDuration:d,calculatedDuration:m}=this;if(this.startTime===null)return a.next(0);const{delay:p=0,keyframes:y,repeat:v,repeatType:x,repeatDelay:_,type:S,onUpdate:T,finalKeyframe:k}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,e):this.speed<0&&(this.startTime=Math.min(e-l/this.speed,this.startTime)),i?this.currentTime=e:this.updateTime(e);const C=this.currentTime-p*(this.playbackSpeed>=0?1:-1),M=this.playbackSpeed>=0?C<0:C>l;this.currentTime=Math.max(C,0),this.state==="finished"&&this.holdTime===null&&(this.currentTime=l);let N=this.currentTime,z=a;if(v){const G=Math.min(this.currentTime,l)/d;let ne=Math.floor(G),Q=G%1;!Q&&G>=1&&(Q=1),Q===1&&ne--,ne=Math.min(ne,v+1),!!(ne%2)&&(x==="reverse"?(Q=1-Q,_&&(Q-=_/d)):x==="mirror"&&(z=u)),N=Zn(0,1,Q)*d}let Y;M?(this.delayState.value=y[0],Y=this.delayState):Y=z.next(N),c&&!M&&(Y.value=c(Y.value));let{done:Z}=Y;!M&&m!==null&&(Z=this.playbackSpeed>=0?this.currentTime>=l:this.currentTime<=0);const F=this.holdTime===null&&(this.state==="finished"||this.state==="running"&&Z);return F&&S!==Pd&&(Y.value=Lc(y,this.options,k,this.speed)),T&&T(Y.value),F&&this.finish(),Y}then(e,i){return this.finished.then(e,i)}get duration(){return An(this.calculatedDuration)}get iterationDuration(){const{delay:e=0}=this.options||{};return this.duration+An(e)}get time(){return An(this.currentTime)}set time(e){e=dn(e),this.currentTime=e,this.startTime===null||this.holdTime!==null||this.playbackSpeed===0?this.holdTime=e:this.driver&&(this.startTime=this.driver.now()-e/this.playbackSpeed),this.driver?this.driver.start(!1):(this.startTime=0,this.state="paused",this.holdTime=e,this.tick(e))}getGeneratorVelocity(){const e=this.currentTime;if(e<=0)return this.options.velocity||0;if(this.generator.velocity)return this.generator.velocity(e);const i=this.generator.next(e).value;return Jv(a=>this.generator.next(a).value,e,i)}get speed(){return this.playbackSpeed}set speed(e){const i=this.playbackSpeed!==e;i&&this.driver&&this.updateTime(Ft.now()),this.playbackSpeed=e,i&&this.driver&&(this.time=An(this.currentTime))}play(){var l,c;if(this.isStopped)return;const{driver:e=G1,startTime:i}=this.options;this.driver||(this.driver=e(u=>this.tick(u))),(c=(l=this.options).onPlay)==null||c.call(l);const a=this.driver.now();this.state==="finished"?(this.updateFinished(),this.startTime=a):this.holdTime!==null?this.startTime=a-this.holdTime:this.startTime||(this.startTime=i??a),this.state==="finished"&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(Ft.now()),this.holdTime=this.currentTime}complete(){this.state!=="running"&&this.play(),this.state="finished",this.holdTime=null}finish(){var e,i;this.notifyFinished(),this.teardown(),this.state="finished",(i=(e=this.options).onComplete)==null||i.call(e)}cancel(){var e,i;this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),(i=(e=this.options).onCancel)==null||i.call(e)}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(e){return this.startTime=0,this.tick(e,!0)}attachTimeline(e){var i;return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),(i=this.driver)==null||i.stop(),e.observe(this)}}function uT(n){for(let e=1;en*180/Math.PI,Hd=n=>{const e=Mi(Math.atan2(n[1],n[0]));return $d(e)},hT={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:n=>(Math.abs(n[0])+Math.abs(n[3]))/2,rotate:Hd,rotateZ:Hd,skewX:n=>Mi(Math.atan(n[1])),skewY:n=>Mi(Math.atan(n[2])),skew:n=>(Math.abs(n[1])+Math.abs(n[2]))/2},$d=n=>(n=n%360,n<0&&(n+=360),n),u0=Hd,h0=n=>Math.sqrt(n[0]*n[0]+n[1]*n[1]),d0=n=>Math.sqrt(n[4]*n[4]+n[5]*n[5]),dT={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:h0,scaleY:d0,scale:n=>(h0(n)+d0(n))/2,rotateX:n=>$d(Mi(Math.atan2(n[6],n[5]))),rotateY:n=>$d(Mi(Math.atan2(-n[2],n[0]))),rotateZ:u0,rotate:u0,skewX:n=>Mi(Math.atan(n[4])),skewY:n=>Mi(Math.atan(n[1])),skew:n=>(Math.abs(n[1])+Math.abs(n[4]))/2};function qd(n){return n.includes("scale")?1:0}function Fd(n,e){if(!n||n==="none")return qd(e);const i=n.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let a,l;if(i)a=dT,l=i;else{const d=n.match(/^matrix\(([-\d.e\s,]+)\)$/u);a=hT,l=d}if(!l)return qd(e);const c=a[e],u=l[1].split(",").map(mT);return typeof c=="function"?c(u):u[c]}const fT=(n,e)=>{const{transform:i="none"}=getComputedStyle(n);return Fd(i,e)};function mT(n){return parseFloat(n.trim())}const Ja=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],Qa=new Set([...Ja,"pathRotation"]),f0=n=>n===Xa||n===oe,pT=new Set(["x","y","z"]),gT=Ja.filter(n=>!pT.has(n));function yT(n){const e=[];return gT.forEach(i=>{const a=n.getValue(i);a!==void 0&&(e.push([i,a.get()]),a.set(i.startsWith("scale")?1:0))}),e}const si={width:({x:n},{paddingLeft:e="0",paddingRight:i="0",boxSizing:a})=>{const l=n.max-n.min;return a==="border-box"?l:l-parseFloat(e)-parseFloat(i)},height:({y:n},{paddingTop:e="0",paddingBottom:i="0",boxSizing:a})=>{const l=n.max-n.min;return a==="border-box"?l:l-parseFloat(e)-parseFloat(i)},top:(n,{top:e})=>parseFloat(e),left:(n,{left:e})=>parseFloat(e),bottom:({y:n},{top:e})=>parseFloat(e)+(n.max-n.min),right:({x:n},{left:e})=>parseFloat(e)+(n.max-n.min),x:(n,{transform:e})=>Fd(e,"x"),y:(n,{transform:e})=>Fd(e,"y")};si.translateX=si.x;si.translateY=si.y;const Ui=new Set;let Id=!1,Gd=!1,Kd=!1;function Zv(){if(Gd){const n=Array.from(Ui).filter(a=>a.needsMeasurement),e=new Set(n.map(a=>a.element)),i=new Map;e.forEach(a=>{const l=yT(a);l.length&&(i.set(a,l),a.render())}),n.forEach(a=>a.measureInitialState()),e.forEach(a=>{a.render();const l=i.get(a);l&&l.forEach(([c,u])=>{var d;(d=a.getValue(c))==null||d.set(u)})}),n.forEach(a=>a.measureEndState()),n.forEach(a=>{a.suspendedScrollY!==void 0&&window.scrollTo(0,a.suspendedScrollY)})}Gd=!1,Id=!1,Ui.forEach(n=>n.complete(Kd)),Ui.clear()}function Wv(){Ui.forEach(n=>{n.readKeyframes(),n.needsMeasurement&&(Gd=!0)})}function bT(){Kd=!0,Wv(),Zv(),Kd=!1}class Vf{constructor(e,i,a,l,c,u=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...e],this.onComplete=i,this.name=a,this.motionValue=l,this.element=c,this.isAsync=u}scheduleResolve(){this.state="scheduled",this.isAsync?(Ui.add(this),Id||(Id=!0,Ke.read(Wv),Ke.resolveKeyframes(Zv))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:e,name:i,element:a,motionValue:l}=this;if(e[0]===null){const c=l==null?void 0:l.get(),u=e[e.length-1];if(c!==void 0)e[0]=c;else if(a&&i){const d=a.readValue(i,u);d!=null&&(e[0]=d)}e[0]===void 0&&(e[0]=u),l&&c===void 0&&l.set(e[0])}uT(e)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(e=!1){this.state="complete",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,e),Ui.delete(this)}cancel(){this.state==="scheduled"&&(Ui.delete(this),this.state="pending")}resume(){this.state==="pending"&&this.scheduleResolve()}}const vT=n=>n.startsWith("--");function ex(n,e,i){vT(e)?n.style.setProperty(e,i):n.style[e]=i}const xT={};function tx(n,e){const i=Nv(n);return()=>xT[e]??i()}const wT=tx(()=>window.ScrollTimeline!==void 0,"scrollTimeline"),nx=tx(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch{return!1}return!0},"linearEasing"),Xr=([n,e,i,a])=>`cubic-bezier(${n}, ${e}, ${i}, ${a})`,m0={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:Xr([0,.65,.55,1]),circOut:Xr([.55,0,1,.45]),backIn:Xr([.31,.01,.66,-.59]),backOut:Xr([.33,1.53,.69,.99])};function sx(n,e){if(n)return typeof n=="function"?nx()?Xv(n,e):"ease-out":Vv(n)?Xr(n):Array.isArray(n)?n.map(i=>sx(i,e)||m0.easeOut):m0[n]}function _T(n,e,i,{delay:a=0,duration:l=300,repeat:c=0,repeatType:u="loop",ease:d="easeOut",times:m}={},p=void 0){const y={[e]:i};m&&(y.offset=m);const v=sx(d,l);Array.isArray(v)&&(y.easing=v);const x={delay:a,duration:l,easing:Array.isArray(v)?"linear":v,fill:"both",iterations:c+1,direction:u==="reverse"?"alternate":"normal"};return p&&(x.pseudoElement=p),n.animate(y,x)}function ix(n){return typeof n=="function"&&"applyToOptions"in n}function ST({type:n,...e}){return ix(n)&&nx()?n.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}class ax extends zf{constructor(e){if(super(),this.finishedTime=null,this.isStopped=!1,this.manualStartTime=null,!e)return;const{element:i,name:a,keyframes:l,pseudoElement:c,allowFlatten:u=!1,finalKeyframe:d,onComplete:m}=e;this.isPseudoElement=!!c,this.allowFlatten=u,this.options=e,kf(typeof e.type!="string");const p=ST(e);this.animation=_T(i,a,l,p,c),p.autoplay===!1&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!c){const y=Lc(l,this.options,d,this.speed);this.updateMotionValue&&this.updateMotionValue(y),ex(i,a,y),this.animation.cancel()}m==null||m(),this.notifyFinished()}}play(){this.isStopped||(this.manualStartTime=null,this.animation.play(),this.state==="finished"&&this.updateFinished())}pause(){this.animation.pause()}complete(){var e,i;(i=(e=this.animation).finish)==null||i.call(e)}cancel(){try{this.animation.cancel()}catch{}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:e}=this;e==="idle"||e==="finished"||(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){var i,a,l;const e=(i=this.options)==null?void 0:i.element;!this.isPseudoElement&&(e!=null&&e.isConnected)&&((l=(a=this.animation).commitStyles)==null||l.call(a))}get duration(){var i,a;const e=((a=(i=this.animation.effect)==null?void 0:i.getComputedTiming)==null?void 0:a.call(i).duration)||0;return An(Number(e))}get iterationDuration(){const{delay:e=0}=this.options||{};return this.duration+An(e)}get time(){return An(Number(this.animation.currentTime)||0)}set time(e){const i=this.finishedTime!==null;this.manualStartTime=null,this.finishedTime=null,this.animation.currentTime=dn(e),i&&this.animation.pause()}get speed(){return this.animation.playbackRate}set speed(e){e<0&&(this.finishedTime=null),this.animation.playbackRate=e}get state(){return this.finishedTime!==null?"finished":this.animation.playState}get startTime(){return this.manualStartTime??Number(this.animation.startTime)}set startTime(e){this.manualStartTime=this.animation.startTime=e}attachTimeline({timeline:e,rangeStart:i,rangeEnd:a,observe:l}){var c;return this.allowFlatten&&((c=this.animation.effect)==null||c.updateTiming({easing:"linear"})),this.animation.onfinish=null,e&&wT()?(this.animation.timeline=e,i&&(this.animation.rangeStart=i),a&&(this.animation.rangeEnd=a),jn):l(this)}}const rx={anticipate:Bv,backInOut:Mv,circInOut:Lv};function TT(n){return n in rx}function ET(n){typeof n.ease=="string"&&TT(n.ease)&&(n.ease=rx[n.ease])}const md=10;class AT extends ax{constructor(e){ET(e),Qv(e),super(e),e.startTime!==void 0&&e.autoplay!==!1&&(this.startTime=e.startTime),this.options=e}updateMotionValue(e){const{motionValue:i,onUpdate:a,onComplete:l,element:c,...u}=this.options;if(!i)return;if(e!==void 0){i.set(e);return}const d=new xc({...u,autoplay:!1}),m=Math.max(md,Ft.now()-this.startTime),p=Zn(0,md,m-md),y=d.sample(m).value,{name:v}=this.options;c&&v&&ex(c,v,y),i.setWithVelocity(d.sample(Math.max(0,m-p)).value,y,p),d.stop()}}const p0=(n,e)=>e==="zIndex"?!1:!!(typeof n=="number"||Array.isArray(n)||typeof n=="string"&&(Pn.test(n)||n==="0")&&!n.startsWith("url("));function jT(n){const e=n[0];if(n.length===1)return!0;for(let i=0;iObject.hasOwnProperty.call(Element.prototype,"animate"));function DT(n){var v;const{motionValue:e,name:i,repeatDelay:a,repeatType:l,damping:c,type:u,keyframes:d}=n;if(!(((v=e==null?void 0:e.owner)==null?void 0:v.current)instanceof HTMLElement))return!1;const{onUpdate:p,transformTemplate:y}=e.owner.getProps();return OT()&&i&&(ox.has(i)||RT.has(i)&&CT(d))&&(i!=="transform"||!y)&&!p&&!a&&l!=="mirror"&&c!==0&&u!=="inertia"}const MT=40;class BT extends zf{constructor({autoplay:e=!0,delay:i=0,type:a="keyframes",repeat:l=0,repeatDelay:c=0,repeatType:u="loop",keyframes:d,name:m,motionValue:p,element:y,...v}){var S;super(),this.stop=()=>{var T,k;this._animation&&(this._animation.stop(),(T=this.stopTimeline)==null||T.call(this)),(k=this.keyframeResolver)==null||k.cancel()},this.createdAt=Ft.now();const x={autoplay:e,delay:i,type:a,repeat:l,repeatDelay:c,repeatType:u,name:m,motionValue:p,element:y,...v},_=(y==null?void 0:y.KeyframeResolver)||Vf;this.keyframeResolver=new _(d,(T,k,C)=>this.onKeyframesResolved(T,k,x,!C),m,p,y),(S=this.keyframeResolver)==null||S.scheduleResolve()}onKeyframesResolved(e,i,a,l){var C,M;this.keyframeResolver=void 0;const{name:c,type:u,velocity:d,delay:m,isHandoff:p,onUpdate:y}=a;this.resolvedAt=Ft.now();let v=!0;NT(e,c,u,d)||(v=!1,(ii.instantAnimations||!m)&&(y==null||y(Lc(e,a,i))),e[0]=e[e.length-1],Yd(a),a.repeat=0);const _={startTime:l?this.resolvedAt?this.resolvedAt-this.createdAt>MT?this.resolvedAt:this.createdAt:this.createdAt:void 0,finalKeyframe:i,...a,keyframes:e},S=v&&!p&&DT(_),T=(M=(C=_.motionValue)==null?void 0:C.owner)==null?void 0:M.current;let k;if(S)try{k=new AT({..._,element:T})}catch{k=new xc(_)}else k=new xc(_);k.finished.then(()=>{this.notifyFinished()}).catch(jn),this.pendingTimeline&&(this.stopTimeline=k.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=k}get finished(){return this._animation?this.animation.finished:this._finished}then(e,i){return this.finished.finally(e).then(()=>{})}get animation(){var e;return this._animation||((e=this.keyframeResolver)==null||e.resume(),bT()),this._animation}get duration(){return this.animation.duration}get iterationDuration(){return this.animation.iterationDuration}get time(){return this.animation.time}set time(e){this.animation.time=e}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(e){this.animation.speed=e}get startTime(){return this.animation.startTime}attachTimeline(e){return this._animation?this.stopTimeline=this.animation.attachTimeline(e):this.pendingTimeline=e,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){var e;this._animation&&this.animation.cancel(),(e=this.keyframeResolver)==null||e.cancel()}}function lx(n,e,i,a=0,l=1){const c=Array.from(n).sort((p,y)=>p.sortNodePosition(y)).indexOf(e),u=n.size,d=(u-1)*a;return typeof i=="function"?i(c,u):l===1?c*a:d-c*a}const g0=30,UT=n=>!isNaN(parseFloat(n));class LT{constructor(e,i={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=a=>{var c;const l=Ft.now();if(this.updatedAt!==l&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(a),this.current!==this.prev&&((c=this.events.change)==null||c.notify(this.current),this.dependents))for(const u of this.dependents)u.dirty()},this.hasAnimated=!1,this.setCurrent(e),this.owner=i.owner}setCurrent(e){this.current=e,this.updatedAt=Ft.now(),this.canTrackVelocity===null&&e!==void 0&&(this.canTrackVelocity=UT(this.current))}setPrevFrameValue(e=this.current){this.prevFrameValue=e,this.prevUpdatedAt=this.updatedAt}onChange(e){return this.on("change",e)}on(e,i){this.events[e]||(this.events[e]=new Cf);const a=this.events[e].add(i);return e==="change"?()=>{a(),Ke.read(()=>{this.events.change.getSize()||this.stop()})}:a}clearListeners(){for(const e in this.events)this.events[e].clear()}attach(e,i){this.passiveEffect=e,this.stopPassiveEffect=i}set(e){this.passiveEffect?this.passiveEffect(e,this.updateAndNotify):this.updateAndNotify(e)}setWithVelocity(e,i,a){this.set(i),this.prev=void 0,this.prevFrameValue=e,this.prevUpdatedAt=this.updatedAt-a}jump(e,i=!0){this.updateAndNotify(e),this.prev=e,this.prevUpdatedAt=this.prevFrameValue=void 0,i&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){var e;(e=this.events.change)==null||e.notify(this.current)}addDependent(e){this.dependents||(this.dependents=new Set),this.dependents.add(e)}removeDependent(e){this.dependents&&this.dependents.delete(e)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const e=Ft.now();if(!this.canTrackVelocity||this.prevFrameValue===void 0||e-this.updatedAt>g0)return 0;const i=Math.min(this.updatedAt-this.prevUpdatedAt,g0);return kv(parseFloat(this.current)-parseFloat(this.prevFrameValue),i)}start(e){return this.stop(),new Promise(i=>{this.hasAnimated=!0,this.animation=e(i),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){var e,i;(e=this.dependents)==null||e.clear(),(i=this.events.destroy)==null||i.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function Ka(n,e){return new LT(n,e)}function cx(n,e){if(n!=null&&n.inherit&&e){const{inherit:i,...a}=n;return{...e,...a}}return n}function Pf(n,e){const i=(n==null?void 0:n[e])??(n==null?void 0:n.default)??n;return i!==n?cx(i,n):i}const zT={type:"spring",stiffness:500,damping:25,restSpeed:10},VT=n=>({type:"spring",stiffness:550,damping:n===0?2*Math.sqrt(550):30,restSpeed:10}),PT={type:"keyframes",duration:.8},HT={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},$T=(n,{keyframes:e})=>e.length>2?PT:Qa.has(n)?n.startsWith("scale")?VT(e[1]):zT:HT,qT=new Set(["when","delay","delayChildren","staggerChildren","staggerDirection","repeat","repeatType","repeatDelay","from","elapsed"]);function FT(n){for(const e in n)if(!qT.has(e))return!0;return!1}const Hf=(n,e,i,a={},l,c)=>u=>{const d=Pf(a,n)||{},m=d.delay||a.delay||0;let{elapsed:p=0}=a;p=p-dn(m);const y={keyframes:Array.isArray(i)?i:[null,i],ease:"easeOut",velocity:e.getVelocity(),...d,delay:-p,onUpdate:x=>{e.set(x),d.onUpdate&&d.onUpdate(x)},onComplete:()=>{u(),d.onComplete&&d.onComplete()},name:n,motionValue:e,element:c?void 0:l};FT(d)||Object.assign(y,$T(n,y)),y.duration&&(y.duration=dn(y.duration)),y.repeatDelay&&(y.repeatDelay=dn(y.repeatDelay)),y.from!==void 0&&(y.keyframes[0]=y.from);let v=!1;if((y.type===!1||y.duration===0&&!y.repeatDelay)&&(Yd(y),y.delay===0&&(v=!0)),(ii.instantAnimations||ii.skipAnimations||l!=null&&l.shouldSkipAnimations||d.skipAnimations)&&(v=!0,Yd(y),y.delay=0),y.allowFlatten=!d.type&&!d.ease,v&&!c&&e.get()!==void 0){const x=Lc(y.keyframes,d);if(x!==void 0){Ke.update(()=>{y.onUpdate(x),y.onComplete()});return}}return d.isSync?new xc(y):new BT(y)},IT=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function GT(n){const e=IT.exec(n);if(!e)return[,];const[,i,a,l]=e;return[`--${i??a}`,l]}function ux(n,e,i=1){const[a,l]=GT(n);if(!a)return;const c=window.getComputedStyle(e).getPropertyValue(a);if(c){const u=c.trim();return Ev(u)?parseFloat(u):u}return Df(l)?ux(l,e,i+1):l}function y0(n){const e=[{},{}];return n==null||n.values.forEach((i,a)=>{e[0][a]=i.get(),e[1][a]=i.getVelocity()}),e}function $f(n,e,i,a){if(typeof e=="function"){const[l,c]=y0(a);e=e(i!==void 0?i:n.custom,l,c)}if(typeof e=="string"&&(e=n.variants&&n.variants[e]),typeof e=="function"){const[l,c]=y0(a);e=e(i!==void 0?i:n.custom,l,c)}return e}function Li(n,e,i){const a=n.getProps();return $f(a,e,i!==void 0?i:a.custom,n)}const hx=new Set(["width","height","top","left","right","bottom",...Ja]),Xd=n=>Array.isArray(n);function KT(n,e,i){n.hasValue(e)?n.getValue(e).set(i):n.addValue(e,Ka(i))}function YT(n){return Xd(n)?n[n.length-1]||0:n}function XT(n,e){const i=Li(n,e);let{transitionEnd:a={},transition:l={},...c}=i||{};c={...c,...a};for(const u in c){const d=YT(c[u]);KT(n,u,d)}}const Bt=n=>!!(n&&n.getVelocity);function JT(n){return!!(Bt(n)&&n.add)}function Jd(n,e){const i=n.getValue("willChange");if(JT(i))return i.add(e);if(!i&&ii.WillChange){const a=new ii.WillChange("auto");n.addValue("willChange",a),a.add(e)}}function qf(n){return n.replace(/([A-Z])/g,e=>`-${e.toLowerCase()}`)}const QT="framerAppearId",dx="data-"+qf(QT);function fx(n){return n.props[dx]}function ZT({protectedKeys:n,needsAnimating:e},i){const a=n.hasOwnProperty(i)&&e[i]!==!0;return e[i]=!1,a}function mx(n,e,{delay:i=0,transitionOverride:a,type:l}={}){let{transition:c,transitionEnd:u,...d}=e;const m=n.getDefaultTransition();c=c?cx(c,m):m;const p=c==null?void 0:c.reduceMotion,y=c==null?void 0:c.skipAnimations;a&&(c=a);const v=[],x=l&&n.animationState&&n.animationState.getState()[l],_=c==null?void 0:c.path;_&&_.animateVisualElement(n,d,c,i,v);for(const S in d){const T=n.getValue(S,n.latestValues[S]??null),k=d[S];if(k===void 0||x&&ZT(x,S))continue;const C={delay:i,...Pf(c||{},S)};y&&(C.skipAnimations=!0);const M=T.get();if(M!==void 0&&!T.isAnimating()&&!Array.isArray(k)&&k===M&&!C.velocity){Ke.update(()=>T.set(k));continue}let N=!1;if(window.MotionHandoffAnimation){const Z=fx(n);if(Z){const F=window.MotionHandoffAnimation(Z,S,Ke);F!==null&&(C.startTime=F,N=!0)}}Jd(n,S);const z=p??n.shouldReduceMotion;T.start(Hf(S,T,k,z&&hx.has(S)?{type:!1}:C,n,N));const Y=T.animation;Y&&v.push(Y)}if(u){const S=()=>Ke.update(()=>{u&&XT(n,u)});v.length?Promise.all(v).then(S):S()}return v}function Qd(n,e,i={}){var m;const a=Li(n,e,i.type==="exit"?(m=n.presenceContext)==null?void 0:m.custom:void 0);let{transition:l=n.getDefaultTransition()||{}}=a||{};i.transitionOverride&&(l=i.transitionOverride);const c=a?()=>Promise.all(mx(n,a,i)):()=>Promise.resolve(),u=n.variantChildren&&n.variantChildren.size?(p=0)=>{const{delayChildren:y=0,staggerChildren:v,staggerDirection:x}=l;return WT(n,e,p,y,v,x,i)}:()=>Promise.resolve(),{when:d}=l;if(d){const[p,y]=d==="beforeChildren"?[c,u]:[u,c];return p().then(()=>y())}else return Promise.all([c(),u(i.delay)])}function WT(n,e,i=0,a=0,l=0,c=1,u){const d=[];for(const m of n.variantChildren)m.notify("AnimationStart",e),d.push(Qd(m,e,{...u,delay:i+(typeof a=="function"?0:a)+lx(n.variantChildren,m,a,l,c)}).then(()=>m.notify("AnimationComplete",e)));return Promise.all(d)}function eE(n,e,i={}){n.notify("AnimationStart",e);let a;if(Array.isArray(e)){const l=e.map(c=>Qd(n,c,i));a=Promise.all(l)}else if(typeof e=="string")a=Qd(n,e,i);else{const l=typeof e=="function"?Li(n,e,i.custom):e;a=Promise.all(mx(n,l,i))}return a.then(()=>{n.notify("AnimationComplete",e)})}const tE={test:n=>n==="auto",parse:n=>n},px=n=>e=>e.test(n),gx=[Xa,oe,Qn,_s,j1,A1,tE],b0=n=>gx.find(px(n));function nE(n){return typeof n=="number"?n===0:n!==null?n==="none"||n==="0"||jv(n):!0}const sE=new Set(["brightness","contrast","saturate","opacity"]);function iE(n){const[e,i]=n.slice(0,-1).split("(");if(e==="drop-shadow")return n;const[a]=i.match(Mf)||[];if(!a)return n;const l=i.replace(a,"");let c=sE.has(e)?1:0;return a!==i&&(c*=100),e+"("+c+l+")"}const aE=/\b([a-z-]*)\(.*?\)/gu,Zd={...Pn,getAnimatableNone:n=>{const e=n.match(aE);return e?e.map(iE).join(" "):n}},Wd={...Pn,getAnimatableNone:n=>{const e=Pn.parse(n);return Pn.createTransformer(n)(e.map(a=>typeof a=="number"?0:typeof a=="object"?{...a,alpha:1}:a))}},v0={...Xa,transform:Math.round},rE={rotate:_s,pathRotation:_s,rotateX:_s,rotateY:_s,rotateZ:_s,scale:Gl,scaleX:Gl,scaleY:Gl,scaleZ:Gl,skew:_s,skewX:_s,skewY:_s,distance:oe,translateX:oe,translateY:oe,translateZ:oe,x:oe,y:oe,z:oe,perspective:oe,transformPerspective:oe,opacity:co,originX:a0,originY:a0,originZ:oe},wc={borderWidth:oe,borderTopWidth:oe,borderRightWidth:oe,borderBottomWidth:oe,borderLeftWidth:oe,borderRadius:oe,borderTopLeftRadius:oe,borderTopRightRadius:oe,borderBottomRightRadius:oe,borderBottomLeftRadius:oe,width:oe,maxWidth:oe,height:oe,maxHeight:oe,top:oe,right:oe,bottom:oe,left:oe,inset:oe,insetBlock:oe,insetBlockStart:oe,insetBlockEnd:oe,insetInline:oe,insetInlineStart:oe,insetInlineEnd:oe,padding:oe,paddingTop:oe,paddingRight:oe,paddingBottom:oe,paddingLeft:oe,paddingBlock:oe,paddingBlockStart:oe,paddingBlockEnd:oe,paddingInline:oe,paddingInlineStart:oe,paddingInlineEnd:oe,margin:oe,marginTop:oe,marginRight:oe,marginBottom:oe,marginLeft:oe,marginBlock:oe,marginBlockStart:oe,marginBlockEnd:oe,marginInline:oe,marginInlineStart:oe,marginInlineEnd:oe,fontSize:oe,backgroundPositionX:oe,backgroundPositionY:oe,...rE,zIndex:v0,fillOpacity:co,strokeOpacity:co,numOctaves:v0},oE={...wc,color:bt,backgroundColor:bt,outlineColor:bt,fill:bt,stroke:bt,borderColor:bt,borderTopColor:bt,borderRightColor:bt,borderBottomColor:bt,borderLeftColor:bt,filter:Zd,WebkitFilter:Zd,mask:Wd,WebkitMask:Wd},yx=n=>oE[n],lE=new Set([Zd,Wd]);function bx(n,e){let i=yx(n);return lE.has(i)||(i=Pn),i.getAnimatableNone?i.getAnimatableNone(e):void 0}const cE=new Set(["auto","none","0"]);function uE(n,e,i){let a=0,l;for(;a{e.getValue(m).set(p)}),this.resolveNoneKeyframes()}}const Ff=["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"];function vx(n,e,i){if(n==null)return[];if(n instanceof EventTarget)return[n];if(typeof n=="string"){let a=document;const l=(i==null?void 0:i[n])??a.querySelectorAll(n);return l?Array.from(l):[]}return Array.from(n).filter(a=>a!=null)}const ef=(n,e)=>e&&typeof n=="number"?e.transform(n):n;function lc(n){return Av(n)&&"offsetHeight"in n&&!("ownerSVGElement"in n)}const{schedule:If}=Pv(queueMicrotask,!1),Bn={x:!1,y:!1};function xx(){return Bn.x||Bn.y}function dE(n){return n==="x"||n==="y"?Bn[n]?null:(Bn[n]=!0,()=>{Bn[n]=!1}):Bn.x||Bn.y?null:(Bn.x=Bn.y=!0,()=>{Bn.x=Bn.y=!1})}function wx(n,e){const i=vx(n),a=new AbortController,l={passive:!0,...e,signal:a.signal};return[i,l,()=>a.abort()]}function fE(n){return!(n.pointerType==="touch"||xx())}function mE(n,e,i={}){const[a,l,c]=wx(n,i);return a.forEach(u=>{let d=!1,m=!1,p;const y=()=>{u.removeEventListener("pointerleave",S)},v=k=>{p&&(p(k),p=void 0),y()},x=k=>{d=!1,window.removeEventListener("pointerup",x),window.removeEventListener("pointercancel",x),m&&(m=!1,v(k))},_=()=>{d=!0,window.addEventListener("pointerup",x,l),window.addEventListener("pointercancel",x,l)},S=k=>{if(k.pointerType!=="touch"){if(d){m=!0;return}v(k)}},T=k=>{if(!fE(k))return;m=!1;const C=e(u,k);typeof C=="function"&&(p=C,u.addEventListener("pointerleave",S,l))};u.addEventListener("pointerenter",T,l),u.addEventListener("pointerdown",_,l)}),c}const _x=(n,e)=>e?n===e?!0:_x(n,e.parentElement):!1,Gf=n=>n.pointerType==="mouse"?typeof n.button!="number"||n.button<=0:n.isPrimary!==!1,pE=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);function gE(n){return pE.has(n.tagName)||n.isContentEditable===!0}const yE=new Set(["INPUT","SELECT","TEXTAREA"]);function bE(n){return yE.has(n.tagName)||n.isContentEditable===!0}const cc=new WeakSet;function x0(n){return e=>{e.key==="Enter"&&n(e)}}function pd(n,e){n.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}const vE=(n,e)=>{const i=n.currentTarget;if(!i)return;const a=x0(()=>{if(cc.has(i))return;pd(i,"down");const l=x0(()=>{pd(i,"up")}),c=()=>pd(i,"cancel");i.addEventListener("keyup",l,e),i.addEventListener("blur",c,e)});i.addEventListener("keydown",a,e),i.addEventListener("blur",()=>i.removeEventListener("keydown",a),e)};function w0(n){return Gf(n)&&!xx()}const _0=new WeakSet;function xE(n,e,i={}){const[a,l,c]=wx(n,i),u=d=>{const m=d.currentTarget;if(!w0(d)||_0.has(d))return;cc.add(m),i.stopPropagation&&_0.add(d);const p=e(m,d),y={...l,capture:!0},v=(S,T)=>{window.removeEventListener("pointerup",x,y),window.removeEventListener("pointercancel",_,y),cc.has(m)&&cc.delete(m),w0(S)&&typeof p=="function"&&p(S,{success:T})},x=S=>{v(S,m===window||m===document||i.useGlobalTarget||_x(m,S.target))},_=S=>{v(S,!1)};window.addEventListener("pointerup",x,y),window.addEventListener("pointercancel",_,y)};return a.forEach(d=>{(i.useGlobalTarget?window:d).addEventListener("pointerdown",u,l),lc(d)&&(d.addEventListener("focus",p=>vE(p,l)),!gE(d)&&!d.hasAttribute("tabindex")&&(d.tabIndex=0))}),c}function Kf(n){return Av(n)&&"ownerSVGElement"in n}const uc=new WeakMap;let Ws;const Sx=(n,e,i)=>(a,l)=>l&&l[0]?l[0][n+"Size"]:Kf(a)&&"getBBox"in a?a.getBBox()[e]:a[i],wE=Sx("inline","width","offsetWidth"),_E=Sx("block","height","offsetHeight");function SE({target:n,borderBoxSize:e}){var i;(i=uc.get(n))==null||i.forEach(a=>{a(n,{get width(){return wE(n,e)},get height(){return _E(n,e)}})})}function TE(n){n.forEach(SE)}function EE(){typeof ResizeObserver>"u"||(Ws=new ResizeObserver(TE))}function AE(n,e){Ws||EE();const i=vx(n);return i.forEach(a=>{let l=uc.get(a);l||(l=new Set,uc.set(a,l)),l.add(e),Ws==null||Ws.observe(a)}),()=>{i.forEach(a=>{const l=uc.get(a);l==null||l.delete(e),l!=null&&l.size||Ws==null||Ws.unobserve(a)})}}const hc=new Set;let Va;function jE(){Va=()=>{const n={get width(){return window.innerWidth},get height(){return window.innerHeight}};hc.forEach(e=>e(n))},window.addEventListener("resize",Va)}function NE(n){return hc.add(n),Va||jE(),()=>{hc.delete(n),!hc.size&&typeof Va=="function"&&(window.removeEventListener("resize",Va),Va=void 0)}}function S0(n,e){return typeof n=="function"?NE(n):AE(n,e)}function kE(n){return Kf(n)&&n.tagName==="svg"}const CE=[...gx,bt,Pn],RE=n=>CE.find(px(n)),T0=()=>({translate:0,scale:1,origin:0,originPoint:0}),Pa=()=>({x:T0(),y:T0()}),E0=()=>({min:0,max:0}),Tt=()=>({x:E0(),y:E0()}),OE=new WeakMap;function zc(n){return n!==null&&typeof n=="object"&&typeof n.start=="function"}function uo(n){return typeof n=="string"||Array.isArray(n)}const Yf=["animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"],Xf=["initial",...Yf];function Vc(n){return zc(n.animate)||Xf.some(e=>uo(n[e]))}function Tx(n){return!!(Vc(n)||n.variants)}function DE(n,e,i){for(const a in e){const l=e[a],c=i[a];if(Bt(l))n.addValue(a,l);else if(Bt(c))n.addValue(a,Ka(l,{owner:n}));else if(c!==l)if(n.hasValue(a)){const u=n.getValue(a);u.liveStyle===!0?u.jump(l):u.hasAnimated||u.set(l)}else{const u=n.getStaticValue(a);n.addValue(a,Ka(u!==void 0?u:l,{owner:n}))}}for(const a in i)e[a]===void 0&&n.removeValue(a);return e}const tf={current:null},Ex={current:!1},ME=typeof window<"u";function BE(){if(Ex.current=!0,!!ME)if(window.matchMedia){const n=window.matchMedia("(prefers-reduced-motion)"),e=()=>tf.current=n.matches;n.addEventListener("change",e),e()}else tf.current=!1}const A0=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];let _c={};function Ax(n){_c=n}function UE(){return _c}class LE{scrapeMotionValuesFromProps(e,i,a){return{}}constructor({parent:e,props:i,presenceContext:a,reducedMotionConfig:l,skipAnimations:c,blockInitialAnimation:u,visualState:d},m={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.shouldSkipAnimations=!1,this.values=new Map,this.KeyframeResolver=Vf,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.hasBeenMounted=!1,this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const _=Ft.now();this.renderScheduledAt<_&&(this.renderScheduledAt=_,Ke.render(this.render,!1,!0))};const{latestValues:p,renderState:y}=d;this.latestValues=p,this.baseTarget={...p},this.initialValues=i.initial?{...p}:{},this.renderState=y,this.parent=e,this.props=i,this.presenceContext=a,this.depth=e?e.depth+1:0,this.reducedMotionConfig=l,this.skipAnimationsConfig=c,this.options=m,this.blockInitialAnimation=!!u,this.isControllingVariants=Vc(i),this.isVariantNode=Tx(i),this.isVariantNode&&(this.variantChildren=new Set),this.manuallyAnimateOnMount=!!(e&&e.current);const{willChange:v,...x}=this.scrapeMotionValuesFromProps(i,{},this);for(const _ in x){const S=x[_];p[_]!==void 0&&Bt(S)&&S.set(p[_])}}mount(e){var i,a;if(this.hasBeenMounted)for(const l in this.initialValues)(i=this.values.get(l))==null||i.jump(this.initialValues[l]),this.latestValues[l]=this.initialValues[l];this.current=e,OE.set(e,this),this.projection&&!this.projection.instance&&this.projection.mount(e),this.parent&&this.isVariantNode&&!this.isControllingVariants&&(this.removeFromVariantTree=this.parent.addVariantChild(this)),this.values.forEach((l,c)=>this.bindToMotionValue(c,l)),this.reducedMotionConfig==="never"?this.shouldReduceMotion=!1:this.reducedMotionConfig==="always"?this.shouldReduceMotion=!0:(Ex.current||BE(),this.shouldReduceMotion=tf.current),this.shouldSkipAnimations=this.skipAnimationsConfig??!1,(a=this.parent)==null||a.addChild(this),this.update(this.props,this.presenceContext),this.hasBeenMounted=!0}unmount(){var e;this.projection&&this.projection.unmount(),ai(this.notifyUpdate),ai(this.render),this.valueSubscriptions.forEach(i=>i()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),(e=this.parent)==null||e.removeChild(this);for(const i in this.events)this.events[i].clear();for(const i in this.features){const a=this.features[i];a&&(a.unmount(),a.isMounted=!1)}this.current=null}addChild(e){this.children.add(e),this.enteringChildren??(this.enteringChildren=new Set),this.enteringChildren.add(e)}removeChild(e){this.children.delete(e),this.enteringChildren&&this.enteringChildren.delete(e)}bindToMotionValue(e,i){if(this.valueSubscriptions.has(e)&&this.valueSubscriptions.get(e)(),i.accelerate&&ox.has(e)&&this.current instanceof HTMLElement){const{factory:u,keyframes:d,times:m,ease:p,duration:y}=i.accelerate,v=new ax({element:this.current,name:e,keyframes:d,times:m,ease:p,duration:dn(y)}),x=u(v);this.valueSubscriptions.set(e,()=>{x(),v.cancel()});return}const a=Qa.has(e);a&&this.onBindTransform&&this.onBindTransform();const l=i.on("change",u=>{this.latestValues[e]=u,this.props.onUpdate&&Ke.preRender(this.notifyUpdate),a&&this.projection&&(this.projection.isTransformDirty=!0),this.scheduleRender()});let c;typeof window<"u"&&window.MotionCheckAppearSync&&(c=window.MotionCheckAppearSync(this,e,i)),this.valueSubscriptions.set(e,()=>{l(),c&&c()})}sortNodePosition(e){return!this.current||!this.sortInstanceNodePosition||this.type!==e.type?0:this.sortInstanceNodePosition(this.current,e.current)}updateFeatures(){let e="animation";for(e in _c){const i=_c[e];if(!i)continue;const{isEnabled:a,Feature:l}=i;if(!this.features[e]&&l&&a(this.props)&&(this.features[e]=new l(this)),this.features[e]){const c=this.features[e];c.isMounted?c.update():(c.mount(),c.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):Tt()}getStaticValue(e){return this.latestValues[e]}setStaticValue(e,i){this.latestValues[e]=i}update(e,i){(e.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=e,this.prevPresenceContext=this.presenceContext,this.presenceContext=i;for(let a=0;ai.variantChildren.delete(e)}addValue(e,i){const a=this.values.get(e);i!==a&&(a&&this.removeValue(e),this.bindToMotionValue(e,i),this.values.set(e,i),this.latestValues[e]=i.get())}removeValue(e){this.values.delete(e);const i=this.valueSubscriptions.get(e);i&&(i(),this.valueSubscriptions.delete(e)),delete this.latestValues[e],this.removeValueFromRenderState(e,this.renderState)}hasValue(e){return this.values.has(e)}getValue(e,i){if(this.props.values&&this.props.values[e])return this.props.values[e];let a=this.values.get(e);return a===void 0&&i!==void 0&&(a=Ka(i===null?void 0:i,{owner:this}),this.addValue(e,a)),a}readValue(e,i){let a=this.latestValues[e]!==void 0||!this.current?this.latestValues[e]:this.getBaseTargetFromProps(this.props,e)??this.readValueFromInstance(this.current,e,this.options);return a!=null&&(typeof a=="string"&&(Ev(a)||jv(a))?a=parseFloat(a):!RE(a)&&Pn.test(i)&&(a=bx(e,i)),this.setBaseTarget(e,Bt(a)?a.get():a)),Bt(a)?a.get():a}setBaseTarget(e,i){this.baseTarget[e]=i}getBaseTarget(e){var c;const{initial:i}=this.props;let a;if(typeof i=="string"||typeof i=="object"){const u=$f(this.props,i,(c=this.presenceContext)==null?void 0:c.custom);u&&(a=u[e])}if(i&&a!==void 0)return a;const l=this.getBaseTargetFromProps(this.props,e);return l!==void 0&&!Bt(l)?l:this.initialValues[e]!==void 0&&a===void 0?void 0:this.baseTarget[e]}on(e,i){return this.events[e]||(this.events[e]=new Cf),this.events[e].add(i)}notify(e,...i){this.events[e]&&this.events[e].notify(...i)}scheduleRenderMicrotask(){If.render(this.render)}}class jx extends LE{constructor(){super(...arguments),this.KeyframeResolver=hE}sortInstanceNodePosition(e,i){return e.compareDocumentPosition(i)&2?1:-1}getBaseTargetFromProps(e,i){const a=e.style;return a?a[i]:void 0}removeValueFromRenderState(e,{vars:i,style:a}){delete i[e],delete a[e]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:e}=this.props;Bt(e)&&(this.childSubscription=e.on("change",i=>{this.current&&(this.current.textContent=`${i}`)}))}}class ri{constructor(e){this.isMounted=!1,this.node=e}update(){}}function Nx({top:n,left:e,right:i,bottom:a}){return{x:{min:e,max:i},y:{min:n,max:a}}}function zE({x:n,y:e}){return{top:e.min,right:n.max,bottom:e.max,left:n.min}}function VE(n,e){if(!e)return n;const i=e({x:n.left,y:n.top}),a=e({x:n.right,y:n.bottom});return{top:i.y,left:i.x,bottom:a.y,right:a.x}}function gd(n){return n===void 0||n===1}function nf({scale:n,scaleX:e,scaleY:i}){return!gd(n)||!gd(e)||!gd(i)}function Ri(n){return nf(n)||kx(n)||n.z||n.rotate||n.rotateX||n.rotateY||n.skewX||n.skewY}function kx(n){return j0(n.x)||j0(n.y)}function j0(n){return n&&n!=="0%"}function Sc(n,e,i){const a=n-i,l=e*a;return i+l}function N0(n,e,i,a,l){return l!==void 0&&(n=Sc(n,l,a)),Sc(n,i,a)+e}function sf(n,e=0,i=1,a,l){n.min=N0(n.min,e,i,a,l),n.max=N0(n.max,e,i,a,l)}function Cx(n,{x:e,y:i}){sf(n.x,e.translate,e.scale,e.originPoint),sf(n.y,i.translate,i.scale,i.originPoint)}const k0=.999999999999,C0=1.0000000000001;function PE(n,e,i,a=!1){var d;const l=i.length;if(!l)return;e.x=e.y=1;let c,u;for(let m=0;mk0&&(e.x=1),e.yk0&&(e.y=1)}function Xn(n,e){n.min+=e,n.max+=e}function R0(n,e,i,a,l=.5){const c=Ge(n.min,n.max,l);sf(n,e,i,c,a)}function O0(n,e){return typeof n=="string"?parseFloat(n)/100*(e.max-e.min):n}function dc(n,e,i){const a=i??n;R0(n.x,O0(e.x,a.x),e.scaleX,e.scale,e.originX),R0(n.y,O0(e.y,a.y),e.scaleY,e.scale,e.originY)}function Rx(n,e){return Nx(VE(n.getBoundingClientRect(),e))}function HE(n,e,i){const a=Rx(n,i),{scroll:l}=e;return l&&(Xn(a.x,l.offset.x),Xn(a.y,l.offset.y)),a}const $E={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},qE=Ja.length;function FE(n,e,i){let a="",l=!0;for(let u=0;u{if(!e.target)return n;if(typeof n=="string")if(oe.test(n))n=parseFloat(n);else return n;const i=D0(n,e.target.x),a=D0(n,e.target.y);return`${i}% ${a}%`}},IE={correct:(n,{treeScale:e,projectionDelta:i})=>{const a=n,l=Pn.parse(n);if(l.length>5)return a;const c=Pn.createTransformer(n),u=typeof l[0]!="number"?1:0,d=i.x.scale*e.x,m=i.y.scale*e.y;l[0+u]/=d,l[1+u]/=m;const p=Ge(d,m,.5);return typeof l[2+u]=="number"&&(l[2+u]/=p),typeof l[3+u]=="number"&&(l[3+u]/=p),c(l)}},af={borderRadius:{...Ir,applyTo:[...Ff]},borderTopLeftRadius:Ir,borderTopRightRadius:Ir,borderBottomLeftRadius:Ir,borderBottomRightRadius:Ir,boxShadow:IE};function Dx(n,{layout:e,layoutId:i}){return Qa.has(n)||n.startsWith("origin")||(e||i!==void 0)&&(!!af[n]||n==="opacity")}function Qf(n,e,i){var u;const a=n.style,l=e==null?void 0:e.style,c={};if(!a)return c;for(const d in a)(Bt(a[d])||l&&Bt(l[d])||Dx(d,n)||((u=i==null?void 0:i.getValue(d))==null?void 0:u.liveStyle)!==void 0)&&(c[d]=a[d]);return c}function GE(n){return window.getComputedStyle(n)}class KE extends jx{constructor(){super(...arguments),this.type="html",this.renderInstance=Ox}readValueFromInstance(e,i){var a;if(Qa.has(i))return(a=this.projection)!=null&&a.isProjecting?qd(i):fT(e,i);{const l=GE(e),c=($v(i)?l.getPropertyValue(i):l[i])||0;return typeof c=="string"?c.trim():c}}measureInstanceViewportBox(e,{transformPagePoint:i}){return Rx(e,i)}build(e,i,a){Jf(e,i,a.transformTemplate)}scrapeMotionValuesFromProps(e,i,a){return Qf(e,i,a)}}const YE={offset:"stroke-dashoffset",array:"stroke-dasharray"},XE={offset:"strokeDashoffset",array:"strokeDasharray"};function JE(n,e,i=1,a=0,l=!0){n.pathLength=1;const c=l?YE:XE;n[c.offset]=`${-a}`,n[c.array]=`${e} ${i}`}const QE=["offsetDistance","offsetPath","offsetRotate","offsetAnchor"];function Mx(n,{attrX:e,attrY:i,attrScale:a,pathLength:l,pathSpacing:c=1,pathOffset:u=0,...d},m,p,y){if(Jf(n,d,p),m){n.style.viewBox&&(n.attrs.viewBox=n.style.viewBox);return}n.attrs=n.style,n.style={};const{attrs:v,style:x}=n;v.transform&&(x.transform=v.transform,delete v.transform),(x.transform||v.transformOrigin)&&(x.transformOrigin=v.transformOrigin??"50% 50%",delete v.transformOrigin),x.transform&&(x.transformBox=(y==null?void 0:y.transformBox)??"fill-box",delete v.transformBox);for(const _ of QE)v[_]!==void 0&&(x[_]=v[_],delete v[_]);e!==void 0&&(v.x=e),i!==void 0&&(v.y=i),a!==void 0&&(v.scale=a),l!==void 0&&JE(v,l,c,u,!1)}const Bx=new Set(["baseFrequency","diffuseConstant","kernelMatrix","kernelUnitLength","keySplines","keyTimes","limitingConeAngle","markerHeight","markerWidth","numOctaves","targetX","targetY","surfaceScale","specularConstant","specularExponent","stdDeviation","tableValues","viewBox","gradientTransform","pathLength","startOffset","textLength","lengthAdjust"]),Ux=n=>typeof n=="string"&&n.toLowerCase()==="svg";function ZE(n,e,i,a){Ox(n,e,void 0,a);for(const l in e.attrs)n.setAttribute(Bx.has(l)?l:qf(l),e.attrs[l])}function Lx(n,e,i){const a=Qf(n,e,i);for(const l in n)if(Bt(n[l])||Bt(e[l])){const c=Ja.indexOf(l)!==-1?"attr"+l.charAt(0).toUpperCase()+l.substring(1):l;a[c]=n[l]}return a}class WE extends jx{constructor(){super(...arguments),this.type="svg",this.isSVGTag=!1,this.measureInstanceViewportBox=Tt}getBaseTargetFromProps(e,i){return e[i]}readValueFromInstance(e,i){if(Qa.has(i)){const a=yx(i);return a&&a.default||0}return i=Bx.has(i)?i:qf(i),e.getAttribute(i)}scrapeMotionValuesFromProps(e,i,a){return Lx(e,i,a)}build(e,i,a){Mx(e,i,this.isSVGTag,a.transformTemplate,a.style)}renderInstance(e,i,a,l){ZE(e,i,a,l)}mount(e){this.isSVGTag=Ux(e.tagName),super.mount(e)}}const eA=Xf.length;function zx(n){if(!n)return;if(!n.isControllingVariants){const i=n.parent?zx(n.parent)||{}:{};return n.props.initial!==void 0&&(i.initial=n.props.initial),i}const e={};for(let i=0;iPromise.all(e.map(({animation:i,options:a})=>eE(n,i,a)))}function iA(n){let e=sA(n),i=M0(),a=!0,l=!1;const c=p=>(y,v)=>{var _;const x=Li(n,v,p==="exit"?(_=n.presenceContext)==null?void 0:_.custom:void 0);if(x){const{transition:S,transitionEnd:T,...k}=x;y={...y,...k,...T}}return y};function u(p){e=p(n)}function d(p){const{props:y}=n,v=zx(n.parent)||{},x=[],_=new Set;let S={},T=1/0;for(let C=0;CT&&Y,Q=!1;const ce=Array.isArray(z)?z:[z];let me=ce.reduce(c(M),{});Z===!1&&(me={});const{prevResolvedValues:_e={}}=N,Re={..._e,...me},Se=W=>{ne=!0,_.has(W)&&(Q=!0,_.delete(W)),N.needsAnimating[W]=!0;const pe=n.getValue(W);pe&&(pe.liveStyle=!1)};for(const W in Re){const pe=me[W],ae=_e[W];if(S.hasOwnProperty(W))continue;let E=!1;Xd(pe)&&Xd(ae)?E=!Vx(pe,ae)||G:E=pe!==ae,E?pe!=null?Se(W):_.add(W):pe!==void 0&&_.has(W)?Se(W):N.protectedKeys[W]=!0}N.prevProp=z,N.prevResolvedValues=me,N.isActive&&(S={...S,...me}),(a||l)&&n.blockInitialAnimation&&(ne=!1);const V=F&&G;ne&&(!V||Q)&&x.push(...ce.map(W=>{const pe={type:M};if(typeof W=="string"&&(a||l)&&!V&&n.manuallyAnimateOnMount&&n.parent){const{parent:ae}=n,E=Li(ae,W);if(ae.enteringChildren&&E){const{delayChildren:P}=E.transition||{};pe.delay=lx(ae.enteringChildren,n,P)}}return{animation:W,options:pe}}))}if(_.size){const C={};if(typeof y.initial!="boolean"){const M=Li(n,Array.isArray(y.initial)?y.initial[0]:y.initial);M&&M.transition&&(C.transition=M.transition)}_.forEach(M=>{const N=n.getBaseTarget(M),z=n.getValue(M);z&&(z.liveStyle=!0),C[M]=N??null}),x.push({animation:C})}let k=!!x.length;return a&&(y.initial===!1||y.initial===y.animate)&&!n.manuallyAnimateOnMount&&(k=!1),a=!1,l=!1,k?e(x):Promise.resolve()}function m(p,y){var x;if(i[p].isActive===y)return Promise.resolve();(x=n.variantChildren)==null||x.forEach(_=>{var S;return(S=_.animationState)==null?void 0:S.setActive(p,y)}),i[p].isActive=y;const v=d(p);for(const _ in i)i[_].protectedKeys={};return v}return{animateChanges:d,setActive:m,setAnimateFunction:u,getState:()=>i,reset:()=>{i=M0(),l=!0}}}function aA(n,e){return typeof e=="string"?e!==n:Array.isArray(e)?!Vx(e,n):!1}function Ni(n=!1){return{isActive:n,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function M0(){return{animate:Ni(!0),whileInView:Ni(),whileHover:Ni(),whileTap:Ni(),whileDrag:Ni(),whileFocus:Ni(),exit:Ni()}}function rf(n,e){n.min=e.min,n.max=e.max}function Dn(n,e){rf(n.x,e.x),rf(n.y,e.y)}function B0(n,e){n.translate=e.translate,n.scale=e.scale,n.originPoint=e.originPoint,n.origin=e.origin}const Px=1e-4,rA=1-Px,oA=1+Px,Hx=.01,lA=0-Hx,cA=0+Hx;function It(n){return n.max-n.min}function uA(n,e,i){return Math.abs(n-e)<=i}function U0(n,e,i,a=.5){n.origin=a,n.originPoint=Ge(e.min,e.max,n.origin),n.scale=It(i)/It(e),n.translate=Ge(i.min,i.max,n.origin)-n.originPoint,(n.scale>=rA&&n.scale<=oA||isNaN(n.scale))&&(n.scale=1),(n.translate>=lA&&n.translate<=cA||isNaN(n.translate))&&(n.translate=0)}function no(n,e,i,a){U0(n.x,e.x,i.x,a?a.originX:void 0),U0(n.y,e.y,i.y,a?a.originY:void 0)}function L0(n,e,i,a=0){const l=a?Ge(i.min,i.max,a):i.min;n.min=l+e.min,n.max=n.min+It(e)}function hA(n,e,i,a){L0(n.x,e.x,i.x,a==null?void 0:a.x),L0(n.y,e.y,i.y,a==null?void 0:a.y)}function z0(n,e,i,a=0){const l=a?Ge(i.min,i.max,a):i.min;n.min=e.min-l,n.max=n.min+It(e)}function Tc(n,e,i,a){z0(n.x,e.x,i.x,a==null?void 0:a.x),z0(n.y,e.y,i.y,a==null?void 0:a.y)}function V0(n,e,i,a,l){return n-=e,n=Sc(n,1/i,a),l!==void 0&&(n=Sc(n,1/l,a)),n}function dA(n,e=0,i=1,a=.5,l,c=n,u=n){if(Qn.test(e)&&(e=parseFloat(e),e=Ge(u.min,u.max,e/100)-u.min),typeof e!="number")return;let d=Ge(c.min,c.max,a);n===c&&(d-=e),n.min=V0(n.min,e,i,d,l),n.max=V0(n.max,e,i,d,l)}function P0(n,e,[i,a,l],c,u){dA(n,e[i],e[a],e[l],e.scale,c,u)}const fA=["x","scaleX","originX"],mA=["y","scaleY","originY"];function H0(n,e,i,a){P0(n.x,e,fA,i?i.x:void 0,a?a.x:void 0),P0(n.y,e,mA,i?i.y:void 0,a?a.y:void 0)}function $0(n){return n.translate===0&&n.scale===1}function $x(n){return $0(n.x)&&$0(n.y)}function q0(n,e){return n.min===e.min&&n.max===e.max}function pA(n,e){return q0(n.x,e.x)&&q0(n.y,e.y)}function F0(n,e){return Math.round(n.min)===Math.round(e.min)&&Math.round(n.max)===Math.round(e.max)}function qx(n,e){return F0(n.x,e.x)&&F0(n.y,e.y)}function I0(n){return It(n.x)/It(n.y)}function G0(n,e){return n.translate===e.translate&&n.scale===e.scale&&n.originPoint===e.originPoint}function Kn(n){return[n("x"),n("y")]}function gA(n,e,i){let a="";const l=n.x.translate/e.x,c=n.y.translate/e.y,u=(i==null?void 0:i.z)||0;if((l||c||u)&&(a=`translate3d(${l}px, ${c}px, ${u}px) `),(e.x!==1||e.y!==1)&&(a+=`scale(${1/e.x}, ${1/e.y}) `),i){const{transformPerspective:p,rotate:y,pathRotation:v,rotateX:x,rotateY:_,skewX:S,skewY:T}=i;p&&(a=`perspective(${p}px) ${a}`),y&&(a+=`rotate(${y}deg) `),v&&(a+=`rotate(${v}deg) `),x&&(a+=`rotateX(${x}deg) `),_&&(a+=`rotateY(${_}deg) `),S&&(a+=`skewX(${S}deg) `),T&&(a+=`skewY(${T}deg) `)}const d=n.x.scale*e.x,m=n.y.scale*e.y;return(d!==1||m!==1)&&(a+=`scale(${d}, ${m})`),a||"none"}const yA=Ff.length,K0=n=>typeof n=="string"?parseFloat(n):n,Y0=n=>typeof n=="number"||oe.test(n);function bA(n,e,i,a,l,c){l?(n.opacity=Ge(0,i.opacity??1,vA(a)),n.opacityExit=Ge(e.opacity??1,0,xA(a))):c&&(n.opacity=Ge(e.opacity??1,i.opacity??1,a));for(let u=0;uae?1:i(lo(n,e,a))}function wA(n,e,i){const a=Bt(n)?n:Ka(n);return a.start(Hf("",a,e,i)),a.animation}function ho(n,e,i,a={passive:!0}){return n.addEventListener(e,i,a),()=>n.removeEventListener(e,i,a)}const _A=(n,e)=>n.depth-e.depth;class SA{constructor(){this.children=[],this.isDirty=!1}add(e){Nf(this.children,e),this.isDirty=!0}remove(e){gc(this.children,e),this.isDirty=!0}forEach(e){this.isDirty&&this.children.sort(_A),this.isDirty=!1,this.children.forEach(e)}}function TA(n,e){const i=Ft.now(),a=({timestamp:l})=>{const c=l-i;c>=e&&(ai(a),n(c-e))};return Ke.setup(a,!0),()=>ai(a)}function fc(n){return Bt(n)?n.get():n}class EA{constructor(){this.members=[]}add(e){Nf(this.members,e);for(let i=this.members.length-1;i>=0;i--){const a=this.members[i];if(a===e||a===this.lead||a===this.prevLead)continue;const l=a.instance;(!l||l.isConnected===!1)&&!a.snapshot&&(gc(this.members,a),a.unmount())}e.scheduleRender()}remove(e){if(gc(this.members,e),e===this.prevLead&&(this.prevLead=void 0),e===this.lead){const i=this.members[this.members.length-1];i&&this.promote(i)}}relegate(e){var i;for(let a=this.members.indexOf(e)-1;a>=0;a--){const l=this.members[a];if(l.isPresent!==!1&&((i=l.instance)==null?void 0:i.isConnected)!==!1)return this.promote(l),!0}return!1}promote(e,i){var l;const a=this.lead;if(e!==a&&(this.prevLead=a,this.lead=e,e.show(),a)){a.updateSnapshot(),e.scheduleRender();const{layoutDependency:c}=a.options,{layoutDependency:u}=e.options;(c===void 0||c!==u)&&(e.resumeFrom=a,i&&(a.preserveOpacity=!0),a.snapshot&&(e.snapshot=a.snapshot,e.snapshot.latestValues=a.animationValues||a.latestValues),(l=e.root)!=null&&l.isUpdating&&(e.isLayoutDirty=!0)),e.options.crossfade===!1&&a.hide()}}exitAnimationComplete(){this.members.forEach(e=>{var i,a,l,c,u;(a=(i=e.options).onExitComplete)==null||a.call(i),(u=(l=e.resumingFrom)==null?void 0:(c=l.options).onExitComplete)==null||u.call(c)})}scheduleRender(){this.members.forEach(e=>e.instance&&e.scheduleRender(!1))}removeLeadSnapshot(){var e;(e=this.lead)!=null&&e.snapshot&&(this.lead.snapshot=void 0)}}const mc={hasAnimatedSinceResize:!0,hasEverUpdated:!1},yd=["","X","Y","Z"],AA=1e3;let jA=0;function bd(n,e,i,a){const{latestValues:l}=e;l[n]&&(i[n]=l[n],e.setStaticValue(n,0),a&&(a[n]=0))}function Ix(n){if(n.hasCheckedOptimisedAppear=!0,n.root===n)return;const{visualElement:e}=n.options;if(!e)return;const i=fx(e);if(window.MotionHasOptimisedAnimation(i,"transform")){const{layout:l,layoutId:c}=n.options;window.MotionCancelOptimisedAnimation(i,"transform",Ke,!(l||c))}const{parent:a}=n;a&&!a.hasCheckedOptimisedAppear&&Ix(a)}function Gx({attachResizeListener:n,defaultParent:e,measureScroll:i,checkIsScrollRoot:a,resetTransform:l}){return class{constructor(u={},d=e==null?void 0:e()){this.id=jA++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.layoutVersion=0,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,this.nodes.forEach(CA),this.nodes.forEach(UA),this.nodes.forEach(LA),this.nodes.forEach(RA)},this.resolvedRelativeTargetAt=0,this.linkedParentVersion=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=u,this.root=d?d.root||d:this,this.path=d?[...d.path,d]:[],this.parent=d,this.depth=d?d.depth+1:0;for(let m=0;mthis.root.updateBlockedByResize=!1;Ke.read(()=>{v=window.innerWidth}),n(u,()=>{const _=window.innerWidth;_!==v&&(v=_,this.root.updateBlockedByResize=!0,y&&y(),y=TA(x,250),mc.hasAnimatedSinceResize&&(mc.hasAnimatedSinceResize=!1,this.nodes.forEach(Z0)))})}d&&this.root.registerSharedNode(d,this),this.options.animate!==!1&&p&&(d||m)&&this.addEventListener("didUpdate",({delta:y,hasLayoutChanged:v,hasRelativeLayoutChanged:x,layout:_})=>{if(this.isTreeAnimationBlocked()){this.target=void 0,this.relativeTarget=void 0;return}const S=this.options.transition||p.getDefaultTransition()||$A,{onLayoutAnimationStart:T,onLayoutAnimationComplete:k}=p.getProps(),C=!this.targetLayout||!qx(this.targetLayout,_),M=!v&&x;if(this.options.layoutRoot||this.resumeFrom||M||v&&(C||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const N={...Pf(S,"layout"),onPlay:T,onComplete:k};(p.shouldReduceMotion||this.options.layoutRoot)&&(N.delay=0,N.type=!1),this.startAnimation(N),this.setAnimationOrigin(y,M,N.path)}else v||Z0(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=_})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const u=this.getStack();u&&u.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),ai(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(zA),this.animationId++)}getTransformTemplate(){const{visualElement:u}=this.options;return u&&u.getProps().transformTemplate}willUpdate(u=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked()){this.options.onExitComplete&&this.options.onExitComplete();return}if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&Ix(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let y=0;y{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){this.snapshot||!this.instance||(this.snapshot=this.measure(),this.snapshot&&!It(this.snapshot.measuredBox.x)&&!It(this.snapshot.measuredBox.y)&&(this.snapshot=void 0))}updateLayout(){if(!this.instance||(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead())&&!this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let m=0;m{const F=Z/1e3,G=Y==null?void 0:Y(F);G?(x.x.translate=G.x,x.x.scale=Ge(u.x.scale,1,F),x.x.origin=u.x.origin,x.x.originPoint=u.x.originPoint,x.y.translate=G.y,x.y.scale=Ge(u.y.scale,1,F),x.y.origin=u.y.origin,x.y.originPoint=u.y.originPoint):(W0(x.x,u.x,F),W0(x.y,u.y,F)),this.setTargetDelta(x),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(Tc(_,this.layout.layoutBox,this.relativeParent.layout.layoutBox,this.options.layoutAnchor||void 0),PA(this.relativeTarget,this.relativeTargetOrigin,_,F),z&&pA(this.relativeTarget,z)&&(this.isProjectionDirty=!1),z||(z=Tt()),Dn(z,this.relativeTarget)),k&&(this.animationValues=v,bA(v,y,this.latestValues,F,N,M)),G&&G.rotate!==void 0&&(this.animationValues||(this.animationValues=v),this.animationValues.pathRotation=G.rotate),this.root.scheduleUpdateProjection(),this.scheduleRender(),this.animationProgress=F},this.mixTargetDelta(this.options.layoutRoot?1e3:0)}startAnimation(u){var d,m,p;this.notifyListeners("animationStart"),(d=this.currentAnimation)==null||d.stop(),(p=(m=this.resumingFrom)==null?void 0:m.currentAnimation)==null||p.stop(),this.pendingAnimation&&(ai(this.pendingAnimation),this.pendingAnimation=void 0),this.pendingAnimation=Ke.update(()=>{mc.hasAnimatedSinceResize=!0,this.motionValue||(this.motionValue=Ka(0)),this.motionValue.jump(0,!1),this.currentAnimation=wA(this.motionValue,[0,1e3],{...u,velocity:0,isSync:!0,onUpdate:y=>{this.mixTargetDelta(y),u.onUpdate&&u.onUpdate(y)},onComplete:()=>{u.onComplete&&u.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const u=this.getStack();u&&u.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners("animationComplete")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(AA),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const u=this.getLead();let{targetWithTransforms:d,target:m,layout:p,latestValues:y}=u;if(!(!d||!m||!p)){if(this!==u&&this.layout&&p&&Kx(this.options.animationType,this.layout.layoutBox,p.layoutBox)){m=this.target||Tt();const v=It(this.layout.layoutBox.x);m.x.min=u.target.x.min,m.x.max=m.x.min+v;const x=It(this.layout.layoutBox.y);m.y.min=u.target.y.min,m.y.max=m.y.min+x}Dn(d,m),dc(d,y),no(this.projectionDeltaWithTransform,this.layoutCorrected,d,y)}}registerSharedNode(u,d){this.sharedNodes.has(u)||this.sharedNodes.set(u,new EA),this.sharedNodes.get(u).add(d);const p=d.options.initialPromotionConfig;d.promote({transition:p?p.transition:void 0,preserveFollowOpacity:p&&p.shouldPreserveFollowOpacity?p.shouldPreserveFollowOpacity(d):void 0})}isLead(){const u=this.getStack();return u?u.lead===this:!0}getLead(){var d;const{layoutId:u}=this.options;return u?((d=this.getStack())==null?void 0:d.lead)||this:this}getPrevLead(){var d;const{layoutId:u}=this.options;return u?(d=this.getStack())==null?void 0:d.prevLead:void 0}getStack(){const{layoutId:u}=this.options;if(u)return this.root.sharedNodes.get(u)}promote({needsReset:u,transition:d,preserveFollowOpacity:m}={}){const p=this.getStack();p&&p.promote(this,m),u&&(this.projectionDelta=void 0,this.needsReset=!0),d&&this.setOptions({transition:d})}relegate(){const u=this.getStack();return u?u.relegate(this):!1}resetSkewAndRotation(){const{visualElement:u}=this.options;if(!u)return;let d=!1;const{latestValues:m}=u;if((m.z||m.rotate||m.rotateX||m.rotateY||m.rotateZ||m.skewX||m.skewY)&&(d=!0),!d)return;const p={};m.z&&bd("z",u,p,this.animationValues);for(let y=0;y{var d;return(d=u.currentAnimation)==null?void 0:d.stop()}),this.root.nodes.forEach(J0),this.root.sharedNodes.clear()}}}function NA(n){n.updateLayout()}function kA(n){var i;const e=((i=n.resumeFrom)==null?void 0:i.snapshot)||n.snapshot;if(n.isLead()&&n.layout&&e&&n.hasListeners("didUpdate")){const{layoutBox:a,measuredBox:l}=n.layout,{animationType:c}=n.options,u=e.source!==n.layout.source;if(c==="size")Kn(v=>{const x=u?e.measuredBox[v]:e.layoutBox[v],_=It(x);x.min=a[v].min,x.max=x.min+_});else if(c==="x"||c==="y"){const v=c==="x"?"y":"x";rf(u?e.measuredBox[v]:e.layoutBox[v],a[v])}else Kx(c,e.layoutBox,a)&&Kn(v=>{const x=u?e.measuredBox[v]:e.layoutBox[v],_=It(a[v]);x.max=x.min+_,n.relativeTarget&&!n.currentAnimation&&(n.isProjectionDirty=!0,n.relativeTarget[v].max=n.relativeTarget[v].min+_)});const d=Pa();no(d,a,e.layoutBox);const m=Pa();u?no(m,n.applyTransform(l,!0),e.measuredBox):no(m,a,e.layoutBox);const p=!$x(d);let y=!1;if(!n.resumeFrom){const v=n.getClosestProjectingParent();if(v&&!v.resumeFrom){const{snapshot:x,layout:_}=v;if(x&&_){const S=n.options.layoutAnchor||void 0,T=Tt();Tc(T,e.layoutBox,x.layoutBox,S);const k=Tt();Tc(k,a,_.layoutBox,S),qx(T,k)||(y=!0),v.options.layoutRoot&&(n.relativeTarget=k,n.relativeTargetOrigin=T,n.relativeParent=v)}}}n.notifyListeners("didUpdate",{layout:a,snapshot:e,delta:m,layoutDelta:d,hasLayoutChanged:p,hasRelativeLayoutChanged:y})}else if(n.isLead()){const{onExitComplete:a}=n.options;a&&a()}n.options.transition=void 0}function CA(n){n.parent&&(n.isProjecting()||(n.isProjectionDirty=n.parent.isProjectionDirty),n.isSharedProjectionDirty||(n.isSharedProjectionDirty=!!(n.isProjectionDirty||n.parent.isProjectionDirty||n.parent.isSharedProjectionDirty)),n.isTransformDirty||(n.isTransformDirty=n.parent.isTransformDirty))}function RA(n){n.isProjectionDirty=n.isSharedProjectionDirty=n.isTransformDirty=!1}function OA(n){n.clearSnapshot()}function J0(n){n.clearMeasurements()}function DA(n){n.isLayoutDirty=!0,n.updateLayout()}function Q0(n){n.isLayoutDirty=!1}function MA(n){n.isAnimationBlocked&&n.layout&&!n.isLayoutDirty&&(n.snapshot=n.layout,n.isLayoutDirty=!0)}function BA(n){const{visualElement:e}=n.options;e&&e.getProps().onBeforeLayoutMeasure&&e.notify("BeforeLayoutMeasure"),n.resetTransform()}function Z0(n){n.finishAnimation(),n.targetDelta=n.relativeTarget=n.target=void 0,n.isProjectionDirty=!0}function UA(n){n.resolveTargetDelta()}function LA(n){n.calcProjection()}function zA(n){n.resetSkewAndRotation()}function VA(n){n.removeLeadSnapshot()}function W0(n,e,i){n.translate=Ge(e.translate,0,i),n.scale=Ge(e.scale,1,i),n.origin=e.origin,n.originPoint=e.originPoint}function eb(n,e,i,a){n.min=Ge(e.min,i.min,a),n.max=Ge(e.max,i.max,a)}function PA(n,e,i,a){eb(n.x,e.x,i.x,a),eb(n.y,e.y,i.y,a)}function HA(n){return n.animationValues&&n.animationValues.opacityExit!==void 0}const $A={duration:.45,ease:[.4,0,.1,1]},tb=n=>typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(n),nb=tb("applewebkit/")&&!tb("chrome/")?Math.round:jn;function sb(n){n.min=nb(n.min),n.max=nb(n.max)}function qA(n){sb(n.x),sb(n.y)}function Kx(n,e,i){return n==="position"||n==="preserve-aspect"&&!uA(I0(e),I0(i),.2)}function FA(n){var e;return n!==n.root&&((e=n.scroll)==null?void 0:e.wasRoot)}const IA=Gx({attachResizeListener:(n,e)=>ho(n,"resize",e),measureScroll:()=>{var n,e;return{x:document.documentElement.scrollLeft||((n=document.body)==null?void 0:n.scrollLeft)||0,y:document.documentElement.scrollTop||((e=document.body)==null?void 0:e.scrollTop)||0}},checkIsScrollRoot:()=>!0}),vd={current:void 0},Yx=Gx({measureScroll:n=>({x:n.scrollLeft,y:n.scrollTop}),defaultParent:()=>{if(!vd.current){const n=new IA({});n.mount(window),n.setOptions({layoutScroll:!0}),vd.current=n}return vd.current},resetTransform:(n,e)=>{n.style.transform=e!==void 0?e:"none"},checkIsScrollRoot:n=>window.getComputedStyle(n).position==="fixed"}),Zf=D.createContext({transformPagePoint:n=>n,isStatic:!1,reducedMotion:"never"});function ib(n,e){if(typeof n=="function")return n(e);n!=null&&(n.current=e)}function GA(...n){return e=>{let i=!1;const a=n.map(l=>{const c=ib(l,e);return!i&&typeof c=="function"&&(i=!0),c});if(i)return()=>{for(let l=0;l{const{width:_,height:S,top:T,left:k,right:C,bottom:M,direction:N}=m.current;if(e||c===!1||!d.current||!_||!S)return;const z=N==="rtl",Y=i==="left"?z?`right: ${C}`:`left: ${k}`:z?`left: ${k}`:`right: ${C}`,Z=a==="bottom"?`bottom: ${M}`:`top: ${T}`;d.current.dataset.motionPopId=u;const F=document.createElement("style");p&&(F.nonce=p);const G=l??document.head;return G.appendChild(F),F.sheet&&F.sheet.insertRule(` - [data-motion-pop-id="${u}"] { - position: absolute !important; - width: ${_}px !important; - height: ${S}px !important; - ${Y}px !important; - ${Z}px !important; - } - `),()=>{var ne;(ne=d.current)==null||ne.removeAttribute("data-motion-pop-id"),G.contains(F)&&G.removeChild(F)}},[e]),g.jsx(YA,{isPresent:e,childRef:d,sizeRef:m,pop:c,children:c===!1?n:D.cloneElement(n,{ref:v})})}const JA=({children:n,initial:e,isPresent:i,onExitComplete:a,custom:l,presenceAffectsLayout:c,mode:u,anchorX:d,anchorY:m,root:p})=>{const y=Af(QA),v=D.useId(),x=D.useRef(i),_=D.useRef(a);jf(()=>{x.current=i,_.current=a});let S=!0,T=D.useMemo(()=>(S=!1,{id:v,initial:e,isPresent:i,custom:l,onExitComplete:k=>{y.set(k,!0);for(const C of y.values())if(!C)return;a&&a()},register:k=>(y.set(k,!1),()=>{var C;y.delete(k),!x.current&&!y.size&&((C=_.current)==null||C.call(_))})}),[i,y,a]);return c&&S&&(T={...T}),D.useMemo(()=>{y.forEach((k,C)=>y.set(C,!1))},[i]),D.useEffect(()=>{!i&&!y.size&&a&&a()},[i]),n=g.jsx(XA,{pop:u==="popLayout",isPresent:i,anchorX:d,anchorY:m,root:p,children:n}),g.jsx(Uc.Provider,{value:T,children:n})};function QA(){return new Map}function Xx(n=!0){const e=D.useContext(Uc);if(e===null)return[!0,null];const{isPresent:i,onExitComplete:a,register:l}=e,c=D.useId();D.useEffect(()=>{if(n)return l(c)},[n]);const u=D.useCallback(()=>n&&a&&a(c),[c,a,n]);return!i&&a?[!1,u]:[!0]}const Kl=n=>n.key||"";function ab(n){const e=[];return D.Children.forEach(n,i=>{D.isValidElement(i)&&e.push(i)}),e}const ei=({children:n,custom:e,initial:i=!0,onExitComplete:a,presenceAffectsLayout:l=!0,mode:c="sync",propagate:u=!1,anchorX:d="left",anchorY:m="top",root:p})=>{const[y,v]=Xx(u),x=D.useMemo(()=>ab(n),[n]),_=u&&!y?[]:x.map(Kl),S=D.useRef(!0),T=D.useRef(x),k=Af(()=>new Map),C=D.useRef(new Set),[M,N]=D.useState(x),[z,Y]=D.useState(x);jf(()=>{S.current=!1,T.current=x;for(let G=0;G{const ne=Kl(G),Q=u&&!y?!1:x===z||_.includes(ne),ce=()=>{if(C.current.has(ne))return;if(k.has(ne))C.current.add(ne),k.set(ne,!0);else return;let me=!0;k.forEach(_e=>{_e||(me=!1)}),me&&(F==null||F(),Y(T.current),u&&(v==null||v()),a&&a())};return g.jsx(JA,{isPresent:Q,initial:!S.current||i?void 0:!1,custom:e,presenceAffectsLayout:l,mode:c,root:p,onExitComplete:Q?void 0:ce,anchorX:d,anchorY:m,children:G},ne)})})},Jx=D.createContext({strict:!1}),rb={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]};let ob=!1;function ZA(){if(ob)return;const n={};for(const e in rb)n[e]={isEnabled:i=>rb[e].some(a=>!!i[a])};Ax(n),ob=!0}function Qx(){return ZA(),UE()}function WA(n){const e=Qx();for(const i in n)e[i]={...e[i],...n[i]};Ax(e)}const e2=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","propagate","ignoreStrict","viewport"]);function Ec(n){return n.startsWith("while")||n.startsWith("drag")&&n!=="draggable"||n.startsWith("layout")||n.startsWith("onTap")||n.startsWith("onPan")||n.startsWith("onLayout")||e2.has(n)}let Zx=n=>!Ec(n);function t2(n){typeof n=="function"&&(Zx=e=>e.startsWith("on")?!Ec(e):n(e))}try{t2(require("@emotion/is-prop-valid").default)}catch{}function n2(n,e,i){const a={};for(const l in n)l==="values"&&typeof n.values=="object"||Bt(n[l])||(Zx(l)||i===!0&&Ec(l)||!e&&!Ec(l)||n.draggable&&l.startsWith("onDrag"))&&(a[l]=n[l]);return a}const Pc=D.createContext({});function s2(n,e){if(Vc(n)){const{initial:i,animate:a}=n;return{initial:i===!1||uo(i)?i:void 0,animate:uo(a)?a:void 0}}return n.inherit!==!1?e:{}}function i2(n){const{initial:e,animate:i}=s2(n,D.useContext(Pc));return D.useMemo(()=>({initial:e,animate:i}),[lb(e),lb(i)])}function lb(n){return Array.isArray(n)?n.join(" "):n}const Wf=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function Wx(n,e,i){for(const a in e)!Bt(e[a])&&!Dx(a,i)&&(n[a]=e[a])}function a2({transformTemplate:n},e){return D.useMemo(()=>{const i=Wf();return Jf(i,e,n),Object.assign({},i.vars,i.style)},[e])}function r2(n,e){const i=n.style||{},a={};return Wx(a,i,n),Object.assign(a,a2(n,e)),a}function o2(n,e){const i={},a=r2(n,e);return n.drag&&n.dragListener!==!1&&(i.draggable=!1,a.userSelect=a.WebkitUserSelect=a.WebkitTouchCallout="none",a.touchAction=n.drag===!0?"none":`pan-${n.drag==="x"?"y":"x"}`),n.tabIndex===void 0&&(n.onTap||n.onTapStart||n.whileTap)&&(i.tabIndex=0),i.style=a,i}const ew=()=>({...Wf(),attrs:{}});function l2(n,e,i,a){const l=D.useMemo(()=>{const c=ew();return Mx(c,e,Ux(a),n.transformTemplate,n.style),{...c.attrs,style:{...c.style}}},[e]);if(n.style){const c={};Wx(c,n.style,n),l.style={...c,...l.style}}return l}const c2=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function em(n){return typeof n!="string"||n.includes("-")?!1:!!(c2.indexOf(n)>-1||/[A-Z]/u.test(n))}function u2(n,e,i,{latestValues:a},l,c=!1,u){const m=(u??em(n)?l2:o2)(e,a,l,n),p=n2(e,typeof n=="string",c),y=n!==D.Fragment?{...p,...m,ref:i}:{},{children:v}=e,x=D.useMemo(()=>Bt(v)?v.get():v,[v]);return D.createElement(n,{...y,children:x})}function h2({scrapeMotionValuesFromProps:n,createRenderState:e},i,a,l){return{latestValues:d2(i,a,l,n),renderState:e()}}function d2(n,e,i,a){const l={},c=a(n,{});for(const x in c)l[x]=fc(c[x]);let{initial:u,animate:d}=n;const m=Vc(n),p=Tx(n);e&&p&&!m&&n.inherit!==!1&&(u===void 0&&(u=e.initial),d===void 0&&(d=e.animate));let y=i?i.initial===!1:!1;y=y||u===!1;const v=y?d:u;if(v&&typeof v!="boolean"&&!zc(v)){const x=Array.isArray(v)?v:[v];for(let _=0;_(e,i)=>{const a=D.useContext(Pc),l=D.useContext(Uc),c=()=>h2(n,e,a,l);return i?c():Af(c)},f2=tw({scrapeMotionValuesFromProps:Qf,createRenderState:Wf}),m2=tw({scrapeMotionValuesFromProps:Lx,createRenderState:ew}),p2=Symbol.for("motionComponentSymbol");function g2(n,e,i){const a=D.useRef(i);D.useInsertionEffect(()=>{a.current=i});const l=D.useRef(null);return D.useCallback(c=>{var d;c&&((d=n.onMount)==null||d.call(n,c)),e&&(c?e.mount(c):e.unmount());const u=a.current;if(typeof u=="function")if(c){const m=u(c);typeof m=="function"&&(l.current=m)}else l.current?(l.current(),l.current=null):u(c);else u&&(u.current=c)},[e])}const nw=D.createContext({});function Oa(n){return n&&typeof n=="object"&&Object.prototype.hasOwnProperty.call(n,"current")}function y2(n,e,i,a,l,c){var N,z;const{visualElement:u}=D.useContext(Pc),d=D.useContext(Jx),m=D.useContext(Uc),p=D.useContext(Zf),y=p.reducedMotion,v=p.skipAnimations,x=D.useRef(null),_=D.useRef(!1);a=a||d.renderer,!x.current&&a&&(x.current=a(n,{visualState:e,parent:u,props:i,presenceContext:m,blockInitialAnimation:m?m.initial===!1:!1,reducedMotionConfig:y,skipAnimations:v,isSVG:c}),_.current&&x.current&&(x.current.manuallyAnimateOnMount=!0));const S=x.current,T=D.useContext(nw);S&&!S.projection&&l&&(S.type==="html"||S.type==="svg")&&b2(x.current,i,l,T);const k=D.useRef(!1);D.useInsertionEffect(()=>{S&&k.current&&S.update(i,m)});const C=i[dx],M=D.useRef(!!C&&typeof window<"u"&&!((N=window.MotionHandoffIsComplete)!=null&&N.call(window,C))&&((z=window.MotionHasOptimisedAnimation)==null?void 0:z.call(window,C)));return jf(()=>{_.current=!0,S&&(k.current=!0,window.MotionIsMounted=!0,S.updateFeatures(),S.scheduleRenderMicrotask(),M.current&&S.animationState&&S.animationState.animateChanges())}),D.useEffect(()=>{S&&(!M.current&&S.animationState&&S.animationState.animateChanges(),M.current&&(queueMicrotask(()=>{var Y;(Y=window.MotionHandoffMarkAsComplete)==null||Y.call(window,C)}),M.current=!1),S.enteringChildren=void 0)}),S}function b2(n,e,i,a){const{layoutId:l,layout:c,drag:u,dragConstraints:d,layoutScroll:m,layoutRoot:p,layoutAnchor:y,layoutCrossfade:v}=e;n.projection=new i(n.latestValues,e["data-framer-portal-id"]?void 0:sw(n.parent)),n.projection.setOptions({layoutId:l,layout:c,alwaysMeasureLayout:!!u||d&&Oa(d),visualElement:n,animationType:typeof c=="string"?c:"both",initialPromotionConfig:a,crossfade:v,layoutScroll:m,layoutRoot:p,layoutAnchor:y})}function sw(n){if(n)return n.options.allowProjection!==!1?n.projection:sw(n.parent)}function xd(n,{forwardMotionProps:e=!1,type:i}={},a,l){a&&WA(a);const c=i?i==="svg":em(n),u=c?m2:f2;function d(p,y){let v;const x={...D.useContext(Zf),...p,layoutId:v2(p)},{isStatic:_}=x,S=i2(p),T=u(p,_);if(!_&&typeof window<"u"){x2();const k=w2(x);v=k.MeasureLayout,S.visualElement=y2(n,T,x,l,k.ProjectionNode,c)}return g.jsxs(Pc.Provider,{value:S,children:[v&&S.visualElement?g.jsx(v,{visualElement:S.visualElement,...x}):null,u2(n,p,g2(T,S.visualElement,y),T,_,e,c)]})}d.displayName=`motion.${typeof n=="string"?n:`create(${n.displayName??n.name??""})`}`;const m=D.forwardRef(d);return m[p2]=n,m}function v2({layoutId:n}){const e=D.useContext(Ef).id;return e&&n!==void 0?e+"-"+n:n}function x2(n,e){D.useContext(Jx).strict}function w2(n){const e=Qx(),{drag:i,layout:a}=e;if(!i&&!a)return{};const l={...i,...a};return{MeasureLayout:i!=null&&i.isEnabled(n)||a!=null&&a.isEnabled(n)?l.MeasureLayout:void 0,ProjectionNode:l.ProjectionNode}}function _2(n,e){if(typeof Proxy>"u")return xd;const i=new Map,a=(c,u)=>xd(c,u,n,e),l=(c,u)=>a(c,u);return new Proxy(l,{get:(c,u)=>u==="create"?a:(i.has(u)||i.set(u,xd(u,void 0,n,e)),i.get(u))})}const S2=(n,e)=>e.isSVG??em(n)?new WE(e):new KE(e,{allowProjection:n!==D.Fragment});class T2 extends ri{constructor(e){super(e),e.animationState||(e.animationState=iA(e))}updateAnimationControlsSubscription(){const{animate:e}=this.node.getProps();zc(e)&&(this.unmountControls=e.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:e}=this.node.getProps(),{animate:i}=this.node.prevProps||{};e!==i&&this.updateAnimationControlsSubscription()}unmount(){var e;this.node.animationState.reset(),(e=this.unmountControls)==null||e.call(this)}}let E2=0;class A2 extends ri{constructor(){super(...arguments),this.id=E2++,this.isExitComplete=!1}update(){var c;if(!this.node.presenceContext)return;const{isPresent:e,onExitComplete:i}=this.node.presenceContext,{isPresent:a}=this.node.prevPresenceContext||{};if(!this.node.animationState||e===a)return;if(e&&a===!1){if(this.isExitComplete){const{initial:u,custom:d}=this.node.getProps();if(typeof u=="string"||typeof u=="object"&&u!==null&&!Array.isArray(u)){const m=Li(this.node,u,d);if(m){const{transition:p,transitionEnd:y,...v}=m;for(const x in v)(c=this.node.getValue(x))==null||c.jump(v[x])}}this.node.animationState.reset(),this.node.animationState.animateChanges()}else this.node.animationState.setActive("exit",!1);this.isExitComplete=!1;return}const l=this.node.animationState.setActive("exit",!e);i&&!e&&l.then(()=>{this.isExitComplete=!0,i(this.id)})}mount(){const{register:e,onExitComplete:i}=this.node.presenceContext||{};i&&i(this.id),e&&(this.unmount=e(this.id))}unmount(){}}const j2={animation:{Feature:T2},exit:{Feature:A2}};function jo(n){return{point:{x:n.pageX,y:n.pageY}}}const N2=n=>e=>Gf(e)&&n(e,jo(e));function so(n,e,i,a){return ho(n,e,N2(i),a)}const iw=({current:n})=>n?n.ownerDocument.defaultView:null,cb=(n,e)=>Math.abs(n-e);function k2(n,e){const i=cb(n.x,e.x),a=cb(n.y,e.y);return Math.sqrt(i**2+a**2)}const ub=new Set(["auto","scroll"]);class aw{constructor(e,i,{transformPagePoint:a,contextWindow:l=window,dragSnapToOrigin:c=!1,distanceThreshold:u=3,element:d}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.lastRawMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.scrollPositions=new Map,this.removeScrollListeners=null,this.onElementScroll=S=>{this.handleScroll(S.target)},this.onWindowScroll=()=>{this.handleScroll(window)},this.updatePoint=()=>{if(!(this.lastMoveEvent&&this.lastMoveEventInfo))return;this.lastRawMoveEventInfo&&(this.lastMoveEventInfo=Yl(this.lastRawMoveEventInfo,this.transformPagePoint));const S=wd(this.lastMoveEventInfo,this.history),T=this.startEvent!==null,k=k2(S.offset,{x:0,y:0})>=this.distanceThreshold;if(!T&&!k)return;const{point:C}=S,{timestamp:M}=Mt;this.history.push({...C,timestamp:M});const{onStart:N,onMove:z}=this.handlers;T||(N&&N(this.lastMoveEvent,S),this.startEvent=this.lastMoveEvent),z&&z(this.lastMoveEvent,S)},this.handlePointerMove=(S,T)=>{this.lastMoveEvent=S,this.lastRawMoveEventInfo=T,this.lastMoveEventInfo=Yl(T,this.transformPagePoint),Ke.update(this.updatePoint,!0)},this.handlePointerUp=(S,T)=>{this.end();const{onEnd:k,onSessionEnd:C,resumeAnimation:M}=this.handlers;if((this.dragSnapToOrigin||!this.startEvent)&&M&&M(),!(this.lastMoveEvent&&this.lastMoveEventInfo))return;const N=wd(S.type==="pointercancel"?this.lastMoveEventInfo:Yl(T,this.transformPagePoint),this.history);this.startEvent&&k&&k(S,N),C&&C(S,N)},!Gf(e))return;this.dragSnapToOrigin=c,this.handlers=i,this.transformPagePoint=a,this.distanceThreshold=u,this.contextWindow=l||window;const m=jo(e),p=Yl(m,this.transformPagePoint),{point:y}=p,{timestamp:v}=Mt;this.history=[{...y,timestamp:v}];const{onSessionStart:x}=i;x&&x(e,wd(p,this.history));const _={passive:!0,capture:!0};this.removeListeners=To(so(this.contextWindow,"pointermove",this.handlePointerMove,_),so(this.contextWindow,"pointerup",this.handlePointerUp,_),so(this.contextWindow,"pointercancel",this.handlePointerUp,_)),d&&this.startScrollTracking(d)}startScrollTracking(e){let i=e.parentElement;for(;i;){const a=getComputedStyle(i);(ub.has(a.overflowX)||ub.has(a.overflowY))&&this.scrollPositions.set(i,{x:i.scrollLeft,y:i.scrollTop}),i=i.parentElement}this.scrollPositions.set(window,{x:window.scrollX,y:window.scrollY}),window.addEventListener("scroll",this.onElementScroll,{capture:!0}),window.addEventListener("scroll",this.onWindowScroll),this.removeScrollListeners=()=>{window.removeEventListener("scroll",this.onElementScroll,{capture:!0}),window.removeEventListener("scroll",this.onWindowScroll)}}handleScroll(e){const i=this.scrollPositions.get(e);if(!i)return;const a=e===window,l=a?{x:window.scrollX,y:window.scrollY}:{x:e.scrollLeft,y:e.scrollTop},c={x:l.x-i.x,y:l.y-i.y};c.x===0&&c.y===0||(a?this.lastMoveEventInfo&&(this.lastMoveEventInfo.point.x+=c.x,this.lastMoveEventInfo.point.y+=c.y):this.history.length>0&&(this.history[0].x-=c.x,this.history[0].y-=c.y),this.scrollPositions.set(e,l),Ke.update(this.updatePoint,!0))}updateHandlers(e){this.handlers=e}end(){this.removeListeners&&this.removeListeners(),this.removeScrollListeners&&this.removeScrollListeners(),this.scrollPositions.clear(),ai(this.updatePoint)}}function Yl(n,e){return e?{point:e(n.point)}:n}function hb(n,e){return{x:n.x-e.x,y:n.y-e.y}}function wd({point:n},e){return{point:n,delta:hb(n,rw(e)),offset:hb(n,C2(e)),velocity:R2(e,.1)}}function C2(n){return n[0]}function rw(n){return n[n.length-1]}function R2(n,e){if(n.length<2)return{x:0,y:0};let i=n.length-1,a=null;const l=rw(n);for(;i>=0&&(a=n[i],!(l.timestamp-a.timestamp>dn(e)));)i--;if(!a)return{x:0,y:0};a===n[0]&&n.length>2&&l.timestamp-a.timestamp>dn(e)*2&&(a=n[1]);const c=An(l.timestamp-a.timestamp);if(c===0)return{x:0,y:0};const u={x:(l.x-a.x)/c,y:(l.y-a.y)/c};return u.x===1/0&&(u.x=0),u.y===1/0&&(u.y=0),u}function O2(n,{min:e,max:i},a){return e!==void 0&&ni&&(n=a?Ge(i,n,a.max):Math.min(n,i)),n}function db(n,e,i){return{min:e!==void 0?n.min+e:void 0,max:i!==void 0?n.max+i-(n.max-n.min):void 0}}function D2(n,{top:e,left:i,bottom:a,right:l}){return{x:db(n.x,i,l),y:db(n.y,e,a)}}function fb(n,e){let i=e.min-n.min,a=e.max-n.max;return e.max-e.mina?i=lo(e.min,e.max-a,n.min):a>l&&(i=lo(n.min,n.max-l,e.min)),Zn(0,1,i)}function U2(n,e){const i={};return e.min!==void 0&&(i.min=e.min-n.min),e.max!==void 0&&(i.max=e.max-n.min),i}const of=.35;function L2(n=of){return n===!1?n=0:n===!0&&(n=of),{x:mb(n,"left","right"),y:mb(n,"top","bottom")}}function mb(n,e,i){return{min:pb(n,e),max:pb(n,i)}}function pb(n,e){return typeof n=="number"?n:n[e]||0}const z2=new WeakMap;class V2{constructor(e){this.openDragLock=null,this.isDragging=!1,this.currentDirection=null,this.originPoint={x:0,y:0},this.constraints=!1,this.hasMutatedConstraints=!1,this.elastic=Tt(),this.latestPointerEvent=null,this.latestPanInfo=null,this.visualElement=e}start(e,{snapToCursor:i=!1,distanceThreshold:a}={}){const{presenceContext:l}=this.visualElement;if(l&&l.isPresent===!1)return;const c=v=>{i&&this.snapToCursor(jo(v).point),this.stopAnimation()},u=(v,x)=>{const{drag:_,dragPropagation:S,onDragStart:T}=this.getProps();if(_&&!S&&(this.openDragLock&&this.openDragLock(),this.openDragLock=dE(_),!this.openDragLock))return;this.latestPointerEvent=v,this.latestPanInfo=x,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),Kn(C=>{let M=this.getAxisMotionValue(C).get()||0;if(Qn.test(M)){const{projection:N}=this.visualElement;if(N&&N.layout){const z=N.layout.layoutBox[C];z&&(M=It(z)*(parseFloat(M)/100))}}this.originPoint[C]=M}),T&&Ke.update(()=>T(v,x),!1,!0),Jd(this.visualElement,"transform");const{animationState:k}=this.visualElement;k&&k.setActive("whileDrag",!0)},d=(v,x)=>{this.latestPointerEvent=v,this.latestPanInfo=x;const{dragPropagation:_,dragDirectionLock:S,onDirectionLock:T,onDrag:k}=this.getProps();if(!_&&!this.openDragLock)return;const{offset:C}=x;if(S&&this.currentDirection===null){this.currentDirection=H2(C),this.currentDirection!==null&&T&&T(this.currentDirection);return}this.updateAxis("x",x.point,C),this.updateAxis("y",x.point,C),this.visualElement.render(),k&&Ke.update(()=>k(v,x),!1,!0)},m=(v,x)=>{this.latestPointerEvent=v,this.latestPanInfo=x,this.stop(v,x),this.latestPointerEvent=null,this.latestPanInfo=null},p=()=>{const{dragSnapToOrigin:v}=this.getProps();(v||this.constraints)&&this.startAnimation({x:0,y:0})},{dragSnapToOrigin:y}=this.getProps();this.panSession=new aw(e,{onSessionStart:c,onStart:u,onMove:d,onSessionEnd:m,resumeAnimation:p},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:y,distanceThreshold:a,contextWindow:iw(this.visualElement),element:this.visualElement.current})}stop(e,i){const a=e||this.latestPointerEvent,l=i||this.latestPanInfo,c=this.isDragging;if(this.cancel(),!c||!l||!a)return;const{velocity:u}=l;this.startAnimation(u);const{onDragEnd:d}=this.getProps();d&&Ke.postRender(()=>d(a,l))}cancel(){this.isDragging=!1;const{projection:e,animationState:i}=this.visualElement;e&&(e.isAnimationBlocked=!1),this.endPanSession();const{dragPropagation:a}=this.getProps();!a&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),i&&i.setActive("whileDrag",!1)}endPanSession(){this.panSession&&this.panSession.end(),this.panSession=void 0}updateAxis(e,i,a){const{drag:l}=this.getProps();if(!a||!Xl(e,l,this.currentDirection))return;const c=this.getAxisMotionValue(e);let u=this.originPoint[e]+a[e];this.constraints&&this.constraints[e]&&(u=O2(u,this.constraints[e],this.elastic[e])),c.set(u)}resolveConstraints(){var c;const{dragConstraints:e,dragElastic:i}=this.getProps(),a=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):(c=this.visualElement.projection)==null?void 0:c.layout,l=this.constraints;e&&Oa(e)?this.constraints||(this.constraints=this.resolveRefConstraints()):e&&a?this.constraints=D2(a.layoutBox,e):this.constraints=!1,this.elastic=L2(i),l!==this.constraints&&!Oa(e)&&a&&this.constraints&&!this.hasMutatedConstraints&&Kn(u=>{this.constraints!==!1&&this.getAxisMotionValue(u)&&(this.constraints[u]=U2(a.layoutBox[u],this.constraints[u]))})}resolveRefConstraints(){const{dragConstraints:e,onMeasureDragConstraints:i}=this.getProps();if(!e||!Oa(e))return!1;const a=e.current,{projection:l}=this.visualElement;if(!l||!l.layout)return!1;l.root&&(l.root.scroll=void 0,l.root.updateScroll());const c=HE(a,l.root,this.visualElement.getTransformPagePoint());let u=M2(l.layout.layoutBox,c);if(i){const d=i(zE(u));this.hasMutatedConstraints=!!d,d&&(u=Nx(d))}return u}startAnimation(e){const{drag:i,dragMomentum:a,dragElastic:l,dragTransition:c,dragSnapToOrigin:u,onDragTransitionEnd:d}=this.getProps(),m=this.constraints||{},p=Kn(y=>{if(!Xl(y,i,this.currentDirection))return;let v=m&&m[y]||{};(u===!0||u===y)&&(v={min:0,max:0});const x=l?200:1e6,_=l?40:1e7,S={type:"inertia",velocity:a?e[y]:0,bounceStiffness:x,bounceDamping:_,timeConstant:750,restDelta:1,restSpeed:10,...c,...v};return this.startAxisValueAnimation(y,S)});return Promise.all(p).then(d)}startAxisValueAnimation(e,i){const a=this.getAxisMotionValue(e);return Jd(this.visualElement,e),a.start(Hf(e,a,0,i,this.visualElement,!1))}stopAnimation(){Kn(e=>this.getAxisMotionValue(e).stop())}getAxisMotionValue(e){const i=`_drag${e.toUpperCase()}`,l=this.visualElement.getProps()[i];return l||this.visualElement.getValue(e,this.visualElement.latestValues[e]??0)}snapToCursor(e){Kn(i=>{const{drag:a}=this.getProps();if(!Xl(i,a,this.currentDirection))return;const{projection:l}=this.visualElement,c=this.getAxisMotionValue(i);if(l&&l.layout){const{min:u,max:d}=l.layout.layoutBox[i],m=c.get()||0;c.set(e[i]-Ge(u,d,.5)+m)}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:e,dragConstraints:i}=this.getProps(),{projection:a}=this.visualElement;if(!Oa(i)||!a||!this.constraints)return;this.stopAnimation();const l={x:0,y:0};Kn(u=>{const d=this.getAxisMotionValue(u);if(d&&this.constraints!==!1){const m=d.get();l[u]=B2({min:m,max:m},this.constraints[u])}});const{transformTemplate:c}=this.visualElement.getProps();this.visualElement.current.style.transform=c?c({},""):"none",a.root&&a.root.updateScroll(),a.updateLayout(),this.constraints=!1,this.resolveConstraints(),Kn(u=>{if(!Xl(u,e,null))return;const d=this.getAxisMotionValue(u),{min:m,max:p}=this.constraints[u];d.set(Ge(m,p,l[u]))}),this.visualElement.render()}addListeners(){if(!this.visualElement.current)return;z2.set(this.visualElement,this);const e=this.visualElement.current,i=so(e,"pointerdown",p=>{const{drag:y,dragListener:v=!0}=this.getProps(),x=p.target,_=x!==e&&bE(x);y&&v&&!_&&this.start(p)});let a;const l=()=>{const{dragConstraints:p}=this.getProps();Oa(p)&&p.current&&(this.constraints=this.resolveRefConstraints(),a||(a=P2(e,p.current,()=>this.scalePositionWithinConstraints())))},{projection:c}=this.visualElement,u=c.addEventListener("measure",l);c&&!c.layout&&(c.root&&c.root.updateScroll(),c.updateLayout()),Ke.read(l);const d=ho(window,"resize",()=>this.scalePositionWithinConstraints()),m=c.addEventListener("didUpdate",(({delta:p,hasLayoutChanged:y})=>{this.isDragging&&y&&(Kn(v=>{const x=this.getAxisMotionValue(v);x&&(this.originPoint[v]+=p[v].translate,x.set(x.get()+p[v].translate))}),this.visualElement.render())}));return()=>{d(),i(),u(),m&&m(),a&&a()}}getProps(){const e=this.visualElement.getProps(),{drag:i=!1,dragDirectionLock:a=!1,dragPropagation:l=!1,dragConstraints:c=!1,dragElastic:u=of,dragMomentum:d=!0}=e;return{...e,drag:i,dragDirectionLock:a,dragPropagation:l,dragConstraints:c,dragElastic:u,dragMomentum:d}}}function gb(n){let e=!0;return()=>{if(e){e=!1;return}n()}}function P2(n,e,i){const a=S0(n,gb(i)),l=S0(e,gb(i));return()=>{a(),l()}}function Xl(n,e,i){return(e===!0||e===n)&&(i===null||i===n)}function H2(n,e=10){let i=null;return Math.abs(n.y)>e?i="y":Math.abs(n.x)>e&&(i="x"),i}class $2 extends ri{constructor(e){super(e),this.removeGroupControls=jn,this.removeListeners=jn,this.controls=new V2(e)}mount(){const{dragControls:e}=this.node.getProps();e&&(this.removeGroupControls=e.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||jn}update(){const{dragControls:e}=this.node.getProps(),{dragControls:i}=this.node.prevProps||{};e!==i&&(this.removeGroupControls(),e&&(this.removeGroupControls=e.subscribe(this.controls)))}unmount(){this.removeGroupControls(),this.removeListeners(),this.controls.isDragging||this.controls.endPanSession()}}const _d=n=>(e,i)=>{n&&Ke.update(()=>n(e,i),!1,!0)};class q2 extends ri{constructor(){super(...arguments),this.removePointerDownListener=jn}onPointerDown(e){this.session=new aw(e,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:iw(this.node)})}createPanHandlers(){const{onPanSessionStart:e,onPanStart:i,onPan:a,onPanEnd:l}=this.node.getProps();return{onSessionStart:_d(e),onStart:_d(i),onMove:_d(a),onEnd:(c,u)=>{delete this.session,l&&Ke.postRender(()=>l(c,u))}}}mount(){this.removePointerDownListener=so(this.node.current,"pointerdown",e=>this.onPointerDown(e))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}let Sd=!1;class F2 extends D.Component{componentDidMount(){const{visualElement:e,layoutGroup:i,switchLayoutGroup:a,layoutId:l}=this.props,{projection:c}=e;c&&(i.group&&i.group.add(c),a&&a.register&&l&&a.register(c),Sd&&c.root.didUpdate(),c.addEventListener("animationComplete",()=>{this.safeToRemove()}),c.setOptions({...c.options,layoutDependency:this.props.layoutDependency,onExitComplete:()=>this.safeToRemove()})),mc.hasEverUpdated=!0}getSnapshotBeforeUpdate(e){const{layoutDependency:i,visualElement:a,drag:l,isPresent:c}=this.props,{projection:u}=a;return u&&(u.isPresent=c,e.layoutDependency!==i&&u.setOptions({...u.options,layoutDependency:i}),Sd=!0,l||e.layoutDependency!==i||i===void 0||e.isPresent!==c?u.willUpdate():this.safeToRemove(),e.isPresent!==c&&(c?u.promote():u.relegate()||Ke.postRender(()=>{const d=u.getStack();(!d||!d.members.length)&&this.safeToRemove()}))),null}componentDidUpdate(){const{visualElement:e,layoutAnchor:i}=this.props,{projection:a}=e;a&&(a.options.layoutAnchor=i,a.root.didUpdate(),If.postRender(()=>{!a.currentAnimation&&a.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:e,layoutGroup:i,switchLayoutGroup:a}=this.props,{projection:l}=e;Sd=!0,l&&(l.scheduleCheckAfterUnmount(),i&&i.group&&i.group.remove(l),a&&a.deregister&&a.deregister(l))}safeToRemove(){const{safeToRemove:e}=this.props;e&&e()}render(){return null}}function ow(n){const[e,i]=Xx(),a=D.useContext(Ef);return g.jsx(F2,{...n,layoutGroup:a,switchLayoutGroup:D.useContext(nw),isPresent:e,safeToRemove:i})}const I2={pan:{Feature:q2},drag:{Feature:$2,ProjectionNode:Yx,MeasureLayout:ow}};function yb(n,e,i){const{props:a}=n;n.animationState&&a.whileHover&&n.animationState.setActive("whileHover",i==="Start");const l="onHover"+i,c=a[l];c&&Ke.postRender(()=>c(e,jo(e)))}class G2 extends ri{mount(){const{current:e}=this.node;e&&(this.unmount=mE(e,(i,a)=>(yb(this.node,a,"Start"),l=>yb(this.node,l,"End"))))}unmount(){}}class K2 extends ri{constructor(){super(...arguments),this.isActive=!1}onFocus(){let e=!1;try{e=this.node.current.matches(":focus-visible")}catch{e=!0}!e||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){!this.isActive||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=To(ho(this.node.current,"focus",()=>this.onFocus()),ho(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}function bb(n,e,i){const{props:a}=n;if(n.current instanceof HTMLButtonElement&&n.current.disabled)return;n.animationState&&a.whileTap&&n.animationState.setActive("whileTap",i==="Start");const l="onTap"+(i==="End"?"":i),c=a[l];c&&Ke.postRender(()=>c(e,jo(e)))}class Y2 extends ri{mount(){const{current:e}=this.node;if(!e)return;const{globalTapTarget:i,propagate:a}=this.node.props;this.unmount=xE(e,(l,c)=>(bb(this.node,c,"Start"),(u,{success:d})=>bb(this.node,u,d?"End":"Cancel")),{useGlobalTarget:i,stopPropagation:(a==null?void 0:a.tap)===!1})}unmount(){}}const lf=new WeakMap,Td=new WeakMap,X2=n=>{const e=lf.get(n.target);e&&e(n)},J2=n=>{n.forEach(X2)};function Q2({root:n,...e}){const i=n||document;Td.has(i)||Td.set(i,{});const a=Td.get(i),l=JSON.stringify(e);return a[l]||(a[l]=new IntersectionObserver(J2,{root:n,...e})),a[l]}function Z2(n,e,i){const a=Q2(e);return lf.set(n,i),a.observe(n),()=>{lf.delete(n),a.unobserve(n)}}const W2={some:0,all:1};class ej extends ri{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){var m;(m=this.stopObserver)==null||m.call(this);const{viewport:e={}}=this.node.getProps(),{root:i,margin:a,amount:l="some",once:c}=e,u={root:i?i.current:void 0,rootMargin:a,threshold:typeof l=="number"?l:W2[l]},d=p=>{const{isIntersecting:y}=p;if(this.isInView===y||(this.isInView=y,c&&!y&&this.hasEnteredView))return;y&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",y);const{onViewportEnter:v,onViewportLeave:x}=this.node.getProps(),_=y?v:x;_&&_(p)};this.stopObserver=Z2(this.node.current,u,d)}mount(){this.startObserver()}update(){if(typeof IntersectionObserver>"u")return;const{props:e,prevProps:i}=this.node;["amount","margin","root"].some(tj(e,i))&&this.startObserver()}unmount(){var e;(e=this.stopObserver)==null||e.call(this),this.hasEnteredView=!1,this.isInView=!1}}function tj({viewport:n={}},{viewport:e={}}={}){return i=>n[i]!==e[i]}const nj={inView:{Feature:ej},tap:{Feature:Y2},focus:{Feature:K2},hover:{Feature:G2}},sj={layout:{ProjectionNode:Yx,MeasureLayout:ow}},ij={...j2,...nj,...I2,...sj},aj=_2(ij,S2),un=aj;/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const rj=n=>n.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),oj=n=>n.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,i,a)=>a?a.toUpperCase():i.toLowerCase()),vb=n=>{const e=oj(n);return e.charAt(0).toUpperCase()+e.slice(1)},lw=(...n)=>n.filter((e,i,a)=>!!e&&e.trim()!==""&&a.indexOf(e)===i).join(" ").trim(),lj=n=>{for(const e in n)if(e.startsWith("aria-")||e==="role"||e==="title")return!0};/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */var cj={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const uj=D.forwardRef(({color:n="currentColor",size:e=24,strokeWidth:i=2,absoluteStrokeWidth:a,className:l="",children:c,iconNode:u,...d},m)=>D.createElement("svg",{ref:m,...cj,width:e,height:e,stroke:n,strokeWidth:a?Number(i)*24/Number(e):i,className:lw("lucide",l),...!c&&!lj(d)&&{"aria-hidden":"true"},...d},[...u.map(([p,y])=>D.createElement(p,y)),...Array.isArray(c)?c:[c]]));/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Ue=(n,e)=>{const i=D.forwardRef(({className:a,...l},c)=>D.createElement(uj,{ref:c,iconNode:e,className:lw(`lucide-${rj(vb(n))}`,`lucide-${n}`,a),...l}));return i.displayName=vb(n),i};/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const hj=[["path",{d:"M8 3 4 7l4 4",key:"9rb6wj"}],["path",{d:"M4 7h16",key:"6tx8e3"}],["path",{d:"m16 21 4-4-4-4",key:"siv7j2"}],["path",{d:"M20 17H4",key:"h6l3hr"}]],dj=Ue("arrow-left-right",hj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const fj=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]],mj=Ue("arrow-right",fj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const pj=[["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2",key:"9lu3g6"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}],["path",{d:"M6 12h.01M18 12h.01",key:"113zkx"}]],xb=Ue("banknote",pj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const gj=[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}],["path",{d:"M8 14h.01",key:"6423bh"}],["path",{d:"M12 14h.01",key:"1etili"}],["path",{d:"M16 14h.01",key:"1gbofw"}],["path",{d:"M8 18h.01",key:"lrp35t"}],["path",{d:"M12 18h.01",key:"mhygvu"}],["path",{d:"M16 18h.01",key:"kzsmim"}]],yj=Ue("calendar-days",gj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const bj=[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}]],vj=Ue("calendar",bj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const xj=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],wj=Ue("chevron-down",xj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const _j=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]],Sj=Ue("circle-alert",_j);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Tj=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],Ac=Ue("circle-check",Tj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Ej=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}],["path",{d:"M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662",key:"154egf"}]],Aj=Ue("circle-user",Ej);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const jj=[["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54",key:"1djwo0"}],["path",{d:"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",key:"1tzkfa"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05",key:"14pb5j"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],cw=Ue("earth",jj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Nj=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],kj=Ue("external-link",Nj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Cj=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],Rj=Ue("eye",Cj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Oj=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M8 13h2",key:"yr2amv"}],["path",{d:"M14 13h2",key:"un5t4a"}],["path",{d:"M8 17h2",key:"2yhykz"}],["path",{d:"M14 17h2",key:"10kma7"}]],Da=Ue("file-spreadsheet",Oj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Dj=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]],Jl=Ue("file-text",Dj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Mj=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z",key:"sc7q7i"}]],Bj=Ue("funnel",Mj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Uj=[["rect",{x:"3",y:"8",width:"18",height:"4",rx:"1",key:"bkv52"}],["path",{d:"M12 8v13",key:"1c76mn"}],["path",{d:"M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7",key:"6wjy6b"}],["path",{d:"M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5",key:"1ihvrl"}]],wb=Ue("gift",Uj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Lj=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]],zj=Ue("history",Lj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Vj=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]],uw=Ue("info",Vj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Pj=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],qa=Ue("loader-circle",Pj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Hj=[["circle",{cx:"12",cy:"16",r:"1",key:"1au0dj"}],["rect",{x:"3",y:"10",width:"18",height:"12",rx:"2",key:"6s8ecr"}],["path",{d:"M7 10V7a5 5 0 0 1 10 0v3",key:"1pqi11"}]],$j=Ue("lock-keyhole",Hj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const qj=[["path",{d:"m16 17 5-5-5-5",key:"1bji2h"}],["path",{d:"M21 12H9",key:"dn1m92"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}]],Fj=Ue("log-out",qj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Ij=[["path",{d:"M4 5h16",key:"1tepv9"}],["path",{d:"M4 12h16",key:"1lakjw"}],["path",{d:"M4 19h16",key:"1djgab"}]],hw=Ue("menu",Ij);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Gj=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}],["path",{d:"M12 15h.01",key:"q59x07"}],["path",{d:"M12 7v4",key:"xawao1"}]],Kj=Ue("message-square-warning",Gj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Yj=[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]],Xj=Ue("play",Yj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Jj=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],Qj=Ue("plus",Jj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const Zj=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],Wj=Ue("refresh-cw",Zj);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const eN=[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]],Jr=Ue("search",eN);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const tN=[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",key:"1ffxy3"}],["path",{d:"m21.854 2.147-10.94 10.939",key:"12cjpa"}]],dw=Ue("send",tN);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const nN=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],Aa=Ue("trash-2",nN);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const sN=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],fo=Ue("triangle-alert",sN);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const iN=[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]],Gr=Ue("upload",iN);/** - * @license lucide-react v0.546.0 - ISC - * - * This source code is licensed under the ISC license. - * See the LICENSE file in the root directory of this source tree. - */const aN=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],Ya=Ue("x",aN),mo=[{code:"GT",name:"Guatemala",flag:"/cruce-cuentas/gt-flag.png",currency:"GTQ",locale:"es-GT",enabled:!0},{code:"TT",name:"Trinidad y Tobago",flag:"/cruce-cuentas/tt-flag.png",currency:"TTD",locale:"en-TT",enabled:!0},{code:"PA",name:"Panamá",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Panama.svg",currency:"USD",locale:"es-PA",enabled:!0},{code:"SV",name:"El Salvador",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_El_Salvador.svg.webp",currency:"USD",locale:"es-SV",enabled:!0},{code:"HN",name:"Honduras",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp",currency:"HNL",locale:"es-HN",enabled:!0}];function _b(n){return mo.find(e=>e.code===n)??mo[0]}const rN="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png";function oN({currentView:n,onViewChange:e,onNewCruce:i,isSidebarOpen:a=!1,onCloseSidebar:l,user:c,isAuthEnabled:u=!1,onLogout:d,country:m,onCountryChange:p,onChooseAnotherCountry:y}){const[v,x]=D.useState(!1),_=D.useRef(null);return D.useEffect(()=>{function S(T){_.current&&!_.current.contains(T.target)&&x(!1)}return document.addEventListener("mousedown",S),()=>document.removeEventListener("mousedown",S)},[]),g.jsxs("aside",{className:`bg-neutral-100 h-screen w-64 fixed left-0 top-0 flex flex-col z-50 pb-6 min-h-screen font-sans transition-transform duration-300 md:translate-x-0 ${a?"translate-x-0":"-translate-x-full"}`,children:[g.jsx("div",{className:"absolute right-0 top-0 h-[80px] w-px bg-[#D0D0D0] pointer-events-none z-10 hidden md:block"}),g.jsx("div",{className:"absolute right-0 top-[84px] bottom-0 w-px bg-[#D0D0D0] pointer-events-none z-10 hidden md:block"}),g.jsxs("div",{className:"h-[84px] px-4 flex justify-between md:justify-center items-center bg-white border-b-4 border-[#6CC24A] relative",children:[g.jsx("img",{alt:"GomezLee Marketing logo",className:"h-10 object-contain selection:bg-transparent",src:rN}),g.jsx("button",{onClick:l,className:"md:hidden p-2 text-neutral-500 hover:text-neutral-800 transition-colors","aria-label":"Cerrar menú",children:g.jsx(Ya,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-4 pt-5",children:[g.jsxs("label",{className:"block",children:[g.jsxs("span",{className:"text-[9px] uppercase tracking-wider font-black text-neutral-400 flex items-center gap-1.5 mb-1.5",children:[g.jsx(cw,{className:"w-3.5 h-3.5"}),"País"]}),g.jsxs("div",{className:"relative",ref:_,children:[g.jsxs("button",{type:"button",onClick:()=>x(!v),className:"w-full border border-neutral-300 bg-white px-3 py-2 text-xs font-bold text-[#4F758B] focus:outline-none focus:border-[#4F758B] flex items-center justify-between transition-colors hover:bg-neutral-50",children:[g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("img",{src:m.flag,alt:"",className:"w-5 h-3.5 object-cover shadow-sm border border-neutral-200"}),g.jsx("span",{children:m.name})]}),g.jsx(wj,{className:`w-4 h-4 text-[#4F758B] transition-transform ${v?"rotate-180":""}`})]}),v&&g.jsx("ul",{className:"absolute left-0 right-0 top-full mt-1 bg-white border border-neutral-300 shadow-lg z-50 py-1",children:mo.map(S=>g.jsx("li",{children:g.jsxs("button",{type:"button",disabled:!S.enabled,onClick:()=>{p(S.code),x(!1)},className:`w-full text-left px-3 py-2.5 text-xs font-bold flex items-center gap-2.5 transition-colors ${S.enabled?"hover:bg-neutral-100 text-[#4F758B]":"opacity-60 cursor-not-allowed text-neutral-500 bg-neutral-50"}`,children:[g.jsx("img",{src:S.flag,alt:"",className:"w-6 h-4 object-cover shadow-sm border border-neutral-200 bg-white flex-shrink-0"}),g.jsxs("span",{className:"min-w-0 text-left leading-tight",children:[g.jsx("span",{className:"block",children:S.name}),!S.enabled&&g.jsx("span",{className:"block mt-1 text-[10px] text-neutral-400 font-medium italic",children:"Próximamente"})]})]})},S.code))})]})]}),g.jsx("button",{type:"button",onClick:y,className:"mt-2 text-[9px] font-bold uppercase tracking-wider text-neutral-400 hover:text-[#4F758B]",children:"Ver países disponibles"})]}),g.jsx("nav",{className:"flex-1 flex flex-col mt-5",children:g.jsxs("ul",{className:"space-y-1",children:[g.jsx("li",{children:g.jsxs("button",{onClick:()=>e("cruce"),className:`w-full flex items-center gap-3 px-6 py-3.5 transition-all duration-150 text-left font-bold text-sm tracking-wide ${n==="cruce"?"bg-[#4F758B] text-white":"text-neutral-600 hover:bg-neutral-200 hover:text-neutral-800"}`,children:[g.jsx(dj,{className:`w-4 h-4 ${n==="cruce"?"text-[#6CC24A]":""}`}),g.jsx("span",{children:"Cruce de Cuentas"})]})}),g.jsx("li",{children:g.jsxs("button",{onClick:()=>e("historial"),className:`w-full flex items-center gap-3 px-6 py-3.5 transition-all duration-150 text-left font-bold text-sm tracking-wide ${n==="historial"?"bg-[#4F758B] text-white":"text-neutral-600 hover:bg-neutral-200 hover:text-neutral-800"}`,children:[g.jsx(zj,{className:`w-4 h-4 ${n==="historial"?"text-[#6CC24A]":""}`}),g.jsx("span",{children:"Reportes Históricos"})]})})]})}),g.jsxs("div",{className:"px-4 space-y-3 pt-4 border-t border-[#D0D0D0] bg-neutral-100",children:[c&&g.jsxs("div",{className:"border border-neutral-200 bg-white px-3 py-3 flex items-center gap-2",children:[g.jsx(Aj,{className:"w-4 h-4 text-[#4F758B] flex-shrink-0"}),g.jsxs("div",{className:"min-w-0",children:[g.jsx("p",{className:"text-[11px] font-black text-neutral-700 truncate",title:c.name,children:c.name}),g.jsx("p",{className:"text-[9px] text-neutral-400 truncate",title:c.email,children:c.email})]})]}),g.jsxs("button",{onClick:i,className:"w-full bg-[#6CC24A] hover:bg-[#5bb03c] text-white py-3 px-4 font-bold text-xs uppercase tracking-wider flex items-center justify-center gap-2 transition-all duration-150 shadow-sm active:scale-98",children:[g.jsx(Qj,{className:"w-4 h-4"}),g.jsx("span",{children:"Nuevo Cruce"})]}),u&&d&&g.jsxs("button",{onClick:d,className:"w-full border border-neutral-300 bg-white hover:bg-neutral-50 text-neutral-600 py-2.5 px-4 font-bold text-[11px] uppercase tracking-wider flex items-center justify-center gap-2 transition-all duration-150",children:[g.jsx(Fj,{className:"w-4 h-4"}),g.jsx("span",{children:"Cerrar sesión"})]})]})]})}function Hc(n,e){var i={};for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&e.indexOf(a)<0&&(i[a]=n[a]);if(n!=null&&typeof Object.getOwnPropertySymbols=="function")for(var l=0,a=Object.getOwnPropertySymbols(n);ln?(...e)=>n(...e):(...e)=>fetch(...e);class tm extends Error{constructor(e,i="FunctionsError",a){super(e),this.name=i,this.context=a}toJSON(){return{name:this.name,message:this.message,context:this.context}}}class uN extends tm{constructor(e){super("Failed to send a request to the Edge Function","FunctionsFetchError",e)}}class Sb extends tm{constructor(e){super("Relay Error invoking the Edge Function","FunctionsRelayError",e)}}class Tb extends tm{constructor(e){super("Edge Function returned a non-2xx status code","FunctionsHttpError",e)}}var cf;(function(n){n.Any="any",n.ApNortheast1="ap-northeast-1",n.ApNortheast2="ap-northeast-2",n.ApSouth1="ap-south-1",n.ApSoutheast1="ap-southeast-1",n.ApSoutheast2="ap-southeast-2",n.CaCentral1="ca-central-1",n.EuCentral1="eu-central-1",n.EuWest1="eu-west-1",n.EuWest2="eu-west-2",n.EuWest3="eu-west-3",n.SaEast1="sa-east-1",n.UsEast1="us-east-1",n.UsWest1="us-west-1",n.UsWest2="us-west-2"})(cf||(cf={}));class hN{constructor(e,{headers:i={},customFetch:a,region:l=cf.Any}={}){this.url=e,this.headers=i,this.region=l,this.fetch=cN(a)}setAuth(e){this.headers.Authorization=`Bearer ${e}`}invoke(e){return lN(this,arguments,void 0,function*(i,a={}){var l;let c,u;try{const{headers:d,method:m,body:p,signal:y,timeout:v}=a;let x={},{region:_}=a;_||(_=this.region);const S=new URL(`${this.url}/${i}`);_&&_!=="any"&&(x["x-region"]=_,S.searchParams.set("forceFunctionRegion",_));let T;const k=!!d&&Object.keys(d).some(Z=>Z.toLowerCase()==="content-type");p&&!k?typeof Blob<"u"&&p instanceof Blob||p instanceof ArrayBuffer?(x["Content-Type"]="application/octet-stream",T=p):typeof p=="string"?(x["Content-Type"]="text/plain",T=p):typeof FormData<"u"&&p instanceof FormData?T=p:(x["Content-Type"]="application/json",T=JSON.stringify(p)):p&&typeof p!="string"&&!(typeof Blob<"u"&&p instanceof Blob)&&!(p instanceof ArrayBuffer)&&!(typeof FormData<"u"&&p instanceof FormData)?T=JSON.stringify(p):T=p;let C=y;v&&(u=new AbortController,c=setTimeout(()=>u.abort(),v),y?(C=u.signal,y.addEventListener("abort",()=>u.abort())):C=u.signal);const M=yield this.fetch(S.toString(),{method:m||"POST",headers:Object.assign(Object.assign(Object.assign({},x),this.headers),d),body:T,signal:C}).catch(Z=>{throw new uN(Z)}),N=M.headers.get("x-relay-error");if(N&&N==="true")throw new Sb(M);if(!M.ok)throw new Tb(M);let z=((l=M.headers.get("Content-Type"))!==null&&l!==void 0?l:"text/plain").split(";")[0].trim(),Y;return z==="application/json"?Y=yield M.json():z==="application/octet-stream"||z==="application/pdf"?Y=yield M.blob():z==="text/event-stream"?Y=M:z==="multipart/form-data"?Y=yield M.formData():Y=yield M.text(),{data:Y,error:null,response:M}}catch(d){return{data:null,error:d,response:d instanceof Tb||d instanceof Sb?d.context:void 0}}finally{c&&clearTimeout(c)}})}}const fw=3,Eb=n=>Math.min(1e3*2**n,3e4),dN=[520,503],mw=["GET","HEAD","OPTIONS"];var Ab=class extends Error{constructor(n){super(n.message),this.name="PostgrestError",this.details=n.details,this.hint=n.hint,this.code=n.code}toJSON(){return{name:this.name,message:this.message,details:this.details,hint:this.hint,code:this.code}}};function jb(n,e){return new Promise(i=>{if(e!=null&&e.aborted){i();return}const a=setTimeout(()=>{e==null||e.removeEventListener("abort",l),i()},n);function l(){clearTimeout(a),i()}e==null||e.addEventListener("abort",l)})}function fN(n,e,i,a){return!(!a||i>=fw||!mw.includes(n)||!dN.includes(e))}var mN=class{constructor(n){var e,i,a,l,c;this.shouldThrowOnError=!1,this.retryEnabled=!0,this.method=n.method,this.url=n.url,this.headers=new Headers(n.headers),this.schema=n.schema,this.body=n.body,this.shouldThrowOnError=(e=n.shouldThrowOnError)!==null&&e!==void 0?e:!1,this.signal=n.signal,this.isMaybeSingle=(i=n.isMaybeSingle)!==null&&i!==void 0?i:!1,this.shouldStripNulls=(a=n.shouldStripNulls)!==null&&a!==void 0?a:!1,this.urlLengthLimit=(l=n.urlLengthLimit)!==null&&l!==void 0?l:8e3,this.retryEnabled=(c=n.retry)!==null&&c!==void 0?c:!0,n.fetch?this.fetch=n.fetch:this.fetch=fetch}throwOnError(){return this.shouldThrowOnError=!0,this}stripNulls(){if(this.headers.get("Accept")==="text/csv")throw new Error("stripNulls() cannot be used with csv()");return this.shouldStripNulls=!0,this}setHeader(n,e){return this.headers=new Headers(this.headers),this.headers.set(n,e),this}retry(n){return this.retryEnabled=n,this}then(n,e){var i=this;if(this.schema===void 0||(["GET","HEAD"].includes(this.method)?this.headers.set("Accept-Profile",this.schema):this.headers.set("Content-Profile",this.schema)),this.method!=="GET"&&this.method!=="HEAD"&&this.headers.set("Content-Type","application/json"),this.shouldStripNulls){const u=this.headers.get("Accept");u==="application/vnd.pgrst.object+json"?this.headers.set("Accept","application/vnd.pgrst.object+json;nulls=stripped"):(!u||u==="application/json")&&this.headers.set("Accept","application/vnd.pgrst.array+json;nulls=stripped")}const a=this.fetch;let c=(async()=>{let u=0;for(;;){const p={};i.headers.forEach((v,x)=>{p[x]=v}),u>0&&(p["X-Retry-Count"]=String(u));let y;try{y=await a(i.url.toString(),{method:i.method,headers:p,body:JSON.stringify(i.body,(v,x)=>typeof x=="bigint"?x.toString():x),signal:i.signal})}catch(v){if((v==null?void 0:v.name)==="AbortError"||(v==null?void 0:v.code)==="ABORT_ERR"||!mw.includes(i.method))throw v;if(i.retryEnabled&&u{var d;let m="",p="",y="";const v=u==null?void 0:u.cause;if(v){var x,_,S,T;const M=(x=v==null?void 0:v.message)!==null&&x!==void 0?x:"",N=(_=v==null?void 0:v.code)!==null&&_!==void 0?_:"";m=`${(S=u==null?void 0:u.name)!==null&&S!==void 0?S:"FetchError"}: ${u==null?void 0:u.message}`,m+=` - -Caused by: ${(T=v==null?void 0:v.name)!==null&&T!==void 0?T:"Error"}: ${M}`,N&&(m+=` (${N})`),v!=null&&v.stack&&(m+=` -${v.stack}`)}else{var k;m=(k=u==null?void 0:u.stack)!==null&&k!==void 0?k:""}const C=this.url.toString().length;return(u==null?void 0:u.name)==="AbortError"||(u==null?void 0:u.code)==="ABORT_ERR"?(y="",p="Request was aborted (timeout or manual cancellation)",C>this.urlLengthLimit&&(p+=`. Note: Your request URL is ${C} characters, which may exceed server limits. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [many IDs])), consider using an RPC function to pass values server-side.`)):((v==null?void 0:v.name)==="HeadersOverflowError"||(v==null?void 0:v.code)==="UND_ERR_HEADERS_OVERFLOW")&&(y="",p="HTTP headers exceeded server limits (typically 16KB)",C>this.urlLengthLimit&&(p+=`. Your request URL is ${C} characters. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [200+ IDs])), consider using an RPC function instead.`)),{success:!1,error:{message:`${(d=u==null?void 0:u.name)!==null&&d!==void 0?d:"FetchError"}: ${u==null?void 0:u.message}`,details:m,hint:p,code:y},data:null,count:null,status:0,statusText:""}})),c.then(n,e)}async processResponse(n){var e=this;let i=null,a=null,l=null,c=n.status,u=n.statusText;if(n.ok){var d,m;if(e.method!=="HEAD"){var p;const x=await n.text();if(x!=="")if(e.headers.get("Accept")==="text/csv")a=x;else if(e.headers.get("Accept")&&(!((p=e.headers.get("Accept"))===null||p===void 0)&&p.includes("application/vnd.pgrst.plan+text")))a=x;else try{a=JSON.parse(x)}catch{if(i={message:x},a=null,e.shouldThrowOnError)throw new Ab({message:x,details:"",hint:"",code:""})}}const y=(d=e.headers.get("Prefer"))===null||d===void 0?void 0:d.match(/count=(exact|planned|estimated)/),v=(m=n.headers.get("content-range"))===null||m===void 0?void 0:m.split("/");y&&v&&v.length>1&&(l=parseInt(v[1])),e.isMaybeSingle&&Array.isArray(a)&&(a.length>1?(i={code:"PGRST116",details:`Results contain ${a.length} rows, application/vnd.pgrst.object+json requires 1 row`,hint:null,message:"JSON object requested, multiple (or no) rows returned"},a=null,l=null,c=406,u="Not Acceptable"):a.length===1?a=a[0]:a=null)}else{const y=await n.text();try{i=JSON.parse(y),Array.isArray(i)&&n.status===404&&(a=[],i=null,c=200,u="OK")}catch{n.status===404&&y===""?(c=204,u="No Content"):i={message:y}}if(i&&e.shouldThrowOnError)throw new Ab(i)}return{success:i===null,error:i,data:a,count:l,status:c,statusText:u}}returns(){return this}overrideTypes(){return this}},pN=class extends mN{throwOnError(){return super.throwOnError()}select(n){let e=!1;const i=(n??"*").split("").map(a=>/\s/.test(a)&&!e?"":(a==='"'&&(e=!e),a)).join("");return this.url.searchParams.set("select",i),this.headers.append("Prefer","return=representation"),this}order(n,{ascending:e=!0,nullsFirst:i,foreignTable:a,referencedTable:l=a}={}){const c=l?`${l}.order`:"order",u=this.url.searchParams.get(c);return this.url.searchParams.set(c,`${u?`${u},`:""}${n}.${e?"asc":"desc"}${i===void 0?"":i?".nullsfirst":".nullslast"}`),this}limit(n,{foreignTable:e,referencedTable:i=e}={}){const a=typeof i>"u"?"limit":`${i}.limit`;return this.url.searchParams.set(a,`${n}`),this}range(n,e,{foreignTable:i,referencedTable:a=i}={}){const l=typeof a>"u"?"offset":`${a}.offset`,c=typeof a>"u"?"limit":`${a}.limit`;return this.url.searchParams.set(l,`${n}`),this.url.searchParams.set(c,`${e-n+1}`),this}abortSignal(n){return this.signal=n,this}single(){return this.headers.set("Accept","application/vnd.pgrst.object+json"),this}maybeSingle(){return this.isMaybeSingle=!0,this}csv(){return this.headers.set("Accept","text/csv"),this}geojson(){return this.headers.set("Accept","application/geo+json"),this}explain({analyze:n=!1,verbose:e=!1,settings:i=!1,buffers:a=!1,wal:l=!1,format:c="text"}={}){var u;const d=[n?"analyze":null,e?"verbose":null,i?"settings":null,a?"buffers":null,l?"wal":null].filter(Boolean).join("|"),m=(u=this.headers.get("Accept"))!==null&&u!==void 0?u:"application/json";return this.headers.set("Accept",`application/vnd.pgrst.plan+${c}; for="${m}"; options=${d};`),c==="json"?this:this}rollback(){return this.headers.append("Prefer","tx=rollback"),this}returns(){return this}maxAffected(n){return this.headers.append("Prefer","handling=strict"),this.headers.append("Prefer",`max-affected=${n}`),this}};const Nb=new RegExp("[,()]");var Ma=class extends pN{throwOnError(){return super.throwOnError()}eq(n,e){return this.url.searchParams.append(n,`eq.${e}`),this}neq(n,e){return this.url.searchParams.append(n,`neq.${e}`),this}gt(n,e){return this.url.searchParams.append(n,`gt.${e}`),this}gte(n,e){return this.url.searchParams.append(n,`gte.${e}`),this}lt(n,e){return this.url.searchParams.append(n,`lt.${e}`),this}lte(n,e){return this.url.searchParams.append(n,`lte.${e}`),this}like(n,e){return this.url.searchParams.append(n,`like.${e}`),this}likeAllOf(n,e){return this.url.searchParams.append(n,`like(all).{${e.join(",")}}`),this}likeAnyOf(n,e){return this.url.searchParams.append(n,`like(any).{${e.join(",")}}`),this}ilike(n,e){return this.url.searchParams.append(n,`ilike.${e}`),this}ilikeAllOf(n,e){return this.url.searchParams.append(n,`ilike(all).{${e.join(",")}}`),this}ilikeAnyOf(n,e){return this.url.searchParams.append(n,`ilike(any).{${e.join(",")}}`),this}regexMatch(n,e){return this.url.searchParams.append(n,`match.${e}`),this}regexIMatch(n,e){return this.url.searchParams.append(n,`imatch.${e}`),this}is(n,e){return this.url.searchParams.append(n,`is.${e}`),this}isDistinct(n,e){return this.url.searchParams.append(n,`isdistinct.${e}`),this}in(n,e){const i=Array.from(new Set(e)).map(a=>typeof a=="string"&&Nb.test(a)?`"${a}"`:`${a}`).join(",");return this.url.searchParams.append(n,`in.(${i})`),this}notIn(n,e){const i=Array.from(new Set(e)).map(a=>typeof a=="string"&&Nb.test(a)?`"${a}"`:`${a}`).join(",");return this.url.searchParams.append(n,`not.in.(${i})`),this}contains(n,e){return typeof e=="string"?this.url.searchParams.append(n,`cs.${e}`):Array.isArray(e)?this.url.searchParams.append(n,`cs.{${e.join(",")}}`):this.url.searchParams.append(n,`cs.${JSON.stringify(e)}`),this}containedBy(n,e){return typeof e=="string"?this.url.searchParams.append(n,`cd.${e}`):Array.isArray(e)?this.url.searchParams.append(n,`cd.{${e.join(",")}}`):this.url.searchParams.append(n,`cd.${JSON.stringify(e)}`),this}rangeGt(n,e){return this.url.searchParams.append(n,`sr.${e}`),this}rangeGte(n,e){return this.url.searchParams.append(n,`nxl.${e}`),this}rangeLt(n,e){return this.url.searchParams.append(n,`sl.${e}`),this}rangeLte(n,e){return this.url.searchParams.append(n,`nxr.${e}`),this}rangeAdjacent(n,e){return this.url.searchParams.append(n,`adj.${e}`),this}overlaps(n,e){return typeof e=="string"?this.url.searchParams.append(n,`ov.${e}`):this.url.searchParams.append(n,`ov.{${e.join(",")}}`),this}textSearch(n,e,{config:i,type:a}={}){let l="";a==="plain"?l="pl":a==="phrase"?l="ph":a==="websearch"&&(l="w");const c=i===void 0?"":`(${i})`;return this.url.searchParams.append(n,`${l}fts${c}.${e}`),this}match(n){return Object.entries(n).filter(([e,i])=>i!==void 0).forEach(([e,i])=>{this.url.searchParams.append(e,`eq.${i}`)}),this}not(n,e,i){return this.url.searchParams.append(n,`not.${e}.${i}`),this}or(n,{foreignTable:e,referencedTable:i=e}={}){const a=i?`${i}.or`:"or";return this.url.searchParams.append(a,`(${n})`),this}filter(n,e,i){return this.url.searchParams.append(n,`${e}.${i}`),this}},gN=class{constructor(n,{headers:e={},schema:i,fetch:a,urlLengthLimit:l=8e3,retry:c}){this.url=n,this.headers=new Headers(e),this.schema=i,this.fetch=a,this.urlLengthLimit=l,this.retry=c}cloneRequestState(){return{url:new URL(this.url.toString()),headers:new Headers(this.headers)}}select(n,e){const{head:i=!1,count:a}=e??{},l=i?"HEAD":"GET";let c=!1;const u=(n??"*").split("").map(p=>/\s/.test(p)&&!c?"":(p==='"'&&(c=!c),p)).join(""),{url:d,headers:m}=this.cloneRequestState();return d.searchParams.set("select",u),a&&m.append("Prefer",`count=${a}`),new Ma({method:l,url:d,headers:m,schema:this.schema,fetch:this.fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}insert(n,{count:e,defaultToNull:i=!0}={}){var a;const l="POST",{url:c,headers:u}=this.cloneRequestState();if(e&&u.append("Prefer",`count=${e}`),i||u.append("Prefer","missing=default"),Array.isArray(n)){const d=n.reduce((m,p)=>m.concat(Object.keys(p)),[]);if(d.length>0){const m=[...new Set(d)].map(p=>`"${p}"`);c.searchParams.set("columns",m.join(","))}}return new Ma({method:l,url:c,headers:u,schema:this.schema,body:n,fetch:(a=this.fetch)!==null&&a!==void 0?a:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}upsert(n,{onConflict:e,ignoreDuplicates:i=!1,count:a,defaultToNull:l=!0}={}){var c;const u="POST",{url:d,headers:m}=this.cloneRequestState();if(m.append("Prefer",`resolution=${i?"ignore":"merge"}-duplicates`),e!==void 0&&d.searchParams.set("on_conflict",e),a&&m.append("Prefer",`count=${a}`),l||m.append("Prefer","missing=default"),Array.isArray(n)){const p=n.reduce((y,v)=>y.concat(Object.keys(v)),[]);if(p.length>0){const y=[...new Set(p)].map(v=>`"${v}"`);d.searchParams.set("columns",y.join(","))}}return new Ma({method:u,url:d,headers:m,schema:this.schema,body:n,fetch:(c=this.fetch)!==null&&c!==void 0?c:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}update(n,{count:e}={}){var i;const a="PATCH",{url:l,headers:c}=this.cloneRequestState();return e&&c.append("Prefer",`count=${e}`),new Ma({method:a,url:l,headers:c,schema:this.schema,body:n,fetch:(i=this.fetch)!==null&&i!==void 0?i:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}delete({count:n}={}){var e;const i="DELETE",{url:a,headers:l}=this.cloneRequestState();return n&&l.append("Prefer",`count=${n}`),new Ma({method:i,url:a,headers:l,schema:this.schema,fetch:(e=this.fetch)!==null&&e!==void 0?e:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}};function po(n){"@babel/helpers - typeof";return po=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},po(n)}function yN(n,e){if(po(n)!="object"||!n)return n;var i=n[Symbol.toPrimitive];if(i!==void 0){var a=i.call(n,e);if(po(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(n)}function bN(n){var e=yN(n,"string");return po(e)=="symbol"?e:e+""}function vN(n,e,i){return(e=bN(e))in n?Object.defineProperty(n,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[e]=i,n}function kb(n,e){var i=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(l){return Object.getOwnPropertyDescriptor(n,l).enumerable})),i.push.apply(i,a)}return i}function Ql(n){for(var e=1;e0?this.fetch=(p,y)=>{const v=new AbortController,x=setTimeout(()=>v.abort(),c),_=y==null?void 0:y.signal;if(_){if(_.aborted)return clearTimeout(x),m(p,y);const S=()=>{clearTimeout(x),v.abort()};return _.addEventListener("abort",S,{once:!0}),m(p,Ql(Ql({},y),{},{signal:v.signal})).finally(()=>{clearTimeout(x),_.removeEventListener("abort",S)})}return m(p,Ql(Ql({},y),{},{signal:v.signal})).finally(()=>clearTimeout(x))}:this.fetch=m,this.retry=d}from(e){if(!e||typeof e!="string"||e.trim()==="")throw new Error("Invalid relation name: relation must be a non-empty string.");return new gN(new URL(`${this.url}/${e}`),{headers:new Headers(this.headers),schema:this.schemaName,fetch:this.fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}schema(e){return new pw(this.url,{headers:this.headers,schema:e,fetch:this.fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}rpc(e,i={},{head:a=!1,get:l=!1,count:c}={}){var u;let d;const m=new URL(`${this.url}/rpc/${e}`);let p;const y=_=>_!==null&&typeof _=="object"&&(!Array.isArray(_)||_.some(y)),v=a&&Object.values(i).some(y);v?(d="POST",p=i):a||l?(d=a?"HEAD":"GET",Object.entries(i).filter(([_,S])=>S!==void 0).map(([_,S])=>[_,Array.isArray(S)?`{${S.join(",")}}`:`${S}`]).forEach(([_,S])=>{m.searchParams.append(_,S)})):(d="POST",p=i);const x=new Headers(this.headers);return v?x.set("Prefer",c?`count=${c},return=minimal`:"return=minimal"):c&&x.set("Prefer",`count=${c}`),new Ma({method:d,url:m,headers:x,schema:this.schemaName,body:p,fetch:(u=this.fetch)!==null&&u!==void 0?u:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}};class wN{constructor(){}static detectEnvironment(){var e;if(typeof WebSocket<"u")return{type:"native",wsConstructor:WebSocket};const i=globalThis;if(typeof globalThis<"u"&&typeof i.WebSocket<"u")return{type:"native",wsConstructor:i.WebSocket};const a=typeof global<"u"?global:void 0;if(a&&typeof a.WebSocket<"u")return{type:"native",wsConstructor:a.WebSocket};if(typeof globalThis<"u"&&typeof i.WebSocketPair<"u"&&typeof globalThis.WebSocket>"u")return{type:"cloudflare",error:"Cloudflare Workers detected. WebSocket clients are not supported in Cloudflare Workers.",workaround:"Use Cloudflare Workers WebSocket API for server-side WebSocket handling, or deploy to a different runtime."};if(typeof globalThis<"u"&&i.EdgeRuntime||typeof navigator<"u"&&(!((e=navigator.userAgent)===null||e===void 0)&&e.includes("Vercel-Edge")))return{type:"unsupported",error:"Edge runtime detected (Vercel Edge/Netlify Edge). WebSockets are not supported in edge functions.",workaround:"Use serverless functions or a different deployment target for WebSocket functionality."};const l=globalThis.process;if(l){const c=l.versions;if(c&&c.node)return{type:"unsupported",error:"Node.js detected but native WebSocket not found.",workaround:"Ensure you are running Node.js 22+ or provide a WebSocket implementation via the transport option."}}return{type:"unsupported",error:"Unknown JavaScript runtime without WebSocket support.",workaround:"Ensure you're running in a supported environment (browser, Node.js, Deno) or provide a custom WebSocket implementation."}}static getWebSocketConstructor(){const e=this.detectEnvironment();if(e.wsConstructor)return e.wsConstructor;let i=e.error||"WebSocket not supported in this environment.";throw e.workaround&&(i+=` - -Suggested solution: ${e.workaround}`),new Error(i)}static isWebSocketSupported(){try{return this.detectEnvironment().type==="native"}catch{return!1}}}const _N="2.110.2",SN=`realtime-js/${_N}`,TN="1.0.0",gw="2.0.0",EN=gw,AN=1e4,jN=100,ti={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},yw={close:"phx_close",error:"phx_error",join:"phx_join",leave:"phx_leave",access_token:"access_token"},uf={connecting:"connecting",closing:"closing",closed:"closed"};class NN{constructor(e){this.HEADER_LENGTH=1,this.USER_BROADCAST_PUSH_META_LENGTH=6,this.KINDS={userBroadcastPush:3,userBroadcast:4},this.BINARY_ENCODING=0,this.JSON_ENCODING=1,this.BROADCAST_EVENT="broadcast",this.allowedMetadataKeys=[],this.allowedMetadataKeys=e??[]}encode(e,i){if(e.event===this.BROADCAST_EVENT&&!(e.payload instanceof ArrayBuffer)&&typeof e.payload.event=="string")return i(this._binaryEncodeUserBroadcastPush(e));let a=[e.join_ref,e.ref,e.topic,e.event,e.payload];return i(JSON.stringify(a))}_binaryEncodeUserBroadcastPush(e){var i;return this._isArrayBuffer((i=e.payload)===null||i===void 0?void 0:i.payload)?this._encodeBinaryUserBroadcastPush(e):this._encodeJsonUserBroadcastPush(e)}_encodeBinaryUserBroadcastPush(e){var i,a;const l=(a=(i=e.payload)===null||i===void 0?void 0:i.payload)!==null&&a!==void 0?a:new ArrayBuffer(0);return this._encodeUserBroadcastPush(e,this.BINARY_ENCODING,l)}_encodeJsonUserBroadcastPush(e){var i,a;const l=(a=(i=e.payload)===null||i===void 0?void 0:i.payload)!==null&&a!==void 0?a:{},u=new TextEncoder().encode(JSON.stringify(l)).buffer;return this._encodeUserBroadcastPush(e,this.JSON_ENCODING,u)}_encodeUserBroadcastPush(e,i,a){var l,c;const u=e.topic,d=(l=e.ref)!==null&&l!==void 0?l:"",m=(c=e.join_ref)!==null&&c!==void 0?c:"",p=e.payload.event,y=this.allowedMetadataKeys?this._pick(e.payload,this.allowedMetadataKeys):{},v=Object.keys(y).length===0?"":JSON.stringify(y);if(m.length>255)throw new Error(`joinRef length ${m.length} exceeds maximum of 255`);if(d.length>255)throw new Error(`ref length ${d.length} exceeds maximum of 255`);if(u.length>255)throw new Error(`topic length ${u.length} exceeds maximum of 255`);if(p.length>255)throw new Error(`userEvent length ${p.length} exceeds maximum of 255`);if(v.length>255)throw new Error(`metadata length ${v.length} exceeds maximum of 255`);const x=this.USER_BROADCAST_PUSH_META_LENGTH+m.length+d.length+u.length+p.length+v.length,_=new ArrayBuffer(this.HEADER_LENGTH+x);let S=new DataView(_),T=0;S.setUint8(T++,this.KINDS.userBroadcastPush),S.setUint8(T++,m.length),S.setUint8(T++,d.length),S.setUint8(T++,u.length),S.setUint8(T++,p.length),S.setUint8(T++,v.length),S.setUint8(T++,i),Array.from(m,C=>S.setUint8(T++,C.charCodeAt(0))),Array.from(d,C=>S.setUint8(T++,C.charCodeAt(0))),Array.from(u,C=>S.setUint8(T++,C.charCodeAt(0))),Array.from(p,C=>S.setUint8(T++,C.charCodeAt(0))),Array.from(v,C=>S.setUint8(T++,C.charCodeAt(0)));var k=new Uint8Array(_.byteLength+a.byteLength);return k.set(new Uint8Array(_),0),k.set(new Uint8Array(a),_.byteLength),k.buffer}decode(e,i){if(this._isArrayBuffer(e)){let a=this._binaryDecode(e);return i(a)}if(typeof e=="string"){const a=JSON.parse(e),[l,c,u,d,m]=a;return i({join_ref:l,ref:c,topic:u,event:d,payload:m})}return i({})}_binaryDecode(e){const i=new DataView(e),a=i.getUint8(0),l=new TextDecoder;switch(a){case this.KINDS.userBroadcast:return this._decodeUserBroadcast(e,i,l)}}_decodeUserBroadcast(e,i,a){const l=i.getUint8(1),c=i.getUint8(2),u=i.getUint8(3),d=i.getUint8(4);let m=this.HEADER_LENGTH+4;const p=a.decode(e.slice(m,m+l));m=m+l;const y=a.decode(e.slice(m,m+c));m=m+c;const v=a.decode(e.slice(m,m+u));m=m+u;const x=e.slice(m,e.byteLength),_=d===this.JSON_ENCODING?JSON.parse(a.decode(x)):x,S={type:this.BROADCAST_EVENT,event:y,payload:_};return u>0&&(S.meta=JSON.parse(v)),{join_ref:null,ref:null,topic:p,event:this.BROADCAST_EVENT,payload:S}}_isArrayBuffer(e){var i;return e instanceof ArrayBuffer||((i=e==null?void 0:e.constructor)===null||i===void 0?void 0:i.name)==="ArrayBuffer"}_pick(e,i){return!e||typeof e!="object"?{}:Object.fromEntries(Object.entries(e).filter(([a])=>i.includes(a)))}}var Xe;(function(n){n.abstime="abstime",n.bool="bool",n.date="date",n.daterange="daterange",n.float4="float4",n.float8="float8",n.int2="int2",n.int4="int4",n.int4range="int4range",n.int8="int8",n.int8range="int8range",n.json="json",n.jsonb="jsonb",n.money="money",n.numeric="numeric",n.oid="oid",n.reltime="reltime",n.text="text",n.time="time",n.timestamp="timestamp",n.timestamptz="timestamptz",n.timetz="timetz",n.tsrange="tsrange",n.tstzrange="tstzrange"})(Xe||(Xe={}));const Cb=(n,e,i={})=>{var a;const l=(a=i.skipTypes)!==null&&a!==void 0?a:[];return e?Object.keys(e).reduce((c,u)=>(c[u]=kN(u,n,e,l),c),{}):{}},kN=(n,e,i,a)=>{const l=e.find(d=>d.name===n),c=l==null?void 0:l.type,u=i[n];return c&&!a.includes(c)?bw(c,u):hf(u)},bw=(n,e)=>{if(n.charAt(0)==="_"){const i=n.slice(1,n.length);return DN(e,i)}switch(n){case Xe.bool:return CN(e);case Xe.float4:case Xe.float8:case Xe.int2:case Xe.int4:case Xe.int8:case Xe.numeric:case Xe.oid:return RN(e);case Xe.json:case Xe.jsonb:return ON(e);case Xe.timestamp:return MN(e);case Xe.abstime:case Xe.date:case Xe.daterange:case Xe.int4range:case Xe.int8range:case Xe.money:case Xe.reltime:case Xe.text:case Xe.time:case Xe.timestamptz:case Xe.timetz:case Xe.tsrange:case Xe.tstzrange:return hf(e);default:return hf(e)}},hf=n=>n,CN=n=>{switch(n){case"t":return!0;case"f":return!1;default:return n}},RN=n=>{if(typeof n=="string"){const e=parseFloat(n);if(!Number.isNaN(e))return e}return n},ON=n=>{if(typeof n=="string")try{return JSON.parse(n)}catch{return n}return n},DN=(n,e)=>{if(typeof n!="string")return n;const i=n.length-1,a=n[i];if(n[0]==="{"&&a==="}"){let c;const u=n.slice(1,i);try{c=JSON.parse("["+u+"]")}catch{c=u?u.split(","):[]}return c.map(d=>bw(e,d))}return n},MN=n=>typeof n=="string"?n.replace(" ","T"):n,vw=n=>{const e=new URL(n);return e.protocol=e.protocol.replace(/^ws/i,"http"),e.pathname=e.pathname.replace(/\/+$/,"").replace(/\/socket\/websocket$/i,"").replace(/\/socket$/i,"").replace(/\/websocket$/i,""),e.pathname===""||e.pathname==="/"?e.pathname="/api/broadcast":e.pathname=e.pathname+"/api/broadcast",e.href};var io=n=>typeof n=="function"?n:function(){return n},BN=typeof self<"u"?self:null,Ba=typeof window<"u"?window:null,Yn=BN||Ba||globalThis,UN="2.0.0",LN=1e4,zN=1e3,Jn={connecting:0,open:1,closing:2,closed:3},en={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},Ss={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},df={longpoll:"longpoll",websocket:"websocket"},VN={complete:4},ff="base64url.bearer.phx.",Zl=class{constructor(n,e,i,a){this.channel=n,this.event=e,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=a,this.timeoutTimer=null,this.recHooks=[],this.sent=!1,this.ref=void 0}resend(n){this.timeout=n,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()}))}receive(n,e){return this.hasReceived(n)&&e(this.receivedResp.response),this.recHooks.push({status:n,callback:e}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}destroy(){this.cancelRefEvent(),this.cancelTimeout()}matchReceive({status:n,response:e,_ref:i}){this.recHooks.filter(a=>a.status===n).forEach(a=>a.callback(e))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,n=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=n,this.matchReceive(n)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(n){return this.receivedResp&&this.receivedResp.status===n}trigger(n,e){this.channel.trigger(this.refEvent,{status:n,response:e})}},xw=class{constructor(n,e){this.callback=n,this.timerCalc=e,this.timer=void 0,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}},PN=class{constructor(n,e,i){this.state=en.closed,this.topic=n,this.params=io(e||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new Zl(this,Ss.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new xw(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=en.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(a=>a.send()),this.pushBuffer=[]}),this.joinPush.receive("error",a=>{this.state=en.errored,this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,a),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic}`),this.state=en.closed,this.socket.remove(this)}),this.onError(a=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,a),this.isJoining()&&this.joinPush.reset(),this.state=en.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic}`,this.joinPush.timeout),new Zl(this,Ss.leave,io({}),this.timeout).send(),this.state=en.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(Ss.reply,(a,l)=>{this.trigger(this.replyEventName(l),a)})}join(n=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=n,this.joinedOnce=!0,this.rejoin(),this.joinPush}teardown(){this.pushBuffer.forEach(n=>n.destroy()),this.pushBuffer=[],this.rejoinTimer.reset(),this.joinPush.destroy(),this.state=en.closed,this.bindings=[]}onClose(n){this.on(Ss.close,n)}onError(n){return this.on(Ss.error,e=>n(e))}on(n,e){let i=this.bindingRef++;return this.bindings.push({event:n,ref:i,callback:e}),i}off(n,e){this.bindings=this.bindings.filter(i=>!(i.event===n&&(typeof e>"u"||e===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(n,e,i=this.timeout){if(e=e||{},!this.joinedOnce)throw new Error(`tried to push '${n}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let a=new Zl(this,n,function(){return e},i);return this.canPush()?a.send():(a.startTimeout(),this.pushBuffer.push(a)),a}leave(n=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=en.leaving;let e=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(Ss.close,"leave")},i=new Zl(this,Ss.leave,io({}),n);return i.receive("ok",()=>e()).receive("timeout",()=>e()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(n,e,i){return e}filterBindings(n,e,i){return!0}isMember(n,e,i,a){return this.topic!==n?!1:a&&a!==this.joinRef()?(this.socket.hasLogger()&&this.socket.log("channel","dropping outdated message",{topic:n,event:e,payload:i,joinRef:a}),!1):!0}joinRef(){return this.joinPush.ref}rejoin(n=this.timeout){this.isLeaving()||(this.socket.leaveOpenTopic(this.topic),this.state=en.joining,this.joinPush.resend(n))}trigger(n,e,i,a){let l=this.onMessage(n,e,i,a);if(e&&!l)throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");let c=this.bindings.filter(u=>u.event===n&&this.filterBindings(u,e,i));for(let u=0;um.abort(),l),d.signal=m.signal),Yn.fetch(e,d).then(p=>p.text()).then(p=>this.parseJSON(p)).then(p=>u&&u(p)).catch(p=>{p.name==="AbortError"&&c?c():u&&u(null)}),m}static xdomainRequest(n,e,i,a,l,c,u){return n.timeout=l,n.open(e,i),n.onload=()=>{let d=this.parseJSON(n.responseText);u&&u(d)},c&&(n.ontimeout=c),n.onprogress=()=>{},n.send(a),n}static xhrRequest(n,e,i,a,l,c,u,d){n.open(e,i,!0),n.timeout=c;for(let[m,p]of Object.entries(a))n.setRequestHeader(m,p);return n.onerror=()=>d&&d(null),n.onreadystatechange=()=>{if(n.readyState===VN.complete&&d){let m=this.parseJSON(n.responseText);d(m)}},u&&(n.ontimeout=u),n.send(l),n}static parseJSON(n){if(!n||n==="")return null;try{return JSON.parse(n)}catch{return console&&console.log("failed to parse JSON response",n),null}}static serialize(n,e){let i=[];for(var a in n){if(!Object.prototype.hasOwnProperty.call(n,a))continue;let l=e?`${e}[${a}]`:a,c=n[a];typeof c=="object"?i.push(this.serialize(c,l)):i.push(encodeURIComponent(l)+"="+encodeURIComponent(c))}return i.join("&")}static appendParams(n,e){if(Object.keys(e).length===0)return n;let i=n.match(/\?/)?"&":"?";return`${n}${i}${this.serialize(e)}`}},HN=n=>{let e="",i=new Uint8Array(n),a=i.byteLength;for(let l=0;lthis.poll(),0)}normalizeEndpoint(n){return n.replace("ws://","http://").replace("wss://","https://").replace(new RegExp("(.*)/"+df.websocket),"$1/"+df.longpoll)}endpointURL(){return jc.appendParams(this.pollEndpoint,{token:this.token})}closeAndRetry(n,e,i){this.close(n,e,i),this.readyState=Jn.connecting}ontimeout(){this.onerror("timeout"),this.closeAndRetry(1005,"timeout",!1)}isActive(){return this.readyState===Jn.open||this.readyState===Jn.connecting}poll(){const n={Accept:"application/json"};this.authToken&&(n["X-Phoenix-AuthToken"]=this.authToken),this.ajax("GET",n,null,()=>this.ontimeout(),e=>{if(e){var{status:i,token:a,messages:l}=e;if(i===410&&this.token!==null){this.onerror(410),this.closeAndRetry(3410,"session_gone",!1);return}this.token=a}else i=0;switch(i){case 200:l.forEach(c=>{setTimeout(()=>this.onmessage({data:c}),0)}),this.poll();break;case 204:this.poll();break;case 410:this.readyState=Jn.open,this.onopen({}),this.poll();break;case 403:this.onerror(403),this.close(1008,"forbidden",!1);break;case 0:case 500:this.onerror(500),this.closeAndRetry(1011,"internal server error",500);break;default:throw new Error(`unhandled poll status ${i}`)}})}send(n){typeof n!="string"&&(n=HN(n)),this.currentBatch?this.currentBatch.push(n):this.awaitingBatchAck?this.batchBuffer.push(n):(this.currentBatch=[n],this.currentBatchTimer=setTimeout(()=>{this.batchSend(this.currentBatch),this.currentBatch=null},0))}batchSend(n){this.awaitingBatchAck=!0,this.ajax("POST",{"Content-Type":"application/x-ndjson"},n.join(` -`),()=>this.onerror("timeout"),e=>{this.awaitingBatchAck=!1,!e||e.status!==200?(this.onerror(e&&e.status),this.closeAndRetry(1011,"internal server error",!1)):this.batchBuffer.length>0&&(this.batchSend(this.batchBuffer),this.batchBuffer=[])})}close(n,e,i){for(let l of this.reqs)l.abort();this.readyState=Jn.closed;let a=Object.assign({code:1e3,reason:void 0,wasClean:!0},{code:n,reason:e,wasClean:i});this.batchBuffer=[],clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,typeof CloseEvent<"u"?this.onclose(new CloseEvent("close",a)):this.onclose(a)}ajax(n,e,i,a,l){let c,u=()=>{this.reqs.delete(c),a()};c=jc.request(n,this.endpointURL(),e,i,this.timeout,u,d=>{this.reqs.delete(c),this.isActive()&&l(d)}),this.reqs.add(c)}},$N=class Qr{constructor(e,i={}){let a=i.events||{state:"presence_state",diff:"presence_diff"};this.state={},this.pendingDiffs=[],this.channel=e,this.joinRef=null,this.caller={onJoin:function(){},onLeave:function(){},onSync:function(){}},this.channel.on(a.state,l=>{let{onJoin:c,onLeave:u,onSync:d}=this.caller;this.joinRef=this.channel.joinRef(),this.state=Qr.syncState(this.state,l,c,u),this.pendingDiffs.forEach(m=>{this.state=Qr.syncDiff(this.state,m,c,u)}),this.pendingDiffs=[],d()}),this.channel.on(a.diff,l=>{let{onJoin:c,onLeave:u,onSync:d}=this.caller;this.inPendingSyncState()?this.pendingDiffs.push(l):(this.state=Qr.syncDiff(this.state,l,c,u),d())})}onJoin(e){this.caller.onJoin=e}onLeave(e){this.caller.onLeave=e}onSync(e){this.caller.onSync=e}list(e){return Qr.list(this.state,e)}inPendingSyncState(){return!this.joinRef||this.joinRef!==this.channel.joinRef()}static syncState(e,i,a,l){let c=this.clone(e),u={},d={};return this.map(c,(m,p)=>{i[m]||(d[m]=p)}),this.map(i,(m,p)=>{let y=c[m];if(y){let v=p.metas.map(T=>T.phx_ref),x=y.metas.map(T=>T.phx_ref),_=p.metas.filter(T=>x.indexOf(T.phx_ref)<0),S=y.metas.filter(T=>v.indexOf(T.phx_ref)<0);_.length>0&&(u[m]=p,u[m].metas=_),S.length>0&&(d[m]=this.clone(y),d[m].metas=S)}else u[m]=p}),this.syncDiff(c,{joins:u,leaves:d},a,l)}static syncDiff(e,i,a,l){let{joins:c,leaves:u}=this.clone(i);return a||(a=function(){}),l||(l=function(){}),this.map(c,(d,m)=>{let p=e[d];if(e[d]=this.clone(m),p){let y=e[d].metas.map(x=>x.phx_ref),v=p.metas.filter(x=>y.indexOf(x.phx_ref)<0);e[d].metas.unshift(...v)}a(d,p,m)}),this.map(u,(d,m)=>{let p=e[d];if(!p)return;let y=m.metas.map(v=>v.phx_ref);p.metas=p.metas.filter(v=>y.indexOf(v.phx_ref)<0),l(d,p,m),p.metas.length===0&&delete e[d]}),e}static list(e,i){return i||(i=function(a,l){return l}),this.map(e,(a,l)=>i(a,l))}static map(e,i){return Object.getOwnPropertyNames(e).map(a=>i(a,e[a]))}static clone(e){return JSON.parse(JSON.stringify(e))}},Wl={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(n,e){if(n.payload.constructor===ArrayBuffer)return e(this.binaryEncode(n));{let i=[n.join_ref,n.ref,n.topic,n.event,n.payload];return e(JSON.stringify(i))}},decode(n,e){if(n.constructor===ArrayBuffer)return e(this.binaryDecode(n));{let[i,a,l,c,u]=JSON.parse(n);return e({join_ref:i,ref:a,topic:l,event:c,payload:u})}},binaryEncode(n){let{join_ref:e,ref:i,event:a,topic:l,payload:c}=n,u=this.META_LENGTH+e.length+i.length+l.length+a.length,d=new ArrayBuffer(this.HEADER_LENGTH+u),m=new DataView(d),p=0;m.setUint8(p++,this.KINDS.push),m.setUint8(p++,e.length),m.setUint8(p++,i.length),m.setUint8(p++,l.length),m.setUint8(p++,a.length),Array.from(e,v=>m.setUint8(p++,v.charCodeAt(0))),Array.from(i,v=>m.setUint8(p++,v.charCodeAt(0))),Array.from(l,v=>m.setUint8(p++,v.charCodeAt(0))),Array.from(a,v=>m.setUint8(p++,v.charCodeAt(0)));var y=new Uint8Array(d.byteLength+c.byteLength);return y.set(new Uint8Array(d),0),y.set(new Uint8Array(c),d.byteLength),y.buffer},binaryDecode(n){let e=new DataView(n),i=e.getUint8(0),a=new TextDecoder;switch(i){case this.KINDS.push:return this.decodePush(n,e,a);case this.KINDS.reply:return this.decodeReply(n,e,a);case this.KINDS.broadcast:return this.decodeBroadcast(n,e,a)}},decodePush(n,e,i){let a=e.getUint8(1),l=e.getUint8(2),c=e.getUint8(3),u=this.HEADER_LENGTH+this.META_LENGTH-1,d=i.decode(n.slice(u,u+a));u=u+a;let m=i.decode(n.slice(u,u+l));u=u+l;let p=i.decode(n.slice(u,u+c));u=u+c;let y=n.slice(u,n.byteLength);return{join_ref:d,ref:null,topic:m,event:p,payload:y}},decodeReply(n,e,i){let a=e.getUint8(1),l=e.getUint8(2),c=e.getUint8(3),u=e.getUint8(4),d=this.HEADER_LENGTH+this.META_LENGTH,m=i.decode(n.slice(d,d+a));d=d+a;let p=i.decode(n.slice(d,d+l));d=d+l;let y=i.decode(n.slice(d,d+c));d=d+c;let v=i.decode(n.slice(d,d+u));d=d+u;let x=n.slice(d,n.byteLength),_={status:v,response:x};return{join_ref:m,ref:p,topic:y,event:Ss.reply,payload:_}},decodeBroadcast(n,e,i){let a=e.getUint8(1),l=e.getUint8(2),c=this.HEADER_LENGTH+2,u=i.decode(n.slice(c,c+a));c=c+a;let d=i.decode(n.slice(c,c+l));c=c+l;let m=n.slice(c,n.byteLength);return{join_ref:null,ref:null,topic:u,event:d,payload:m}}},qN=class{constructor(n,e={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.timeout=e.timeout||LN,this.transport=e.transport||Yn.WebSocket||ja,this.conn=void 0,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=e.longPollFallbackMs,this.fallbackTimer=null;let i=null;try{i=Yn&&Yn.sessionStorage}catch{}this.sessionStore=e.sessionStorage||i,this.establishedConnections=0,this.defaultEncoder=Wl.encode.bind(Wl),this.defaultDecoder=Wl.decode.bind(Wl),this.closeWasClean=!0,this.disconnecting=!1,this.binaryType=e.binaryType||"arraybuffer",this.connectClock=1,this.pageHidden=!1,this.encode=void 0,this.decode=void 0,this.transport!==ja?(this.encode=e.encode||this.defaultEncoder,this.decode=e.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let a=null;Ba&&Ba.addEventListener&&(Ba.addEventListener("pagehide",l=>{this.conn&&(this.disconnect(),a=this.connectClock)}),Ba.addEventListener("pageshow",l=>{a===this.connectClock&&(a=null,this.connect())}),Ba.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.pageHidden=!0:(this.pageHidden=!1,!this.isConnected()&&!this.closeWasClean&&this.teardown(()=>this.connect()))})),this.heartbeatIntervalMs=e.heartbeatIntervalMs||3e4,this.autoSendHeartbeat=e.autoSendHeartbeat??!0,this.heartbeatCallback=e.heartbeatCallback??(()=>{}),this.rejoinAfterMs=l=>e.rejoinAfterMs?e.rejoinAfterMs(l):[1e3,2e3,5e3][l-1]||1e4,this.reconnectAfterMs=l=>e.reconnectAfterMs?e.reconnectAfterMs(l):[10,50,100,150,200,250,500,1e3,2e3][l-1]||5e3,this.logger=e.logger||null,!this.logger&&e.debug&&(this.logger=(l,c,u)=>{console.log(`${l}: ${c}`,u)}),this.longpollerTimeout=e.longpollerTimeout||2e4,this.params=io(e.params||{}),this.endPoint=`${n}/${df.websocket}`,this.vsn=e.vsn||UN,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.heartbeatSentAt=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new xw(()=>{if(this.pageHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardown(async()=>{e.beforeReconnect&&await e.beforeReconnect(),this.connect()})},this.reconnectAfterMs),this.authToken=e.authToken}getLongPollTransport(){return ja}replaceTransport(n){this.connectClock++,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.conn&&(this.conn.close(),this.conn=null),this.transport=n}protocol(){return location.protocol.match(/^https/)?"wss":"ws"}endPointURL(){let n=jc.appendParams(jc.appendParams(this.endPoint,this.params()),{vsn:this.vsn});return n.charAt(0)!=="/"?n:n.charAt(1)==="/"?`${this.protocol()}:${n}`:`${this.protocol()}://${location.host}${n}`}disconnect(n,e,i){this.connectClock++,this.disconnecting=!0,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.teardown(()=>{this.disconnecting=!1,n&&n()},e,i)}connect(n){n&&(console&&console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"),this.params=io(n)),!(this.conn&&!this.disconnecting)&&(this.longPollFallbackMs&&this.transport!==ja?this.connectWithFallback(ja,this.longPollFallbackMs):this.transportConnect())}log(n,e,i){this.logger&&this.logger(n,e,i)}hasLogger(){return this.logger!==null}onOpen(n){let e=this.makeRef();return this.stateChangeCallbacks.open.push([e,n]),e}onClose(n){let e=this.makeRef();return this.stateChangeCallbacks.close.push([e,n]),e}onError(n){let e=this.makeRef();return this.stateChangeCallbacks.error.push([e,n]),e}onMessage(n){let e=this.makeRef();return this.stateChangeCallbacks.message.push([e,n]),e}onHeartbeat(n){this.heartbeatCallback=n}ping(n){if(!this.isConnected())return!1;let e=this.makeRef(),i=Date.now();this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:e});let a=this.onMessage(l=>{l.ref===e&&(this.off([a]),n(Date.now()-i))});return!0}transportName(n){switch(n){case ja:return"LongPoll";default:return n.name}}transportConnect(){this.connectClock++,this.closeWasClean=!1;let n;this.authToken&&(n=["phoenix",`${ff}${btoa(this.authToken).replace(/=/g,"")}`]),this.conn=new this.transport(this.endPointURL(),n),this.conn.binaryType=this.binaryType,this.conn.timeout=this.longpollerTimeout,this.conn.onopen=()=>this.onConnOpen(),this.conn.onerror=e=>this.onConnError(e),this.conn.onmessage=e=>this.onConnMessage(e),this.conn.onclose=e=>this.onConnClose(e)}getSession(n){return this.sessionStore&&this.sessionStore.getItem(n)}storeSession(n,e){this.sessionStore&&this.sessionStore.setItem(n,e)}connectWithFallback(n,e=2500){clearTimeout(this.fallbackTimer);let i=!1,a=!0,l,c,u=this.transportName(n),d=m=>{this.log("transport",`falling back to ${u}...`,m),this.off([l,c]),a=!1,this.replaceTransport(n),this.transportConnect()};if(this.getSession(`phx:fallback:${u}`))return d("memorized");this.fallbackTimer=setTimeout(d,e),c=this.onError(m=>{this.log("transport","error",m),a&&!i&&(clearTimeout(this.fallbackTimer),d(m))}),this.fallbackRef&&this.off([this.fallbackRef]),this.fallbackRef=this.onOpen(()=>{if(i=!0,!a){let m=this.transportName(n);return this.primaryPassedHealthCheck||this.storeSession(`phx:fallback:${m}`,"true"),this.log("transport",`established ${m} fallback`)}clearTimeout(this.fallbackTimer),this.fallbackTimer=setTimeout(d,e),this.ping(m=>{this.log("transport","connected to primary after",m),this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer)})}),this.transportConnect()}clearHeartbeats(){clearTimeout(this.heartbeatTimer),clearTimeout(this.heartbeatTimeoutTimer)}onConnOpen(){this.hasLogger()&&this.log("transport",`connected to ${this.endPointURL()}`),this.closeWasClean=!1,this.disconnecting=!1,this.establishedConnections++,this.flushSendBuffer(),this.reconnectTimer.reset(),this.autoSendHeartbeat&&this.resetHeartbeat(),this.triggerStateCallbacks("open")}heartbeatTimeout(){if(this.pendingHeartbeatRef){this.pendingHeartbeatRef=null,this.heartbeatSentAt=null,this.hasLogger()&&this.log("transport","heartbeat timeout. Attempting to re-establish connection");try{this.heartbeatCallback("timeout")}catch(n){this.log("error","error in heartbeat callback",n)}this.triggerChanError(new Error("heartbeat timeout")),this.closeWasClean=!1,this.teardown(()=>this.reconnectTimer.scheduleTimeout(),zN,"heartbeat timeout")}}resetHeartbeat(){this.conn&&this.conn.skipHeartbeat||(this.pendingHeartbeatRef=null,this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}teardown(n,e,i){if(!this.conn)return n&&n();const a=this.conn;this.waitForBufferDone(a,()=>{e?a.close(e,i||""):a.close(),this.waitForSocketClosed(a,()=>{this.conn===a&&(this.conn.onopen=function(){},this.conn.onerror=function(){},this.conn.onmessage=function(){},this.conn.onclose=function(){},this.conn=null),n&&n()})})}waitForBufferDone(n,e,i=1){if(i===5||!n.bufferedAmount){e();return}setTimeout(()=>{this.waitForBufferDone(n,e,i+1)},150*i)}waitForSocketClosed(n,e,i=1){if(i===5||n.readyState===Jn.closed){e();return}setTimeout(()=>{this.waitForSocketClosed(n,e,i+1)},150*i)}onConnClose(n){this.conn&&(this.conn.onclose=()=>{}),this.hasLogger()&&this.log("transport","close",n),this.triggerChanError(n),this.clearHeartbeats(),this.closeWasClean||this.reconnectTimer.scheduleTimeout(),this.triggerStateCallbacks("close",n)}onConnError(n){this.hasLogger()&&this.log("transport","error",n);let e=this.transport,i=this.establishedConnections;this.triggerStateCallbacks("error",n,e,i),(e===this.transport||i>0)&&this.triggerChanError(n)}triggerChanError(n){this.channels.forEach(e=>{e.isErrored()||e.isLeaving()||e.isClosed()||e.trigger(Ss.error,n)})}connectionState(){switch(this.conn&&this.conn.readyState){case Jn.connecting:return"connecting";case Jn.open:return"open";case Jn.closing:return"closing";default:return"closed"}}isConnected(){return this.connectionState()==="open"}remove(n){this.off(n.stateChangeRefs),this.channels=this.channels.filter(e=>e!==n)}off(n){for(let e in this.stateChangeCallbacks)this.stateChangeCallbacks[e]=this.stateChangeCallbacks[e].filter(([i])=>n.indexOf(i)===-1)}channel(n,e={}){let i=new PN(n,e,this);return this.channels.push(i),i}push(n){if(this.hasLogger()){let{topic:e,event:i,payload:a,ref:l,join_ref:c}=n;this.log("push",`${e} ${i} (${c}, ${l})`,a)}this.isConnected()?this.encode(n,e=>this.conn.send(e)):this.sendBuffer.push(()=>this.encode(n,e=>this.conn.send(e)))}makeRef(){let n=this.ref+1;return n===this.ref?this.ref=0:this.ref=n,this.ref.toString()}sendHeartbeat(){if(!this.isConnected()){try{this.heartbeatCallback("disconnected")}catch(n){this.log("error","error in heartbeat callback",n)}return}if(this.pendingHeartbeatRef){this.heartbeatTimeout();return}this.pendingHeartbeatRef=this.makeRef(),this.heartbeatSentAt=Date.now(),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:this.pendingHeartbeatRef});try{this.heartbeatCallback("sent")}catch(n){this.log("error","error in heartbeat callback",n)}this.heartbeatTimeoutTimer=setTimeout(()=>this.heartbeatTimeout(),this.heartbeatIntervalMs)}flushSendBuffer(){this.isConnected()&&this.sendBuffer.length>0&&(this.sendBuffer.forEach(n=>n()),this.sendBuffer=[])}onConnMessage(n){this.decode(n.data,e=>{let{topic:i,event:a,payload:l,ref:c,join_ref:u}=e;if(c&&c===this.pendingHeartbeatRef){const d=this.heartbeatSentAt?Date.now()-this.heartbeatSentAt:void 0;this.clearHeartbeats();try{this.heartbeatCallback(l.status==="ok"?"ok":"error",d)}catch(m){this.log("error","error in heartbeat callback",m)}this.pendingHeartbeatRef=null,this.heartbeatSentAt=null,this.autoSendHeartbeat&&(this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}this.hasLogger()&&this.log("receive",`${l.status||""} ${i} ${a} ${c&&"("+c+")"||""}`.trim(),l);for(let d=0;d{try{a(...e)}catch(l){this.log("error",`error in ${n} callback`,l)}})}catch(i){this.log("error",`error triggering ${n} callbacks`,i)}}leaveOpenTopic(n){let e=this.channels.find(i=>i.topic===n&&(i.isJoined()||i.isJoining()));e&&(this.hasLogger()&&this.log("transport",`leaving duplicate topic "${n}"`),e.leave())}};class ao{constructor(e,i){const a=IN(i);this.presence=new $N(e.getChannel(),a),this.presence.onJoin((l,c,u)=>{const d=ao.onJoinPayload(l,c,u);e.getChannel().trigger("presence",d)}),this.presence.onLeave((l,c,u)=>{const d=ao.onLeavePayload(l,c,u);e.getChannel().trigger("presence",d)}),this.presence.onSync(()=>{e.getChannel().trigger("presence",{event:"sync"})})}get state(){return ao.transformState(this.presence.state)}static transformState(e){return e=FN(e),Object.getOwnPropertyNames(e).reduce((i,a)=>{const l=e[a];return i[a]=pc(l),i},{})}static onJoinPayload(e,i,a){const l=Rb(i),c=pc(a);return{event:"join",key:e,currentPresences:l,newPresences:c}}static onLeavePayload(e,i,a){const l=Rb(i),c=pc(a);return{event:"leave",key:e,currentPresences:l,leftPresences:c}}}function pc(n){return n.metas.map(e=>(e.presence_ref=e.phx_ref,delete e.phx_ref,delete e.phx_ref_prev,e))}function FN(n){return JSON.parse(JSON.stringify(n))}function IN(n){return(n==null?void 0:n.events)&&{events:n.events}}function Rb(n){return n!=null&&n.metas?pc(n):[]}var Ob;(function(n){n.SYNC="sync",n.JOIN="join",n.LEAVE="leave"})(Ob||(Ob={}));class GN{get state(){return this.presenceAdapter.state}constructor(e,i){this.channel=e,this.presenceAdapter=new ao(this.channel.channelAdapter,i)}}function KN(n){if(n instanceof Error)return n;if(typeof n=="string")return new Error(n);if(n&&typeof n=="object"){const e=n;if(typeof e.code=="number"){const i=typeof e.reason=="string"&&e.reason?` (${e.reason})`:"";return new Error(`socket closed: ${e.code}${i}`,{cause:n})}return new Error("channel error: transport failure",{cause:n})}return new Error("channel error: connection lost")}class YN{constructor(e,i,a){const l=XN(a);this.channel=e.getSocket().channel(i,l),this.socket=e}get state(){return this.channel.state}set state(e){this.channel.state=e}get joinedOnce(){return this.channel.joinedOnce}get joinPush(){return this.channel.joinPush}get rejoinTimer(){return this.channel.rejoinTimer}on(e,i){return this.channel.on(e,i)}off(e,i){this.channel.off(e,i)}subscribe(e){return this.channel.join(e)}unsubscribe(e){return this.channel.leave(e)}teardown(){this.channel.teardown()}onClose(e){this.channel.onClose(e)}onError(e){return this.channel.onError(e)}push(e,i,a){let l;try{l=this.channel.push(e,i,a)}catch{throw new Error(`tried to push '${e}' to '${this.channel.topic}' before joining. Use channel.subscribe() before pushing events`)}if(this.channel.pushBuffer.length>jN){const c=this.channel.pushBuffer.shift();c.cancelTimeout(),this.socket.log("channel",`discarded push due to buffer overflow: ${c.event}`,c.payload())}return l}updateJoinPayload(e){const i=this.channel.joinPush.payload();this.channel.joinPush.payload=()=>Object.assign(Object.assign({},i),e)}canPush(){return this.socket.isConnected()&&this.state===ti.joined}isJoined(){return this.state===ti.joined}isJoining(){return this.state===ti.joining}isClosed(){return this.state===ti.closed}isLeaving(){return this.state===ti.leaving}updateFilterBindings(e){this.channel.filterBindings=e}updatePayloadTransform(e){this.channel.onMessage=e}getChannel(){return this.channel}}function XN(n){return{config:Object.assign({broadcast:{ack:!1,self:!1},presence:{key:"",enabled:!1},private:!1},n.config)}}const JN=/[,()"\\]/,QN=n=>JN.test(n)||n!==n.trim(),ZN=n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`,Db=n=>{const e=n===null?"null":String(n);return QN(e)?ZN(e):e},WN=n=>n===null?"null":String(n),ek=(n,e)=>{if(n==="in"){const i=Array.isArray(e)?e:[e];if(i.length===0)throw new Error("Realtime `in` filter requires at least one value.");return`in.(${Array.from(new Set(i)).map(l=>Db(l)).join(",")})`}return n==="is"?`is.${WN(e)}`:`${n}.${Db(e)}`};class tk{constructor(){this.filters=[]}add(e,i,a,l=!1){const c=l?"not.":"";return this.filters.push(`${e}=${c}${ek(i,a)}`),this}eq(e,i){return this.add(e,"eq",i)}neq(e,i){return this.add(e,"neq",i)}gt(e,i){return this.add(e,"gt",i)}gte(e,i){return this.add(e,"gte",i)}lt(e,i){return this.add(e,"lt",i)}lte(e,i){return this.add(e,"lte",i)}in(e,i){return this.add(e,"in",i)}like(e,i){return this.add(e,"like",i)}ilike(e,i){return this.add(e,"ilike",i)}match(e,i){return this.add(e,"match",i)}imatch(e,i){return this.add(e,"imatch",i)}is(e,i){return this.add(e,"is",i)}isDistinct(e,i){return this.add(e,"isdistinct",i)}not(e,i,a){return this.add(e,i,a,!0)}build(){return this.filters.join(",")}toString(){return this.build()}}var Mb;(function(n){n.ALL="*",n.INSERT="INSERT",n.UPDATE="UPDATE",n.DELETE="DELETE"})(Mb||(Mb={}));var Ha;(function(n){n.BROADCAST="broadcast",n.PRESENCE="presence",n.POSTGRES_CHANGES="postgres_changes",n.SYSTEM="system"})(Ha||(Ha={}));var Ts;(function(n){n.SUBSCRIBED="SUBSCRIBED",n.TIMED_OUT="TIMED_OUT",n.CLOSED="CLOSED",n.CHANNEL_ERROR="CHANNEL_ERROR"})(Ts||(Ts={}));class ro{get state(){return this.channelAdapter.state}set state(e){this.channelAdapter.state=e}get joinedOnce(){return this.channelAdapter.joinedOnce}get timeout(){return this.socket.timeout}get joinPush(){return this.channelAdapter.joinPush}get rejoinTimer(){return this.channelAdapter.rejoinTimer}constructor(e,i={config:{}},a){var l,c;if(this.topic=e,this.params=i,this.socket=a,this.bindings={},this.subTopic=e.replace(/^realtime:/i,""),this.params.config=Object.assign({broadcast:{ack:!1,self:!1},presence:{key:"",enabled:!1},private:!1},i.config),this.channelAdapter=new YN(this.socket.socketAdapter,e,this.params),this.presence=new GN(this),this._onClose(()=>{this.socket._remove(this)}),this._updateFilterTransform(),this.broadcastEndpointURL=vw(this.socket.socketAdapter.endPointURL()),this.private=this.params.config.private||!1,!this.private&&(!((c=(l=this.params.config)===null||l===void 0?void 0:l.broadcast)===null||c===void 0)&&c.replay))throw new Error(`tried to use replay on public channel '${this.topic}'. It must be a private channel.`)}subscribe(e,i=this.timeout){var a,l,c;if(this.socket.isConnected()||this.socket.connect(),this.channelAdapter.isClosed()){const{config:{broadcast:u,presence:d,private:m}}=this.params,p=(l=(a=this.bindings.postgres_changes)===null||a===void 0?void 0:a.map(_=>_.filter))!==null&&l!==void 0?l:[],y=!!this.bindings[Ha.PRESENCE]&&this.bindings[Ha.PRESENCE].length>0||((c=this.params.config.presence)===null||c===void 0?void 0:c.enabled)===!0,v={},x={broadcast:u,presence:Object.assign(Object.assign({},d),{enabled:y}),postgres_changes:p,private:m};this.socket.accessTokenValue&&(v.access_token=this.socket.accessTokenValue),this._onError(_=>{e==null||e(Ts.CHANNEL_ERROR,KN(_))}),this._onClose(()=>e==null?void 0:e(Ts.CLOSED)),this.updateJoinPayload(Object.assign({config:x},v)),this._updateFilterMessage(),this.channelAdapter.subscribe(i).receive("ok",async({postgres_changes:_})=>{if(this.socket._isManualToken()||this.socket.setAuth(),_===void 0){e==null||e(Ts.SUBSCRIBED);return}this._updatePostgresBindings(_,e)}).receive("error",_=>{this.state=ti.errored;const S=Object.values(_).join(", ")||"error";e==null||e(Ts.CHANNEL_ERROR,new Error(S,{cause:_}))}).receive("timeout",()=>{e==null||e(Ts.TIMED_OUT)})}return this}_updatePostgresBindings(e,i){var a;const l=this.bindings.postgres_changes,c=(a=l==null?void 0:l.length)!==null&&a!==void 0?a:0,u=[];for(let d=0;d{var u,d,m;const p=this.channelAdapter.push(e.type,e,i.timeout||this.timeout);e.type==="broadcast"&&!(!((m=(d=(u=this.params)===null||u===void 0?void 0:u.config)===null||d===void 0?void 0:d.broadcast)===null||m===void 0)&&m.ack)&&c("ok"),p.receive("ok",()=>c("ok")),p.receive("error",()=>c("error")),p.receive("timeout",()=>c("timed out"))})}updateJoinPayload(e){this.channelAdapter.updateJoinPayload(e)}async unsubscribe(e=this.timeout){return new Promise(i=>{this.channelAdapter.unsubscribe(e).receive("ok",()=>i("ok")).receive("timeout",()=>i("timed out")).receive("error",()=>i("error"))})}teardown(){this.channelAdapter.teardown()}async _fetchWithTimeout(e,i,a){const l=new AbortController,c=setTimeout(()=>l.abort(),a),u=await this.socket.fetch(e,Object.assign(Object.assign({},i),{signal:l.signal}));return clearTimeout(c),u}_on(e,i,a){const l=e.toLocaleLowerCase(),c=i==null?void 0:i.filter;(c instanceof tk||typeof c=="object"&&c!==null&&typeof c.build=="function")&&(i=Object.assign(Object.assign({},i),{filter:c.build()}));const u=this.channelAdapter.on(e,a),d={type:l,filter:i,callback:a,ref:u};return this.bindings[l]?this.bindings[l].push(d):this.bindings[l]=[d],this._updateFilterMessage(),this}_onClose(e){this.channelAdapter.onClose(e)}_onError(e){this.channelAdapter.onError(e)}_updateFilterMessage(){this.channelAdapter.updateFilterBindings((e,i,a)=>{var l,c,u,d,m,p,y;const v=e.event.toLocaleLowerCase();if(this._notThisChannelEvent(v,a))return!1;const x=(l=this.bindings[v])===null||l===void 0?void 0:l.find(_=>_.ref===e.ref);if(!x)return!0;if(["broadcast","presence","postgres_changes"].includes(v))if("id"in x){const _=x.id,S=(c=x.filter)===null||c===void 0?void 0:c.event;return _&&((u=i.ids)===null||u===void 0?void 0:u.includes(_))&&(S==="*"||(S==null?void 0:S.toLocaleLowerCase())===((d=i.data)===null||d===void 0?void 0:d.type.toLocaleLowerCase()))}else{const _=(p=(m=x==null?void 0:x.filter)===null||m===void 0?void 0:m.event)===null||p===void 0?void 0:p.toLocaleLowerCase();return _==="*"||_===((y=i==null?void 0:i.event)===null||y===void 0?void 0:y.toLocaleLowerCase())}else return x.type.toLocaleLowerCase()===v})}_notThisChannelEvent(e,i){const{close:a,error:l,leave:c,join:u}=yw;return i&&[a,l,c,u].includes(e)&&i!==this.joinPush.ref}_updateFilterTransform(){this.channelAdapter.updatePayloadTransform((e,i,a)=>{if(typeof i=="object"&&"ids"in i){const l=i.data,{schema:c,table:u,commit_timestamp:d,type:m,errors:p}=l;return Object.assign(Object.assign({},{schema:c,table:u,commit_timestamp:d,eventType:m,new:{},old:{},errors:p}),this._getPayloadRecords(l))}return i})}copyBindings(e){if(this.joinedOnce)throw new Error("cannot copy bindings into joined channel");for(const i in e.bindings)for(const a of e.bindings[i])this._on(a.type,a.filter,a.callback)}static isFilterValueEqual(e,i){return(e??void 0)===(i??void 0)}_getPayloadRecords(e){const i={new:{},old:{}};return(e.type==="INSERT"||e.type==="UPDATE")&&(i.new=Cb(e.columns,e.record)),(e.type==="UPDATE"||e.type==="DELETE")&&(i.old=Cb(e.columns,e.old_record)),i}}class nk{constructor(e,i){this.socket=new qN(e,i)}get timeout(){return this.socket.timeout}get endPoint(){return this.socket.endPoint}get transport(){return this.socket.transport}get heartbeatIntervalMs(){return this.socket.heartbeatIntervalMs}get heartbeatCallback(){return this.socket.heartbeatCallback}set heartbeatCallback(e){this.socket.heartbeatCallback=e}get heartbeatTimer(){return this.socket.heartbeatTimer}get pendingHeartbeatRef(){return this.socket.pendingHeartbeatRef}get reconnectTimer(){return this.socket.reconnectTimer}get vsn(){return this.socket.vsn}get encode(){return this.socket.encode}get decode(){return this.socket.decode}get reconnectAfterMs(){return this.socket.reconnectAfterMs}get sendBuffer(){return this.socket.sendBuffer}get stateChangeCallbacks(){return this.socket.stateChangeCallbacks}connect(){this.socket.connect()}disconnect(e,i,a,l=1e4){return new Promise(c=>{setTimeout(()=>c("timeout"),l),this.socket.disconnect(()=>{e(),c("ok")},i,a)})}push(e){this.socket.push(e)}log(e,i,a){this.socket.log(e,i,a)}makeRef(){return this.socket.makeRef()}onOpen(e){this.socket.onOpen(e)}onClose(e){this.socket.onClose(e)}onError(e){this.socket.onError(e)}onMessage(e){this.socket.onMessage(e)}isConnected(){return this.socket.isConnected()}isConnecting(){return this.socket.connectionState()==uf.connecting}isDisconnecting(){return this.socket.connectionState()==uf.closing}connectionState(){return this.socket.connectionState()}endPointURL(){return this.socket.endPointURL()}sendHeartbeat(){this.socket.sendHeartbeat()}getSocket(){return this.socket}}const Bb={HEARTBEAT_INTERVAL:25e3},sk=[1e3,2e3,5e3,1e4],ik=1e4;function ak(){const n=new Map;return{get length(){return n.size},clear(){n.clear()},getItem(e){return n.has(e)?n.get(e):null},key(e){var i;return(i=Array.from(n.keys())[e])!==null&&i!==void 0?i:null},removeItem(e){n.delete(e)},setItem(e,i){n.set(e,String(i))}}}function rk(){try{if(typeof globalThis<"u"&&globalThis.sessionStorage)return globalThis.sessionStorage}catch{}return ak()}const ok=` - addEventListener("message", (e) => { - if (e.data.event === "start") { - setInterval(() => postMessage({ event: "keepAlive" }), e.data.interval); - } - });`;class lk{get endPoint(){return this.socketAdapter.endPoint}get timeout(){return this.socketAdapter.timeout}get transport(){return this.socketAdapter.transport}get heartbeatCallback(){return this.socketAdapter.heartbeatCallback}get heartbeatIntervalMs(){return this.socketAdapter.heartbeatIntervalMs}get heartbeatTimer(){return this.worker?this._workerHeartbeatTimer:this.socketAdapter.heartbeatTimer}get pendingHeartbeatRef(){return this.worker?this._pendingWorkerHeartbeatRef:this.socketAdapter.pendingHeartbeatRef}get reconnectTimer(){return this.socketAdapter.reconnectTimer}get vsn(){return this.socketAdapter.vsn}get encode(){return this.socketAdapter.encode}get decode(){return this.socketAdapter.decode}get reconnectAfterMs(){return this.socketAdapter.reconnectAfterMs}get sendBuffer(){return this.socketAdapter.sendBuffer}get stateChangeCallbacks(){return this.socketAdapter.stateChangeCallbacks}constructor(e,i){var a;if(this.channels=new Array,this.accessTokenValue=null,this.accessToken=null,this.apiKey=null,this.httpEndpoint="",this.headers={},this.params={},this.ref=0,this.serializer=new NN,this._manuallySetToken=!1,this._authPromise=null,this._workerHeartbeatTimer=void 0,this._pendingWorkerHeartbeatRef=null,this._pendingDisconnectTimer=null,this._disconnectOnEmptyChannelsAfterMs=0,this._resolveFetch=c=>c?(...u)=>c(...u):(...u)=>fetch(...u),!(!((a=i==null?void 0:i.params)===null||a===void 0)&&a.apikey))throw new Error("API key is required to connect to Realtime");this.apiKey=i.params.apikey;const l=this._initializeOptions(i);this.socketAdapter=new nk(e,l),this.httpEndpoint=vw(e),this.fetch=this._resolveFetch(i==null?void 0:i.fetch)}connect(){if(!(this.isConnecting()||this.isDisconnecting()||this.isConnected())){this.accessToken&&!this._authPromise&&this._setAuthSafely("connect"),this._setupConnectionHandlers();try{this.socketAdapter.connect()}catch(e){const i=e.message;throw new Error(`WebSocket not available: ${i}`)}this._handleNodeJsRaceCondition()}}endpointURL(){return this.socketAdapter.endPointURL()}async disconnect(e,i){return this._cancelPendingDisconnect(),this.isDisconnecting()?"ok":await this.socketAdapter.disconnect(()=>{clearInterval(this._workerHeartbeatTimer),this._terminateWorker()},e,i)}getChannels(){return this.channels}async removeChannel(e){const i=await e.unsubscribe();return i==="ok"&&e.teardown(),i}async removeAllChannels(){const e=this.channels.map(async a=>{const l=await a.unsubscribe();return a.teardown(),l}),i=await Promise.all(e);return await this.disconnect(),i}log(e,i,a){this.socketAdapter.log(e,i,a)}connectionState(){return this.socketAdapter.connectionState()||uf.closed}isConnected(){return this.socketAdapter.isConnected()}isConnecting(){return this.socketAdapter.isConnecting()}isDisconnecting(){return this.socketAdapter.isDisconnecting()}channel(e,i={config:{}}){const a=`realtime:${e}`,l=this.getChannels().find(c=>c.topic===a);if(l)return l;{const c=new ro(`realtime:${e}`,i,this);return this._cancelPendingDisconnect(),this.channels.push(c),c}}push(e){this.socketAdapter.push(e)}async setAuth(e=null){this._authPromise=this._performAuth(e);try{await this._authPromise}finally{this._authPromise=null}}_isManualToken(){return this._manuallySetToken}async sendHeartbeat(){this.socketAdapter.sendHeartbeat()}onHeartbeat(e){this.socketAdapter.heartbeatCallback=this._wrapHeartbeatCallback(e)}_makeRef(){return this.socketAdapter.makeRef()}_remove(e){this.channels=this.channels.filter(i=>i.topic!==e.topic),this.channels.length===0&&(this.log("transport","no channels remaining, scheduling disconnect"),this._schedulePendingDisconnect())}_schedulePendingDisconnect(){if(this._cancelPendingDisconnect(),this._disconnectOnEmptyChannelsAfterMs===0){this.log("transport","disconnecting immediately - no channels"),this.disconnect();return}this._pendingDisconnectTimer=setTimeout(()=>{this._pendingDisconnectTimer=null,this.channels.length===0&&(this.log("transport","deferred disconnect fired - no channels, disconnecting"),this.disconnect())},this._disconnectOnEmptyChannelsAfterMs),this.log("transport",`deferred disconnect scheduled in ${this._disconnectOnEmptyChannelsAfterMs}ms`)}_cancelPendingDisconnect(){this._pendingDisconnectTimer!==null&&(this.log("transport","pending disconnect cancelled - channel activity detected"),clearTimeout(this._pendingDisconnectTimer),this._pendingDisconnectTimer=null)}async _performAuth(e=null){let i,a=!1;if(e)i=e,a=!0;else if(this.accessToken)try{i=await this.accessToken()}catch(l){this.log("error","Error fetching access token from callback",l),i=this.accessTokenValue}else i=this.accessTokenValue;a?this._manuallySetToken=!0:this.accessToken&&(this._manuallySetToken=!1),this.accessTokenValue!=i&&(this.accessTokenValue=i,this.channels.forEach(l=>{const c={access_token:i,version:SN};i&&l.updateJoinPayload(c),l.joinedOnce&&l.channelAdapter.isJoined()&&l.channelAdapter.push(yw.access_token,{access_token:i})}))}async _waitForAuthIfNeeded(){this._authPromise&&await this._authPromise}_setAuthSafely(e="general"){this._isManualToken()||this.setAuth().catch(i=>{this.log("error",`Error setting auth in ${e}`,i)})}_setupConnectionHandlers(){this.socketAdapter.onOpen(()=>{(this._authPromise||(this.accessToken&&!this.accessTokenValue?this.setAuth():Promise.resolve())).catch(i=>{this.log("error","error waiting for auth on connect",i)}),this.worker&&!this.workerRef&&this._startWorkerHeartbeat()}),this.socketAdapter.onClose(()=>{this.worker&&this.workerRef&&this._terminateWorker()}),this.socketAdapter.onMessage(e=>{e.ref&&e.ref===this._pendingWorkerHeartbeatRef&&(this._pendingWorkerHeartbeatRef=null)})}_handleNodeJsRaceCondition(){this.socketAdapter.isConnected()&&this.socketAdapter.getSocket().onConnOpen()}_wrapHeartbeatCallback(e){return(i,a)=>{i!=="disconnected"&&(i=="sent"&&this._setAuthSafely(),e&&e(i,a))}}_startWorkerHeartbeat(){this.workerUrl?this.log("worker",`starting worker for from ${this.workerUrl}`):this.log("worker","starting default worker");const e=this._workerObjectUrl(this.workerUrl);this.workerRef=new Worker(e),this.workerRef.onerror=i=>{this.log("worker","worker error",i.message),this._terminateWorker(),this.disconnect()},this.workerRef.onmessage=i=>{i.data.event==="keepAlive"&&this.sendHeartbeat()},this.workerRef.postMessage({event:"start",interval:this.heartbeatIntervalMs})}_terminateWorker(){this.workerRef&&(this.log("worker","terminating worker"),this.workerRef.terminate(),this.workerRef=void 0)}_workerObjectUrl(e){let i;if(e)i=e;else{const a=new Blob([ok],{type:"application/javascript"});i=URL.createObjectURL(a)}return i}_initializeOptions(e){var i,a,l,c,u,d,m,p,y,v,x,_;this.worker=(i=e==null?void 0:e.worker)!==null&&i!==void 0?i:!1,this.accessToken=(a=e==null?void 0:e.accessToken)!==null&&a!==void 0?a:null;const S={};S.timeout=(l=e==null?void 0:e.timeout)!==null&&l!==void 0?l:AN,S.heartbeatIntervalMs=(c=e==null?void 0:e.heartbeatIntervalMs)!==null&&c!==void 0?c:Bb.HEARTBEAT_INTERVAL,this._disconnectOnEmptyChannelsAfterMs=(u=e==null?void 0:e.disconnectOnEmptyChannelsAfterMs)!==null&&u!==void 0?u:2*((d=e==null?void 0:e.heartbeatIntervalMs)!==null&&d!==void 0?d:Bb.HEARTBEAT_INTERVAL),S.transport=(m=e==null?void 0:e.transport)!==null&&m!==void 0?m:wN.getWebSocketConstructor(),S.params=e==null?void 0:e.params,S.logger=e==null?void 0:e.logger,S.heartbeatCallback=this._wrapHeartbeatCallback(e==null?void 0:e.heartbeatCallback),S.sessionStorage=(p=e==null?void 0:e.sessionStorage)!==null&&p!==void 0?p:rk(),S.reconnectAfterMs=(y=e==null?void 0:e.reconnectAfterMs)!==null&&y!==void 0?y:(M=>sk[M-1]||ik);let T,k;const C=(v=e==null?void 0:e.vsn)!==null&&v!==void 0?v:EN;switch(C){case TN:T=(M,N)=>N(JSON.stringify(M)),k=(M,N)=>N(JSON.parse(M));break;case gw:T=this.serializer.encode.bind(this.serializer),k=this.serializer.decode.bind(this.serializer);break;default:throw new Error(`Unsupported serializer version: ${S.vsn}`)}if(S.vsn=C,S.encode=(x=e==null?void 0:e.encode)!==null&&x!==void 0?x:T,S.decode=(_=e==null?void 0:e.decode)!==null&&_!==void 0?_:k,S.beforeReconnect=this._reconnectAuth.bind(this),(e!=null&&e.logLevel||e!=null&&e.log_level)&&(this.logLevel=e.logLevel||e.log_level,S.params=Object.assign(Object.assign({},S.params),{log_level:this.logLevel})),this.worker){if(typeof window<"u"&&!window.Worker)throw new Error("Web Worker is not supported");this.workerUrl=e==null?void 0:e.workerUrl,S.autoSendHeartbeat=!this.worker}return S}async _reconnectAuth(){await this._waitForAuthIfNeeded(),this.isConnected()||this.connect()}}var go=class extends Error{constructor(n,e){var i;super(n),this.name="IcebergError",this.status=e.status,this.icebergType=e.icebergType,this.icebergCode=e.icebergCode,this.details=e.details,this.isCommitStateUnknown=e.icebergType==="CommitStateUnknownException"||[500,502,504].includes(e.status)&&((i=e.icebergType)==null?void 0:i.includes("CommitState"))===!0}isNotFound(){return this.status===404}isConflict(){return this.status===409}isAuthenticationTimeout(){return this.status===419}};function ck(n,e,i){const a=new URL(e,n);if(i)for(const[l,c]of Object.entries(i))c!==void 0&&a.searchParams.set(l,c);return a.toString()}async function uk(n){return!n||n.type==="none"?{}:n.type==="bearer"?{Authorization:`Bearer ${n.token}`}:n.type==="header"?{[n.name]:n.value}:n.type==="custom"?await n.getHeaders():{}}function hk(n){const e=n.fetchImpl??globalThis.fetch;return{async request({method:i,path:a,query:l,body:c,headers:u}){const d=ck(n.baseUrl,a,l),m=await uk(n.auth),p=await e(d,{method:i,headers:{...c?{"Content-Type":"application/json"}:{},...m,...u},body:c?JSON.stringify(c):void 0}),y=await p.text(),v=(p.headers.get("content-type")||"").includes("application/json"),x=v&&y?JSON.parse(y):y;if(!p.ok){const _=v?x:void 0,S=_==null?void 0:_.error;throw new go((S==null?void 0:S.message)??`Request failed with status ${p.status}`,{status:p.status,icebergType:S==null?void 0:S.type,icebergCode:S==null?void 0:S.code,details:_})}return{status:p.status,headers:p.headers,data:x}}}}function ec(n){return n.join("")}var dk=class{constructor(n,e=""){this.client=n,this.prefix=e}async listNamespaces(n){const e=n?{parent:ec(n.namespace)}:void 0;return(await this.client.request({method:"GET",path:`${this.prefix}/namespaces`,query:e})).data.namespaces.map(a=>({namespace:a}))}async createNamespace(n,e){const i={namespace:n.namespace,properties:e==null?void 0:e.properties};return(await this.client.request({method:"POST",path:`${this.prefix}/namespaces`,body:i})).data}async dropNamespace(n){await this.client.request({method:"DELETE",path:`${this.prefix}/namespaces/${ec(n.namespace)}`})}async loadNamespaceMetadata(n){return{properties:(await this.client.request({method:"GET",path:`${this.prefix}/namespaces/${ec(n.namespace)}`})).data.properties}}async namespaceExists(n){try{return await this.client.request({method:"HEAD",path:`${this.prefix}/namespaces/${ec(n.namespace)}`}),!0}catch(e){if(e instanceof go&&e.status===404)return!1;throw e}}async createNamespaceIfNotExists(n,e){try{return await this.createNamespace(n,e)}catch(i){if(i instanceof go&&i.status===409)return;throw i}}};function Na(n){return n.join("")}var fk=class{constructor(n,e="",i){this.client=n,this.prefix=e,this.accessDelegation=i}async listTables(n){return(await this.client.request({method:"GET",path:`${this.prefix}/namespaces/${Na(n.namespace)}/tables`})).data.identifiers}async createTable(n,e){const i={};return this.accessDelegation&&(i["X-Iceberg-Access-Delegation"]=this.accessDelegation),(await this.client.request({method:"POST",path:`${this.prefix}/namespaces/${Na(n.namespace)}/tables`,body:e,headers:i})).data.metadata}async updateTable(n,e){const i=await this.client.request({method:"POST",path:`${this.prefix}/namespaces/${Na(n.namespace)}/tables/${n.name}`,body:e});return{"metadata-location":i.data["metadata-location"],metadata:i.data.metadata}}async dropTable(n,e){await this.client.request({method:"DELETE",path:`${this.prefix}/namespaces/${Na(n.namespace)}/tables/${n.name}`,query:{purgeRequested:String((e==null?void 0:e.purge)??!1)}})}async loadTable(n){const e={};return this.accessDelegation&&(e["X-Iceberg-Access-Delegation"]=this.accessDelegation),(await this.client.request({method:"GET",path:`${this.prefix}/namespaces/${Na(n.namespace)}/tables/${n.name}`,headers:e})).data.metadata}async tableExists(n){const e={};this.accessDelegation&&(e["X-Iceberg-Access-Delegation"]=this.accessDelegation);try{return await this.client.request({method:"HEAD",path:`${this.prefix}/namespaces/${Na(n.namespace)}/tables/${n.name}`,headers:e}),!0}catch(i){if(i instanceof go&&i.status===404)return!1;throw i}}async createTableIfNotExists(n,e){try{return await this.createTable(n,e)}catch(i){if(i instanceof go&&i.status===409)return await this.loadTable({namespace:n.namespace,name:e.name});throw i}}},mk=class{constructor(n){var a;let e="v1";n.catalogName&&(e+=`/${n.catalogName}`);const i=n.baseUrl.endsWith("/")?n.baseUrl:`${n.baseUrl}/`;this.client=hk({baseUrl:i,auth:n.auth,fetchImpl:n.fetch}),this.accessDelegation=(a=n.accessDelegation)==null?void 0:a.join(","),this.namespaceOps=new dk(this.client,e),this.tableOps=new fk(this.client,e,this.accessDelegation)}async listNamespaces(n){return this.namespaceOps.listNamespaces(n)}async createNamespace(n,e){return this.namespaceOps.createNamespace(n,e)}async dropNamespace(n){await this.namespaceOps.dropNamespace(n)}async loadNamespaceMetadata(n){return this.namespaceOps.loadNamespaceMetadata(n)}async listTables(n){return this.tableOps.listTables(n)}async createTable(n,e){return this.tableOps.createTable(n,e)}async updateTable(n,e){return this.tableOps.updateTable(n,e)}async dropTable(n,e){await this.tableOps.dropTable(n,e)}async loadTable(n){return this.tableOps.loadTable(n)}async namespaceExists(n){return this.namespaceOps.namespaceExists(n)}async tableExists(n){return this.tableOps.tableExists(n)}async createNamespaceIfNotExists(n,e){return this.namespaceOps.createNamespaceIfNotExists(n,e)}async createTableIfNotExists(n,e){return this.tableOps.createTableIfNotExists(n,e)}};function yo(n){"@babel/helpers - typeof";return yo=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},yo(n)}function pk(n,e){if(yo(n)!="object"||!n)return n;var i=n[Symbol.toPrimitive];if(i!==void 0){var a=i.call(n,e);if(yo(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(n)}function gk(n){var e=pk(n,"string");return yo(e)=="symbol"?e:e+""}function yk(n,e,i){return(e=gk(e))in n?Object.defineProperty(n,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[e]=i,n}function Ub(n,e){var i=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(l){return Object.getOwnPropertyDescriptor(n,l).enumerable})),i.push.apply(i,a)}return i}function ge(n){for(var e=1;en?(...e)=>n(...e):(...e)=>fetch(...e),xk=n=>{if(typeof n!="object"||n===null)return!1;const e=Object.getPrototypeOf(n);return(e===null||e===Object.prototype||Object.getPrototypeOf(e)===null)&&!(Symbol.toStringTag in n)&&!(Symbol.iterator in n)},pf=n=>{if(Array.isArray(n))return n.map(i=>pf(i));if(typeof n=="function"||n!==Object(n))return n;const e={};return Object.entries(n).forEach(([i,a])=>{const l=i.replace(/([-_][a-z])/gi,c=>c.toUpperCase().replace(/[-_]/g,""));e[l]=pf(a)}),e},wk=n=>!n||typeof n!="string"||n.length===0||n.length>100||n.trim()!==n||n.includes("/")||n.includes("\\")?!1:/^[\w!.\*'() &$@=;:+,?-]+$/.test(n),Lb=n=>{if(typeof n=="object"&&n!==null){const e=n;if(typeof e.msg=="string")return e.msg;if(typeof e.message=="string")return e.message;if(typeof e.error_description=="string")return e.error_description;if(typeof e.error=="string")return e.error;if(typeof e.error=="object"&&e.error!==null){const i=e.error;if(typeof i.message=="string")return i.message}}return JSON.stringify(n)},_k=async(n,e,i,a)=>{if(n!==null&&typeof n=="object"&&"json"in n&&typeof n.json=="function"){const l=n;let c=parseInt(String(l.status),10);Number.isFinite(c)||(c=500),l.json().then(u=>{const d=(u==null?void 0:u.statusCode)||(u==null?void 0:u.code)||c+"";e(new mf(Lb(u),c,d,a))}).catch(()=>{const u=c+"";e(new mf(l.statusText||`HTTP ${c} error`,c,u,a))})}else e(new ww(Lb(n),n,a))},Sk=(n,e,i,a)=>{const l={method:n,headers:(e==null?void 0:e.headers)||{}};if(n==="GET"||n==="HEAD"||!a)return ge(ge({},l),i);if(xk(a)){var c;const u=(e==null?void 0:e.headers)||{};let d;for(const[m,p]of Object.entries(u))m.toLowerCase()==="content-type"&&(d=p);l.headers=Nc(u,"Content-Type",(c=d)!==null&&c!==void 0?c:"application/json"),l.body=JSON.stringify(a)}else l.body=a;return e!=null&&e.duplex&&(l.duplex=e.duplex),ge(ge({},l),i)};async function Kr(n,e,i,a,l,c,u){return new Promise((d,m)=>{n(i,Sk(e,a,l,c)).then(p=>{if(!p.ok)throw p;if(a!=null&&a.noResolveJson)return p;if(u==="vectors"){const y=p.headers.get("content-type");if(p.headers.get("content-length")==="0"||p.status===204)return{};if(!y||!y.includes("application/json"))return{}}return p.json()}).then(p=>d(p)).catch(p=>_k(p,m,a,u))})}function _w(n="storage"){return{get:async(e,i,a,l)=>Kr(e,"GET",i,a,l,void 0,n),post:async(e,i,a,l,c)=>Kr(e,"POST",i,l,c,a,n),put:async(e,i,a,l,c)=>Kr(e,"PUT",i,l,c,a,n),head:async(e,i,a,l)=>Kr(e,"HEAD",i,ge(ge({},a),{},{noResolveJson:!0}),l,void 0,n),remove:async(e,i,a,l,c)=>Kr(e,"DELETE",i,l,c,a,n)}}const Tk=_w("storage"),{get:bo,post:zn,put:gf,head:Ek,remove:vo}=Tk,hn=_w("vectors");var Za=class{constructor(n,e={},i,a="storage"){this.shouldThrowOnError=!1,this.url=n,this.headers=bk(e),this.fetch=vk(i),this.namespace=a}throwOnError(){return this.shouldThrowOnError=!0,this}setHeader(n,e){return this.headers=Nc(this.headers,n,e),this}async handleOperation(n){var e=this;try{return{data:await n(),error:null}}catch(i){if(e.shouldThrowOnError)throw i;if(qc(i))return{data:null,error:i};throw i}}};let Sw;Sw=Symbol.toStringTag;var Ak=class{constructor(n,e){this.downloadFn=n,this.shouldThrowOnError=e,this[Sw]="StreamDownloadBuilder",this.promise=null}then(n,e){return this.getPromise().then(n,e)}catch(n){return this.getPromise().catch(n)}finally(n){return this.getPromise().finally(n)}getPromise(){return this.promise||(this.promise=this.execute()),this.promise}async execute(){var n=this;try{return{data:(await n.downloadFn()).body,error:null}}catch(e){if(n.shouldThrowOnError)throw e;if(qc(e))return{data:null,error:e};throw e}}};let Tw;Tw=Symbol.toStringTag;var jk=class{constructor(n,e){this.downloadFn=n,this.shouldThrowOnError=e,this[Tw]="BlobDownloadBuilder",this.promise=null}asStream(){return new Ak(this.downloadFn,this.shouldThrowOnError)}then(n,e){return this.getPromise().then(n,e)}catch(n){return this.getPromise().catch(n)}finally(n){return this.getPromise().finally(n)}getPromise(){return this.promise||(this.promise=this.execute()),this.promise}async execute(){var n=this;try{return{data:await(await n.downloadFn()).blob(),error:null}}catch(e){if(n.shouldThrowOnError)throw e;if(qc(e))return{data:null,error:e};throw e}}};const Ed={limit:100,offset:0,sortBy:{column:"name",order:"asc"}},zb={cacheControl:"3600",contentType:"text/plain;charset=UTF-8",upsert:!1};var Nk=class extends Za{constructor(n,e={},i,a){super(n,e,a,"storage"),this.bucketId=i}async uploadOrUpdate(n,e,i,a){var l=this;return l.handleOperation(async()=>{let c;const u=ge(ge({},zb),a);let d=ge(ge({},l.headers),n==="POST"&&{"x-upsert":String(u.upsert)});const m=u.metadata;if(typeof Blob<"u"&&i instanceof Blob?(c=new FormData,c.append("cacheControl",u.cacheControl),m&&c.append("metadata",l.encodeMetadata(m)),c.append("",i)):typeof FormData<"u"&&i instanceof FormData?(c=i,c.has("cacheControl")||c.append("cacheControl",u.cacheControl),m&&!c.has("metadata")&&c.append("metadata",l.encodeMetadata(m))):(c=i,d["cache-control"]=`max-age=${u.cacheControl}`,d["content-type"]=u.contentType,m&&(d["x-metadata"]=l.toBase64(l.encodeMetadata(m))),(typeof ReadableStream<"u"&&c instanceof ReadableStream||c&&typeof c=="object"&&"pipe"in c&&typeof c.pipe=="function")&&!u.duplex&&(u.duplex="half")),a!=null&&a.headers)for(const[x,_]of Object.entries(a.headers))d=Nc(d,x,_);const p=l._removeEmptyFolders(e),y=l._getFinalPath(p),v=await(n=="PUT"?gf:zn)(l.fetch,`${l.url}/object/${y}`,c,ge({headers:d},u!=null&&u.duplex?{duplex:u.duplex}:{}));return{path:p,id:v.Id,fullPath:v.Key}})}async upload(n,e,i){return this.uploadOrUpdate("POST",n,e,i)}async uploadToSignedUrl(n,e,i,a){var l=this;const c=l._removeEmptyFolders(n),u=l._getFinalPath(c),d=new URL(l.url+`/object/upload/sign/${u}`);return d.searchParams.set("token",e),l.handleOperation(async()=>{let m;const p=ge(ge({},zb),a);let y=ge(ge({},l.headers),{"x-upsert":String(p.upsert)});const v=p.metadata;if(typeof Blob<"u"&&i instanceof Blob?(m=new FormData,m.append("cacheControl",p.cacheControl),v&&m.append("metadata",l.encodeMetadata(v)),m.append("",i)):typeof FormData<"u"&&i instanceof FormData?(m=i,m.has("cacheControl")||m.append("cacheControl",p.cacheControl),v&&!m.has("metadata")&&m.append("metadata",l.encodeMetadata(v))):(m=i,y["cache-control"]=`max-age=${p.cacheControl}`,y["content-type"]=p.contentType,v&&(y["x-metadata"]=l.toBase64(l.encodeMetadata(v))),(typeof ReadableStream<"u"&&m instanceof ReadableStream||m&&typeof m=="object"&&"pipe"in m&&typeof m.pipe=="function")&&!p.duplex&&(p.duplex="half")),a!=null&&a.headers)for(const[x,_]of Object.entries(a.headers))y=Nc(y,x,_);return{path:c,fullPath:(await gf(l.fetch,d.toString(),m,ge({headers:y},p!=null&&p.duplex?{duplex:p.duplex}:{}))).Key}})}async createSignedUploadUrl(n,e){var i=this;return i.handleOperation(async()=>{let a=i._getFinalPath(n);const l=ge({},i.headers);e!=null&&e.upsert&&(l["x-upsert"]="true");const c=await zn(i.fetch,`${i.url}/object/upload/sign/${a}`,{},{headers:l}),u=new URL(i.url+c.url),d=u.searchParams.get("token");if(!d)throw new $c("No token returned by API");return{signedUrl:u.toString(),path:n,token:d}})}async update(n,e,i){return this.uploadOrUpdate("PUT",n,e,i)}async move(n,e,i){var a=this;return a.handleOperation(async()=>await zn(a.fetch,`${a.url}/object/move`,{bucketId:a.bucketId,sourceKey:n,destinationKey:e,destinationBucket:i==null?void 0:i.destinationBucket},{headers:a.headers}))}async copy(n,e,i){var a=this;return a.handleOperation(async()=>({path:(await zn(a.fetch,`${a.url}/object/copy`,{bucketId:a.bucketId,sourceKey:n,destinationKey:e,destinationBucket:i==null?void 0:i.destinationBucket},{headers:a.headers})).Key}))}async createSignedUrl(n,e,i){var a=this;return a.handleOperation(async()=>{let l=a._getFinalPath(n);const c=typeof(i==null?void 0:i.transform)=="object"&&i.transform!==null&&Object.keys(i.transform).length>0;let u=await zn(a.fetch,`${a.url}/object/sign/${l}`,ge({expiresIn:e},c?{transform:i.transform}:{}),{headers:a.headers});const d=new URLSearchParams;i!=null&&i.download&&d.set("download",i.download===!0?"":i.download),(i==null?void 0:i.cacheNonce)!=null&&d.set("cacheNonce",String(i.cacheNonce));const m=d.toString();return{signedUrl:encodeURI(`${a.url}${u.signedURL}${m?`&${m}`:""}`)}})}async createSignedUrls(n,e,i){var a=this;return a.handleOperation(async()=>{const l=await zn(a.fetch,`${a.url}/object/sign/${a.bucketId}`,{expiresIn:e,paths:n},{headers:a.headers}),c=new URLSearchParams;i!=null&&i.download&&c.set("download",i.download===!0?"":i.download),(i==null?void 0:i.cacheNonce)!=null&&c.set("cacheNonce",String(i.cacheNonce));const u=c.toString();return l.map(d=>ge(ge({},d),{},{signedUrl:d.signedURL?encodeURI(`${a.url}${d.signedURL}${u?`&${u}`:""}`):null}))})}download(n,e,i){const a=typeof(e==null?void 0:e.transform)=="object"&&e.transform!==null&&Object.keys(e.transform).length>0?"render/image/authenticated":"object",l=new URLSearchParams;e!=null&&e.transform&&this.applyTransformOptsToQuery(l,e.transform),(e==null?void 0:e.cacheNonce)!=null&&l.set("cacheNonce",String(e.cacheNonce));const c=l.toString(),u=this._getFinalPath(n),d=()=>bo(this.fetch,`${this.url}/${a}/${u}${c?`?${c}`:""}`,{headers:this.headers,noResolveJson:!0},i);return new jk(d,this.shouldThrowOnError)}async info(n){var e=this;const i=e._getFinalPath(n);return e.handleOperation(async()=>pf(await bo(e.fetch,`${e.url}/object/info/${i}`,{headers:e.headers})))}async exists(n){var e=this;const i=e._getFinalPath(n);try{return await Ek(e.fetch,`${e.url}/object/${i}`,{headers:e.headers}),{data:!0,error:null}}catch(l){if(e.shouldThrowOnError)throw l;if(qc(l)){var a;const c=l instanceof mf?l.status:l instanceof ww?(a=l.originalError)===null||a===void 0?void 0:a.status:void 0;if(c!==void 0&&[400,404].includes(c))return{data:!1,error:l}}throw l}}getPublicUrl(n,e){const i=this._getFinalPath(n),a=new URLSearchParams;e!=null&&e.download&&a.set("download",e.download===!0?"":e.download),e!=null&&e.transform&&this.applyTransformOptsToQuery(a,e.transform),(e==null?void 0:e.cacheNonce)!=null&&a.set("cacheNonce",String(e.cacheNonce));const l=a.toString(),c=typeof(e==null?void 0:e.transform)=="object"&&e.transform!==null&&Object.keys(e.transform).length>0?"render/image":"object";return{data:{publicUrl:encodeURI(`${this.url}/${c}/public/${i}`)+(l?`?${l}`:"")}}}async remove(n){var e=this;return e.handleOperation(async()=>await vo(e.fetch,`${e.url}/object/${e.bucketId}`,{prefixes:n},{headers:e.headers}))}async purgeCache(n,e,i){var a=this;return a.handleOperation(async()=>{const l=a._getFinalPath(n),c=new URLSearchParams;e!=null&&e.transformations&&c.set("transformations","true");const u=c.toString();return await vo(a.fetch,`${a.url}/cdn/${l}${u?`?${u}`:""}`,{},{headers:a.headers},i)})}async list(n,e,i){var a=this;return a.handleOperation(async()=>{const l=e!=null&&e.sortBy?ge(ge({},Ed.sortBy),e.sortBy):Ed.sortBy,c=ge(ge(ge({},Ed),e),{},{sortBy:l,prefix:n||""});return await zn(a.fetch,`${a.url}/object/list/${a.bucketId}`,c,{headers:a.headers},i)})}async listV2(n,e){var i=this;return i.handleOperation(async()=>{const a=ge({},n);return await zn(i.fetch,`${i.url}/object/list-v2/${i.bucketId}`,a,{headers:i.headers},e)})}encodeMetadata(n){return JSON.stringify(n)}toBase64(n){return typeof Buffer<"u"?Buffer.from(n).toString("base64"):btoa(n)}_getFinalPath(n){return`${this.bucketId}/${n.replace(/^\/+/,"")}`}_removeEmptyFolders(n){return n.replace(/^\/|\/$/g,"").replace(/\/+/g,"/")}applyTransformOptsToQuery(n,e){return e.width&&n.set("width",e.width.toString()),e.height&&n.set("height",e.height.toString()),e.resize&&n.set("resize",e.resize),e.format&&n.set("format",e.format),e.quality&&n.set("quality",e.quality.toString()),n}};const kk="2.110.2",No={"X-Client-Info":`storage-js/${kk}`};var Ck=class extends Za{constructor(n,e={},i,a){const l=new URL(n);a!=null&&a.useNewHostname&&/supabase\.(co|in|red)$/.test(l.hostname)&&!l.hostname.includes("storage.supabase.")&&(l.hostname=l.hostname.replace("supabase.","storage.supabase."));const c=l.href.replace(/\/$/,""),u=ge(ge({},No),e);super(c,u,i,"storage")}async listBuckets(n){var e=this;return e.handleOperation(async()=>{const i=e.listBucketOptionsToQueryString(n);return await bo(e.fetch,`${e.url}/bucket${i}`,{headers:e.headers})})}async getBucket(n){var e=this;return e.handleOperation(async()=>await bo(e.fetch,`${e.url}/bucket/${n}`,{headers:e.headers}))}async createBucket(n,e={public:!1}){var i=this;return i.handleOperation(async()=>await zn(i.fetch,`${i.url}/bucket`,{id:n,name:n,type:e.type,public:e.public,file_size_limit:e.fileSizeLimit,allowed_mime_types:e.allowedMimeTypes},{headers:i.headers}))}async updateBucket(n,e){var i=this;return i.handleOperation(async()=>await gf(i.fetch,`${i.url}/bucket/${n}`,{id:n,name:n,public:e.public,file_size_limit:e.fileSizeLimit,allowed_mime_types:e.allowedMimeTypes},{headers:i.headers}))}async emptyBucket(n){var e=this;return e.handleOperation(async()=>await zn(e.fetch,`${e.url}/bucket/${n}/empty`,{},{headers:e.headers}))}async deleteBucket(n){var e=this;return e.handleOperation(async()=>await vo(e.fetch,`${e.url}/bucket/${n}`,{},{headers:e.headers}))}async purgeBucketCache(n,e,i){var a=this;return a.handleOperation(async()=>{const l=new URLSearchParams;e!=null&&e.transformations&&l.set("transformations","true");const c=l.toString();return await vo(a.fetch,`${a.url}/cdn/${n}${c?`?${c}`:""}`,{},{headers:a.headers},i)})}listBucketOptionsToQueryString(n){const e={};return n&&("limit"in n&&(e.limit=String(n.limit)),"offset"in n&&(e.offset=String(n.offset)),n.search&&(e.search=n.search),n.sortColumn&&(e.sortColumn=n.sortColumn),n.sortOrder&&(e.sortOrder=n.sortOrder)),Object.keys(e).length>0?"?"+new URLSearchParams(e).toString():""}},Rk=class extends Za{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),e);super(a,l,i,"storage")}async createBucket(n){var e=this;return e.handleOperation(async()=>await zn(e.fetch,`${e.url}/bucket`,{name:n},{headers:e.headers}))}async listBuckets(n){var e=this;return e.handleOperation(async()=>{const i=new URLSearchParams;(n==null?void 0:n.limit)!==void 0&&i.set("limit",n.limit.toString()),(n==null?void 0:n.offset)!==void 0&&i.set("offset",n.offset.toString()),n!=null&&n.sortColumn&&i.set("sortColumn",n.sortColumn),n!=null&&n.sortOrder&&i.set("sortOrder",n.sortOrder),n!=null&&n.search&&i.set("search",n.search);const a=i.toString(),l=a?`${e.url}/bucket?${a}`:`${e.url}/bucket`;return await bo(e.fetch,l,{headers:e.headers})})}async deleteBucket(n){var e=this;return e.handleOperation(async()=>await vo(e.fetch,`${e.url}/bucket/${n}`,{},{headers:e.headers}))}from(n){var e=this;if(!wk(n))throw new $c("Invalid bucket name: File, folder, and bucket names must follow AWS object key naming guidelines and should avoid the use of any other characters.");const i=new mk({baseUrl:this.url,catalogName:n,auth:{type:"custom",getHeaders:async()=>e.headers},fetch:this.fetch}),a=this.shouldThrowOnError;return new Proxy(i,{get(l,c){const u=l[c];return typeof u!="function"?u:async(...d)=>{try{return{data:await u.apply(l,d),error:null}}catch(m){if(a)throw m;return{data:null,error:m}}}}})}},Ok=class extends Za{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),{},{"Content-Type":"application/json"},e);super(a,l,i,"vectors")}async createIndex(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/CreateIndex`,n,{headers:e.headers})||{})}async getIndex(n,e){var i=this;return i.handleOperation(async()=>await hn.post(i.fetch,`${i.url}/GetIndex`,{vectorBucketName:n,indexName:e},{headers:i.headers}))}async listIndexes(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/ListIndexes`,n,{headers:e.headers}))}async deleteIndex(n,e){var i=this;return i.handleOperation(async()=>await hn.post(i.fetch,`${i.url}/DeleteIndex`,{vectorBucketName:n,indexName:e},{headers:i.headers})||{})}},Dk=class extends Za{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),{},{"Content-Type":"application/json"},e);super(a,l,i,"vectors")}async putVectors(n){var e=this;if(n.vectors.length<1||n.vectors.length>500)throw new Error("Vector batch size must be between 1 and 500 items");return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/PutVectors`,n,{headers:e.headers})||{})}async getVectors(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/GetVectors`,n,{headers:e.headers}))}async listVectors(n){var e=this;if(n.segmentCount!==void 0){if(n.segmentCount<1||n.segmentCount>16)throw new Error("segmentCount must be between 1 and 16");if(n.segmentIndex!==void 0&&(n.segmentIndex<0||n.segmentIndex>=n.segmentCount))throw new Error(`segmentIndex must be between 0 and ${n.segmentCount-1}`)}return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/ListVectors`,n,{headers:e.headers}))}async queryVectors(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/QueryVectors`,n,{headers:e.headers}))}async deleteVectors(n){var e=this;if(n.keys.length<1||n.keys.length>500)throw new Error("Keys batch size must be between 1 and 500 items");return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/DeleteVectors`,n,{headers:e.headers})||{})}},Mk=class extends Za{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),{},{"Content-Type":"application/json"},e);super(a,l,i,"vectors")}async createBucket(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/CreateVectorBucket`,{vectorBucketName:n},{headers:e.headers})||{})}async getBucket(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/GetVectorBucket`,{vectorBucketName:n},{headers:e.headers}))}async listBuckets(n={}){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/ListVectorBuckets`,n,{headers:e.headers}))}async deleteBucket(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/DeleteVectorBucket`,{vectorBucketName:n},{headers:e.headers})||{})}},Bk=class extends Mk{constructor(n,e={}){super(n,e.headers||{},e.fetch)}from(n){return new Uk(this.url,this.headers,n,this.fetch)}async createBucket(n){var e=()=>super.createBucket,i=this;return e().call(i,n)}async getBucket(n){var e=()=>super.getBucket,i=this;return e().call(i,n)}async listBuckets(n={}){var e=()=>super.listBuckets,i=this;return e().call(i,n)}async deleteBucket(n){var e=()=>super.deleteBucket,i=this;return e().call(i,n)}},Uk=class extends Ok{constructor(n,e,i,a){super(n,e,a),this.vectorBucketName=i}async createIndex(n){var e=()=>super.createIndex,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName}))}async listIndexes(n={}){var e=()=>super.listIndexes,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName}))}async getIndex(n){var e=()=>super.getIndex,i=this;return e().call(i,i.vectorBucketName,n)}async deleteIndex(n){var e=()=>super.deleteIndex,i=this;return e().call(i,i.vectorBucketName,n)}index(n){return new Lk(this.url,this.headers,this.vectorBucketName,n,this.fetch)}},Lk=class extends Dk{constructor(n,e,i,a,l){super(n,e,l),this.vectorBucketName=i,this.indexName=a}async putVectors(n){var e=()=>super.putVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async getVectors(n){var e=()=>super.getVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async listVectors(n={}){var e=()=>super.listVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async queryVectors(n){var e=()=>super.queryVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async deleteVectors(n){var e=()=>super.deleteVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}},zk=class extends Ck{constructor(n,e={},i,a){super(n,e,i,a)}from(n){return new Nk(this.url,this.headers,n,this.fetch)}get vectors(){return new Bk(this.url+"/vector",{headers:this.headers,fetch:this.fetch})}get analytics(){return new Rk(this.url+"/iceberg",this.headers,this.fetch)}};const Ew="2.110.2",Es=30*1e3,Zr=3,Ad=Zr*Es,Vk=2*Es,Pk="http://localhost:9999",Hk="supabase.auth.token",$k={"X-Client-Info":`gotrue-js/${Ew}`},yf="X-Supabase-Api-Version",Aw={"2024-01-01":{timestamp:Date.parse("2024-01-01T00:00:00.0Z"),name:"2024-01-01"}},qk=/^([a-z0-9_-]{4})*($|[a-z0-9_-]{3}$|[a-z0-9_-]{2}$)$/i,Fk=600*1e3;class xo extends Error{constructor(e,i,a){super(e),this.__isAuthError=!0,this.name="AuthError",this.status=i,this.code=a}toJSON(){return{name:this.name,message:this.message,status:this.status,code:this.code}}}function ie(n){return typeof n=="object"&&n!==null&&"__isAuthError"in n}class Ik extends xo{constructor(e,i,a){super(e,i,a),this.name="AuthApiError",this.status=i,this.code=a}}function Gk(n){return ie(n)&&n.name==="AuthApiError"}class Vn extends xo{constructor(e,i){super(e),this.name="AuthUnknownError",this.originalError=i}}class es extends xo{constructor(e,i,a,l){super(e,a,l),this.name=i,this.status=a}}class kt extends es{constructor(){super("Auth session missing!","AuthSessionMissingError",400,void 0)}}function tc(n){return ie(n)&&n.name==="AuthSessionMissingError"}class ka extends es{constructor(){super("Auth session or user missing","AuthInvalidTokenResponseError",500,void 0)}}class nc extends es{constructor(e){super(e,"AuthInvalidCredentialsError",400,void 0)}}class sc extends es{constructor(e,i=null){super(e,"AuthImplicitGrantRedirectError",500,void 0),this.details=null,this.details=i}toJSON(){return Object.assign(Object.assign({},super.toJSON()),{details:this.details})}}function Kk(n){return ie(n)&&n.name==="AuthImplicitGrantRedirectError"}class Vb extends es{constructor(e,i=null){super(e,"AuthPKCEGrantCodeExchangeError",500,void 0),this.details=null,this.details=i}toJSON(){return Object.assign(Object.assign({},super.toJSON()),{details:this.details})}}class Yk extends es{constructor(){super("PKCE code verifier not found in storage. This can happen if the auth flow was initiated in a different browser or device, or if the storage was cleared. For SSR frameworks (Next.js, SvelteKit, etc.), use @supabase/ssr on both the server and client to store the code verifier in cookies.","AuthPKCECodeVerifierMissingError",400,"pkce_code_verifier_not_found")}}class bf extends es{constructor(e,i){super(e,"AuthRetryableFetchError",i,void 0)}}function Pb(n){return ie(n)&&n.name==="AuthRetryableFetchError"}class Hb extends es{constructor(e="Refresh result discarded: session state changed mid-flight (e.g., concurrent signOut)"){super(e,"AuthRefreshDiscardedError",409,void 0)}}function Xk(n){return ie(n)&&n.name==="AuthRefreshDiscardedError"}class $b extends es{constructor(e,i,a){super(e,"AuthWeakPasswordError",i,"weak_password"),this.reasons=a}toJSON(){return Object.assign(Object.assign({},super.toJSON()),{reasons:this.reasons})}}class kc extends es{constructor(e){super(e,"AuthInvalidJwtError",400,"invalid_jwt")}}const Cc="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".split(""),qb=` -\r=`.split(""),Jk=(()=>{const n=new Array(128);for(let e=0;e=6;){const a=e.queue>>e.queuedBits-6&63;i(Cc[a]),e.queuedBits-=6}else if(e.queuedBits>0)for(e.queue=e.queue<<6-e.queuedBits,e.queuedBits=6;e.queuedBits>=6;){const a=e.queue>>e.queuedBits-6&63;i(Cc[a]),e.queuedBits-=6}}function jw(n,e,i){const a=Jk[n];if(a>-1)for(e.queue=e.queue<<6|a,e.queuedBits+=6;e.queuedBits>=8;)i(e.queue>>e.queuedBits-8&255),e.queuedBits-=8;else{if(a===-2)return;throw new Error(`Invalid Base64-URL character "${String.fromCharCode(n)}"`)}}function Ib(n){const e=[],i=u=>{e.push(String.fromCodePoint(u))},a={utf8seq:0,codepoint:0},l={queue:0,queuedBits:0},c=u=>{Wk(u,a,i)};for(let u=0;u>6),e(128|n&63);return}else if(n<=65535){e(224|n>>12),e(128|n>>6&63),e(128|n&63);return}else if(n<=1114111){e(240|n>>18),e(128|n>>12&63),e(128|n>>6&63),e(128|n&63);return}throw new Error(`Unrecognized Unicode codepoint: ${n.toString(16)}`)}function Zk(n,e){for(let i=0;i55295&&a<=56319){const l=(a-55296)*1024&65535;a=(n.charCodeAt(i+1)-56320&65535|l)+65536,i+=1}Qk(a,e)}}function Wk(n,e,i){if(e.utf8seq===0){if(n<=127){i(n);return}for(let a=1;a<6;a+=1)if((n>>7-a&1)===0){e.utf8seq=a;break}if(e.utf8seq===2)e.codepoint=n&31;else if(e.utf8seq===3)e.codepoint=n&15;else if(e.utf8seq===4)e.codepoint=n&7;else throw new Error("Invalid UTF-8 sequence");e.utf8seq-=1}else if(e.utf8seq>0){if(n<=127)throw new Error("Invalid UTF-8 sequence");e.codepoint=e.codepoint<<6|n&63,e.utf8seq-=1,e.utf8seq===0&&i(e.codepoint)}}function Fa(n){const e=[],i={queue:0,queuedBits:0},a=l=>{e.push(l)};for(let l=0;le.push(i)),new Uint8Array(e)}function Bi(n){const e=[],i={queue:0,queuedBits:0},a=l=>{e.push(l)};return n.forEach(l=>Fb(l,i,a)),Fb(null,i,a),e.join("")}function tC(n){return Math.round(Date.now()/1e3)+n}function nC(){return Symbol("auth-callback")}const Vt=()=>typeof window<"u"&&typeof document<"u",ki={tested:!1,writable:!1},Nw=()=>{if(!Vt())return!1;try{if(typeof globalThis.localStorage!="object")return!1}catch{return!1}if(ki.tested)return ki.writable;const n=`lswt-${Math.random()}${Math.random()}`;try{globalThis.localStorage.setItem(n,n),globalThis.localStorage.removeItem(n),ki.tested=!0,ki.writable=!0}catch{ki.tested=!0,ki.writable=!1}return ki.writable};function sC(n){const e={},i=new URL(n);if(i.hash&&i.hash[0]==="#")try{new URLSearchParams(i.hash.substring(1)).forEach((l,c)=>{e[c]=l})}catch{}return i.searchParams.forEach((a,l)=>{e[l]=a}),e}const kw=n=>n?(...e)=>n(...e):(...e)=>fetch(...e),iC=n=>typeof n=="object"&&n!==null&&"status"in n&&"ok"in n&&"json"in n&&typeof n.json=="function",Ua=async(n,e,i)=>{await n.setItem(e,JSON.stringify(i))},Mn=async(n,e)=>{const i=await n.getItem(e);if(!i)return null;try{return JSON.parse(i)}catch{return null}},dt=async(n,e)=>{await n.removeItem(e)};class Fc{constructor(){this.promise=new Fc.promiseConstructor((e,i)=>{this.resolve=e,this.reject=i})}}Fc.promiseConstructor=Promise;function ic(n){const e=n.split(".");if(e.length!==3)throw new kc("Invalid JWT structure");for(let a=0;a{setTimeout(()=>e(null),n)})}function rC(n,e){return new Promise((a,l)=>{(async()=>{for(let c=0;c<1/0;c++)try{const u=await n(c);if(!e(c,null,u)){a(u);return}}catch(u){if(!e(c,u)){l(u);return}}})()})}function oC(n){return("0"+n.toString(16)).substr(-2)}function lC(){const e=new Uint32Array(56);if(typeof crypto>"u"){const i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",a=i.length;let l="";for(let c=0;c<56;c++)l+=i.charAt(Math.floor(Math.random()*a));return l}return crypto.getRandomValues(e),Array.from(e,oC).join("")}async function cC(n){const i=new TextEncoder().encode(n),a=await crypto.subtle.digest("SHA-256",i),l=new Uint8Array(a);return Array.from(l).map(c=>String.fromCharCode(c)).join("")}async function uC(n){if(!(typeof crypto<"u"&&typeof crypto.subtle<"u"&&typeof TextEncoder<"u"))return console.warn("WebCrypto API is not supported. Code challenge method will default to use plain instead of sha256."),n;const i=await cC(n);return btoa(i).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function Ci(n,e,i=!1){const a=lC();let l=a;i&&(l+="/recovery"),await Ua(n,`${e}-code-verifier`,l);const c=await uC(a);return[c,a===c?"plain":"s256"]}const hC=/^2[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$/i;function dC(n){const e=n.headers.get(yf);if(!e||!e.match(hC))return null;try{return new Date(`${e}T00:00:00.0Z`)}catch{return null}}function fC(n){if(!n)throw new Error("Missing exp claim");const e=Math.floor(Date.now()/1e3);if(n<=e)throw new Error("JWT has expired")}function mC(n){switch(n){case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new Error("Invalid alg claim")}}const pC=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;function ws(n){if(!pC.test(n))throw new Error("@supabase/auth-js: Expected parameter to be UUID but is not")}function Un(n){if(!n.passkey)throw new Error("@supabase/auth-js: the passkey API is experimental and disabled by default. Enable it by passing `auth: { experimental: { passkey: true } }` to createClient (or to the GoTrueClient constructor).")}function jd(){const n={};return new Proxy(n,{get:(e,i)=>{if(i==="__isUserNotAvailableProxy")return!0;if(typeof i=="symbol"){const a=i.toString();if(a==="Symbol(Symbol.toPrimitive)"||a==="Symbol(Symbol.toStringTag)"||a==="Symbol(util.inspect.custom)")return}throw new Error(`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Accessing the "${i}" property of the session object is not supported. Please use getUser() instead.`)},set:(e,i)=>{throw new Error(`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Setting the "${i}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`)},deleteProperty:(e,i)=>{throw new Error(`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Deleting the "${i}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`)}})}function gC(n,e){return new Proxy(n,{get:(i,a,l)=>{if(a==="__isInsecureUserWarningProxy")return!0;if(typeof a=="symbol"){const c=a.toString();if(c==="Symbol(Symbol.toPrimitive)"||c==="Symbol(Symbol.toStringTag)"||c==="Symbol(util.inspect.custom)"||c==="Symbol(nodejs.util.inspect.custom)")return Reflect.get(i,a,l)}return!e.value&&typeof a=="string"&&(console.warn("Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server."),e.value=!0),Reflect.get(i,a,l)}})}function Gb(n){return JSON.parse(JSON.stringify(n))}const Oi=n=>{if(typeof n=="object"&&n!==null){const e=n;if(typeof e.msg=="string")return e.msg;if(typeof e.message=="string")return e.message;if(typeof e.error_description=="string")return e.error_description;if(typeof e.error=="string")return e.error}return JSON.stringify(n)},yC=[500,501,502,503,504,520,521,522,523,524,525,526,527,528,529,530];async function Kb(n){var e;if(!iC(n))throw new bf(Oi(n),0);if(yC.includes(n.status))throw new bf(Oi(n),n.status);let i;try{i=await n.json()}catch(c){throw new Vn(Oi(c),c)}let a;const l=dC(n);if(l&&l.getTime()>=Aw["2024-01-01"].timestamp&&typeof i=="object"&&i&&typeof i.code=="string"?a=i.code:typeof i=="object"&&i&&typeof i.error_code=="string"&&(a=i.error_code),a){if(a==="weak_password")throw new $b(Oi(i),n.status,((e=i.weak_password)===null||e===void 0?void 0:e.reasons)||[]);if(a==="session_not_found")throw new kt}else if(typeof i=="object"&&i&&typeof i.weak_password=="object"&&i.weak_password&&Array.isArray(i.weak_password.reasons)&&i.weak_password.reasons.length&&i.weak_password.reasons.reduce((c,u)=>c&&typeof u=="string",!0))throw new $b(Oi(i),n.status,i.weak_password.reasons);throw new Ik(Oi(i),n.status||500,a)}const bC=(n,e,i,a)=>{const l={method:n,headers:(e==null?void 0:e.headers)||{}};return n==="GET"?l:(l.headers=Object.assign({"Content-Type":"application/json;charset=UTF-8"},e==null?void 0:e.headers),l.body=JSON.stringify(a),Object.assign(Object.assign({},l),i))};async function le(n,e,i,a){var l;const c=Object.assign({},a==null?void 0:a.headers);c[yf]||(c[yf]=Aw["2024-01-01"].name),a!=null&&a.jwt&&(c.Authorization=`Bearer ${a.jwt}`);const u=(l=a==null?void 0:a.query)!==null&&l!==void 0?l:{};a!=null&&a.redirectTo&&(u.redirect_to=a.redirectTo);const d=Object.keys(u).length?"?"+new URLSearchParams(u).toString():"",m=await vC(n,e,i+d,{headers:c,noResolveJson:a==null?void 0:a.noResolveJson},{},a==null?void 0:a.body);return a!=null&&a.xform?a==null?void 0:a.xform(m):{data:Object.assign({},m),error:null}}async function vC(n,e,i,a,l,c){const u=bC(e,a,l,c);let d;try{d=await n(i,Object.assign({},u))}catch(m){throw console.error(m),new bf(Oi(m),0)}if(d.ok||await Kb(d),a!=null&&a.noResolveJson)return d;try{return await d.json()}catch(m){await Kb(m)}}function En(n){var e;let i=null;_C(n)&&(i=Object.assign({},n),n.expires_at||(i.expires_at=tC(n.expires_in)));const a=(e=n.user)!==null&&e!==void 0?e:typeof(n==null?void 0:n.id)=="string"?n:null;return{data:{session:i,user:a},error:null}}function Yb(n){const e=En(n);return!e.error&&n.weak_password&&typeof n.weak_password=="object"&&Array.isArray(n.weak_password.reasons)&&n.weak_password.reasons.length&&n.weak_password.message&&typeof n.weak_password.message=="string"&&n.weak_password.reasons.reduce((i,a)=>i&&typeof a=="string",!0)&&(e.data.weak_password=n.weak_password),e}function ni(n){var e;return{data:{user:(e=n.user)!==null&&e!==void 0?e:n},error:null}}function xC(n){return{data:n,error:null}}function wC(n){const{action_link:e,email_otp:i,hashed_token:a,redirect_to:l,verification_type:c}=n,u=Hc(n,["action_link","email_otp","hashed_token","redirect_to","verification_type"]),d={action_link:e,email_otp:i,hashed_token:a,redirect_to:l,verification_type:c},m=Object.assign({},u);return{data:{properties:d,user:m},error:null}}function Xb(n){return n}function _C(n){return!!n.access_token&&!!n.refresh_token&&!!n.expires_in}const Nd=["global","local","others"];class SC{constructor({url:e="",headers:i={},fetch:a,experimental:l}){this.url=e,this.headers=i,this.fetch=kw(a),this.experimental=l??{},this.mfa={listFactors:this._listFactors.bind(this),deleteFactor:this._deleteFactor.bind(this)},this.oauth={listClients:this._listOAuthClients.bind(this),createClient:this._createOAuthClient.bind(this),getClient:this._getOAuthClient.bind(this),updateClient:this._updateOAuthClient.bind(this),deleteClient:this._deleteOAuthClient.bind(this),regenerateClientSecret:this._regenerateOAuthClientSecret.bind(this)},this.customProviders={listProviders:this._listCustomProviders.bind(this),createProvider:this._createCustomProvider.bind(this),getProvider:this._getCustomProvider.bind(this),updateProvider:this._updateCustomProvider.bind(this),deleteProvider:this._deleteCustomProvider.bind(this)},this.passkey={listPasskeys:this._adminListPasskeys.bind(this),deletePasskey:this._adminDeletePasskey.bind(this)}}async signOut(e,i=Nd[0]){if(Nd.indexOf(i)<0)throw new Error(`@supabase/auth-js: Parameter scope must be one of ${Nd.join(", ")}`);try{return await le(this.fetch,"POST",`${this.url}/logout?scope=${i}`,{headers:this.headers,jwt:e,noResolveJson:!0}),{data:null,error:null}}catch(a){if(ie(a))return{data:null,error:a};throw a}}async inviteUserByEmail(e,i={}){try{return await le(this.fetch,"POST",`${this.url}/invite`,{body:{email:e,data:i.data},headers:this.headers,redirectTo:i.redirectTo,xform:ni})}catch(a){if(ie(a))return{data:{user:null},error:a};throw a}}async generateLink(e){try{const{options:i}=e,a=Hc(e,["options"]),l=Object.assign(Object.assign({},a),i);return"newEmail"in a&&(l.new_email=a==null?void 0:a.newEmail,delete l.newEmail),await le(this.fetch,"POST",`${this.url}/admin/generate_link`,{body:l,headers:this.headers,xform:wC,redirectTo:i==null?void 0:i.redirectTo})}catch(i){if(ie(i))return{data:{properties:null,user:null},error:i};throw i}}async createUser(e){try{return await le(this.fetch,"POST",`${this.url}/admin/users`,{body:e,headers:this.headers,xform:ni})}catch(i){if(ie(i))return{data:{user:null},error:i};throw i}}async listUsers(e){var i,a,l,c,u,d,m;try{const p={nextPage:null,lastPage:0,total:0},y=await le(this.fetch,"GET",`${this.url}/admin/users`,{headers:this.headers,noResolveJson:!0,query:{page:(a=(i=e==null?void 0:e.page)===null||i===void 0?void 0:i.toString())!==null&&a!==void 0?a:"",per_page:(c=(l=e==null?void 0:e.perPage)===null||l===void 0?void 0:l.toString())!==null&&c!==void 0?c:""},xform:Xb});if(y.error)throw y.error;const v=await y.json(),x=(u=y.headers.get("x-total-count"))!==null&&u!==void 0?u:0,_=(m=(d=y.headers.get("link"))===null||d===void 0?void 0:d.split(","))!==null&&m!==void 0?m:[];return _.length>0&&(_.forEach(S=>{const T=parseInt(S.split(";")[0].split("=")[1].substring(0,1)),k=JSON.parse(S.split(";")[1].split("=")[1]);p[`${k}Page`]=T}),p.total=parseInt(x)),{data:Object.assign(Object.assign({},v),p),error:null}}catch(p){if(ie(p))return{data:{users:[]},error:p};throw p}}async getUserById(e){ws(e);try{return await le(this.fetch,"GET",`${this.url}/admin/users/${e}`,{headers:this.headers,xform:ni})}catch(i){if(ie(i))return{data:{user:null},error:i};throw i}}async updateUserById(e,i){ws(e);try{return await le(this.fetch,"PUT",`${this.url}/admin/users/${e}`,{body:i,headers:this.headers,xform:ni})}catch(a){if(ie(a))return{data:{user:null},error:a};throw a}}async deleteUser(e,i=!1){ws(e);try{return await le(this.fetch,"DELETE",`${this.url}/admin/users/${e}`,{headers:this.headers,body:{should_soft_delete:i},xform:ni})}catch(a){if(ie(a))return{data:{user:null},error:a};throw a}}async _listFactors(e){ws(e.userId);try{const{data:i,error:a}=await le(this.fetch,"GET",`${this.url}/admin/users/${e.userId}/factors`,{headers:this.headers,xform:l=>({data:{factors:l},error:null})});return{data:i,error:a}}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _deleteFactor(e){ws(e.userId),ws(e.id);try{return{data:await le(this.fetch,"DELETE",`${this.url}/admin/users/${e.userId}/factors/${e.id}`,{headers:this.headers}),error:null}}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _listOAuthClients(e){var i,a,l,c,u,d,m;try{const p={nextPage:null,lastPage:0,total:0},y=await le(this.fetch,"GET",`${this.url}/admin/oauth/clients`,{headers:this.headers,noResolveJson:!0,query:{page:(a=(i=e==null?void 0:e.page)===null||i===void 0?void 0:i.toString())!==null&&a!==void 0?a:"",per_page:(c=(l=e==null?void 0:e.perPage)===null||l===void 0?void 0:l.toString())!==null&&c!==void 0?c:""},xform:Xb});if(y.error)throw y.error;const v=await y.json(),x=(u=y.headers.get("x-total-count"))!==null&&u!==void 0?u:0,_=(m=(d=y.headers.get("link"))===null||d===void 0?void 0:d.split(","))!==null&&m!==void 0?m:[];return _.length>0&&(_.forEach(S=>{const T=parseInt(S.split(";")[0].split("=")[1].substring(0,1)),k=JSON.parse(S.split(";")[1].split("=")[1]);p[`${k}Page`]=T}),p.total=parseInt(x)),{data:Object.assign(Object.assign({},v),p),error:null}}catch(p){if(ie(p))return{data:{clients:[]},error:p};throw p}}async _createOAuthClient(e){try{return await le(this.fetch,"POST",`${this.url}/admin/oauth/clients`,{body:e,headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _getOAuthClient(e){try{return await le(this.fetch,"GET",`${this.url}/admin/oauth/clients/${e}`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _updateOAuthClient(e,i){try{return await le(this.fetch,"PUT",`${this.url}/admin/oauth/clients/${e}`,{body:i,headers:this.headers,xform:a=>({data:a,error:null})})}catch(a){if(ie(a))return{data:null,error:a};throw a}}async _deleteOAuthClient(e){try{return await le(this.fetch,"DELETE",`${this.url}/admin/oauth/clients/${e}`,{headers:this.headers,noResolveJson:!0}),{data:null,error:null}}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _regenerateOAuthClientSecret(e){try{return await le(this.fetch,"POST",`${this.url}/admin/oauth/clients/${e}/regenerate_secret`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _listCustomProviders(e){try{const i={};return e!=null&&e.type&&(i.type=e.type),await le(this.fetch,"GET",`${this.url}/admin/custom-providers`,{headers:this.headers,query:i,xform:a=>{var l;return{data:{providers:(l=a==null?void 0:a.providers)!==null&&l!==void 0?l:[]},error:null}}})}catch(i){if(ie(i))return{data:{providers:[]},error:i};throw i}}async _createCustomProvider(e){try{return await le(this.fetch,"POST",`${this.url}/admin/custom-providers`,{body:e,headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _getCustomProvider(e){try{return await le(this.fetch,"GET",`${this.url}/admin/custom-providers/${e}`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _updateCustomProvider(e,i){try{return await le(this.fetch,"PUT",`${this.url}/admin/custom-providers/${e}`,{body:i,headers:this.headers,xform:a=>({data:a,error:null})})}catch(a){if(ie(a))return{data:null,error:a};throw a}}async _deleteCustomProvider(e){try{return await le(this.fetch,"DELETE",`${this.url}/admin/custom-providers/${e}`,{headers:this.headers,noResolveJson:!0}),{data:null,error:null}}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _adminListPasskeys(e){Un(this.experimental),ws(e.userId);try{return await le(this.fetch,"GET",`${this.url}/admin/users/${e.userId}/passkeys`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ie(i))return{data:null,error:i};throw i}}async _adminDeletePasskey(e){Un(this.experimental),ws(e.userId),ws(e.passkeyId);try{return await le(this.fetch,"DELETE",`${this.url}/admin/users/${e.userId}/passkeys/${e.passkeyId}`,{headers:this.headers,noResolveJson:!0}),{data:null,error:null}}catch(i){if(ie(i))return{data:null,error:i};throw i}}}function Jb(n={}){return{getItem:e=>n[e]||null,setItem:(e,i)=>{n[e]=i},removeItem:e=>{delete n[e]}}}globalThis&&Nw()&&globalThis.localStorage&&globalThis.localStorage.getItem("supabase.gotrue-js.locks.debug");class TC extends Error{constructor(e){super(e),this.isAcquireTimeout=!0}}function EC(){if(typeof globalThis!="object")try{Object.defineProperty(Object.prototype,"__magic__",{get:function(){return this},configurable:!0}),__magic__.globalThis=__magic__,delete Object.prototype.__magic__}catch{typeof self<"u"&&(self.globalThis=self)}}function Cw(n){if(!/^0x[a-fA-F0-9]{40}$/.test(n))throw new Error(`@supabase/auth-js: Address "${n}" is invalid.`);return n.toLowerCase()}function AC(n){return parseInt(n,16)}function jC(n){const e=new TextEncoder().encode(n);return"0x"+Array.from(e,a=>a.toString(16).padStart(2,"0")).join("")}function NC(n){var e;const{chainId:i,domain:a,expirationTime:l,issuedAt:c=new Date,nonce:u,notBefore:d,requestId:m,resources:p,scheme:y,uri:v,version:x}=n;{if(!Number.isInteger(i))throw new Error(`@supabase/auth-js: Invalid SIWE message field "chainId". Chain ID must be a EIP-155 chain ID. Provided value: ${i}`);if(!a)throw new Error('@supabase/auth-js: Invalid SIWE message field "domain". Domain must be provided.');if(u&&u.length<8)throw new Error(`@supabase/auth-js: Invalid SIWE message field "nonce". Nonce must be at least 8 characters. Provided value: ${u}`);if(!v)throw new Error('@supabase/auth-js: Invalid SIWE message field "uri". URI must be provided.');if(x!=="1")throw new Error(`@supabase/auth-js: Invalid SIWE message field "version". Version must be '1'. Provided value: ${x}`);if(!((e=n.statement)===null||e===void 0)&&e.includes(` -`))throw new Error(`@supabase/auth-js: Invalid SIWE message field "statement". Statement must not include '\\n'. Provided value: ${n.statement}`)}const _=Cw(n.address),S=y?`${y}://${a}`:a,T=n.statement?`${n.statement} -`:"",k=`${S} wants you to sign in with your Ethereum account: -${_} - -${T}`;let C=`URI: ${v} -Version: ${x} -Chain ID: ${i}${u?` -Nonce: ${u}`:""} -Issued At: ${c.toISOString()}`;if(l&&(C+=` -Expiration Time: ${l.toISOString()}`),d&&(C+=` -Not Before: ${d.toISOString()}`),m&&(C+=` -Request ID: ${m}`),p){let M=` -Resources:`;for(const N of p){if(!N||typeof N!="string")throw new Error(`@supabase/auth-js: Invalid SIWE message field "resources". Every resource must be a valid string. Provided value: ${N}`);M+=` -- ${N}`}C+=M}return`${k} -${C}`}class yt extends Error{constructor({message:e,code:i,cause:a,name:l}){var c;super(e,{cause:a}),this.__isWebAuthnError=!0,this.name=(c=l??(a instanceof Error?a.name:void 0))!==null&&c!==void 0?c:"Unknown Error",this.code=i}toJSON(){return{name:this.name,message:this.message,code:this.code}}}class Rc extends yt{constructor(e,i){super({code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:i,message:e}),this.name="WebAuthnUnknownError",this.originalError=i}}function kC({error:n,options:e}){var i,a,l;const{publicKey:c}=e;if(!c)throw Error("options was missing required publicKey property");if(n.name==="AbortError"){if(e.signal instanceof AbortSignal)return new yt({message:"Registration ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:n})}else if(n.name==="ConstraintError"){if(((i=c.authenticatorSelection)===null||i===void 0?void 0:i.requireResidentKey)===!0)return new yt({message:"Discoverable credentials were required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",cause:n});if(e.mediation==="conditional"&&((a=c.authenticatorSelection)===null||a===void 0?void 0:a.userVerification)==="required")return new yt({message:"User verification was required during automatic registration but it could not be performed",code:"ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",cause:n});if(((l=c.authenticatorSelection)===null||l===void 0?void 0:l.userVerification)==="required")return new yt({message:"User verification was required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",cause:n})}else{if(n.name==="InvalidStateError")return new yt({message:"The authenticator was previously registered",code:"ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",cause:n});if(n.name==="NotAllowedError")return new yt({message:n.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n});if(n.name==="NotSupportedError")return c.pubKeyCredParams.filter(d=>d.type==="public-key").length===0?new yt({message:'No entry in pubKeyCredParams was of type "public-key"',code:"ERROR_MALFORMED_PUBKEYCREDPARAMS",cause:n}):new yt({message:"No available authenticator supported any of the specified pubKeyCredParams algorithms",code:"ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",cause:n});if(n.name==="SecurityError"){const u=window.location.hostname;if(Rw(u)){if(c.rp.id!==u)return new yt({message:`The RP ID "${c.rp.id}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:n})}else return new yt({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:n})}else if(n.name==="TypeError"){if(c.user.id.byteLength<1||c.user.id.byteLength>64)return new yt({message:"User ID was not between 1 and 64 characters",code:"ERROR_INVALID_USER_ID_LENGTH",cause:n})}else if(n.name==="UnknownError")return new yt({message:"The authenticator was unable to process the specified options, or could not create a new credential",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:n})}return new yt({message:"a Non-Webauthn related error has occurred",code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n})}function CC({error:n,options:e}){const{publicKey:i}=e;if(!i)throw Error("options was missing required publicKey property");if(n.name==="AbortError"){if(e.signal instanceof AbortSignal)return new yt({message:"Authentication ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:n})}else{if(n.name==="NotAllowedError")return new yt({message:n.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n});if(n.name==="SecurityError"){const a=window.location.hostname;if(Rw(a)){if(i.rpId!==a)return new yt({message:`The RP ID "${i.rpId}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:n})}else return new yt({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:n})}else if(n.name==="UnknownError")return new yt({message:"The authenticator was unable to process the specified options, or could not create a new assertion signature",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:n})}return new yt({message:"a Non-Webauthn related error has occurred",code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n})}class RC{createNewAbortSignal(){if(this.controller){const i=new Error("Cancelling existing WebAuthn API call for new one");i.name="AbortError",this.controller.abort(i)}const e=new AbortController;return this.controller=e,e.signal}cancelCeremony(){if(this.controller){const e=new Error("Manually cancelling existing WebAuthn API call");e.name="AbortError",this.controller.abort(e),this.controller=void 0}}}const vf=new RC;function Qb(n){if(!n)throw new Error("Credential creation options are required");if(typeof PublicKeyCredential<"u"&&"parseCreationOptionsFromJSON"in PublicKeyCredential&&typeof PublicKeyCredential.parseCreationOptionsFromJSON=="function")return PublicKeyCredential.parseCreationOptionsFromJSON(n);const{challenge:e,user:i,excludeCredentials:a}=n,l=Hc(n,["challenge","user","excludeCredentials"]),c=Fa(e).buffer,u=Object.assign(Object.assign({},i),{id:Fa(i.id).buffer}),d=Object.assign(Object.assign({},l),{challenge:c,user:u});if(a&&a.length>0){d.excludeCredentials=new Array(a.length);for(let m=0;m0){c.allowCredentials=new Array(i.length);for(let u=0;ul!==null&&typeof l=="object"&&!Array.isArray(l),i=l=>l instanceof ArrayBuffer||ArrayBuffer.isView(l),a={};for(const l of n)if(l)for(const c in l){const u=l[c];if(u!==void 0)if(Array.isArray(u))a[c]=u;else if(i(u))a[c]=u;else if(e(u)){const d=a[c];e(d)?a[c]=Dc(d,u):a[c]=Dc(u)}else a[c]=u}return a}function MC(n,e){return Dc(OC,n,e||{})}function BC(n,e){return Dc(DC,n,e||{})}class UC{constructor(e){this.client=e,this.enroll=this._enroll.bind(this),this.challenge=this._challenge.bind(this),this.verify=this._verify.bind(this),this.authenticate=this._authenticate.bind(this),this.register=this._register.bind(this)}async _enroll(e){return this.client.mfa.enroll(Object.assign(Object.assign({},e),{factorType:"webauthn"}))}async _challenge({factorId:e,webauthn:i,friendlyName:a,signal:l},c){var u;try{const{data:d,error:m}=await this.client.mfa.challenge({factorId:e,webauthn:i});if(!d)return{data:null,error:m};const p=l??vf.createNewAbortSignal();if(d.webauthn.type==="create"){const{user:y}=d.webauthn.credential_options.publicKey;if(!y.name){const v=a;if(v)y.name=`${y.id}:${v}`;else{const _=(await this.client.getUser()).data.user,S=((u=_==null?void 0:_.user_metadata)===null||u===void 0?void 0:u.name)||(_==null?void 0:_.email)||(_==null?void 0:_.id)||"User";y.name=`${y.id}:${S}`}}y.displayName||(y.displayName=y.name)}switch(d.webauthn.type){case"create":{const y=MC(d.webauthn.credential_options.publicKey,c==null?void 0:c.create),{data:v,error:x}=await Ow({publicKey:y,signal:p});return v?{data:{factorId:e,challengeId:d.id,webauthn:{type:d.webauthn.type,credential_response:v}},error:null}:{data:null,error:x}}case"request":{const y=BC(d.webauthn.credential_options.publicKey,c==null?void 0:c.request),{data:v,error:x}=await Dw(Object.assign(Object.assign({},d.webauthn.credential_options),{publicKey:y,signal:p}));return v?{data:{factorId:e,challengeId:d.id,webauthn:{type:d.webauthn.type,credential_response:v}},error:null}:{data:null,error:x}}}}catch(d){return ie(d)?{data:null,error:d}:{data:null,error:new Vn("Unexpected error in challenge",d)}}}async _verify({challengeId:e,factorId:i,webauthn:a}){return this.client.mfa.verify({factorId:i,challengeId:e,webauthn:a})}async _authenticate({factorId:e,webauthn:{rpId:i=typeof window<"u"?window.location.hostname:void 0,rpOrigins:a=typeof window<"u"?[window.location.origin]:void 0,signal:l}={}},c){if(!i)return{data:null,error:new xo("rpId is required for WebAuthn authentication")};try{if(!Oc())return{data:null,error:new Vn("Browser does not support WebAuthn",null)};const{data:u,error:d}=await this.challenge({factorId:e,webauthn:{rpId:i,rpOrigins:a},signal:l},{request:c});if(!u)return{data:null,error:d};const{webauthn:m}=u;return this._verify({factorId:e,challengeId:u.challengeId,webauthn:{type:m.type,rpId:i,rpOrigins:a,credential_response:m.credential_response}})}catch(u){return ie(u)?{data:null,error:u}:{data:null,error:new Vn("Unexpected error in authenticate",u)}}}async _register({friendlyName:e,webauthn:{rpId:i=typeof window<"u"?window.location.hostname:void 0,rpOrigins:a=typeof window<"u"?[window.location.origin]:void 0,signal:l}={}},c){if(!i)return{data:null,error:new xo("rpId is required for WebAuthn registration")};try{if(!Oc())return{data:null,error:new Vn("Browser does not support WebAuthn",null)};const{data:u,error:d}=await this._enroll({friendlyName:e});if(!u)return await this.client.mfa.listFactors().then(y=>{var v;return(v=y.data)===null||v===void 0?void 0:v.all.find(x=>x.factor_type==="webauthn"&&x.friendly_name===e&&x.status!=="unverified")}).then(y=>y?this.client.mfa.unenroll({factorId:y==null?void 0:y.id}):void 0),{data:null,error:d};const{data:m,error:p}=await this._challenge({factorId:u.id,friendlyName:u.friendly_name,webauthn:{rpId:i,rpOrigins:a},signal:l},{create:c});return m?this._verify({factorId:u.id,challengeId:m.challengeId,webauthn:{rpId:i,rpOrigins:a,type:m.webauthn.type,credential_response:m.webauthn.credential_response}}):{data:null,error:p}}catch(u){return ie(u)?{data:null,error:u}:{data:null,error:new Vn("Unexpected error in register",u)}}}}EC();const LC={url:Pk,storageKey:Hk,autoRefreshToken:!0,persistSession:!0,detectSessionInUrl:!0,headers:$k,flowType:"implicit",debug:!1,hasCustomAuthorizationHeader:!1,throwOnError:!1,lockAcquireTimeout:5e3,skipAutoInitialize:!1,experimental:{}},Ca={};class wo{get jwks(){var e,i;return(i=(e=Ca[this.storageKey])===null||e===void 0?void 0:e.jwks)!==null&&i!==void 0?i:{keys:[]}}set jwks(e){Ca[this.storageKey]=Object.assign(Object.assign({},Ca[this.storageKey]),{jwks:e})}get jwks_cached_at(){var e,i;return(i=(e=Ca[this.storageKey])===null||e===void 0?void 0:e.cachedAt)!==null&&i!==void 0?i:Number.MIN_SAFE_INTEGER}set jwks_cached_at(e){Ca[this.storageKey]=Object.assign(Object.assign({},Ca[this.storageKey]),{cachedAt:e})}constructor(e){var i,a,l;this.userStorage=null,this.memoryStorage=null,this.stateChangeEmitters=new Map,this.autoRefreshTicker=null,this.autoRefreshTickTimeout=null,this.visibilityChangedCallback=null,this.refreshingDeferred=null,this.lastRefreshFailure=null,this._sessionRemovalEpoch=0,this.initializePromise=null,this._pendingInitNotifications=null,this.detectSessionInUrl=!0,this.hasCustomAuthorizationHeader=!1,this.suppressGetSessionWarning=!1,this.lock=null,this.lockAcquired=!1,this.pendingInLock=[],this.broadcastChannel=null,this.logger=console.log;const c=Object.assign(Object.assign({},LC),e);if(this.storageKey=c.storageKey,this.instanceID=(i=wo.nextInstanceID[this.storageKey])!==null&&i!==void 0?i:0,wo.nextInstanceID[this.storageKey]=this.instanceID+1,this.logDebugMessages=!!c.debug,typeof c.debug=="function"&&(this.logger=c.debug),this.instanceID>0&&Vt()){const u=`${this._logPrefix()} Multiple GoTrueClient instances detected in the same browser context. It is not an error, but this should be avoided as it may produce undefined behavior when used concurrently under the same storage key.`;console.warn(u),this.logDebugMessages&&console.trace(u)}if(this.persistSession=c.persistSession,this.autoRefreshToken=c.autoRefreshToken,this.experimental=(a=c.experimental)!==null&&a!==void 0?a:{},this.admin=new SC({url:c.url,headers:c.headers,fetch:c.fetch,experimental:this.experimental}),this.url=c.url,this.headers=c.headers,this.fetch=kw(c.fetch),this.detectSessionInUrl=c.detectSessionInUrl,this.flowType=c.flowType,this.hasCustomAuthorizationHeader=c.hasCustomAuthorizationHeader,this.throwOnError=c.throwOnError,this.lockAcquireTimeout=c.lockAcquireTimeout,c.lock!=null&&(this.lock=c.lock),this.jwks||(this.jwks={keys:[]},this.jwks_cached_at=Number.MIN_SAFE_INTEGER),this.mfa={verify:this._verify.bind(this),enroll:this._enroll.bind(this),unenroll:this._unenroll.bind(this),challenge:this._challenge.bind(this),listFactors:this._listFactors.bind(this),challengeAndVerify:this._challengeAndVerify.bind(this),getAuthenticatorAssuranceLevel:this._getAuthenticatorAssuranceLevel.bind(this),webauthn:new UC(this)},this.oauth={getAuthorizationDetails:this._getAuthorizationDetails.bind(this),approveAuthorization:this._approveAuthorization.bind(this),denyAuthorization:this._denyAuthorization.bind(this),listGrants:this._listOAuthGrants.bind(this),revokeGrant:this._revokeOAuthGrant.bind(this)},this.passkey={startRegistration:this._startPasskeyRegistration.bind(this),verifyRegistration:this._verifyPasskeyRegistration.bind(this),startAuthentication:this._startPasskeyAuthentication.bind(this),verifyAuthentication:this._verifyPasskeyAuthentication.bind(this),list:this._listPasskeys.bind(this),update:this._updatePasskey.bind(this),delete:this._deletePasskey.bind(this)},this.persistSession?(c.storage?this.storage=c.storage:Nw()?this.storage=globalThis.localStorage:(this.memoryStorage={},this.storage=Jb(this.memoryStorage)),c.userStorage&&(this.userStorage=c.userStorage)):(this.memoryStorage={},this.storage=Jb(this.memoryStorage)),Vt()&&globalThis.BroadcastChannel&&this.persistSession&&this.storageKey){try{this.broadcastChannel=new globalThis.BroadcastChannel(this.storageKey)}catch(u){console.error("Failed to create a new BroadcastChannel, multi-tab state changes will not be available",u)}(l=this.broadcastChannel)===null||l===void 0||l.addEventListener("message",async u=>{this._debug("received broadcast notification from other tab or client",u),(u.data.event==="TOKEN_REFRESHED"||u.data.event==="SIGNED_IN")&&(this.lastRefreshFailure=null);try{await this._notifyAllSubscribers(u.data.event,u.data.session,!1)}catch(d){this._debug("#broadcastChannel","error",d)}})}c.skipAutoInitialize||this.initialize().catch(u=>{this._debug("#initialize()","error",u)})}isThrowOnErrorEnabled(){return this.throwOnError}_returnResult(e){if(this.throwOnError&&e&&e.error)throw e.error;return e}_logPrefix(){return`GoTrueClient@${this.storageKey}:${this.instanceID} (${Ew}) ${new Date().toISOString()}`}_debug(...e){return this.logDebugMessages&&this.logger(this._logPrefix(),...e),this}async initialize(){var e;if(this.initializePromise)return await this.initializePromise;this._pendingInitNotifications=[],this.initializePromise=(async()=>this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._initialize()):await this._initialize())();const i=await this.initializePromise,a=(e=this._pendingInitNotifications)!==null&&e!==void 0?e:[];this._pendingInitNotifications=null;for(const l of a)await this._notifyAllSubscribers(l.event,l.session,l.broadcast);return i}async _initialize(){var e;try{let i={},a="none";if(Vt()&&(i=sC(window.location.href),this._isImplicitGrantCallback(i)?a="implicit":await this._isPKCECallback(i)&&(a="pkce")),Vt()&&this.detectSessionInUrl&&a!=="none"){const{data:l,error:c}=await this._getSessionFromURL(i,a);if(c){if(this._debug("#_initialize()","error detecting session from URL",c),Kk(c)){const m=(e=c.details)===null||e===void 0?void 0:e.code;if(m==="identity_already_exists"||m==="identity_not_found"||m==="single_identity_not_deletable")return{error:c}}return{error:c}}const{session:u,redirectType:d}=l;return this._debug("#_initialize()","detected session in URL",u,"redirect type",d),await this._saveSession(u),setTimeout(async()=>{d==="recovery"?await this._notifyAllSubscribers("PASSWORD_RECOVERY",u):await this._notifyAllSubscribers("SIGNED_IN",u)},0),{error:null}}return await this._recoverAndRefresh(),{error:null}}catch(i){return ie(i)?this._returnResult({error:i}):this._returnResult({error:new Vn("Unexpected error during initialization",i)})}finally{await this._handleVisibilityChange(),this._debug("#_initialize()","end")}}async signInAnonymously(e){var i,a,l;try{const c=await le(this.fetch,"POST",`${this.url}/signup`,{headers:this.headers,body:{data:(a=(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.data)!==null&&a!==void 0?a:{},gotrue_meta_security:{captcha_token:(l=e==null?void 0:e.options)===null||l===void 0?void 0:l.captchaToken}},xform:En}),{data:u,error:d}=c;if(d||!u)return this._returnResult({data:{user:null,session:null},error:d});const m=u.session,p=u.user;return u.session&&(await this._saveSession(u.session),await this._notifyAllSubscribers("SIGNED_IN",m)),this._returnResult({data:{user:p,session:m},error:null})}catch(c){if(ie(c))return this._returnResult({data:{user:null,session:null},error:c});throw c}}async signUp(e){var i,a,l;try{let c;if("email"in e){const{email:y,password:v,options:x}=e;let _=null,S=null;this.flowType==="pkce"&&([_,S]=await Ci(this.storage,this.storageKey)),c=await le(this.fetch,"POST",`${this.url}/signup`,{headers:this.headers,redirectTo:x==null?void 0:x.emailRedirectTo,body:{email:y,password:v,data:(i=x==null?void 0:x.data)!==null&&i!==void 0?i:{},gotrue_meta_security:{captcha_token:x==null?void 0:x.captchaToken},code_challenge:_,code_challenge_method:S},xform:En})}else if("phone"in e){const{phone:y,password:v,options:x}=e;c=await le(this.fetch,"POST",`${this.url}/signup`,{headers:this.headers,body:{phone:y,password:v,data:(a=x==null?void 0:x.data)!==null&&a!==void 0?a:{},channel:(l=x==null?void 0:x.channel)!==null&&l!==void 0?l:"sms",gotrue_meta_security:{captcha_token:x==null?void 0:x.captchaToken}},xform:En})}else throw new nc("You must provide either an email or phone number and a password");const{data:u,error:d}=c;if(d||!u)return await dt(this.storage,`${this.storageKey}-code-verifier`),this._returnResult({data:{user:null,session:null},error:d});const m=u.session,p=u.user;return u.session&&(await this._saveSession(u.session),await this._notifyAllSubscribers("SIGNED_IN",m)),this._returnResult({data:{user:p,session:m},error:null})}catch(c){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(c))return this._returnResult({data:{user:null,session:null},error:c});throw c}}async signInWithPassword(e){try{let i;if("email"in e){const{email:c,password:u,options:d}=e;i=await le(this.fetch,"POST",`${this.url}/token?grant_type=password`,{headers:this.headers,body:{email:c,password:u,gotrue_meta_security:{captcha_token:d==null?void 0:d.captchaToken}},xform:Yb})}else if("phone"in e){const{phone:c,password:u,options:d}=e;i=await le(this.fetch,"POST",`${this.url}/token?grant_type=password`,{headers:this.headers,body:{phone:c,password:u,gotrue_meta_security:{captcha_token:d==null?void 0:d.captchaToken}},xform:Yb})}else throw new nc("You must provide either an email or phone number and a password");const{data:a,error:l}=i;if(l)return this._returnResult({data:{user:null,session:null},error:l});if(!a||!a.session||!a.user){const c=new ka;return this._returnResult({data:{user:null,session:null},error:c})}return a.session&&(await this._saveSession(a.session),await this._notifyAllSubscribers("SIGNED_IN",a.session)),this._returnResult({data:Object.assign({user:a.user,session:a.session},a.weak_password?{weakPassword:a.weak_password}:null),error:l})}catch(i){if(ie(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async signInWithOAuth(e){var i,a,l,c;return await this._handleProviderSignIn(e.provider,{redirectTo:(i=e.options)===null||i===void 0?void 0:i.redirectTo,scopes:(a=e.options)===null||a===void 0?void 0:a.scopes,queryParams:(l=e.options)===null||l===void 0?void 0:l.queryParams,skipBrowserRedirect:(c=e.options)===null||c===void 0?void 0:c.skipBrowserRedirect})}async exchangeCodeForSession(e){return await this.initializePromise,this.lock!=null?this._acquireLock(this.lockAcquireTimeout,async()=>this._exchangeCodeForSession(e)):this._exchangeCodeForSession(e)}async signInWithWeb3(e){const{chain:i}=e;switch(i){case"ethereum":return await this.signInWithEthereum(e);case"solana":return await this.signInWithSolana(e);default:throw new Error(`@supabase/auth-js: Unsupported chain "${i}"`)}}async signInWithEthereum(e){var i,a,l,c,u,d,m,p,y,v,x;let _,S;if("message"in e)_=e.message,S=e.signature;else{const{chain:T,wallet:k,statement:C,options:M}=e;let N;if(Vt())if(typeof k=="object")N=k;else{const ne=window;if("ethereum"in ne&&typeof ne.ethereum=="object"&&"request"in ne.ethereum&&typeof ne.ethereum.request=="function")N=ne.ethereum;else throw new Error("@supabase/auth-js: No compatible Ethereum wallet interface on the window object (window.ethereum) detected. Make sure the user already has a wallet installed and connected for this app. Prefer passing the wallet interface object directly to signInWithWeb3({ chain: 'ethereum', wallet: resolvedUserWallet }) instead.")}else{if(typeof k!="object"||!(M!=null&&M.url))throw new Error("@supabase/auth-js: Both wallet and url must be specified in non-browser environments.");N=k}const z=new URL((i=M==null?void 0:M.url)!==null&&i!==void 0?i:window.location.href),Y=await N.request({method:"eth_requestAccounts"}).then(ne=>ne).catch(()=>{throw new Error("@supabase/auth-js: Wallet method eth_requestAccounts is missing or invalid")});if(!Y||Y.length===0)throw new Error("@supabase/auth-js: No accounts available. Please ensure the wallet is connected.");const Z=Cw(Y[0]);let F=(a=M==null?void 0:M.signInWithEthereum)===null||a===void 0?void 0:a.chainId;if(!F){const ne=await N.request({method:"eth_chainId"});F=AC(ne)}const G={domain:z.host,address:Z,statement:C,uri:z.href,version:"1",chainId:F,nonce:(l=M==null?void 0:M.signInWithEthereum)===null||l===void 0?void 0:l.nonce,issuedAt:(u=(c=M==null?void 0:M.signInWithEthereum)===null||c===void 0?void 0:c.issuedAt)!==null&&u!==void 0?u:new Date,expirationTime:(d=M==null?void 0:M.signInWithEthereum)===null||d===void 0?void 0:d.expirationTime,notBefore:(m=M==null?void 0:M.signInWithEthereum)===null||m===void 0?void 0:m.notBefore,requestId:(p=M==null?void 0:M.signInWithEthereum)===null||p===void 0?void 0:p.requestId,resources:(y=M==null?void 0:M.signInWithEthereum)===null||y===void 0?void 0:y.resources};_=NC(G),S=await N.request({method:"personal_sign",params:[jC(_),Z]})}try{const{data:T,error:k}=await le(this.fetch,"POST",`${this.url}/token?grant_type=web3`,{headers:this.headers,body:Object.assign({chain:"ethereum",message:_,signature:S},!((v=e.options)===null||v===void 0)&&v.captchaToken?{gotrue_meta_security:{captcha_token:(x=e.options)===null||x===void 0?void 0:x.captchaToken}}:null),xform:En});if(k)throw k;if(!T||!T.session||!T.user){const C=new ka;return this._returnResult({data:{user:null,session:null},error:C})}return T.session&&(await this._saveSession(T.session),await this._notifyAllSubscribers("SIGNED_IN",T.session)),this._returnResult({data:Object.assign({},T),error:k})}catch(T){if(ie(T))return this._returnResult({data:{user:null,session:null},error:T});throw T}}async signInWithSolana(e){var i,a,l,c,u,d,m,p,y,v,x,_;let S,T;if("message"in e)S=e.message,T=e.signature;else{const{chain:k,wallet:C,statement:M,options:N}=e;let z;if(Vt())if(typeof C=="object")z=C;else{const Z=window;if("solana"in Z&&typeof Z.solana=="object"&&("signIn"in Z.solana&&typeof Z.solana.signIn=="function"||"signMessage"in Z.solana&&typeof Z.solana.signMessage=="function"))z=Z.solana;else throw new Error("@supabase/auth-js: No compatible Solana wallet interface on the window object (window.solana) detected. Make sure the user already has a wallet installed and connected for this app. Prefer passing the wallet interface object directly to signInWithWeb3({ chain: 'solana', wallet: resolvedUserWallet }) instead.")}else{if(typeof C!="object"||!(N!=null&&N.url))throw new Error("@supabase/auth-js: Both wallet and url must be specified in non-browser environments.");z=C}const Y=new URL((i=N==null?void 0:N.url)!==null&&i!==void 0?i:window.location.href);if("signIn"in z&&z.signIn){const Z=await z.signIn(Object.assign(Object.assign(Object.assign({issuedAt:new Date().toISOString()},N==null?void 0:N.signInWithSolana),{version:"1",domain:Y.host,uri:Y.href}),M?{statement:M}:null));let F;if(Array.isArray(Z)&&Z[0]&&typeof Z[0]=="object")F=Z[0];else if(Z&&typeof Z=="object"&&"signedMessage"in Z&&"signature"in Z)F=Z;else throw new Error("@supabase/auth-js: Wallet method signIn() returned unrecognized value");if("signedMessage"in F&&"signature"in F&&(typeof F.signedMessage=="string"||F.signedMessage instanceof Uint8Array)&&F.signature instanceof Uint8Array)S=typeof F.signedMessage=="string"?F.signedMessage:new TextDecoder().decode(F.signedMessage),T=F.signature;else throw new Error("@supabase/auth-js: Wallet method signIn() API returned object without signedMessage and signature fields")}else{if(!("signMessage"in z)||typeof z.signMessage!="function"||!("publicKey"in z)||typeof z!="object"||!z.publicKey||!("toBase58"in z.publicKey)||typeof z.publicKey.toBase58!="function")throw new Error("@supabase/auth-js: Wallet does not have a compatible signMessage() and publicKey.toBase58() API");S=[`${Y.host} wants you to sign in with your Solana account:`,z.publicKey.toBase58(),...M?["",M,""]:[""],"Version: 1",`URI: ${Y.href}`,`Issued At: ${(l=(a=N==null?void 0:N.signInWithSolana)===null||a===void 0?void 0:a.issuedAt)!==null&&l!==void 0?l:new Date().toISOString()}`,...!((c=N==null?void 0:N.signInWithSolana)===null||c===void 0)&&c.notBefore?[`Not Before: ${N.signInWithSolana.notBefore}`]:[],...!((u=N==null?void 0:N.signInWithSolana)===null||u===void 0)&&u.expirationTime?[`Expiration Time: ${N.signInWithSolana.expirationTime}`]:[],...!((d=N==null?void 0:N.signInWithSolana)===null||d===void 0)&&d.chainId?[`Chain ID: ${N.signInWithSolana.chainId}`]:[],...!((m=N==null?void 0:N.signInWithSolana)===null||m===void 0)&&m.nonce?[`Nonce: ${N.signInWithSolana.nonce}`]:[],...!((p=N==null?void 0:N.signInWithSolana)===null||p===void 0)&&p.requestId?[`Request ID: ${N.signInWithSolana.requestId}`]:[],...!((v=(y=N==null?void 0:N.signInWithSolana)===null||y===void 0?void 0:y.resources)===null||v===void 0)&&v.length?["Resources",...N.signInWithSolana.resources.map(F=>`- ${F}`)]:[]].join(` -`);const Z=await z.signMessage(new TextEncoder().encode(S),"utf8");if(!Z||!(Z instanceof Uint8Array))throw new Error("@supabase/auth-js: Wallet signMessage() API returned an recognized value");T=Z}}try{const{data:k,error:C}=await le(this.fetch,"POST",`${this.url}/token?grant_type=web3`,{headers:this.headers,body:Object.assign({chain:"solana",message:S,signature:Bi(T)},!((x=e.options)===null||x===void 0)&&x.captchaToken?{gotrue_meta_security:{captcha_token:(_=e.options)===null||_===void 0?void 0:_.captchaToken}}:null),xform:En});if(C)throw C;if(!k||!k.session||!k.user){const M=new ka;return this._returnResult({data:{user:null,session:null},error:M})}return k.session&&(await this._saveSession(k.session),await this._notifyAllSubscribers("SIGNED_IN",k.session)),this._returnResult({data:Object.assign({},k),error:C})}catch(k){if(ie(k))return this._returnResult({data:{user:null,session:null},error:k});throw k}}async _exchangeCodeForSession(e){const i=await Mn(this.storage,`${this.storageKey}-code-verifier`),[a,l]=(i??"").split("/");try{if(!a&&this.flowType==="pkce")throw new Yk;const{data:c,error:u}=await le(this.fetch,"POST",`${this.url}/token?grant_type=pkce`,{headers:this.headers,body:{auth_code:e,code_verifier:a},xform:En});if(await dt(this.storage,`${this.storageKey}-code-verifier`),u)throw u;if(!c||!c.session||!c.user){const d=new ka;return this._returnResult({data:{user:null,session:null,redirectType:null},error:d})}return c.session&&(await this._saveSession(c.session),await this._notifyAllSubscribers(l==="recovery"?"PASSWORD_RECOVERY":"SIGNED_IN",c.session)),this._returnResult({data:Object.assign(Object.assign({},c),{redirectType:l??null}),error:u})}catch(c){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(c))return this._returnResult({data:{user:null,session:null,redirectType:null},error:c});throw c}}async signInWithIdToken(e){try{const{options:i,provider:a,token:l,access_token:c,nonce:u}=e,d=await le(this.fetch,"POST",`${this.url}/token?grant_type=id_token`,{headers:this.headers,body:{provider:a,id_token:l,access_token:c,nonce:u,gotrue_meta_security:{captcha_token:i==null?void 0:i.captchaToken}},xform:En}),{data:m,error:p}=d;if(p)return this._returnResult({data:{user:null,session:null},error:p});if(!m||!m.session||!m.user){const y=new ka;return this._returnResult({data:{user:null,session:null},error:y})}return m.session&&(await this._saveSession(m.session),await this._notifyAllSubscribers("SIGNED_IN",m.session)),this._returnResult({data:m,error:p})}catch(i){if(ie(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async signInWithOtp(e){var i,a,l,c,u;try{if("email"in e){const{email:d,options:m}=e;let p=null,y=null;this.flowType==="pkce"&&([p,y]=await Ci(this.storage,this.storageKey));const{error:v}=await le(this.fetch,"POST",`${this.url}/otp`,{headers:this.headers,body:{email:d,data:(i=m==null?void 0:m.data)!==null&&i!==void 0?i:{},create_user:(a=m==null?void 0:m.shouldCreateUser)!==null&&a!==void 0?a:!0,gotrue_meta_security:{captcha_token:m==null?void 0:m.captchaToken},code_challenge:p,code_challenge_method:y},redirectTo:m==null?void 0:m.emailRedirectTo});return this._returnResult({data:{user:null,session:null},error:v})}if("phone"in e){const{phone:d,options:m}=e,{data:p,error:y}=await le(this.fetch,"POST",`${this.url}/otp`,{headers:this.headers,body:{phone:d,data:(l=m==null?void 0:m.data)!==null&&l!==void 0?l:{},create_user:(c=m==null?void 0:m.shouldCreateUser)!==null&&c!==void 0?c:!0,gotrue_meta_security:{captcha_token:m==null?void 0:m.captchaToken},channel:(u=m==null?void 0:m.channel)!==null&&u!==void 0?u:"sms"}});return this._returnResult({data:{user:null,session:null,messageId:p==null?void 0:p.message_id},error:y})}throw new nc("You must provide either an email or phone number.")}catch(d){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(d))return this._returnResult({data:{user:null,session:null},error:d});throw d}}async verifyOtp(e){var i,a;try{let l,c;"options"in e&&(l=(i=e.options)===null||i===void 0?void 0:i.redirectTo,c=(a=e.options)===null||a===void 0?void 0:a.captchaToken);const{data:u,error:d}=await le(this.fetch,"POST",`${this.url}/verify`,{headers:this.headers,body:Object.assign(Object.assign({},e),{gotrue_meta_security:{captcha_token:c}}),redirectTo:l,xform:En});if(d)throw d;if(!u)throw new Error("An error occurred on token verification.");const m=u.session,p=u.user;return m!=null&&m.access_token&&(await this._saveSession(m),await this._notifyAllSubscribers(e.type=="recovery"?"PASSWORD_RECOVERY":"SIGNED_IN",m)),this._returnResult({data:{user:p,session:m},error:null})}catch(l){if(ie(l))return this._returnResult({data:{user:null,session:null},error:l});throw l}}async signInWithSSO(e){var i,a,l,c,u;try{let d=null,m=null;this.flowType==="pkce"&&([d,m]=await Ci(this.storage,this.storageKey));const p=await le(this.fetch,"POST",`${this.url}/sso`,{body:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},"providerId"in e?{provider_id:e.providerId}:null),"domain"in e?{domain:e.domain}:null),{redirect_to:(a=(i=e.options)===null||i===void 0?void 0:i.redirectTo)!==null&&a!==void 0?a:void 0}),!((l=e==null?void 0:e.options)===null||l===void 0)&&l.captchaToken?{gotrue_meta_security:{captcha_token:e.options.captchaToken}}:null),{skip_http_redirect:!0,code_challenge:d,code_challenge_method:m}),headers:this.headers,xform:xC});return!((c=p.data)===null||c===void 0)&&c.url&&Vt()&&!(!((u=e.options)===null||u===void 0)&&u.skipBrowserRedirect)&&window.location.assign(p.data.url),this._returnResult(p)}catch(d){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(d))return this._returnResult({data:null,error:d});throw d}}async reauthenticate(){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._reauthenticate()):await this._reauthenticate()}async _reauthenticate(){try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;if(a)throw a;if(!i)throw new kt;const{error:l}=await le(this.fetch,"GET",`${this.url}/reauthenticate`,{headers:this.headers,jwt:i.access_token});return this._returnResult({data:{user:null,session:null},error:l})})}catch(e){if(ie(e))return this._returnResult({data:{user:null,session:null},error:e});throw e}}async resend(e){try{const i=`${this.url}/resend`;if("email"in e){const{email:a,type:l,options:c}=e;let u=null,d=null;this.flowType==="pkce"&&([u,d]=await Ci(this.storage,this.storageKey));const{error:m}=await le(this.fetch,"POST",i,{headers:this.headers,body:{email:a,type:l,gotrue_meta_security:{captcha_token:c==null?void 0:c.captchaToken},code_challenge:u,code_challenge_method:d},redirectTo:c==null?void 0:c.emailRedirectTo});return m&&await dt(this.storage,`${this.storageKey}-code-verifier`),this._returnResult({data:{user:null,session:null},error:m})}else if("phone"in e){const{phone:a,type:l,options:c}=e,{data:u,error:d}=await le(this.fetch,"POST",i,{headers:this.headers,body:{phone:a,type:l,gotrue_meta_security:{captcha_token:c==null?void 0:c.captchaToken}}});return this._returnResult({data:{user:null,session:null,messageId:u==null?void 0:u.message_id},error:d})}throw new nc("You must provide either an email or phone number and a type")}catch(i){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async getSession(){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>this._useSession(async e=>e)):await this._useSession(async e=>e)}async _acquireLock(e,i){this._debug("#_acquireLock","begin",e);try{if(this.lockAcquired){const a=this.pendingInLock.length?this.pendingInLock[this.pendingInLock.length-1]:Promise.resolve(),l=(async()=>(await a,await i()))();return this.pendingInLock.push((async()=>{try{await l}catch{}})()),l}return await this.lock(`lock:${this.storageKey}`,e,async()=>{this._debug("#_acquireLock","lock acquired for storage key",this.storageKey);try{this.lockAcquired=!0;const a=i();for(this.pendingInLock.push((async()=>{try{await a}catch{}})()),await a;this.pendingInLock.length;){const l=[...this.pendingInLock];await Promise.all(l),this.pendingInLock.splice(0,l.length)}return await a}finally{this._debug("#_acquireLock","lock released for storage key",this.storageKey),this.lockAcquired=!1}})}finally{this._debug("#_acquireLock","end")}}async _useSession(e){this._debug("#_useSession","begin");try{const i=await this.__loadSession();return await e(i)}finally{this._debug("#_useSession","end")}}async __loadSession(){this._debug("#__loadSession()","begin"),this.lock!=null&&!this.lockAcquired&&this._debug("#__loadSession()","used outside of an acquired lock!",new Error().stack);try{let e=null;const i=await Mn(this.storage,this.storageKey);if(this._debug("#getSession()","session from storage",i),i!==null&&(this._isValidSession(i)?e=i:(this._debug("#getSession()","session from storage is not valid"),await this._removeSession())),!e)return{data:{session:null},error:null};const a=e.expires_at?e.expires_at*1e3-Date.now()Date.now())){const d=await Mn(this.storage,this.storageKey);if(d&&d.refresh_token===e.refresh_token)return this._returnResult({data:{session:e},error:null})}return this._returnResult({data:{session:null},error:c})}return this._returnResult({data:{session:l},error:null})}finally{this._debug("#__loadSession()","end")}}async getUser(e){if(e)return await this._getUser(e);await this.initializePromise;let i;return this.lock!=null?i=await this._acquireLock(this.lockAcquireTimeout,async()=>await this._getUser()):i=await this._getUser(),i.data.user&&(this.suppressGetSessionWarning=!0),i}async _getUser(e){try{return e?await le(this.fetch,"GET",`${this.url}/user`,{headers:this.headers,jwt:e,xform:ni}):await this._useSession(async i=>{var a,l,c;const{data:u,error:d}=i;if(d)throw d;return!(!((a=u.session)===null||a===void 0)&&a.access_token)&&!this.hasCustomAuthorizationHeader?{data:{user:null},error:new kt}:await le(this.fetch,"GET",`${this.url}/user`,{headers:this.headers,jwt:(c=(l=u.session)===null||l===void 0?void 0:l.access_token)!==null&&c!==void 0?c:void 0,xform:ni})})}catch(i){if(ie(i))return tc(i)&&(await this._removeSession(),await dt(this.storage,`${this.storageKey}-code-verifier`)),this._returnResult({data:{user:null},error:i});throw i}}async updateUser(e,i={}){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._updateUser(e,i)):await this._updateUser(e,i)}async _updateUser(e,i={}){try{return await this._useSession(async a=>{const{data:l,error:c}=a;if(c)throw c;if(!l.session)throw new kt;const u=l.session;let d=null,m=null;this.flowType==="pkce"&&e.email!=null&&([d,m]=await Ci(this.storage,this.storageKey));const{data:p,error:y}=await le(this.fetch,"PUT",`${this.url}/user`,{headers:this.headers,redirectTo:i==null?void 0:i.emailRedirectTo,body:Object.assign(Object.assign({},e),{code_challenge:d,code_challenge_method:m}),jwt:u.access_token,xform:ni});if(y)throw y;return u.user=p.user,await this._saveSession(u),await this._notifyAllSubscribers("USER_UPDATED",u),this._returnResult({data:{user:u.user},error:null})})}catch(a){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(a))return this._returnResult({data:{user:null},error:a});throw a}}async setSession(e){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._setSession(e)):await this._setSession(e)}async _setSession(e){try{if(!e.access_token||!e.refresh_token)throw new kt;const i=Date.now()/1e3;let a=i,l=!0,c=null;const{payload:u}=ic(e.access_token);if(u.exp&&(a=u.exp,l=a<=i),l){const{data:d,error:m}=await this._callRefreshToken(e.refresh_token);if(m)return this._returnResult({data:{user:null,session:null},error:m});if(!d)return{data:{user:null,session:null},error:null};c=d}else{const{data:d,error:m}=await this._getUser(e.access_token);if(m)return this._returnResult({data:{user:null,session:null},error:m});c={access_token:e.access_token,refresh_token:e.refresh_token,user:d.user,token_type:"bearer",expires_in:a-i,expires_at:a},await this._saveSession(c),await this._notifyAllSubscribers("SIGNED_IN",c)}return this._returnResult({data:{user:c.user,session:c},error:null})}catch(i){if(ie(i))return this._returnResult({data:{session:null,user:null},error:i});throw i}}async refreshSession(e){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._refreshSession(e)):await this._refreshSession(e)}async _refreshSession(e){try{return await this._useSession(async i=>{var a;if(!e){const{data:u,error:d}=i;if(d)throw d;e=(a=u.session)!==null&&a!==void 0?a:void 0}if(!(e!=null&&e.refresh_token))throw new kt;const{data:l,error:c}=await this._callRefreshToken(e.refresh_token);return c?this._returnResult({data:{user:null,session:null},error:c}):l?this._returnResult({data:{user:l.user,session:l},error:null}):this._returnResult({data:{user:null,session:null},error:null})})}catch(i){if(ie(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async _getSessionFromURL(e,i){var a;try{if(!Vt())throw new sc("No browser detected.");if(e.error||e.error_description||e.error_code)throw new sc(e.error_description||"Error in URL with unspecified error_description",{error:e.error||"unspecified_error",code:e.error_code||"unspecified_code"});switch(i){case"implicit":if(this.flowType==="pkce")throw new Vb("Not a valid PKCE flow url.");break;case"pkce":if(this.flowType==="implicit")throw new sc("Not a valid implicit grant flow url.");break;default:}if(i==="pkce"){if(this._debug("#_initialize()","begin","is PKCE flow",!0),!e.code)throw new Vb("No code detected.");const{data:N,error:z}=await this._exchangeCodeForSession(e.code);if(z)throw z;const Y=new URL(window.location.href);return Y.searchParams.delete("code"),window.history.replaceState(window.history.state,"",Y.toString()),{data:{session:N.session,redirectType:(a=N.redirectType)!==null&&a!==void 0?a:null},error:null}}const{provider_token:l,provider_refresh_token:c,access_token:u,refresh_token:d,expires_in:m,expires_at:p,token_type:y}=e;if(!u||!m||!d||!y)throw new sc("No session defined in URL");const v=Math.round(Date.now()/1e3),x=parseInt(m);let _=v+x;p&&(_=parseInt(p));const S=_-v;S*1e3<=Es&&console.warn(`@supabase/gotrue-js: Session as retrieved from URL expires in ${S}s, should have been closer to ${x}s`);const T=_-x;v-T>=120?console.warn("@supabase/gotrue-js: Session as retrieved from URL was issued over 120s ago, URL could be stale",T,_,v):v-T<0&&console.warn("@supabase/gotrue-js: Session as retrieved from URL was issued in the future? Check the device clock for skew",T,_,v);const{data:k,error:C}=await this._getUser(u);if(C)throw C;const M={provider_token:l,provider_refresh_token:c,access_token:u,expires_in:x,expires_at:_,refresh_token:d,token_type:y,user:k.user};return window.location.hash="",this._debug("#_getSessionFromURL()","clearing window.location.hash"),this._returnResult({data:{session:M,redirectType:e.type},error:null})}catch(l){if(ie(l))return this._returnResult({data:{session:null,redirectType:null},error:l});throw l}}_isImplicitGrantCallback(e){return typeof this.detectSessionInUrl=="function"?this.detectSessionInUrl(new URL(window.location.href),e):!!(e.access_token||e.error||e.error_description||e.error_code)}async _isPKCECallback(e){const i=await Mn(this.storage,`${this.storageKey}-code-verifier`);return!!(e.code&&i)}async signOut(e={scope:"global"}){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._signOut(e)):await this._signOut(e)}async _signOut({scope:e}={scope:"global"}){return await this._useSession(async i=>{var a;const l=async()=>{await this._removeSession(),await dt(this.storage,`${this.storageKey}-code-verifier`)},{data:c,error:u}=i;if(u&&!tc(u))return this._returnResult({error:u});const d=(a=c.session)===null||a===void 0?void 0:a.access_token;if(d){const{error:m}=await this.admin.signOut(d,e);if(m&&!(Gk(m)&&(m.status===404||m.status===401||m.status===403)||tc(m)))return e!=="others"&&await l(),this._returnResult({error:m})}return e!=="others"&&await l(),this._returnResult({error:null})})}onAuthStateChange(e){const i=nC(),a={id:i,callback:e,unsubscribe:()=>{this._debug("#unsubscribe()","state change callback with id removed",i),this.stateChangeEmitters.delete(i)}};return this._debug("#onAuthStateChange()","registered callback with id",i),this.stateChangeEmitters.set(i,a),(async()=>(await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>{this._emitInitialSession(i)}):await this._emitInitialSession(i)))(),{data:{subscription:a}}}async _emitInitialSession(e){return await this._useSession(async i=>{var a,l;try{const{data:{session:c},error:u}=i;if(u)throw u;await((a=this.stateChangeEmitters.get(e))===null||a===void 0?void 0:a.callback("INITIAL_SESSION",c)),this._debug("INITIAL_SESSION","callback id",e,"session",c)}catch(c){await((l=this.stateChangeEmitters.get(e))===null||l===void 0?void 0:l.callback("INITIAL_SESSION",null)),this._debug("INITIAL_SESSION","callback id",e,"error",c),tc(c)?console.warn(c):console.error(c)}})}async resetPasswordForEmail(e,i={}){let a=null,l=null;this.flowType==="pkce"&&([a,l]=await Ci(this.storage,this.storageKey,!0));try{return await le(this.fetch,"POST",`${this.url}/recover`,{body:{email:e,code_challenge:a,code_challenge_method:l,gotrue_meta_security:{captcha_token:i.captchaToken}},headers:this.headers,redirectTo:i.redirectTo})}catch(c){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(c))return this._returnResult({data:null,error:c});throw c}}async getUserIdentities(){var e;try{const{data:i,error:a}=await this.getUser();if(a)throw a;return this._returnResult({data:{identities:(e=i.user.identities)!==null&&e!==void 0?e:[]},error:null})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async linkIdentity(e){return"token"in e?this.linkIdentityIdToken(e):this.linkIdentityOAuth(e)}async linkIdentityOAuth(e){var i;try{const{data:a,error:l}=await this._useSession(async c=>{var u,d,m,p,y;const{data:v,error:x}=c;if(x)throw x;const _=await this._getUrlForProvider(`${this.url}/user/identities/authorize`,e.provider,{redirectTo:(u=e.options)===null||u===void 0?void 0:u.redirectTo,scopes:(d=e.options)===null||d===void 0?void 0:d.scopes,queryParams:(m=e.options)===null||m===void 0?void 0:m.queryParams,skipBrowserRedirect:!0});return await le(this.fetch,"GET",_,{headers:this.headers,jwt:(y=(p=v.session)===null||p===void 0?void 0:p.access_token)!==null&&y!==void 0?y:void 0})});if(l)throw l;return Vt()&&!(!((i=e.options)===null||i===void 0)&&i.skipBrowserRedirect)&&window.location.assign(a==null?void 0:a.url),this._returnResult({data:{provider:e.provider,url:a==null?void 0:a.url},error:null})}catch(a){if(ie(a))return this._returnResult({data:{provider:e.provider,url:null},error:a});throw a}}async linkIdentityIdToken(e){return await this._useSession(async i=>{var a;try{const{error:l,data:{session:c}}=i;if(l)throw l;const{options:u,provider:d,token:m,access_token:p,nonce:y}=e,v=await le(this.fetch,"POST",`${this.url}/token?grant_type=id_token`,{headers:this.headers,jwt:(a=c==null?void 0:c.access_token)!==null&&a!==void 0?a:void 0,body:{provider:d,id_token:m,access_token:p,nonce:y,link_identity:!0,gotrue_meta_security:{captcha_token:u==null?void 0:u.captchaToken}},xform:En}),{data:x,error:_}=v;return _?this._returnResult({data:{user:null,session:null},error:_}):!x||!x.session||!x.user?this._returnResult({data:{user:null,session:null},error:new ka}):(x.session&&(await this._saveSession(x.session),await this._notifyAllSubscribers("USER_UPDATED",x.session)),this._returnResult({data:x,error:_}))}catch(l){if(await dt(this.storage,`${this.storageKey}-code-verifier`),ie(l))return this._returnResult({data:{user:null,session:null},error:l});throw l}})}async unlinkIdentity(e){try{return await this._useSession(async i=>{var a,l;const{data:c,error:u}=i;if(u)throw u;return await le(this.fetch,"DELETE",`${this.url}/user/identities/${e.identity_id}`,{headers:this.headers,jwt:(l=(a=c.session)===null||a===void 0?void 0:a.access_token)!==null&&l!==void 0?l:void 0})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _refreshAccessToken(e){const i="#_refreshAccessToken()";this._debug(i,"begin");try{const a=Date.now();return await rC(async l=>(l>0&&await aC(200*Math.pow(2,l-1)),this._debug(i,"refreshing attempt",l),await le(this.fetch,"POST",`${this.url}/token?grant_type=refresh_token`,{body:{refresh_token:e},headers:this.headers,xform:En})),(l,c)=>{const u=200*Math.pow(2,l);return c&&Pb(c)&&Date.now()+u-aDate.now())?this._debug(l,"proactive refresh failed, access token still valid — preserving session"):await this._removeSession()}return this.lastRefreshFailure={refreshToken:e,result:u,expiresAt:Date.now()+Vk},(i=this.refreshingDeferred)===null||i===void 0||i.resolve(u),u}throw(a=this.refreshingDeferred)===null||a===void 0||a.reject(c),c}finally{this.refreshingDeferred=null,this._debug(l,"end")}}async _notifyAllSubscribers(e,i,a=!0){if(this._pendingInitNotifications!==null&&a){this._pendingInitNotifications.push({event:e,session:i,broadcast:a});return}const l=`#_notifyAllSubscribers(${e})`;this._debug(l,"begin",i,`broadcast = ${a}`);try{this.broadcastChannel&&a&&this.broadcastChannel.postMessage({event:e,session:i});const c=[],u=Array.from(this.stateChangeEmitters.values()).map(async d=>{try{await d.callback(e,i)}catch(m){c.push(m)}});if(await Promise.all(u),c.length>0){for(let d=0;dthis._autoRefreshTokenTick(),Es);this.autoRefreshTicker=e,e&&typeof e=="object"&&typeof e.unref=="function"?e.unref():typeof Deno<"u"&&typeof Deno.unrefTimer=="function"&&Deno.unrefTimer(e);const i=setTimeout(async()=>{await this.initializePromise,await this._autoRefreshTokenTick()},0);this.autoRefreshTickTimeout=i,i&&typeof i=="object"&&typeof i.unref=="function"?i.unref():typeof Deno<"u"&&typeof Deno.unrefTimer=="function"&&Deno.unrefTimer(i)}async _stopAutoRefresh(){this._debug("#_stopAutoRefresh()");const e=this.autoRefreshTicker;this.autoRefreshTicker=null,e&&clearInterval(e);const i=this.autoRefreshTickTimeout;this.autoRefreshTickTimeout=null,i&&clearTimeout(i)}async startAutoRefresh(){this._removeVisibilityChangedCallback(),await this._startAutoRefresh()}async stopAutoRefresh(){this._removeVisibilityChangedCallback(),await this._stopAutoRefresh()}async dispose(){var e;this._removeVisibilityChangedCallback(),await this._stopAutoRefresh(),(e=this.broadcastChannel)===null||e===void 0||e.close(),this.broadcastChannel=null,this.stateChangeEmitters.clear()}async _autoRefreshTokenTick(){if(this._debug("#_autoRefreshTokenTick()","begin"),this.lock!=null){try{await this._acquireLock(0,async()=>{try{const e=Date.now();try{return await this._useSession(async i=>{const{data:{session:a}}=i;if(!a||!a.refresh_token||!a.expires_at){this._debug("#_autoRefreshTokenTick()","no session");return}const l=Math.floor((a.expires_at*1e3-e)/Es);this._debug("#_autoRefreshTokenTick()",`access token expires in ${l} ticks, a tick lasts ${Es}ms, refresh threshold is ${Zr} ticks`),l<=Zr&&await this._callRefreshToken(a.refresh_token)})}catch(i){console.error("Auto refresh tick failed with error. This is likely a transient error.",i)}}finally{this._debug("#_autoRefreshTokenTick()","end")}})}catch(e){if(e instanceof TC)this._debug("auto refresh token tick lock not available");else throw e}return}if(this.refreshingDeferred!==null){this._debug("#_autoRefreshTokenTick()","refresh already in flight, skipping");return}try{const e=Date.now();try{await this._useSession(async i=>{const{data:{session:a}}=i;if(!a||!a.refresh_token||!a.expires_at){this._debug("#_autoRefreshTokenTick()","no session");return}const l=Math.floor((a.expires_at*1e3-e)/Es);this._debug("#_autoRefreshTokenTick()",`access token expires in ${l} ticks, a tick lasts ${Es}ms, refresh threshold is ${Zr} ticks`),l<=Zr&&await this._callRefreshToken(a.refresh_token)})}catch(i){console.error("Auto refresh tick failed with error. This is likely a transient error.",i)}}finally{this._debug("#_autoRefreshTokenTick()","end")}}async _handleVisibilityChange(){if(this._debug("#_handleVisibilityChange()"),!Vt()||!(window!=null&&window.addEventListener))return this.autoRefreshToken&&this.startAutoRefresh(),!1;try{this.visibilityChangedCallback=async()=>{try{await this._onVisibilityChanged(!1)}catch(e){this._debug("#visibilityChangedCallback","error",e)}},window==null||window.addEventListener("visibilitychange",this.visibilityChangedCallback),await this._onVisibilityChanged(!0)}catch(e){console.error("_handleVisibilityChange",e)}}async _onVisibilityChanged(e){const i=`#_onVisibilityChanged(${e})`;if(this._debug(i,"visibilityState",document.visibilityState),document.visibilityState==="visible"){if(this.autoRefreshToken&&this._startAutoRefresh(),!e)if(await this.initializePromise,this.lock!=null)await this._acquireLock(this.lockAcquireTimeout,async()=>{if(document.visibilityState!=="visible"){this._debug(i,"acquired the lock to recover the session, but the browser visibilityState is no longer visible, aborting");return}await this._recoverAndRefresh()});else{if(document.visibilityState!=="visible"){this._debug(i,"visibilityState is no longer visible, skipping recovery");return}await this._recoverAndRefresh()}}else document.visibilityState==="hidden"&&this.autoRefreshToken&&this._stopAutoRefresh()}async _getUrlForProvider(e,i,a){const l=[`provider=${encodeURIComponent(i)}`];if(a!=null&&a.redirectTo&&l.push(`redirect_to=${encodeURIComponent(a.redirectTo)}`),a!=null&&a.scopes&&l.push(`scopes=${encodeURIComponent(a.scopes)}`),this.flowType==="pkce"){const[c,u]=await Ci(this.storage,this.storageKey),d=new URLSearchParams({code_challenge:`${encodeURIComponent(c)}`,code_challenge_method:`${encodeURIComponent(u)}`});l.push(d.toString())}if(a!=null&&a.queryParams){const c=new URLSearchParams(a.queryParams);l.push(c.toString())}return a!=null&&a.skipBrowserRedirect&&l.push(`skip_http_redirect=${a.skipBrowserRedirect}`),`${e}?${l.join("&")}`}async _unenroll(e){try{return await this._useSession(async i=>{var a;const{data:l,error:c}=i;return c?this._returnResult({data:null,error:c}):await le(this.fetch,"DELETE",`${this.url}/factors/${e.factorId}`,{headers:this.headers,jwt:(a=l==null?void 0:l.session)===null||a===void 0?void 0:a.access_token})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _enroll(e){try{return await this._useSession(async i=>{var a,l;const{data:c,error:u}=i;if(u)return this._returnResult({data:null,error:u});const d=Object.assign({friendly_name:e.friendlyName,factor_type:e.factorType},e.factorType==="phone"?{phone:e.phone}:e.factorType==="totp"?{issuer:e.issuer}:{}),{data:m,error:p}=await le(this.fetch,"POST",`${this.url}/factors`,{body:d,headers:this.headers,jwt:(a=c==null?void 0:c.session)===null||a===void 0?void 0:a.access_token});return p?this._returnResult({data:null,error:p}):(e.factorType==="totp"&&m.type==="totp"&&(!((l=m==null?void 0:m.totp)===null||l===void 0)&&l.qr_code)&&(m.totp.qr_code=`data:image/svg+xml;utf-8,${m.totp.qr_code}`),this._returnResult({data:m,error:null}))})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _verify(e){const i=async()=>{try{return await this._useSession(async a=>{var l;const{data:c,error:u}=a;if(u)return this._returnResult({data:null,error:u});const d=Object.assign({challenge_id:e.challengeId},"webauthn"in e?{webauthn:Object.assign(Object.assign({},e.webauthn),{credential_response:e.webauthn.type==="create"?Wb(e.webauthn.credential_response):ev(e.webauthn.credential_response)})}:{code:e.code}),{data:m,error:p}=await le(this.fetch,"POST",`${this.url}/factors/${e.factorId}/verify`,{body:d,headers:this.headers,jwt:(l=c==null?void 0:c.session)===null||l===void 0?void 0:l.access_token});return p?this._returnResult({data:null,error:p}):(await this._saveSession(Object.assign({expires_at:Math.round(Date.now()/1e3)+m.expires_in},m)),await this._notifyAllSubscribers("MFA_CHALLENGE_VERIFIED",m),this._returnResult({data:m,error:p}))})}catch(a){if(ie(a))return this._returnResult({data:null,error:a});throw a}};return this.lock!=null?this._acquireLock(this.lockAcquireTimeout,i):i()}async _challenge(e){const i=async()=>{try{return await this._useSession(async a=>{var l;const{data:c,error:u}=a;if(u)return this._returnResult({data:null,error:u});const d=await le(this.fetch,"POST",`${this.url}/factors/${e.factorId}/challenge`,{body:e,headers:this.headers,jwt:(l=c==null?void 0:c.session)===null||l===void 0?void 0:l.access_token});if(d.error)return d;const{data:m}=d;if(m.type!=="webauthn")return{data:m,error:null};switch(m.webauthn.type){case"create":return{data:Object.assign(Object.assign({},m),{webauthn:Object.assign(Object.assign({},m.webauthn),{credential_options:Object.assign(Object.assign({},m.webauthn.credential_options),{publicKey:Qb(m.webauthn.credential_options.publicKey)})})}),error:null};case"request":return{data:Object.assign(Object.assign({},m),{webauthn:Object.assign(Object.assign({},m.webauthn),{credential_options:Object.assign(Object.assign({},m.webauthn.credential_options),{publicKey:Zb(m.webauthn.credential_options.publicKey)})})}),error:null}}})}catch(a){if(ie(a))return this._returnResult({data:null,error:a});throw a}};return this.lock!=null?this._acquireLock(this.lockAcquireTimeout,i):i()}async _challengeAndVerify(e){const{data:i,error:a}=await this._challenge({factorId:e.factorId});return a?this._returnResult({data:null,error:a}):await this._verify({factorId:e.factorId,challengeId:i.id,code:e.code})}async _listFactors(){var e;const{data:{user:i},error:a}=await this.getUser();if(a)return{data:null,error:a};const l={all:[],phone:[],totp:[],webauthn:[]};for(const c of(e=i==null?void 0:i.factors)!==null&&e!==void 0?e:[])l.all.push(c),c.status==="verified"&&l[c.factor_type].push(c);return{data:l,error:null}}async _getAuthenticatorAssuranceLevel(e){var i,a,l,c;if(e)try{const{payload:_}=ic(e);let S=null;_.aal&&(S=_.aal);let T=S;const{data:{user:k},error:C}=await this.getUser(e);if(C)return this._returnResult({data:null,error:C});((a=(i=k==null?void 0:k.factors)===null||i===void 0?void 0:i.filter(z=>z.status==="verified"))!==null&&a!==void 0?a:[]).length>0&&(T="aal2");const N=_.amr||[];return{data:{currentLevel:S,nextLevel:T,currentAuthenticationMethods:N},error:null}}catch(_){if(ie(_))return this._returnResult({data:null,error:_});throw _}const{data:{session:u},error:d}=await this.getSession();if(d)return this._returnResult({data:null,error:d});if(!u)return{data:{currentLevel:null,nextLevel:null,currentAuthenticationMethods:[]},error:null};const{payload:m}=ic(u.access_token);let p=null;m.aal&&(p=m.aal);let y=p;((c=(l=u.user.factors)===null||l===void 0?void 0:l.filter(_=>_.status==="verified"))!==null&&c!==void 0?c:[]).length>0&&(y="aal2");const x=m.amr||[];return{data:{currentLevel:p,nextLevel:y,currentAuthenticationMethods:x},error:null}}async _getAuthorizationDetails(e){try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;return l?this._returnResult({data:null,error:l}):a?await le(this.fetch,"GET",`${this.url}/oauth/authorizations/${e}`,{headers:this.headers,jwt:a.access_token,xform:c=>({data:c,error:null})}):this._returnResult({data:null,error:new kt})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _approveAuthorization(e,i){try{return await this._useSession(async a=>{const{data:{session:l},error:c}=a;if(c)return this._returnResult({data:null,error:c});if(!l)return this._returnResult({data:null,error:new kt});const u=await le(this.fetch,"POST",`${this.url}/oauth/authorizations/${e}/consent`,{headers:this.headers,jwt:l.access_token,body:{action:"approve"},xform:d=>({data:d,error:null})});return u.data&&u.data.redirect_url&&Vt()&&!(i!=null&&i.skipBrowserRedirect)&&window.location.assign(u.data.redirect_url),u})}catch(a){if(ie(a))return this._returnResult({data:null,error:a});throw a}}async _denyAuthorization(e,i){try{return await this._useSession(async a=>{const{data:{session:l},error:c}=a;if(c)return this._returnResult({data:null,error:c});if(!l)return this._returnResult({data:null,error:new kt});const u=await le(this.fetch,"POST",`${this.url}/oauth/authorizations/${e}/consent`,{headers:this.headers,jwt:l.access_token,body:{action:"deny"},xform:d=>({data:d,error:null})});return u.data&&u.data.redirect_url&&Vt()&&!(i!=null&&i.skipBrowserRedirect)&&window.location.assign(u.data.redirect_url),u})}catch(a){if(ie(a))return this._returnResult({data:null,error:a});throw a}}async _listOAuthGrants(){try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;return a?this._returnResult({data:null,error:a}):i?await le(this.fetch,"GET",`${this.url}/user/oauth/grants`,{headers:this.headers,jwt:i.access_token,xform:l=>({data:l,error:null})}):this._returnResult({data:null,error:new kt})})}catch(e){if(ie(e))return this._returnResult({data:null,error:e});throw e}}async _revokeOAuthGrant(e){try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;return l?this._returnResult({data:null,error:l}):a?(await le(this.fetch,"DELETE",`${this.url}/user/oauth/grants`,{headers:this.headers,jwt:a.access_token,query:{client_id:e.clientId},noResolveJson:!0}),{data:{},error:null}):this._returnResult({data:null,error:new kt})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async fetchJwk(e,i={keys:[]}){let a=i.keys.find(d=>d.kid===e);if(a)return a;const l=Date.now();if(a=this.jwks.keys.find(d=>d.kid===e),a&&this.jwks_cached_at+Fk>l)return a;const{data:c,error:u}=await le(this.fetch,"GET",`${this.url}/.well-known/jwks.json`,{headers:this.headers});if(u)throw u;return!c.keys||c.keys.length===0||(this.jwks=c,this.jwks_cached_at=l,a=c.keys.find(d=>d.kid===e),!a)?null:a}async getClaims(e,i={}){try{let a=e;if(!a){const{data:_,error:S}=await this.getSession();if(S||!_.session)return this._returnResult({data:null,error:S});a=_.session.access_token}const{header:l,payload:c,signature:u,raw:{header:d,payload:m}}=ic(a);if(!(i!=null&&i.allowExpired))try{fC(c.exp)}catch(_){throw new kc(_ instanceof Error?_.message:"JWT validation failed")}const p=!l.alg||l.alg.startsWith("HS")||!l.kid||!("crypto"in globalThis&&"subtle"in globalThis.crypto)?null:await this.fetchJwk(l.kid,i!=null&&i.keys?{keys:i.keys}:i==null?void 0:i.jwks);if(!p){const{error:_}=await this.getUser(a);if(_)throw _;return{data:{claims:c,header:l,signature:u},error:null}}const y=mC(l.alg),v=await crypto.subtle.importKey("jwk",p,y,!0,["verify"]);if(!await crypto.subtle.verify(y,v,u,eC(`${d}.${m}`)))throw new kc("Invalid JWT signature");return{data:{claims:c,header:l,signature:u},error:null}}catch(a){if(ie(a))return this._returnResult({data:null,error:a});throw a}}async signInWithPasskey(e){var i,a,l;Un(this.experimental);try{if(!Oc())return this._returnResult({data:null,error:new Vn("Browser does not support WebAuthn",null)});const{data:c,error:u}=await this._startPasskeyAuthentication({options:{captchaToken:(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.captchaToken}});if(u||!c)return this._returnResult({data:null,error:u});const d=Zb(c.options),m=(l=(a=e==null?void 0:e.options)===null||a===void 0?void 0:a.signal)!==null&&l!==void 0?l:vf.createNewAbortSignal(),{data:p,error:y}=await Dw({publicKey:d,signal:m});if(y||!p)return this._returnResult({data:null,error:y??new Vn("WebAuthn ceremony failed",null)});const v=ev(p);return this._verifyPasskeyAuthentication({challengeId:c.challenge_id,credential:v})}catch(c){if(ie(c))return this._returnResult({data:null,error:c});throw c}}async registerPasskey(e){var i,a;Un(this.experimental);try{if(!Oc())return this._returnResult({data:null,error:new Vn("Browser does not support WebAuthn",null)});const{data:l,error:c}=await this._startPasskeyRegistration();if(c||!l)return this._returnResult({data:null,error:c});const u=Qb(l.options),d=(a=(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.signal)!==null&&a!==void 0?a:vf.createNewAbortSignal(),{data:m,error:p}=await Ow({publicKey:u,signal:d});if(p||!m)return this._returnResult({data:null,error:p??new Vn("WebAuthn ceremony failed",null)});const y=Wb(m);return this._verifyPasskeyRegistration({challengeId:l.challenge_id,credential:y})}catch(l){if(ie(l))return this._returnResult({data:null,error:l});throw l}}async _startPasskeyRegistration(){Un(this.experimental);try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;if(a)return this._returnResult({data:null,error:a});if(!i)return this._returnResult({data:null,error:new kt});const{data:l,error:c}=await le(this.fetch,"POST",`${this.url}/passkeys/registration/options`,{headers:this.headers,jwt:i.access_token,body:{}});return c?this._returnResult({data:null,error:c}):this._returnResult({data:l,error:null})})}catch(e){if(ie(e))return this._returnResult({data:null,error:e});throw e}}async _verifyPasskeyRegistration(e){Un(this.experimental);try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;if(l)return this._returnResult({data:null,error:l});if(!a)return this._returnResult({data:null,error:new kt});const{data:c,error:u}=await le(this.fetch,"POST",`${this.url}/passkeys/registration/verify`,{headers:this.headers,jwt:a.access_token,body:{challenge_id:e.challengeId,credential:e.credential}});return u?this._returnResult({data:null,error:u}):this._returnResult({data:c,error:null})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _startPasskeyAuthentication(e){var i;Un(this.experimental);try{const{data:a,error:l}=await le(this.fetch,"POST",`${this.url}/passkeys/authentication/options`,{headers:this.headers,body:{gotrue_meta_security:{captcha_token:(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.captchaToken}}});return l?this._returnResult({data:null,error:l}):this._returnResult({data:a,error:null})}catch(a){if(ie(a))return this._returnResult({data:null,error:a});throw a}}async _verifyPasskeyAuthentication(e){Un(this.experimental);try{const{data:i,error:a}=await le(this.fetch,"POST",`${this.url}/passkeys/authentication/verify`,{headers:this.headers,body:{challenge_id:e.challengeId,credential:e.credential},xform:En});return a?this._returnResult({data:null,error:a}):(i.session&&(await this._saveSession(i.session),await this._notifyAllSubscribers("SIGNED_IN",i.session)),this._returnResult({data:i,error:null}))}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _listPasskeys(){Un(this.experimental);try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;if(a)return this._returnResult({data:null,error:a});if(!i)return this._returnResult({data:null,error:new kt});const{data:l,error:c}=await le(this.fetch,"GET",`${this.url}/passkeys`,{headers:this.headers,jwt:i.access_token,xform:u=>({data:u,error:null})});return c?this._returnResult({data:null,error:c}):this._returnResult({data:l,error:null})})}catch(e){if(ie(e))return this._returnResult({data:null,error:e});throw e}}async _updatePasskey(e){Un(this.experimental);try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;if(l)return this._returnResult({data:null,error:l});if(!a)return this._returnResult({data:null,error:new kt});const{data:c,error:u}=await le(this.fetch,"PATCH",`${this.url}/passkeys/${e.passkeyId}`,{headers:this.headers,jwt:a.access_token,body:{friendly_name:e.friendlyName}});return u?this._returnResult({data:null,error:u}):this._returnResult({data:c,error:null})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}async _deletePasskey(e){Un(this.experimental);try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;if(l)return this._returnResult({data:null,error:l});if(!a)return this._returnResult({data:null,error:new kt});const{error:c}=await le(this.fetch,"DELETE",`${this.url}/passkeys/${e.passkeyId}`,{headers:this.headers,jwt:a.access_token,noResolveJson:!0});return c?this._returnResult({data:null,error:c}):this._returnResult({data:null,error:null})})}catch(i){if(ie(i))return this._returnResult({data:null,error:i});throw i}}}wo.nextInstanceID={};const zC=wo,VC="2.110.2";let Wr="",Mc;if(typeof Deno<"u"){var kd;Wr="deno",Mc=(kd=Deno.version)===null||kd===void 0?void 0:kd.deno}else if(typeof document<"u")Wr="web";else if(typeof navigator<"u"&&navigator.product==="ReactNative")Wr="react-native";else{var Cd;Wr="node",Mc=typeof process<"u"?(Cd=process.version)===null||Cd===void 0?void 0:Cd.replace(/^v/,""):void 0}const Mw=[`runtime=${Wr}`];Mc&&Mw.push(`runtime-version=${Mc}`);const PC={"X-Client-Info":`supabase-js/${VC}; ${Mw.join("; ")}`},HC={headers:PC},$C={schema:"public"},qC={autoRefreshToken:!0,persistSession:!0,detectSessionInUrl:!0,flowType:"implicit"},FC={},IC={enabled:!1,respectSamplingDecision:!0};function GC(n,e,i,a){function l(c){return c instanceof i?c:new i(function(u){u(c)})}return new(i||(i=Promise))(function(c,u){function d(y){try{p(a.next(y))}catch(v){u(v)}}function m(y){try{p(a.throw(y))}catch(v){u(v)}}function p(y){y.done?c(y.value):l(y.value).then(d,m)}p((a=a.apply(n,[])).next())})}let Rd=null;const KC="@opentelemetry/api";function YC(){return Rd===null&&(Rd=import(KC).catch(()=>null)),Rd}function XC(){return GC(this,void 0,void 0,function*(){try{const n=yield YC();if(!n||!n.propagation||!n.context)return null;const e={};n.propagation.inject(n.context.active(),e);const i=e.traceparent;return i?{traceparent:i,tracestate:e.tracestate,baggage:e.baggage}:null}catch{return null}})}function JC(n){if(!n||typeof n!="string")return null;const e=n.split("-");if(e.length!==4)return null;const[i,a,l,c]=e;if(i.length!==2||a.length!==32||l.length!==16||c.length!==2)return null;const u=/^[0-9a-f]+$/i;return!u.test(i)||!u.test(a)||!u.test(l)||!u.test(c)||a==="00000000000000000000000000000000"||l==="0000000000000000"?null:{version:i,traceId:a,parentId:l,traceFlags:c,isSampled:(parseInt(c,16)&1)===1}}function QC(n,e){if(!n||!e||e.length===0)return!1;let i;if(n instanceof URL)i=n;else try{i=new URL(n)}catch{return!1}for(const a of e)try{if(typeof a=="string"){if(ZC(i.hostname,a))return!0}else if(a instanceof RegExp){if(a.test(i.hostname))return!0}else if(typeof a=="function"&&a(i))return!0}catch{continue}return!1}function ZC(n,e){if(e===n)return!0;if(e.startsWith("*.")){const i=e.slice(2);if(n.endsWith(i)&&(n===i||n.endsWith("."+i)))return!0}return!1}function WC(n){const e=[];try{const i=new URL(n);e.push(i.hostname)}catch{}return e.push("*.supabase.co","*.supabase.in"),e.push("localhost","127.0.0.1","[::1]"),e}function _o(n){"@babel/helpers - typeof";return _o=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_o(n)}function eR(n,e){if(_o(n)!="object"||!n)return n;var i=n[Symbol.toPrimitive];if(i!==void 0){var a=i.call(n,e);if(_o(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(n)}function tR(n){var e=eR(n,"string");return _o(e)=="symbol"?e:e+""}function nR(n,e,i){return(e=tR(e))in n?Object.defineProperty(n,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[e]=i,n}function tv(n,e){var i=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(l){return Object.getOwnPropertyDescriptor(n,l).enumerable})),i.push.apply(i,a)}return i}function rt(n){for(var e=1;en?(...e)=>n(...e):(...e)=>fetch(...e),iR=()=>Headers,aR=(n,e,i,a,l)=>{const c=sR(a),u=iR(),d=(l==null?void 0:l.enabled)===!0,m=(l==null?void 0:l.respectSamplingDecision)!==!1,p=d?WC(e):null;return async(y,v)=>{var x;const _=(x=await i())!==null&&x!==void 0?x:n;let S=new u(v==null?void 0:v.headers);if(S.has("apikey")||S.set("apikey",n),S.has("Authorization")||S.set("Authorization",`Bearer ${_}`),p){const T=await rR(y,p,m);T&&(T.traceparent&&!S.has("traceparent")&&S.set("traceparent",T.traceparent),T.tracestate&&!S.has("tracestate")&&S.set("tracestate",T.tracestate),T.baggage&&!S.has("baggage")&&S.set("baggage",T.baggage))}return c(y,rt(rt({},v),{},{headers:S}))}};async function rR(n,e,i){if(!QC(typeof n=="string"||n instanceof URL?n:n.url,e))return null;const a=await XC();if(!a||!a.traceparent)return null;if(i){const l=JC(a.traceparent);if(l&&!l.isSampled)return null}return a}function nv(n){return typeof n=="boolean"?{enabled:n}:n}function oR(n){return n.endsWith("/")?n:n+"/"}function lR(n,e){var i,a,l,c,u,d;const{db:m,auth:p,realtime:y,global:v}=n,{db:x,auth:_,realtime:S,global:T}=e,k=nv(n.tracePropagation),C=nv(e.tracePropagation),M={db:rt(rt({},x),m),auth:rt(rt({},_),p),realtime:rt(rt({},S),y),storage:{},global:rt(rt(rt({},T),v),{},{headers:rt(rt({},(i=T==null?void 0:T.headers)!==null&&i!==void 0?i:{}),(a=v==null?void 0:v.headers)!==null&&a!==void 0?a:{})}),tracePropagation:{enabled:(l=(c=k==null?void 0:k.enabled)!==null&&c!==void 0?c:C==null?void 0:C.enabled)!==null&&l!==void 0?l:!1,respectSamplingDecision:(u=(d=k==null?void 0:k.respectSamplingDecision)!==null&&d!==void 0?d:C==null?void 0:C.respectSamplingDecision)!==null&&u!==void 0?u:!0},accessToken:async()=>""};return n.accessToken?M.accessToken=n.accessToken:delete M.accessToken,M}function cR(n){const e=n==null?void 0:n.trim();if(!e)throw new Error("supabaseUrl is required.");if(!e.match(/^https?:\/\//i))throw new Error("Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.");try{return new URL(oR(e))}catch{throw Error("Invalid supabaseUrl: Provided URL is malformed.")}}var uR=class extends zC{constructor(n){super(n)}},hR=class{constructor(n,e,i){var a,l;this.supabaseUrl=n,this.supabaseKey=e;const c=cR(n);if(!e)throw new Error("supabaseKey is required.");this.realtimeUrl=new URL("realtime/v1",c),this.realtimeUrl.protocol=this.realtimeUrl.protocol.replace("http","ws"),this.authUrl=new URL("auth/v1",c),this.storageUrl=new URL("storage/v1",c),this.functionsUrl=new URL("functions/v1",c);const u=`sb-${c.hostname.split(".")[0]}-auth-token`,d={db:$C,realtime:FC,auth:rt(rt({},qC),{},{storageKey:u}),global:HC,tracePropagation:IC},m=lR(i??{},d);if(this.settings=m,this.storageKey=(a=m.auth.storageKey)!==null&&a!==void 0?a:"",this.headers=(l=m.global.headers)!==null&&l!==void 0?l:{},m.accessToken)this.accessToken=m.accessToken,this.auth=new Proxy({},{get:(y,v)=>{throw new Error(`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(v)} is not possible`)}});else{var p;this.auth=this._initSupabaseAuthClient((p=m.auth)!==null&&p!==void 0?p:{},this.headers,m.global.fetch)}this.fetch=aR(e,n,this._getAccessToken.bind(this),m.global.fetch,m.tracePropagation),this.realtime=this._initRealtimeClient(rt({headers:this.headers,accessToken:this._getAccessToken.bind(this),fetch:this.fetch},m.realtime)),this.accessToken&&Promise.resolve(this.accessToken()).then(y=>this.realtime.setAuth(y)).catch(y=>console.warn("Failed to set initial Realtime auth token:",y)),this.rest=new xN(new URL("rest/v1",c).href,{headers:this.headers,schema:m.db.schema,fetch:this.fetch,timeout:m.db.timeout,urlLengthLimit:m.db.urlLengthLimit}),this.storage=new zk(this.storageUrl.href,this.headers,this.fetch,i==null?void 0:i.storage),m.accessToken||this._listenForAuthEvents()}get functions(){return new hN(this.functionsUrl.href,{headers:this.headers,customFetch:this.fetch})}from(n){return this.rest.from(n)}schema(n){return this.rest.schema(n)}rpc(n,e={},i={head:!1,get:!1,count:void 0}){return this.rest.rpc(n,e,i)}channel(n,e={config:{}}){return this.realtime.channel(n,e)}getChannels(){return this.realtime.getChannels()}removeChannel(n){return this.realtime.removeChannel(n)}removeAllChannels(){return this.realtime.removeAllChannels()}async _getAccessToken(){var n=this,e,i;if(n.accessToken)return await n.accessToken();const{data:a}=await n.auth.getSession();return(e=(i=a.session)===null||i===void 0?void 0:i.access_token)!==null&&e!==void 0?e:n.supabaseKey}_initSupabaseAuthClient({autoRefreshToken:n,persistSession:e,detectSessionInUrl:i,storage:a,userStorage:l,storageKey:c,flowType:u,lock:d,debug:m,throwOnError:p,experimental:y,lockAcquireTimeout:v,skipAutoInitialize:x},_,S){const T={Authorization:`Bearer ${this.supabaseKey}`,apikey:`${this.supabaseKey}`};return new uR({url:this.authUrl.href,headers:rt(rt({},T),_),storageKey:c,autoRefreshToken:n,persistSession:e,detectSessionInUrl:i,storage:a,userStorage:l,flowType:u,lock:d,debug:m,throwOnError:p,experimental:y,fetch:S,lockAcquireTimeout:v,skipAutoInitialize:x,hasCustomAuthorizationHeader:Object.keys(this.headers).some(k=>k.toLowerCase()==="authorization")})}_initRealtimeClient(n){return new lk(this.realtimeUrl.href,rt(rt({},n),{},{params:rt(rt({},{apikey:this.supabaseKey}),n==null?void 0:n.params)}))}_listenForAuthEvents(){return this.auth.onAuthStateChange((n,e)=>{this._handleTokenChanged(n,"CLIENT",e==null?void 0:e.access_token)})}_handleTokenChanged(n,e,i){(n==="TOKEN_REFRESHED"||n==="SIGNED_IN")&&this.changedAccessToken!==i?(this.changedAccessToken=i,this.realtime.setAuth(i)):n==="SIGNED_OUT"&&(this.realtime.setAuth(),e=="STORAGE"&&this.auth.signOut(),this.changedAccessToken=void 0)}};const dR=(n,e,i)=>new hR(n,e,i);function fR(){if(typeof window<"u")return!1;const n=globalThis.process;if(!n)return!1;const e=n.version;if(e==null)return!1;const i=e.match(/^v(\d+)\./);return i?parseInt(i[1],10)<=20:!1}fR()&&console.warn("⚠️ Node.js 20 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 22 or later. For more information, visit: https://github.com/orgs/supabase/discussions/45715");const mR="https://dbit.digitalcompass.agency",Bw="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE",oo=!!Bw,lt=oo?dR(mR,Bw,{auth:{persistSession:!0,autoRefreshToken:!0,detectSessionInUrl:!0}}):null;function Uw(){return new URL("/cruce-cuentas/",window.location.origin).toString()}async function pR(){if(typeof window>"u"||typeof fetch>"u")return;const n=Uw(),e=new AbortController,i=window.setTimeout(()=>e.abort(),8e3);try{await(await fetch(n,{method:"GET",cache:"reload",credentials:"same-origin",redirect:"follow",headers:{Accept:"text/html,application/xhtml+xml"},signal:e.signal})).text()}catch(a){console.warn("No se pudo refrescar preventivamente la entrada de la app:",a)}finally{window.clearTimeout(i)}}const gR=180,yR=8e3;async function So(n=gR){if(!lt)return null;const{data:e,error:i}=await lt.auth.getSession();if(i)throw i;let a=e.session;if(!a)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de continuar.");const l=Number(a.expires_at||0)*1e3;if(!l||l-Date.now()<=n*1e3){const u=await lt.auth.refreshSession();if(u.error)throw u.error;if(a=u.data.session,!a)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de continuar.")}return a}async function sv(n){if(typeof window>"u"||typeof fetch>"u")return;if(typeof navigator<"u"&&navigator.onLine===!1)throw new Error("No hay conexión a Internet. Verifica tu conexión e intenta nuevamente.");let e;try{e=new URL(n,window.location.origin)}catch{return}if(e.origin===window.location.origin)return;const i=new URL("/favicon.ico",e.origin);i.searchParams.set("_glm_resume",String(Date.now()));const a=new AbortController,l=window.setTimeout(()=>a.abort(),yR);try{await fetch(i.toString(),{method:"GET",mode:"no-cors",cache:"no-store",credentials:"omit",signal:a.signal})}catch(c){if(typeof navigator<"u"&&navigator.onLine===!1)throw new Error("No hay conexión a Internet. Verifica tu conexión e intenta nuevamente.");console.warn("No se pudo precalentar la conexión con n8n:",c)}finally{window.clearTimeout(l)}}function iv(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`glm-${Date.now()}-${Math.random().toString(36).slice(2,12)}`}function av(n,e){try{const i=new URL(n,window.location.origin);return i.searchParams.set("client_request_id",e),i.searchParams.set("_ts",String(Date.now())),i.toString()}catch{return n}}const bR="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-bamboo-correccion",Od=["Nombre mal escrito en BambooHR","Apodo o nombre de uso","Nombre incompleto / nombres adicionales","Otro"];function vR(n){try{return n?JSON.parse(n):null}catch{return null}}function Lw({row:n,country:e,periodRange:i,operatorName:a,operatorEmail:l,executionId:c,reportUrl:u,onClose:d,onSuccess:m,onShowToast:p}){const y=n.bestBambooCandidate,[v,x]=D.useState((y==null?void 0:y.employeeName)||""),[_,S]=D.useState((y==null?void 0:y.employeeNumber)||""),[T,k]=D.useState(Od[0]),[C,M]=D.useState(""),[N,z]=D.useState(!1),[Y,Z]=D.useState(!1);D.useEffect(()=>{x((y==null?void 0:y.employeeName)||""),S((y==null?void 0:y.employeeNumber)||""),k(Od[0]),M(""),z(!1)},[n.id,y==null?void 0:y.employeeName,y==null?void 0:y.employeeNumber]);const F=D.useMemo(()=>e.code==="TT"?"Laurele Nicome-Jack":e.code==="PA"?"Delarys Peñalba":e.code==="SV"?"Carlos Antonio Malta Méndez":e.code==="HN"?"Melody Chang Blandin":"haguirre@gomezleemarketing.com",[e.code]),G=D.useMemo(()=>Array.from(new Set([n.employee,n.bankNameFile,n.bankAccountHolder].map(Q=>String(Q||"").trim()).filter(Boolean))),[n.employee,n.bankNameFile,n.bankAccountHolder]),ne=async()=>{var Q;if(!v.trim()){p("Escribe el nombre correcto del empleado en BambooHR.","error");return}if(!N){p("Confirma que se trata de la misma persona antes de enviar el caso.","error");return}if(!lt){p("Supabase no está configurado en esta aplicación.","error");return}Z(!0);try{const ce=await So(300),me=ce==null?void 0:ce.access_token;if(!me)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de reportar el caso.");const _e={country:e.code,country_name:e.name,source_name:n.employee,source_names:G,bank_name_file:n.bankNameFile||"",bank_account_holder:n.bankAccountHolder||"",bank_account:n.account||"",bank_amount:n.amountBank,currency:e.currency,bamboo_name:v.trim(),bamboo_employee_number:_.trim(),reason:T,comment:C.trim(),confirmed_same_person:!0,execution_id:c||"",report_url:u||"",period_label:i.label,period_start:i.start,period_end:i.end,requested_by_name:a,requested_by_email:l},Re=new FormData;Re.append("payload",JSON.stringify(_e)),Re.append("access_token",me);const Se=await fetch(bR,{method:"POST",body:Re}),V=await Se.text(),K=vR(V);if(!Se.ok||(K==null?void 0:K.ok)===!1){const W=(K==null?void 0:K.message)||(K==null?void 0:K.error)||((Q=K==null?void 0:K.errors)==null?void 0:Q[0])||V||"No se pudo enviar el caso a RRHH.";throw new Error(String(W))}m(n.id),p(`Caso enviado a RRHH de ${e.name}. La equivalencia quedó registrada para futuros cruces.`,"success"),d()}catch(ce){const me=ce instanceof Error?ce.message:"No se pudo enviar el caso a RRHH.";p(me,"error")}finally{Z(!1)}};return g.jsxs("div",{className:"fixed inset-0 z-[90] flex items-center justify-center px-4 py-6",children:[g.jsx("button",{type:"button",className:"absolute inset-0 bg-black/45",onClick:Y?void 0:d,"aria-label":"Cerrar"}),g.jsxs("div",{className:"relative w-full max-w-2xl bg-white border border-[#D0D0D0] shadow-2xl max-h-[92vh] overflow-y-auto",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A]"}),g.jsxs("div",{className:"px-6 py-5 border-b border-neutral-200 flex items-start justify-between gap-4",children:[g.jsxs("div",{children:[g.jsx("p",{className:"text-[10px] uppercase tracking-[0.18em] font-black text-[#6B8FA3]",children:"Banco sin BambooHR"}),g.jsx("h2",{className:"text-xl font-black text-[#4F758B] mt-1",children:"Reportar corrección de identidad a RRHH"}),g.jsx("p",{className:"text-xs text-neutral-500 mt-2 leading-relaxed",children:"Esta validación crea una equivalencia permanente para que el mismo falso positivo no vuelva a aparecer mientras RRHH corrige BambooHR."})]}),g.jsx("button",{type:"button",onClick:d,disabled:Y,className:"p-2 text-neutral-400 hover:text-neutral-700 hover:bg-neutral-100 disabled:opacity-40","aria-label":"Cerrar modal",children:g.jsx(Ya,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-6 py-5 space-y-5",children:[g.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-3 bg-[#F5F5F5] border border-neutral-200 p-4",children:[g.jsxs("div",{children:[g.jsx("p",{className:"text-[9px] uppercase tracking-wider font-black text-[#6B8FA3]",children:"Nombre detectado"}),g.jsx("p",{className:"text-sm font-black text-neutral-800 mt-1",children:n.employee})]}),g.jsxs("div",{children:[g.jsx("p",{className:"text-[9px] uppercase tracking-wider font-black text-[#6B8FA3]",children:"Cuenta bancaria"}),g.jsx("p",{className:"text-sm font-mono font-bold text-neutral-700 mt-1",children:n.account||"—"})]}),g.jsxs("div",{className:"sm:col-span-2",children:[g.jsx("p",{className:"text-[9px] uppercase tracking-wider font-black text-[#6B8FA3]",children:"Se notificará a"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700 mt-1",children:F})]})]}),(y==null?void 0:y.employeeName)&&g.jsxs("div",{className:"border border-[#D6E8F4] bg-[#F7FBFD] p-4 flex items-start gap-3",children:[g.jsx(fo,{className:"w-4 h-4 text-[#4F758B] mt-0.5 flex-shrink-0"}),g.jsxs("div",{className:"text-xs text-neutral-600 leading-relaxed",children:[g.jsx("strong",{className:"text-[#4F758B]",children:"Candidato sugerido por el cruce:"})," ",y.employeeName,y.employeeNumber?` · Employee Number ${y.employeeNumber}`:"",". Verifícalo manualmente antes de confirmar."]})]}),g.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[g.jsxs("label",{className:"sm:col-span-2",children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Nombre correcto en BambooHR *"}),g.jsx("input",{type:"text",value:v,onChange:Q=>x(Q.target.value),placeholder:"Ej.: Jonathan Alexander Pérez",className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B]"})]}),g.jsxs("label",{children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Employee Number"}),g.jsx("input",{type:"text",value:_,onChange:Q=>S(Q.target.value),placeholder:"Recomendado para mayor seguridad",className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B]"})]}),g.jsxs("label",{children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Motivo *"}),g.jsx("select",{value:T,onChange:Q=>k(Q.target.value),className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B] bg-white",children:Od.map(Q=>g.jsx("option",{value:Q,children:Q},Q))})]}),g.jsxs("label",{className:"sm:col-span-2",children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Comentario para RRHH"}),g.jsx("textarea",{value:C,onChange:Q=>M(Q.target.value),rows:3,placeholder:"Detalle adicional que ayude a RRHH a corregir el registro.",className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B] resize-y"})]})]}),g.jsxs("label",{className:"flex items-start gap-3 border border-[#D0D0D0] p-4 cursor-pointer",children:[g.jsx("input",{type:"checkbox",checked:N,onChange:Q=>z(Q.target.checked),className:"mt-0.5 accent-[#6CC24A]"}),g.jsxs("span",{className:"text-xs text-neutral-600 leading-relaxed",children:["Confirmo que revisé el caso manualmente y que ",g.jsx("strong",{children:n.employee})," corresponde a la misma persona indicada arriba en BambooHR."]})]})]}),g.jsxs("div",{className:"px-6 py-4 border-t border-neutral-200 bg-neutral-50 flex flex-col-reverse sm:flex-row sm:justify-end gap-2",children:[g.jsx("button",{type:"button",onClick:d,disabled:Y,className:"px-4 py-2.5 border border-neutral-300 bg-white text-neutral-600 text-xs font-black uppercase tracking-wider disabled:opacity-40",children:"Cancelar"}),g.jsx("button",{type:"button",onClick:ne,disabled:Y||!N||!v.trim(),className:"px-5 py-2.5 bg-[#4F758B] text-white text-xs font-black uppercase tracking-wider inline-flex items-center justify-center gap-2 border-l-4 border-[#6CC24A] disabled:bg-neutral-300 disabled:border-neutral-300 disabled:cursor-not-allowed",children:Y?g.jsxs(g.Fragment,{children:[g.jsx("span",{className:"w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin"}),"Enviando..."]}):g.jsxs(g.Fragment,{children:[g.jsx(dw,{className:"w-4 h-4"}),"Enviar a RRHH"]})})]})]})]})}const xR="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-reportar";function wR(n){try{return n?JSON.parse(n):null}catch{return null}}function _R(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`report-${Date.now()}-${Math.random().toString(36).slice(2,10)}`}function zw({country:n,operatorName:e,operatorEmail:i,section:a,onShowToast:l}){const[c,u]=D.useState(!1),[d,m]=D.useState(""),[p,y]=D.useState(!1),v=()=>{p||u(!1)},x=async()=>{const _=d.trim();if(!_){l("Escribe una descripción del reporte.","error");return}if(!lt){l("Supabase no está configurado en esta aplicación.","error");return}y(!0);try{const S=await So(300),T=S==null?void 0:S.access_token;if(!T)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de enviar el reporte.");const k={message:_.slice(0,5e3),userName:e,userEmail:i,country:n.code,countryName:n.name,section:a,submittedAt:new Date().toISOString(),pageUrl:window.location.href,appSessionId:_R()},C=new FormData;C.append("payload",JSON.stringify(k)),C.append("access_token",T);const M=await fetch(xR,{method:"POST",body:C}),N=await M.text(),z=wR(N);if(!M.ok||(z==null?void 0:z.ok)===!1){const Y=(z==null?void 0:z.message)||(z==null?void 0:z.error)||N||"No se pudo enviar el reporte.";throw new Error(String(Y))}m(""),u(!1),l("Reporte enviado correctamente.","success")}catch(S){l(S instanceof Error?S.message:"No se pudo enviar el reporte.","error")}finally{y(!1)}};return g.jsxs(g.Fragment,{children:[g.jsxs("button",{type:"button",onClick:()=>u(!0),className:"inline-flex items-center justify-center gap-2 border border-[#4F758B]/25 bg-white text-[#4F758B] h-9 px-2.5 sm:px-3 text-xs font-black hover:border-[#4F758B]/55 hover:bg-[#4F758B]/5 transition-colors shadow-sm",title:"Reportar un error o incidencia","aria-label":"Reportar",children:[g.jsx(Kj,{className:"w-4 h-4"}),g.jsx("span",{className:"hidden sm:inline",children:"Reportar"})]}),c&&g.jsxs("div",{className:"fixed inset-0 z-[90] flex items-center justify-center px-4 py-6",children:[g.jsx("button",{type:"button",className:"absolute inset-0 bg-black/45",onClick:v,"aria-label":"Cerrar reporte"}),g.jsxs("div",{className:"relative w-full max-w-2xl bg-white border border-[#D0D0D0] shadow-2xl",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A]"}),g.jsxs("div",{className:"px-6 py-5 border-b border-neutral-200 flex items-center justify-between gap-4",children:[g.jsx("h2",{className:"text-xl font-black text-[#4F758B]",children:"Reporte"}),g.jsx("button",{type:"button",onClick:v,disabled:p,className:"p-2 text-neutral-400 hover:text-neutral-700 hover:bg-neutral-100 disabled:opacity-40","aria-label":"Cerrar modal",children:g.jsx(Ya,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-6 py-5",children:[g.jsxs("label",{className:"block",children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Descripción del Reporte *"}),g.jsx("textarea",{value:d,onChange:_=>m(_.target.value.slice(0,5e3)),rows:7,autoFocus:!0,disabled:p,placeholder:"Escribe aquí el error, incidencia o mejora que deseas reportar...",className:"w-full resize-y min-h-[150px] border border-[#D0D0D0] bg-white px-3.5 py-3 text-sm text-neutral-700 placeholder-neutral-400 focus:outline-none focus:border-[#4F758B] disabled:bg-neutral-50"})]}),g.jsxs("div",{className:"mt-1.5 text-right text-[10px] text-neutral-400",children:[d.length,"/5000"]})]}),g.jsxs("div",{className:"px-6 py-4 border-t border-neutral-200 bg-neutral-50 flex items-center justify-end gap-3",children:[g.jsx("button",{type:"button",onClick:v,disabled:p,className:"border border-neutral-300 bg-white text-neutral-600 px-4 py-2.5 text-xs font-bold hover:bg-neutral-100 disabled:opacity-50",children:"Cancelar"}),g.jsxs("button",{type:"button",onClick:()=>void x(),disabled:p||!d.trim(),className:"bg-[#4F758B] hover:bg-[#3F6174] text-white px-4 py-2.5 text-xs font-black inline-flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",children:[p?g.jsx(qa,{className:"w-4 h-4 animate-spin"}):g.jsx(dw,{className:"w-4 h-4"}),g.jsx("span",{children:p?"Enviando...":"Enviar reporte"})]})]})]})]})]})}const Vw=[{value:1,label:"Enero"},{value:2,label:"Febrero"},{value:3,label:"Marzo"},{value:4,label:"Abril"},{value:5,label:"Mayo"},{value:6,label:"Junio"},{value:7,label:"Julio"},{value:8,label:"Agosto"},{value:9,label:"Septiembre"},{value:10,label:"Octubre"},{value:11,label:"Noviembre"},{value:12,label:"Diciembre"}],rv=[{value:"quincena_15",label:"Quincena 15",description:"Del día 1 al 15 del mes."},{value:"quincena_30",label:"Quincena 30 / fin de mes",description:"Del día 16 al último día del mes."}],SR="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-analizar-nomina";function TR(n){return n==="TT"?"https://agenteit.digitalcompass.agency/webhook/nominatt-bamboo-test":n==="PA"?"https://agenteit.digitalcompass.agency/webhook/nominapa-bamboo-test":n==="SV"?"https://agenteit.digitalcompass.agency/webhook/nominasv-bamboo-test":n==="HN"?"https://agenteit.digitalcompass.agency/webhook/nominahn-bamboo-test":"https://agenteit.digitalcompass.agency/webhook/nominagt-bamboo-test"}function ER(){return"https://agenteit.digitalcompass.agency/webhook/bono14gt-bamboo-test"}function AR(){const n=new Date().getFullYear(),e=n-5,i=n+10;return Array.from({length:i-e+1},(a,l)=>e+l)}function jR(){const n=new Date,e=n.getDate();let i=n.getFullYear(),a=n.getMonth()+1,l="quincena_15";return e<=15&&(a-=1,l="quincena_30",a===0&&(a=12,i-=1)),{year:i,month:a,periodType:l}}function Bc(n){return n<1024*1024?`${(n/1024).toFixed(1)} KB`:`${(n/(1024*1024)).toFixed(2)} MB`}function Yr(n){const e=n.name.toLowerCase();return e.endsWith(".xlsx")||e.endsWith(".xls")}function Dd(n){return n.name.toLowerCase().endsWith(".csv")}function NR(n){return Array.from(n).map(e=>({name:e.name,size:Bc(e.size),type:"payroll",rawFile:e}))}function Md(n){return Array.from(n).map(e=>({name:e.name,size:Bc(e.size),type:"bank",rawFile:e}))}function $a(n,e,i){return`${n}-${String(e).padStart(2,"0")}-${String(i).padStart(2,"0")}`}function ov(n){const e=/^(\d{4})-(\d{2})-(\d{2})$/.exec(n);return e?`${e[3]}-${e[2]}-${e[1]}`:n}function lv(n,e,i){var c;const a=new Date(n,e,0).getDate(),l=((c=Vw.find(u=>u.value===e))==null?void 0:c.label)??"Mes";return i==="quincena_15"?{start:$a(n,e,1),end:$a(n,e,15),label:`${l} ${n} · Quincena 15`}:{start:$a(n,e,16),end:$a(n,e,a),label:`${l} ${n} · Quincena 30 / fin de mes`}}function kR(n){const e=n-1;return{start:$a(e,7,1),end:$a(n,6,30),label:`Bono 14 ${n} · Julio ${e} a junio ${n}`}}function Bd(n,e,i){if(n==null||Number.isNaN(n))return"—";const a=i==="USD"||i==="GTQ"||i==="TTD"?i:e.currency,l=a==="USD"?"en-US":e.locale;return new Intl.NumberFormat(l,{style:"currency",currency:a,minimumFractionDigits:2}).format(n)}function Gn(n,e=0){const i=Number(n);return Number.isFinite(i)?i:e}function Et(n){return n.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toLowerCase()}function CR(n,e){const i=Et(String(n??e??""));return i.includes("coincid")?"Coincidencia":i.includes("resuelto")?"Resuelto":i.includes("no encontrado")?"No encontrado":i.includes("pendiente")?"Pendiente revisión":i.includes("riesgo")||i.includes("discrep")||i.includes("mal digitada")?"Riesgo":e==="coincidencia"?"Coincidencia":e==="posible_cuenta_mal_digitada"||e==="discrepancia"?"Riesgo":"Pendiente revisión"}function ac(n,e){for(const i of e){if(typeof n[i]=="number")return n[i];if(typeof n[i]=="string"&&n[i]!==""){const a=n[i].replace(/[Q,$\s]/g,"").replace(/,/g,""),l=Number(a);if(Number.isFinite(l))return l}}return null}function cv(n){return Array.isArray(n)?n.map((e,i)=>{const a=e,l=String(a.category??a.categoria??a.tipo??a.source??a.fuente??""),c=ac(a,["amountPayroll","monto_nomina","payroll_amount","amount_payroll","montoNomina"]),u=ac(a,["amountBank","monto_banco","bank_amount","amount_bank","montoBanco"]),d=ac(a,["difference","diferencia","diff"]);return{id:String(a.id??a.employeeNumber??a.employee_number??a.account??a.cuenta??`row-${i+1}`),employee:String(a.employee??a.empleado??a.name??a.nombre??a.employee_name??"Sin nombre"),employeeNumber:String(a.employeeNumber??a.employee_number??a.codigo??""),account:a.account||a.cuenta||a.bank_account?String(a.account??a.cuenta??a.bank_account):void 0,currency:String(a.currency??a.moneda??"").toUpperCase()||void 0,amountPayroll:c,amountBank:u,difference:d??(c!==null&&u!==null?c-u:null),status:CR(a.status??a.estado,l),category:l,observation:String(a.observation??a.observacion??a.note??a.nota??a.comentario??""),source:a.source??a.fuente??l,bankNameFile:String(a.bank_name_file??a.bankNameFile??""),bankAccountHolder:String(a.bank_account_holder??a.bankAccountHolder??""),bestBambooCandidate:a.best_bamboo_candidate&&typeof a.best_bamboo_candidate=="object"?{employeeNumber:String(a.best_bamboo_candidate.employee_number??""),employeeName:String(a.best_bamboo_candidate.employee_name??""),score:ac(a.best_bamboo_candidate,["score"])}:null,ambiguousBambooMatch:!!a.ambiguous_bamboo_match}}):[]}function uv(n){const i=(Array.isArray(n)?n[0]:n)??{},a=i.originalResponse??i.original_response??i.response??i,l=a.summary??a.resumen??i.summary??i.resumen??{},c=cv(a.rows??i.rows??a.resultados??i.resultados??[]),u=cv(a.bankWithoutBamboo??i.bankWithoutBamboo??a.banco_sin_bamboo_rows??i.banco_sin_bamboo_rows??[]).map(N=>({...N,category:N.category||"banco_sin_bamboo",source:"banco_sin_bamboo",status:N.status==="Coincidencia"?"Pendiente revisión":N.status})),d=new Map;[...c,...u].forEach((N,z)=>{const Y=N.id||`${N.source??N.category??"row"}-${z}`;d.set(Y,N)});const m=Array.from(d.values()),p=Gn(l.matches??l.coincidencias,c.filter(N=>N.status==="Coincidencia").length),y=Gn(l.discrepancies??l.discrepancias,c.filter(N=>{const z=Et(String(N.category??N.source??""));return z.includes("discrepancia")||z.includes("posible_cuenta_mal_digitada")}).length),v=Gn(l.bankWithoutPayroll??l.bancoSinNomina??l.banco_sin_nomina,c.filter(N=>La(N,"banco_sin_nomina")).length),x=Gn(l.bankWithoutBamboo??l.bancoSinBamboo??l.banco_sin_bamboo,u.length),_=Gn(l.payrollWithoutAccount??l.nominaSinCuenta??l.nomina_sin_cuenta),S=Gn(l.nameDifferences??l.diferenciasNombreBanco??l.diferencias_nombre_banco,c.filter(N=>Et(String(N.category??"")).includes("diferencia_nombre_banco")).length),T=Gn(l.possibleWrongAccounts??l.posiblesCuentasMalDigitadas??l.posibles_cuentas_mal_digitadas,c.filter(N=>Et(String(N.category??"")).includes("posible_cuenta_mal_digitada")).length),k=y+v+x+_+S,C=a.metadata??i.metadata??{},M=String(C.period_type??C.periodType??"").toLowerCase();return{ok:!!(a.ok??i.ok??!0),reportKind:M==="bono14"?"bono14":"nomina",executionId:a.executionId||a.execution_id||i.executionId||i.execution_id?String(a.executionId??a.execution_id??i.executionId??i.execution_id):void 0,reportUrl:a.reportUrl||a.report_url||i.reportUrl||i.report_url||i.google_sheet_url?String(a.reportUrl??a.report_url??i.reportUrl??i.report_url??i.google_sheet_url):void 0,summary:{matches:p,discrepancies:y,bankWithoutPayroll:v,bankWithoutBamboo:x,payrollWithoutAccount:_,nameDifferences:S,possibleWrongAccounts:T,totalPending:k,totalRows:m.length,totalPayroll:Gn(l.totalNomina??l.total_nomina,void 0),totalBank:Gn(l.totalBanco??l.total_banco,void 0),totalDifference:Gn(l.diferenciaTotal??l.diferencia_total,void 0)},rows:m,message:a.message||i.message?String(a.message??i.message):void 0}}function RR(n){return n==="Coincidencia"?"bg-emerald-50 text-emerald-800 border-emerald-200":n==="Resuelto"?"bg-blue-50 text-blue-800 border-blue-200":n==="No encontrado"?"bg-amber-50 text-amber-800 border-amber-200":n==="Pendiente revisión"?"bg-neutral-100 text-neutral-700 border-neutral-300":"bg-red-50 text-[#ba1a1a] border-red-200"}function hv(n){const e=Et(String(n.category??n.source??"")),i=Et(n.observation??"");return n.status==="Coincidencia"||e.includes("coincidencia")?90:e.includes("posible_cuenta_mal_digitada")||i.includes("mal digitada")?1:e.includes("discrepancia")||n.status==="Riesgo"?2:e.includes("banco_sin_bamboo")?3:e.includes("banco_sin_nomina")?4:e.includes("diferencia_nombre_banco")?5:e.includes("nomina_sin_cuenta")?6:n.status==="Pendiente revisión"?7:n.status==="No encontrado"?10:20}function OR(n){return[...n].sort((e,i)=>{const a=hv(e)-hv(i);return a!==0?a:e.employee.localeCompare(i.employee,"es")})}function DR(n){const e=Et(String(n.category??n.source??""));return e.includes("discrepancia")||e.includes("posible_cuenta_mal_digitada")}function La(n,e){return n.source===e||Et(String(n.category??"")).includes(e)}function MR(n,e="nomina"){const i=Et(String(n??""));return i?i.includes("posible_cuenta_mal_digitada")?"Posible cuenta mal digitada":i.includes("banco_sin_bamboo")?"Banco sin Bamboo":i.includes("banco_sin_nomina")?e==="bono14"?"Banco sin Bono 14":"Banco sin nómina":i.includes("diferencia_nombre_banco")?"Diferencia nombre banco":i.includes("nomina_sin_cuenta")?e==="bono14"?"Bono 14 sin cuenta":"Nómina sin cuenta":i.includes("discrepancia")?"Discrepancia":i.includes("coincidencia")?"Coincidencia":String(n).replace(/_/g," "):"—"}function dv(n){const e=n instanceof Error?n.message:"";return e.includes("planillas del banco repetidas")||e.toLowerCase().includes("sesión expiró")||e.toLowerCase().includes("no hay conexión a internet")?e:"No se pudo procesar el cruce. Verifica los archivos e intenta nuevamente."}function BR(n){try{return new TextDecoder("windows-1252").decode(n)}catch{return new TextDecoder("utf-8").decode(n)}}function fv(n,e){let i=0,a=!1;for(let l=0;lfv(n,a)>fv(n,i)?a:i)}function mv(n,e=Pw(n)){const i=[];let a="",l=!1;for(let c=0;cc.replace(/^\uFEFF/,"").trim())}function UR(n){const e=String(n??"").replace(/\D/g,"");return/^\d{1,20}$/.test(e)?e:null}function LR(n){const e=n.split(/\r?\n/).slice(0,120);for(const i of e.slice(0,25)){const l=Et(i).match(/(?:consulta\s+(?:del\s+)?)?detalle\s+(?:del\s+)?envio\s+(\d{1,20})/);if(l!=null&&l[1])return l[1]}for(let i=0;iEt(m).trim()).findIndex(m=>m==="numero de envio"||m.includes("numero de envio"));if(!(d<0))for(let m=i+1;mi.length>1).map(([i,a])=>({shipmentNumber:i,fileNames:a}))}const VR={1:["enero","january","jan"],2:["febrero","february","feb"],3:["marzo","march","mar"],4:["abril","april","apr"],5:["mayo","may"],6:["junio","june","jun"],7:["julio","july","jul"],8:["agosto","august","aug"],9:["septiembre","setiembre","september","sep"],10:["octubre","october","oct"],11:["noviembre","november","nov"],12:["diciembre","december","dec"]};function PR(n){const e=Et(n).replace(/[_\-.()]+/g," ").replace(/\s+/g," ").trim(),i=e.match(/\b(20\d{2})\b/),a=i?Number(i[1]):void 0;let l;for(const[d,m]of Object.entries(VR))if(m.some(p=>new RegExp(`\\b${p}\\b`,"i").test(e))){l=Number(d);break}const c=e.match(/\b(1[0-2]|[1-9])\s+(1|2)\s+(20\d{2})\b/);!l&&c&&(l=Number(c[1]));let u;return(c==null?void 0:c[2])==="1"?u="quincena_15":(c==null?void 0:c[2])==="2"?u="quincena_30":/\b(?:1q|q1|quincena\s*15|primera\s+quincena)\b/.test(e)?u="quincena_15":/\b(?:2q|q2|quincena\s*30|segunda\s+quincena|fin\s+de\s+mes)\b/.test(e)?u="quincena_30":/\b15\b/.test(e)?u="quincena_15":/\b(?:30|31)\b/.test(e)&&(u="quincena_30"),{year:a,month:l,periodType:u}}function gv(n){const e=jR();try{const i=window.localStorage.getItem(`glm_cruce_period_${n}`);if(!i)return e;const a=JSON.parse(i);if(Number.isInteger(a.year)&&Number.isInteger(a.month)&&a.month>=1&&a.month<=12&&(a.periodType==="quincena_15"||a.periodType==="quincena_30"))return a}catch{}return e}async function HR(n,e){const i=PR(n);try{const a=await fetch(SR,{method:"POST",headers:{"Content-Type":"application/json; charset=utf-8"},body:JSON.stringify({fileName:n,country:e.code,countryName:e.name})});if(!a.ok)return i;const l=await a.json().catch(()=>null),c=(l==null?void 0:l.detected)??(l==null?void 0:l.result)??(l==null?void 0:l.analysis)??l??{},u=String(c.periodType??c.period_type??""),d=Number(c.year)||i.year,m=Number(c.month)||i.month,p=u==="quincena_15"||u==="quincena_30"?u:i.periodType;return{year:d,month:m,periodType:p,confidence:Number.isFinite(Number(c.confidence))?Number(c.confidence):void 0,requiresManualReview:!!(c.requiresManualReview??c.requires_manual_review??!1),source:String(c.source??"gemini")}}catch{return{...i,source:"local_fallback",requiresManualReview:!(Number.isInteger(i.year)&&Number.isInteger(i.month)&&(i.periodType==="quincena_15"||i.periodType==="quincena_30"))}}}function $R({onShowToast:n,uploadedPayroll:e,setUploadedPayroll:i,uploadedPayrollFiles:a,setUploadedPayrollFiles:l,uploadedBankFiles:c,setUploadedBankFiles:u,isCruceExecuted:d,setIsCruceExecuted:m,response:p,setResponse:y,onOpenSidebar:v,operatorName:x,operatorEmail:_,resetToken:S=0,country:T}){const k=D.useMemo(()=>gv(T.code),[T.code]),C=T.code==="PA",M=T.code==="SV",N=T.code==="HN",z=C||M,[Y,Z]=D.useState(!1),[F,G]=D.useState(""),[ne,Q]=D.useState("todos"),[ce,me]=D.useState(k.year),[_e,Re]=D.useState(k.month),[Se,V]=D.useState(k.periodType),[K,W]=D.useState(1),[pe,ae]=D.useState(""),[E,P]=D.useState(""),[X,ee]=D.useState(null),[se,ue]=D.useState([]),[ve,Je]=D.useState(""),[Ye,fn]=D.useState(null),[As,ts]=D.useState(()=>new Set),oi=`glm_cruce_period_${T.code}`,J=D.useRef(null),je=D.useRef(null),Oe=D.useRef(null),Ne=D.useRef(null),ct=D.useMemo(()=>AR(),[]),vt=D.useMemo(()=>lv(ce,_e,Se),[ce,_e,Se]),Pt=T.code==="GT"&&_e===7&&Se==="quincena_15",et=D.useMemo(()=>kR(ce),[ce]),Ht=(p==null?void 0:p.reportKind)==="bono14",Hn=Ht?et.label:vt.label,At=D.useMemo(()=>OR((p==null?void 0:p.rows)??[]),[p==null?void 0:p.rows]),js=D.useMemo(()=>{const R=Et(E.trim());return c.map((te,de)=>({file:te,originalIndex:de})).filter(({file:te})=>!R||Et(te.name).includes(R))},[c,E]),Gt=D.useMemo(()=>{const R=Et(ve.trim());return se.map((te,de)=>({file:te,originalIndex:de})).filter(({file:te})=>!R||Et(te.name).includes(R))},[se,ve]),tn=At.filter(R=>{const te=Et(pe.trim());return Et(`${R.employee} ${R.employeeNumber} ${R.account??""} ${R.observation} ${R.category??""} ${R.source??""}`).includes(te)?ne==="discrepancias"?DR(R):ne==="coincidencias"?R.status==="Coincidencia":ne==="banco_sin_nomina"?La(R,"banco_sin_nomina"):ne==="banco_sin_bamboo"?La(R,"banco_sin_bamboo"):ne==="diferencia_nombre_banco"?La(R,"diferencia_nombre_banco"):ne==="nomina_sin_cuenta"?La(R,"nomina_sin_cuenta"):!0:!1}),mn=10,Ut=Math.max(1,Math.ceil(tn.length/mn)),Ns=tn.slice((K-1)*mn,K*mn);D.useEffect(()=>{K>Ut&&W(Ut)},[K,Ut]);const zi=D.useMemo(()=>{var R;return((R=rv.find(te=>te.value===Se))==null?void 0:R.description)??""},[Se]);D.useEffect(()=>{const R=gv(T.code);me(R.year),Re(R.month),V(R.periodType),W(1),ae(""),P(""),Je(""),ee(null),ue([]),Q("todos"),fn(null),ts(new Set)},[T.code]),D.useEffect(()=>{window.localStorage.setItem(oi,JSON.stringify({year:ce,month:_e,periodType:Se}))},[oi,ce,_e,Se]),D.useEffect(()=>{ae(""),P(""),Je(""),ee(null),ue([]),Q("todos"),W(1),G(""),Z(!1),fn(null),ts(new Set),J.current&&(J.current.value=""),je.current&&(je.current.value=""),Oe.current&&(Oe.current.value=""),Ne.current&&(Ne.current.value="")},[S]),D.useEffect(()=>{c.length===0&&E&&P("")},[c.length,E]),D.useEffect(()=>{se.length===0&&ve&&Je("")},[se.length,ve]);const xt=()=>{m(!1),y(null),W(1),ae(""),Q("todos")},ks=async(R,te=1)=>{const de=await HR(R.name,T);if(Number.isInteger(de.year)&&Number.isInteger(de.month)&&(de.periodType==="quincena_15"||de.periodType==="quincena_30")){me(de.year),Re(de.month),V(de.periodType);const _t=lv(de.year,de.month,de.periodType),Te=te>1?`${te} archivos de nómina cargados.`:"Nómina cargada.";return de.requiresManualReview?n(`${Te} Período sugerido: ${_t.label}. Confirma los datos antes de ejecutar.`,"info"):n(`${Te} Período detectado: ${_t.label}.`,"success"),!0}return!1},Lt=async R=>{i({name:R.name,size:Bc(R.size),type:"payroll",rawFile:R}),l([]),xt(),await ks(R)||n("Archivo de nómina cargado con éxito.","success")},Nn=async R=>{const te=R.filter(Yr);if(te.length===0){n(`Formato inválido. ${T.name} requiere uno o varios archivos Excel (.xlsx, .xls).`,"error");return}const de=new Set(a.map(Fe=>{var nt;return`${Fe.name}|${((nt=Fe.rawFile)==null?void 0:nt.size)??Fe.size}`})),kn=NR(te).filter(Fe=>{var nt;return!de.has(`${Fe.name}|${((nt=Fe.rawFile)==null?void 0:nt.size)??Fe.size}`)}),_t=[...a,...kn];l(_t),i(null),xt();const Te=te.find(Fe=>/quincenal/i.test(Fe.name))||te[0];await ks(Te,_t.length)||n(`${kn.length} archivo(s) de nómina de ${T.name} cargado(s) con éxito.`,"success")},wt=R=>{R.preventDefault()},ko=async R=>{R.preventDefault();const te=Array.from(R.dataTransfer.files);if(te.length===0)return;if(z){await Nn(te);return}const de=te[0];Yr(de)?await Lt(de):n("Formato inválido. Por favor carga un archivo Excel (.xlsx, .xls).","error")},Gc=R=>{R.preventDefault();const de=Array.from(R.dataTransfer.files).filter(Dd);if(de.length===0){n("Formato inválido. Por favor carga uno o varios archivos CSV (.csv).","error");return}u([...c,...Md(de)]),xt(),n(`${de.length} planilla(s) del banco cargada(s) con éxito.`,"success")},Kc=async R=>{const te=Array.from(R.target.files||[]);if(te.length===0)return;if(z){await Nn(te);return}const de=te[0];Yr(de)?await Lt(de):n("Formato inválido. Por favor carga un archivo Excel (.xlsx, .xls).","error")},Vi=R=>{l(a.filter((te,de)=>de!==R)),J.current&&(J.current.value=""),xt(),n("Archivo de nómina removido.","info")},Pi=R=>{const te=R.target.files;if(!te||te.length===0)return;const de=Array.from(te).filter(Dd);if(de.length===0){n("Formato inválido. Por favor carga uno o varios archivos CSV (.csv).","error");return}u([...c,...Md(de)]),xt(),n(`${de.length} planilla(s) del banco cargada(s) con éxito.`,"success")},Hi=R=>{u(c.filter((te,de)=>de!==R)),je.current&&(je.current.value=""),xt(),n("Planilla de banco removida.","info")},ns=()=>{i(null),l([]),u([]),P(""),J.current&&(J.current.value=""),je.current&&(je.current.value=""),xt(),n("Todos los archivos cargados fueron eliminados.","info")},li=R=>{ee({name:R.name,size:Bc(R.size),type:"payroll",rawFile:R}),xt(),n("Archivo Excel de Bono 14 cargado con éxito.","success")},ci=R=>{R.preventDefault();const te=R.dataTransfer.files[0];if(te){if(!Yr(te)){n("Formato inválido. El Bono 14 requiere un archivo Excel (.xlsx, .xls).","error");return}li(te)}},Yc=R=>{var de;const te=(de=R.target.files)==null?void 0:de[0];if(te){if(!Yr(te)){n("Formato inválido. El Bono 14 requiere un archivo Excel (.xlsx, .xls).","error");return}li(te)}},Wa=R=>{const te=R.filter(Dd);if(te.length===0){n("Formato inválido. Carga uno o varios archivos CSV del Bono 14.","error");return}ue([...se,...Md(te)]),xt(),n(`${te.length} archivo(s) bancario(s) de Bono 14 cargado(s).`,"success")},er=R=>{R.preventDefault(),Wa(Array.from(R.dataTransfer.files))},ui=R=>{var te;(te=R.target.files)!=null&&te.length&&Wa(Array.from(R.target.files))},Xc=R=>{ue(se.filter((te,de)=>de!==R)),Ne.current&&(Ne.current.value=""),xt(),n("Archivo bancario de Bono 14 removido.","info")},Co=async()=>{if(Y)return;if(!ce||!_e||!Se){n("Debes seleccionar año, mes y tipo de cruce antes de ejecutar.","error");return}const R=z?a:e?[e]:[];if(R.length===0||R.some(te=>!te.rawFile)){n(z?`Debes cargar todos los archivos de nómina de ${T.name} correspondientes al período antes de ejecutar el cruce.`:"Debes cargar la nómina antes de ejecutar el cruce.","error");return}if(c.length===0||c.some(te=>!te.rawFile)){n("Debes cargar al menos una planilla del banco antes de ejecutar el cruce.","error");return}Z(!0),G("Validando archivos..."),W(1);try{const te=await pv(c);if(te.length>0){const ze=te.map(Kt=>`Número de envío ${Kt.shipmentNumber}: ${Kt.fileNames.join(", ")}`).join(` -`);throw new Error(`Hay planillas del banco repetidas. Revisa los archivos cargados antes de continuar. -${ze}`)}const de=TR(T.code);if(!de)throw new Error("No se pudo procesar el cruce. El servicio de conciliación no está disponible.");const kn=iv();G("Revalidando sesión..."),await So(300),G("Reactivando conexión segura..."),await sv(de);const _t=av(de,kn),Te=R.find(ze=>/quincenal/i.test(ze.name))||R[0],tt={country:T.code,country_name:T.name,year:ce,month:_e,period_type:Se,period_label:vt.label,period_start:vt.start,period_end:vt.end,payroll_file_name:Te.name,payroll_file_names:R.map(ze=>ze.name),bank_file_names:c.map(ze=>ze.name),requested_by_name:x,requested_by_email:_,auth_mode:"supabase_google",source_app:"portal-cruce-cuentas-glm",client_request_id:kn};G("Procesando cruce de cuentas...");const Fe=new FormData;Fe.append("metadata",JSON.stringify(tt)),Object.entries(tt).forEach(([ze,Kt])=>{Fe.append(ze,Array.isArray(Kt)?JSON.stringify(Kt):String(Kt))}),z&&R.forEach((ze,Kt)=>{ze.rawFile&&(Fe.append("payroll_files",ze.rawFile,ze.name),Fe.append(`payroll_file_${Kt+1}`,ze.rawFile,ze.name))}),Te.rawFile&&Fe.append("payroll_file",Te.rawFile,Te.name),c.forEach((ze,Kt)=>{ze.rawFile&&(Fe.append("bank_files",ze.rawFile,ze.name),Fe.append(`bank_file_${Kt+1}`,ze.rawFile,ze.name))});const nt=await fetch(_t,{method:"POST",body:Fe,cache:"no-store",credentials:"omit"});G("Generando reporte...");const Qe=(nt.headers.get("content-type")??"").includes("application/json")?await nt.json():{ok:nt.ok,message:await nt.text()};if(!nt.ok||(Qe==null?void 0:Qe.ok)===!1)throw new Error(typeof(Qe==null?void 0:Qe.message)=="string"?Qe.message:"No se pudo procesar el cruce. Verifica los archivos e intenta nuevamente.");const $t=uv(Qe);$t.reportKind="nomina",y($t),m(!0),n($t.message||"Cruce procesado correctamente.","success"),setTimeout(()=>{const ze=document.getElementById("resultados-seccion");ze&&ze.scrollIntoView({behavior:"smooth",block:"start"})},150)}catch(te){const de=dv(te);m(!1),y(null),n(de,"error")}finally{Z(!1),G("")}},Ro=async()=>{if(!Y){if(!Pt){n("El cruce de Bono 14 solo está disponible en Guatemala para julio · Quincena 15.","error");return}if(!(X!=null&&X.rawFile)){n("Debes cargar el Excel de Bono 14 antes de generar el reporte.","error");return}if(se.length===0||se.some(R=>!R.rawFile)){n("Debes cargar al menos un CSV bancario del Bono 14.","error");return}Z(!0),G("Validando archivos de Bono 14..."),W(1);try{const R=await pv(se);if(R.length>0){const Qe=R.map($t=>`Número de envío ${$t.shipmentNumber}: ${$t.fileNames.join(", ")}`).join(` -`);throw new Error(`Hay planillas del banco repetidas. Revisa los archivos cargados antes de continuar. -${Qe}`)}const te=ER(),de=iv();G("Revalidando sesión..."),await So(300),G("Reactivando conexión segura..."),await sv(te);const kn=av(te,de),_t={country:"GT",country_name:"Guatemala",year:ce,month:7,period_type:"bono14",report_kind:"bono14",period_label:et.label,period_start:et.start,period_end:et.end,payroll_file_name:X.name,bank_file_names:se.map(Qe=>Qe.name),requested_by_name:x,requested_by_email:_,auth_mode:"supabase_google",source_app:"portal-cruce-cuentas-glm-bono14",client_request_id:de},Te=new FormData;Te.append("metadata",JSON.stringify(_t)),Object.entries(_t).forEach(([Qe,$t])=>{Te.append(Qe,Array.isArray($t)?JSON.stringify($t):String($t))}),Te.append("payroll_file",X.rawFile,X.name),se.forEach((Qe,$t)=>{Qe.rawFile&&(Te.append("bank_files",Qe.rawFile,Qe.name),Te.append(`bank_file_${$t+1}`,Qe.rawFile,Qe.name))}),G("Procesando Bono 14...");const tt=await fetch(kn,{method:"POST",body:Te,cache:"no-store",credentials:"omit"});G("Generando reporte independiente de Bono 14...");const nt=(tt.headers.get("content-type")??"").includes("application/json")?await tt.json():{ok:tt.ok,message:await tt.text()};if(!tt.ok||(nt==null?void 0:nt.ok)===!1)throw new Error(typeof(nt==null?void 0:nt.message)=="string"?nt.message:"No se pudo procesar el Bono 14. Verifica los archivos e intenta nuevamente.");const $i=uv(nt);$i.reportKind="bono14",y($i),m(!0),n($i.message||"Bono 14 procesado correctamente.","success"),setTimeout(()=>{const Qe=document.getElementById("resultados-seccion");Qe&&Qe.scrollIntoView({behavior:"smooth",block:"start"})},150)}catch(R){m(!1),y(null),n(dv(R),"error")}finally{Z(!1),G("")}}},Oo=()=>{if(p!=null&&p.reportUrl){window.open(p.reportUrl,"_blank","noopener,noreferrer");return}n("El reporte aún no tiene un enlace disponible.","info")};return g.jsxs("div",{className:"flex-1 flex flex-col min-h-screen bg-neutral-50 text-neutral-800 font-sans",children:[g.jsxs("header",{className:"bg-white border-b-4 border-[#6CC24A] flex justify-between items-center w-full px-4 sm:px-8 h-[84px] sticky top-0 z-40 shadow-sm gap-x-4",children:[g.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 min-w-0",children:[g.jsx("button",{onClick:v,className:"md:hidden p-2 -ml-2 text-neutral-600 hover:text-neutral-800 hover:bg-neutral-100 transition-colors flex-shrink-0","aria-label":"Abrir menú",children:g.jsx(hw,{className:"w-6 h-6"})}),g.jsx("h1",{className:"text-xs sm:text-sm md:text-lg lg:text-2xl font-black text-[#4F758B] tracking-tight uppercase flex items-center gap-1.5 sm:gap-2 min-w-0",children:g.jsxs("span",{className:"truncate",children:["Cruce de Cuentas GLM - ",T.name]})})]}),g.jsxs("div",{className:"flex-shrink-0 flex items-center gap-2",children:[g.jsx(zw,{country:T,operatorName:x,operatorEmail:_,section:"Cruce de Cuentas",onShowToast:n}),g.jsx("div",{className:"border border-neutral-200 bg-white px-2.5 py-2 shadow-sm","aria-label":`Bandera de ${T.name}`,children:g.jsx("img",{src:T.flag,alt:`Bandera de ${T.name}`,className:"h-5 sm:h-6 w-auto object-contain"})})]})]}),g.jsxs("main",{className:"flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 py-8 flex flex-col gap-8",children:[g.jsxs("section",{className:"bg-white border border-[#D0D0D0] p-5 shadow-sm",children:[g.jsx("div",{className:"flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-5",children:g.jsxs("div",{className:"flex items-center gap-3",children:[g.jsx("div",{className:"bg-[#4F758B]/10 p-2 rounded-full",children:g.jsx(uw,{className:"w-5 h-5 text-[#4F758B]"})}),g.jsx("div",{className:"pt-0.5",children:g.jsx("h2",{className:"text-sm font-black text-[#4F758B] uppercase tracking-wider leading-none",children:"Datos del cruce"})})]})}),g.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[g.jsxs("label",{className:"flex flex-col gap-1.5",children:[g.jsx("span",{className:"text-[10px] font-black uppercase tracking-wider text-neutral-500",children:"Año"}),g.jsx("select",{value:ce,onChange:R=>{me(Number(R.target.value)),xt()},className:"border border-[#D0D0D0] bg-white px-3 py-2.5 text-sm font-bold text-neutral-700 focus:outline-none focus:border-[#4F758B]",children:ct.map(R=>g.jsx("option",{value:R,children:R},R))})]}),g.jsxs("label",{className:"flex flex-col gap-1.5",children:[g.jsx("span",{className:"text-[10px] font-black uppercase tracking-wider text-neutral-500",children:"Mes"}),g.jsx("select",{value:_e,onChange:R=>{Re(Number(R.target.value)),xt()},className:"border border-[#D0D0D0] bg-white px-3 py-2.5 text-sm font-bold text-neutral-700 focus:outline-none focus:border-[#4F758B]",children:Vw.map(R=>g.jsx("option",{value:R.value,children:R.label},R.value))})]}),g.jsxs("label",{className:"flex flex-col gap-1.5",children:[g.jsx("span",{className:"text-[10px] font-black uppercase tracking-wider text-neutral-500",children:"Tipo de cruce"}),g.jsx("select",{value:Se,onChange:R=>{V(R.target.value),xt()},className:"border border-[#D0D0D0] bg-white px-3 py-2.5 text-sm font-bold text-neutral-700 focus:outline-none focus:border-[#4F758B]",children:rv.map(R=>g.jsx("option",{value:R.value,children:R.label},R.value))})]})]}),g.jsxs("div",{className:"mt-4 grid grid-cols-1 md:grid-cols-[1fr_auto] gap-3 items-center bg-[#4F758B]/5 border border-[#4F758B]/10 px-4 py-3",children:[g.jsxs("div",{className:"flex items-start gap-2 text-xs text-neutral-600",children:[g.jsx(yj,{className:"w-4 h-4 text-[#4F758B] mt-0.5 flex-shrink-0"}),g.jsxs("div",{children:[g.jsx("span",{className:"font-black text-[#4F758B]",children:"Período calculado:"})," ",ov(vt.start)," al ",ov(vt.end),g.jsx("p",{className:"text-[10px] text-neutral-500 mt-0.5",children:zi})]})]}),g.jsx("div",{className:"text-[10px] font-bold text-neutral-500 uppercase tracking-wider",children:vt.label})]})]}),(z||N)&&(e!==null||a.length>0||c.length>0)&&g.jsx("div",{className:"flex justify-end",style:{marginBottom:"-20px"},children:g.jsxs("button",{type:"button",onClick:ns,className:"inline-flex items-center gap-2 border border-red-100 bg-white px-3 py-2 text-[10px] font-black text-red-600 hover:bg-red-50 transition-colors uppercase tracking-wide shadow-sm","aria-label":"Borrar todos los archivos cargados",title:"Eliminar de una vez todas las nóminas y planillas del banco cargadas",children:[g.jsx(Aa,{className:"w-4 h-4"}),"Borrar todo"]})}),g.jsxs("section",{className:"grid grid-cols-1 lg:grid-cols-2 gap-6",children:[g.jsxs("div",{className:"bg-white border border-[#D0D0D0] p-6 flex flex-col justify-between relative shadow-sm hover:shadow-md transition-shadow",children:[g.jsx("div",{className:"absolute top-0 left-0 w-full h-1 bg-[#4F758B]"}),g.jsxs("div",{children:[g.jsxs("div",{className:"flex items-center gap-2.5 mb-4",children:[g.jsx(Da,{className:"w-5 h-5 text-[#4F758B]"}),g.jsx("h2",{className:"text-sm font-black text-neutral-800 uppercase tracking-wider",children:"1. Carga de Nómina"})]}),g.jsx("input",{type:"file",ref:J,onChange:Kc,accept:".xlsx,.xls",multiple:z,className:"hidden"}),z?a.length>0?g.jsxs("div",{className:"space-y-3",children:[g.jsxs("div",{className:"border border-[#4F758B]/20 bg-[#4F758B]/5 px-4 py-3",children:[g.jsxs("p",{className:"text-[10px] font-black uppercase tracking-wider text-[#4F758B]",children:[a.length," archivo(s) de nómina cargado(s)"]}),g.jsx("p",{className:"text-[10px] text-neutral-500 mt-1",children:C?"Incluye la quincenal y todas las planillas extraordinarias, vacaciones o XIII Mes que correspondan al período.":"Incluye todos los archivos de nómina de El Salvador que correspondan a la quincena seleccionada (General, Karla Elias, Nestlé, Motorola u otros del mismo formato)."})]}),g.jsx("div",{className:"max-h-[260px] overflow-y-auto border border-neutral-200 divide-y divide-neutral-100",children:a.map((R,te)=>g.jsxs("div",{className:"bg-white px-4 py-3 flex items-center justify-between gap-3",children:[g.jsxs("div",{className:"flex items-center gap-3 min-w-0",children:[g.jsx(Da,{className:"w-6 h-6 text-[#6CC24A] flex-shrink-0"}),g.jsxs("div",{className:"overflow-hidden",children:[g.jsx("p",{className:"text-xs font-bold text-neutral-700 truncate max-w-[290px]",title:R.name,children:R.name}),g.jsxs("p",{className:"text-[10px] text-neutral-400 font-mono mt-0.5",children:[R.size," • Excel"]})]})]}),g.jsx("button",{onClick:()=>Vi(te),className:"p-1.5 text-neutral-400 hover:text-red-500 hover:bg-neutral-100 transition-all rounded","aria-label":`Remover ${R.name}`,children:g.jsx(Aa,{className:"w-4.5 h-4.5"})})]},`${R.name}-${te}`))}),g.jsx("button",{type:"button",onClick:()=>{var R;return(R=J.current)==null?void 0:R.click()},className:"w-full border border-dashed border-[#4F758B]/40 bg-white px-4 py-2.5 text-[10px] font-black text-[#4F758B] hover:bg-[#4F758B]/5 transition-colors uppercase tracking-wide",children:"Agregar más archivos de nómina"})]}):g.jsxs("div",{onDragOver:wt,onDrop:ko,onClick:()=>{var R;return(R=J.current)==null?void 0:R.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-neutral-50 hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors flex flex-col items-center justify-center py-10 px-4 text-center cursor-pointer min-h-[190px]",children:[g.jsx(Gr,{className:"w-8 h-8 text-neutral-300 mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre la nómina y sus suplementos"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1 font-medium",children:"Puede seleccionar varios .xlsx / .xls a la vez"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivos"})]}):e?g.jsxs("div",{className:"border border-[#6CC24A]/30 bg-[#6CC24A]/5 p-5 flex items-center justify-between gap-3",children:[g.jsxs("div",{className:"flex items-center gap-3 min-w-0",children:[g.jsx(Da,{className:"w-8 h-8 text-[#6CC24A] flex-shrink-0"}),g.jsxs("div",{className:"overflow-hidden",children:[g.jsx("p",{className:"text-xs font-bold text-neutral-700 truncate max-w-[300px]",title:e.name,children:e.name}),g.jsxs("p",{className:"text-[10px] text-neutral-400 font-mono mt-0.5",children:[e.size," • Excel"]})]})]}),g.jsx("button",{onClick:()=>{i(null),J.current&&(J.current.value=""),xt(),n("Nómina removida.","info")},className:"p-1.5 text-neutral-400 hover:text-red-500 hover:bg-neutral-100 transition-all rounded","aria-label":"Remover nómina",children:g.jsx(Aa,{className:"w-4.5 h-4.5"})})]}):g.jsxs("div",{onDragOver:wt,onDrop:ko,onClick:()=>{var R;return(R=J.current)==null?void 0:R.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-neutral-50 hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors flex flex-col items-center justify-center py-10 px-4 text-center cursor-pointer min-h-[190px]",children:[g.jsx(Gr,{className:"w-8 h-8 text-neutral-300 mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre y suelte archivo Excel"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1 font-medium",children:"Formato .xlsx, .xls"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivo"})]})]})]}),g.jsxs("div",{className:"bg-white border border-[#D0D0D0] p-6 flex flex-col justify-between relative shadow-sm hover:shadow-md transition-shadow",children:[g.jsx("div",{className:"absolute top-0 left-0 w-full h-1 bg-[#4F758B]"}),g.jsxs("div",{children:[g.jsxs("div",{className:"flex items-center gap-2.5 mb-2",children:[g.jsx(Jl,{className:"w-5 h-5 text-[#4F758B]"}),g.jsx("h2",{className:"text-sm font-black text-neutral-800 uppercase tracking-wider",children:"2. Carga de Planillas del Banco"})]}),g.jsx("p",{className:"text-[11px] text-neutral-500 mb-4 leading-relaxed",children:"Puede subir múltiples archivos separados o un archivo consolidado del período."}),g.jsx("input",{type:"file",ref:je,onChange:Pi,accept:".csv",multiple:!0,className:"hidden"}),g.jsxs("div",{onDragOver:wt,onDrop:Gc,onClick:()=>{var R;return(R=je.current)==null?void 0:R.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-neutral-50 hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors flex flex-col items-center justify-center py-8 px-4 text-center cursor-pointer min-h-[130px]",children:[g.jsx(Gr,{className:"w-7 h-7 text-neutral-300 mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre y suelte archivo CSV"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1 font-medium",children:"Formato .csv"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivos"})]}),c.length>0&&g.jsxs("div",{className:"mt-4 border border-neutral-200 bg-white",children:[g.jsxs("div",{className:"p-3 border-b border-neutral-200 bg-neutral-50",children:[g.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-2",children:[g.jsxs("div",{children:[g.jsxs("p",{className:"text-[11px] font-black text-neutral-700 uppercase tracking-wide",children:[c.length," ",c.length===1?"archivo cargado":"archivos cargados"]}),g.jsx("p",{className:"text-[9px] text-neutral-400 mt-0.5",children:"Busque por nombre para localizar y eliminar una planilla rápidamente."})]}),E&&g.jsx("button",{type:"button",onClick:R=>{R.stopPropagation(),P("")},className:"self-start sm:self-auto text-[9px] font-bold uppercase tracking-wide text-[#4F758B] hover:underline",children:"Limpiar búsqueda"})]}),g.jsxs("div",{className:"relative",children:[g.jsx(Jr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-neutral-400 pointer-events-none"}),g.jsx("input",{type:"search",value:E,onChange:R=>P(R.target.value),onClick:R=>R.stopPropagation(),placeholder:"Buscar archivo CSV por nombre...",className:"w-full h-9 pl-9 pr-3 border border-[#D0D0D0] bg-white text-[11px] text-neutral-700 outline-none focus:border-[#4F758B] focus:ring-2 focus:ring-[#4F758B]/10 transition-all","aria-label":"Buscar archivos CSV cargados"})]}),E&&g.jsxs("p",{className:"text-[9px] text-neutral-400 mt-2",children:["Mostrando ",js.length," de ",c.length," archivos."]})]}),g.jsx("div",{className:"max-h-[330px] overflow-y-auto overscroll-contain p-2 space-y-2",children:js.length>0?js.map(({file:R,originalIndex:te})=>g.jsxs("div",{className:"border border-neutral-200 bg-white px-3 py-3 flex items-center justify-between gap-3 hover:border-[#4F758B]/40 hover:bg-[#4F758B]/[0.025] transition-colors",children:[g.jsxs("div",{className:"flex items-start gap-3 min-w-0 flex-1",children:[g.jsx("div",{className:"w-7 h-7 mt-0.5 border border-[#4F758B]/20 bg-[#4F758B]/5 flex items-center justify-center flex-shrink-0",children:g.jsx(Jl,{className:"w-4 h-4 text-[#4F758B]"})}),g.jsxs("div",{className:"min-w-0 flex-1",children:[g.jsx("p",{className:"text-[11px] leading-4 font-bold text-neutral-700 whitespace-normal break-words",title:R.name,children:R.name}),g.jsxs("div",{className:"flex items-center gap-2 mt-1",children:[g.jsx("span",{className:"text-[9px] text-neutral-400 font-mono",children:R.size}),g.jsx("span",{className:"text-[8px] font-bold uppercase tracking-wide text-[#4F758B] bg-[#4F758B]/5 border border-[#4F758B]/10 px-1.5 py-0.5",children:"CSV"})]})]})]}),g.jsx("button",{type:"button",onClick:de=>{de.stopPropagation(),Hi(te)},className:"w-8 h-8 flex items-center justify-center flex-shrink-0 text-neutral-400 hover:text-red-600 hover:bg-red-50 border border-transparent hover:border-red-100 rounded transition-colors","aria-label":`Remover ${R.name}`,title:`Eliminar ${R.name}`,children:g.jsx(Aa,{className:"w-4 h-4"})})]},`${R.name}-${te}`)):g.jsxs("div",{className:"py-8 px-4 text-center",children:[g.jsx(Jr,{className:"w-5 h-5 text-neutral-300 mx-auto mb-2"}),g.jsx("p",{className:"text-[11px] font-bold text-neutral-600",children:"No encontramos archivos con ese nombre."}),g.jsx("p",{className:"text-[9px] text-neutral-400 mt-1",children:"Pruebe con otra parte del nombre del CSV."})]})})]})]})]})]}),Pt&&g.jsx("section",{className:"bg-white border border-[#D0D0D0] shadow-sm",children:g.jsxs("div",{className:"border-t-4 border-[#6CC24A] p-5 sm:p-6",children:[g.jsxs("div",{className:"flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4 mb-5",children:[g.jsxs("div",{className:"flex items-start gap-3",children:[g.jsx("div",{className:"bg-[#6CC24A]/10 p-2 rounded-full",children:g.jsx(wb,{className:"w-5 h-5 text-[#4F758B]"})}),g.jsxs("div",{children:[g.jsx("h2",{className:"text-sm font-black text-[#4F758B] uppercase tracking-wider",children:"Bono 14 · Reporte independiente"}),g.jsx("p",{className:"text-[11px] text-neutral-500 mt-1 max-w-3xl leading-relaxed",children:"Esta sección aparece únicamente para Guatemala · Julio · Quincena 15. El Bono 14 se procesa separado de la nómina quincenal y genera su propio Google Sheet e histórico."})]})]}),g.jsxs("div",{className:"text-left lg:text-right",children:[g.jsx("p",{className:"text-[9px] font-black uppercase tracking-wider text-neutral-400",children:"Período del Bono 14"}),g.jsxs("p",{className:"text-[11px] font-bold text-[#4F758B] mt-1",children:[et.start," al ",et.end]})]})]}),g.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-5",children:[g.jsxs("div",{className:"border border-neutral-200 p-4 bg-neutral-50/50",children:[g.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[g.jsx(Da,{className:"w-4.5 h-4.5 text-[#4F758B]"}),g.jsx("h3",{className:"text-xs font-black uppercase tracking-wider text-neutral-700",children:"1. Excel de Bono 14"})]}),g.jsx("p",{className:"text-[10px] text-neutral-500 mb-3",children:"Archivo anual con los montos calculados de Bono 14."}),g.jsx("input",{type:"file",ref:Oe,onChange:Yc,accept:".xlsx,.xls",className:"hidden"}),X?g.jsxs("div",{className:"border border-neutral-200 bg-white px-3 py-3 flex items-center justify-between gap-3",children:[g.jsxs("div",{className:"min-w-0",children:[g.jsx("p",{className:"text-[11px] font-bold text-neutral-700 break-words",title:X.name,children:X.name}),g.jsxs("p",{className:"text-[9px] text-neutral-400 font-mono mt-1",children:[X.size," · Excel"]})]}),g.jsx("button",{type:"button",onClick:()=>{ee(null),Oe.current&&(Oe.current.value=""),xt(),n("Excel de Bono 14 removido.","info")},className:"w-8 h-8 flex items-center justify-center text-neutral-400 hover:text-red-600 hover:bg-red-50 border border-transparent hover:border-red-100 rounded transition-colors","aria-label":"Remover Excel de Bono 14",children:g.jsx(Aa,{className:"w-4 h-4"})})]}):g.jsxs("div",{onDragOver:wt,onDrop:ci,onClick:()=>{var R;return(R=Oe.current)==null?void 0:R.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-white hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors py-8 px-4 text-center cursor-pointer",children:[g.jsx(Gr,{className:"w-7 h-7 text-neutral-300 mx-auto mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre el Excel de Bono 14"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1",children:"Formato .xlsx, .xls"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivo"})]})]}),g.jsxs("div",{className:"border border-neutral-200 p-4 bg-neutral-50/50",children:[g.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[g.jsx(Jl,{className:"w-4.5 h-4.5 text-[#4F758B]"}),g.jsx("h3",{className:"text-xs font-black uppercase tracking-wider text-neutral-700",children:"2. CSV bancarios del Bono 14"})]}),g.jsx("p",{className:"text-[10px] text-neutral-500 mb-3",children:"Puede cargar todos los envíos bancarios correspondientes al Bono 14."}),g.jsx("input",{type:"file",ref:Ne,onChange:ui,accept:".csv",multiple:!0,className:"hidden"}),g.jsxs("div",{onDragOver:wt,onDrop:er,onClick:()=>{var R;return(R=Ne.current)==null?void 0:R.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-white hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors py-6 px-4 text-center cursor-pointer",children:[g.jsx(Gr,{className:"w-6 h-6 text-neutral-300 mx-auto mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre uno o varios CSV"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1",children:"Formato .csv"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivos"})]}),se.length>0&&g.jsxs("div",{className:"mt-3 border border-neutral-200 bg-white",children:[g.jsxs("div",{className:"p-3 border-b border-neutral-200 bg-neutral-50",children:[g.jsxs("div",{className:"relative",children:[g.jsx(Jr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-neutral-400 pointer-events-none"}),g.jsx("input",{type:"search",value:ve,onChange:R=>Je(R.target.value),onClick:R=>R.stopPropagation(),placeholder:"Buscar CSV de Bono 14...",className:"w-full h-9 pl-9 pr-3 border border-[#D0D0D0] bg-white text-[11px] text-neutral-700 outline-none focus:border-[#4F758B]"})]}),g.jsxs("p",{className:"text-[9px] text-neutral-400 mt-2",children:[se.length," archivo(s) cargado(s)."]})]}),g.jsx("div",{className:"max-h-[250px] overflow-y-auto p-2 space-y-2",children:Gt.map(({file:R,originalIndex:te})=>g.jsxs("div",{className:"border border-neutral-200 px-3 py-2.5 flex items-start justify-between gap-3",children:[g.jsxs("div",{className:"min-w-0",children:[g.jsx("p",{className:"text-[10px] leading-4 font-bold text-neutral-700 break-words",children:R.name}),g.jsx("p",{className:"text-[9px] text-neutral-400 font-mono mt-0.5",children:R.size})]}),g.jsx("button",{type:"button",onClick:de=>{de.stopPropagation(),Xc(te)},className:"w-8 h-8 flex items-center justify-center flex-shrink-0 text-neutral-400 hover:text-red-600 hover:bg-red-50 rounded transition-colors","aria-label":`Remover ${R.name}`,children:g.jsx(Aa,{className:"w-4 h-4"})})]},`bonus14-${R.name}-${te}`))})]})]})]}),g.jsxs("div",{className:"mt-5 flex flex-col items-center gap-2",children:[g.jsxs("button",{type:"button",onClick:Ro,disabled:Y||!(X!=null&&X.rawFile)||se.length===0,className:"inline-flex items-center justify-center gap-2 bg-[#4F758B] hover:bg-[#3d6378] text-white px-7 py-3 text-xs font-black uppercase tracking-wider transition-colors disabled:opacity-45 disabled:cursor-not-allowed min-w-[280px]",children:[Y?g.jsx(qa,{className:"w-4 h-4 animate-spin"}):g.jsx(wb,{className:"w-4 h-4"}),"Generar reporte Bono 14"]}),g.jsxs("p",{className:"text-[9px] text-neutral-400 text-center",children:["Se guardará como un reporte separado: GT · ",ce," · Bono 14."]})]})]})}),g.jsx("section",{className:"flex flex-col items-center gap-3",children:g.jsx("button",{onClick:Co,disabled:Y,className:"w-full sm:w-auto min-w-[280px] bg-[#4F758B] hover:bg-[#41677b] disabled:bg-neutral-300 disabled:cursor-not-allowed text-white px-8 py-4 font-black text-sm uppercase tracking-wider shadow-md flex items-center justify-center gap-3 transition-all duration-150 active:scale-[0.99] border-l-4 border-[#6CC24A]",children:Y?g.jsxs(g.Fragment,{children:[g.jsx(qa,{className:"w-5 h-5 animate-spin"}),g.jsx("span",{children:F||"Procesando cruce de cuentas..."})]}):g.jsxs(g.Fragment,{children:[g.jsx(Xj,{className:"w-5 h-5"}),g.jsx("span",{children:"Ejecutar Cruce de Cuentas"})]})})}),g.jsx(ei,{children:d&&p&&g.jsxs(un.section,{id:"resultados-seccion",initial:{opacity:0,y:20},animate:{opacity:1,y:0},exit:{opacity:0,y:10},className:"flex flex-col gap-5 scroll-mt-28",children:[g.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 gap-4",children:[g.jsxs("div",{className:"bg-white border border-emerald-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-emerald-700",children:[g.jsx(Ac,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Coincidencias"})]}),g.jsx("p",{className:"text-3xl font-black text-emerald-800 mt-2",children:p.summary.matches})]}),g.jsxs("div",{className:"bg-white border border-red-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-[#ba1a1a]",children:[g.jsx(fo,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Discrepancias"})]}),g.jsx("p",{className:"text-3xl font-black text-[#ba1a1a] mt-2",children:p.summary.discrepancies})]}),g.jsxs("div",{className:"bg-white border border-amber-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-amber-800",children:[g.jsx(xb,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:Ht?"Banco sin Bono 14":"Banco sin nómina"})]}),g.jsx("p",{className:"text-3xl font-black text-amber-800 mt-2",children:p.summary.bankWithoutPayroll})]}),g.jsxs("div",{className:"bg-white border border-orange-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-orange-800",children:[g.jsx(xb,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Banco sin Bamboo"})]}),g.jsx("p",{className:"text-3xl font-black text-orange-800 mt-2",children:p.summary.bankWithoutBamboo})]}),g.jsxs("div",{className:"bg-white border border-violet-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-violet-800",children:[g.jsx(Jl,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Diferencias nombre banco"})]}),g.jsx("p",{className:"text-3xl font-black text-violet-800 mt-2",children:p.summary.nameDifferences})]}),g.jsxs("div",{className:"bg-white border border-blue-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-blue-800",children:[g.jsx(Da,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:Ht?"Bono 14 sin cuenta":"Nómina sin cuenta"})]}),g.jsx("p",{className:"text-3xl font-black text-blue-800 mt-2",children:p.summary.payrollWithoutAccount})]})]}),g.jsxs("div",{className:"bg-white border border-[#D0D0D0] shadow-sm overflow-hidden",children:[g.jsxs("div",{className:"p-5 border-b border-neutral-200 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4",children:[g.jsxs("div",{children:[g.jsx("h2",{className:"text-lg font-black text-[#4F758B] uppercase tracking-tight",children:Ht?"Resultados del Bono 14":"Resultados del Cruce"}),g.jsx("p",{className:"text-[11px] text-neutral-500 mt-1",children:Hn})]}),g.jsx("div",{className:"flex flex-col sm:flex-row gap-2",children:g.jsxs("button",{onClick:Oo,disabled:!p.reportUrl,className:"inline-flex items-center justify-center gap-2 border border-[#4F758B] text-[#4F758B] hover:bg-[#4F758B] hover:text-white px-4 py-2 text-xs font-bold transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[g.jsx(kj,{className:"w-4 h-4"}),"Abrir Google Sheet"]})})]}),g.jsxs("div",{className:"p-4 border-b border-neutral-200 bg-neutral-50 flex flex-col lg:flex-row gap-3 lg:items-center lg:justify-between",children:[g.jsxs("div",{className:"relative w-full lg:max-w-sm",children:[g.jsx(Jr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400"}),g.jsx("input",{type:"text",value:pe,onChange:R=>{ae(R.target.value),W(1)},placeholder:"Buscar empleado, Employee Number o cuenta...",className:"w-full pl-9 pr-3 py-2 border border-[#D0D0D0] text-xs font-medium focus:outline-none focus:border-[#4F758B]"})]}),g.jsx("div",{className:"flex flex-wrap gap-2",children:[["todos","Todos"],["discrepancias","Discrepancias"],["coincidencias","Coincidencias"],["banco_sin_nomina",Ht?"Banco sin Bono 14":"Banco sin nómina"],["banco_sin_bamboo","Banco sin Bamboo"],["diferencia_nombre_banco","Diferencias nombre banco"],["nomina_sin_cuenta",Ht?"Bono 14 sin cuenta":"Nómina sin cuenta"]].map(([R,te])=>g.jsx("button",{onClick:()=>{Q(R),W(1)},className:`px-3 py-1.5 text-[10px] uppercase tracking-wider font-black border transition-colors ${ne===R?"bg-[#4F758B] text-white border-[#4F758B]":"bg-white text-neutral-500 border-neutral-300 hover:border-[#4F758B] hover:text-[#4F758B]"}`,children:te},R))})]}),g.jsx("div",{className:"overflow-x-auto",children:g.jsxs("table",{className:"w-full text-left border-collapse min-w-[960px] xl:min-w-full",children:[g.jsx("thead",{children:g.jsxs("tr",{className:"bg-[#4F758B] text-white text-xs font-bold uppercase tracking-wider",children:[g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Empleado"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Employee Number"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Cuenta"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-right",children:Ht?"Monto Bono 14":"Monto Nómina"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-right",children:"Monto Banco"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-right",children:"Diferencia"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Estado"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Categoría"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Observación"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Acciones"})]})}),g.jsx("tbody",{className:"divide-y divide-neutral-200 text-xs",children:Ns.length>0?Ns.map(R=>g.jsxs("tr",{className:"hover:bg-neutral-50 transition-colors",children:[g.jsx("td",{className:"py-3 px-3 xl:px-4 font-bold text-neutral-800",children:R.employee}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-mono text-neutral-600",children:R.employeeNumber||"—"}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-mono text-neutral-500",children:R.account??"—"}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-right font-mono text-neutral-700",children:Bd(R.amountPayroll,T,R.currency)}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-right font-mono text-neutral-700",children:Bd(R.amountBank,T,R.currency)}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-right font-mono text-neutral-700",children:Bd(R.difference,T,R.currency)}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsx("span",{className:`inline-block px-2.5 py-1 text-[9px] font-black uppercase tracking-wider border ${RR(R.status)}`,children:R.status})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-neutral-600 max-w-[180px] capitalize",children:MR(R.category??R.source,(p==null?void 0:p.reportKind)??"nomina")}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-neutral-600 max-w-[300px]",children:R.observation}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:La(R,"banco_sin_bamboo")?As.has(R.id)?g.jsx("span",{className:"inline-flex items-center px-2.5 py-1 border border-emerald-200 bg-emerald-50 text-emerald-800 text-[9px] font-black uppercase tracking-wider",children:"Reportado a RRHH"}):g.jsx("button",{type:"button",onClick:()=>fn(R),className:"inline-flex items-center justify-center px-3 py-2 border border-[#4F758B] text-[#4F758B] hover:bg-[#4F758B] hover:text-white text-[9px] font-black uppercase tracking-wider transition-colors whitespace-nowrap",children:"Reportar a RRHH"}):g.jsx("span",{className:"text-neutral-300",children:"—"})})]},R.id)):g.jsx("tr",{children:g.jsx("td",{colSpan:10,className:"py-12 text-center text-neutral-400 font-medium",children:"No hay resultados para el filtro seleccionado."})})})]})}),g.jsxs("div",{className:"p-4 border-t border-neutral-200 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 text-xs text-neutral-500",children:[g.jsxs("span",{children:["Mostrando ",Ns.length," de ",tn.length," resultado(s)."]}),g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("button",{onClick:()=>W(R=>Math.max(1,R-1)),disabled:K===1,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Anterior"}),g.jsxs("span",{className:"font-bold text-neutral-700",children:["Página ",K," de ",Ut]}),g.jsx("button",{onClick:()=>W(R=>Math.min(Ut,R+1)),disabled:K===Ut,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Siguiente"})]})]})]})]})})]}),Ye&&p&&g.jsx(Lw,{row:Ye,country:T,periodRange:Ht?et:vt,operatorName:x,operatorEmail:_,executionId:p.executionId,reportUrl:p.reportUrl,onClose:()=>fn(null),onSuccess:R=>ts(te=>{const de=new Set(te);return de.add(R),de}),onShowToast:n})]})}const rc=10;function qR(n){return n==="TT"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-tt-historicos":n==="PA"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pa-historicos":n==="SV"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-historicos":n==="HN"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-historicos":"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-historicos"}function FR(n){return n==="TT"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-tt-marcar-resuelto":n==="PA"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pa-marcar-resuelto":n==="SV"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-marcar-resuelto":n==="HN"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-marcar-resuelto":"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-marcar-resuelto"}function Ln(n){const e=Number(n);return Number.isFinite(e)?e:0}function IR(n,e){if(!n)return"";const i=new Date(String(n));return Number.isNaN(i.getTime())?String(n).slice(0,10):i.toLocaleDateString(e,{year:"numeric",month:"2-digit",day:"2-digit"})}function xf(n,e){if(!n)return"";const i=new Date(String(n));return Number.isNaN(i.getTime())?String(n):i.toLocaleString(e,{dateStyle:"short",timeStyle:"short"})}function GR(n){return String(n??"").toLowerCase().includes("resuelto")?"Resuelto":"Pendiente revisión"}function yv(n,e){const i=n,a=!Array.isArray(i)&&i?i:{};return(Array.isArray(i)?i:Array.isArray(a.reports)?a.reports:[]).map((c,u)=>{const d=c,m=Ln(d.discrepancias??d.discrepancies),p=Ln(d.bancoSinNomina??d.banco_sin_nomina??d.bankWithoutPayroll),y=Ln(d.bancoSinBamboo??d.banco_sin_bamboo??d.bankWithoutBamboo),v=Ln(d.nominaSinCuenta??d.nomina_sin_cuenta??d.payrollWithoutAccount),x=Ln(d.diferenciasNombreBanco??d.diferencias_nombre_banco??d.nameDifferences),_=m+p+y+v+x,S=String(d.country??d.pais??d.country_code??e.code).toUpperCase(),T=Array.isArray(d.archivosBanco)?d.archivosBanco.map(String):Array.isArray(d.bank_file_names)?d.bank_file_names.map(String):[],k=d.updated_at??d.fechaEjecucion??d.ejecutado_en??d.created_at;return{id:String(d.id??`reporte-${u+1}`),country:S==="TT"?"TT":S==="PA"?"PA":S==="SV"?"SV":S==="HN"?"HN":"GT",date:IR(d.fecha??d.period_end??k,e.locale),executionDate:xf(k,e.locale),period:String(d.periodo??d.period_label??d.period??"Período no especificado"),periodStart:d.period_start?String(d.period_start):void 0,periodEnd:d.period_end?String(d.period_end):void 0,payrollFile:String(d.archivoNomina??d.payroll_file_name??d.payrollFile??""),bankFiles:T,matches:Ln(d.coincidencias??d.matches),discrepancies:m,bankWithoutPayroll:p,bankWithoutBamboo:y,payrollWithoutAccount:v,nameDifferences:x,totalPending:_,totalPayroll:Ln(d.totalNomina??d.total_nomina),totalBank:Ln(d.totalBanco??d.total_banco),totalDifference:Ln(d.diferenciaTotal??d.diferencia_total),status:GR(d.estado??d.status??d.estadoLabel),reportUrl:d.reportUrl||d.report_url?String(d.reportUrl??d.report_url):void 0,spreadsheetId:d.spreadsheetId||d.spreadsheet_id?String(d.spreadsheetId??d.spreadsheet_id):void 0,executedBy:d.ejecutadoPorNombre||d.ejecutado_por_nombre?String(d.ejecutadoPorNombre??d.ejecutado_por_nombre):void 0,executedByEmail:d.ejecutadoPorEmail||d.ejecutado_por_email?String(d.ejecutadoPorEmail??d.ejecutado_por_email):void 0,resolvedBy:d.resueltoPorNombre||d.resuelto_por_nombre?String(d.resueltoPorNombre??d.resuelto_por_nombre):void 0,resolvedByEmail:d.resueltoPorEmail||d.resuelto_por_email?String(d.resueltoPorEmail??d.resuelto_por_email):void 0,resolvedAt:xf(d.resueltoEn??d.resuelto_en,e.locale),resolutionComment:d.comentarioResolucion||d.comentario_resolucion?String(d.comentarioResolucion??d.comentario_resolucion):void 0,_sortTime:new Date(String(k??d.period_end??"")).getTime()||0}}).filter(c=>c.country===e.code).sort((c,u)=>u._sortTime-c._sortTime).map(({_sortTime:c,...u})=>u)}function wf(n){return String(n??"").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9 ]/g," ").replace(/\s+/g," ").trim()}function _f(n){return String(n??"").replace(/\.0$/g,"").replace(/\D/g,"").trim()}function bv(n,e){for(const i of e){const a=n[i];if(typeof a=="number"&&Number.isFinite(a))return a;if(typeof a=="string"&&a.trim()){const l=Number(a.replace(/[A-Za-z$Q,\s]/g,"").trim());if(Number.isFinite(l))return l}}return null}function KR(n){return Array.isArray(n)?n.map((e,i)=>{const a=e??{},l=String(a.employee??a.employee_name??a.bank_name_file??a.bank_account_holder??"Sin nombre"),c=bv(a,["bankAmount","bank_amount","amount","monto_banco"]),u=a.best_bamboo_candidate&&typeof a.best_bamboo_candidate=="object"?a.best_bamboo_candidate:null;return{id:String(a.id??`historical-bamboo-${wf(l)}-${_f(a.account??a.bank_account)}-${i+1}`),employee:l,employeeNumber:"",account:String(a.account??a.bank_account??a.bankAccount??""),amountPayroll:0,amountBank:c,difference:c===null?null:0-c,status:"Pendiente revisión",category:"banco_sin_bamboo",source:"banco_sin_bamboo",observation:String(a.observation??a.observacion??"Pago bancario sin empleado identificado de forma confiable en BambooHR."),bankNameFile:String(a.bank_name_file??a.bankNameFile??""),bankAccountHolder:String(a.bank_account_holder??a.bankAccountHolder??""),bestBambooCandidate:u?{employeeNumber:String(u.employee_number??u.employeeNumber??""),employeeName:String(u.employee_name??u.employeeName??""),score:bv(u,["score"])}:null,ambiguousBambooMatch:!!a.ambiguous_bamboo_match}}):[]}function YR(n){return Array.isArray(n)?n.map(e=>{const i=e??{};return{id:String(i.id??""),sourceName:String(i.nombre_origen_principal??i.source_name??""),sourceNames:Array.isArray(i.nombres_origen)?i.nombres_origen.map(String):[],bankAccount:String(i.cuenta_bancaria??i.bank_account??""),status:String(i.estado??""),emailSent:i.correo_enviado===!0}}):[]}function XR(n,e){const i=n.status.toLowerCase();if(!(n.emailSent||i==="notificado_rrhh"||i==="corregido_bamboo"))return!1;const l=_f(n.bankAccount),c=_f(e.account);if(l&&c&&l!==c)return!1;const u=new Set([n.sourceName,...n.sourceNames].map(wf).filter(Boolean));return[e.employee,e.bankNameFile,e.bankAccountHolder].map(wf).filter(Boolean).some(m=>u.has(m))}function JR(n){return n==="Resuelto"?"bg-blue-50 text-blue-800 border-blue-200":"bg-amber-50 text-amber-800 border-amber-200"}function QR(n){return n==="Resuelto"?"RESUELTO":"PENDIENTE REVISIÓN"}function ZR({onShowToast:n,onOpenSidebar:e,operatorName:i,operatorEmail:a,country:l}){const[c,u]=D.useState(""),[d,m]=D.useState("todos"),[p,y]=D.useState([]),[v,x]=D.useState(1),[_,S]=D.useState(!1),[T,k]=D.useState(!1),[C,M]=D.useState(""),[N,z]=D.useState(null),[Y,Z]=D.useState(""),[F,G]=D.useState(null),[ne,Q]=D.useState([]),[ce,me]=D.useState(new Set),[_e,Re]=D.useState(!1),[Se,V]=D.useState(""),[K,W]=D.useState(null),[pe,ae]=D.useState(0),[E,P]=D.useState(0),X=D.useRef(0),ee=D.useRef(null),se=Math.max(1,Math.ceil(E/rc)),ue=p;D.useEffect(()=>{x(1)},[c,d,l.code]),D.useEffect(()=>{v>se&&x(se)},[v,se]);const ve=async()=>{var Ne;const J=++X.current;(Ne=ee.current)==null||Ne.abort();const je=new AbortController;ee.current=je;const Oe=()=>X.current===J&&!je.signal.aborted;S(!0),M("");try{if(lt){const Gt=d==="pendiente"?"pendiente_revision":d==="resuelto"?"resuelto":null,{data:tn,error:mn}=await lt.rpc("cruce_cuentas_get_historicos",{p_country:l.code,p_page:v,p_page_size:rc,p_search:c.trim()||null,p_status:Gt});if(!Oe())return;if(!mn&&tn){const Ut=Array.isArray(tn)?tn[0]:tn,Ns=yv(Ut,l),zi=Ln(Ut==null?void 0:Ut.total),xt=Ln((Ut==null?void 0:Ut.filtered_total)??zi);y(Ns),ae(zi),P(xt);return}}if(!Oe())return;const ct=qR(l.code);if(!ct)throw new Error(`No está configurado el origen histórico de ${l.name}.`);const vt=new URL(ct);vt.searchParams.set("country",l.code);const Pt=await fetch(vt.toString(),{method:"GET",signal:je.signal}),et=await Pt.json().catch(()=>null);if(!Oe())return;if(!Pt.ok||(et==null?void 0:et.ok)===!1)throw new Error("No se pudieron cargar los reportes históricos.");const Ht=yv(et,l),Hn=c.trim().toLowerCase(),At=Ht.filter(Gt=>{const tn=d==="todos"||d==="pendiente"&&Gt.status==="Pendiente revisión"||d==="resuelto"&&Gt.status==="Resuelto",mn=!Hn||Gt.period.toLowerCase().includes(Hn)||Gt.payrollFile.toLowerCase().includes(Hn)||Gt.id.toLowerCase().includes(Hn)||(Gt.resolvedBy??"").toLowerCase().includes(Hn)||(Gt.executedBy??"").toLowerCase().includes(Hn);return tn&&mn});if(!Oe())return;const js=(v-1)*rc;y(At.slice(js,js+rc)),ae(Ht.length),P(At.length)}catch(ct){if(!Oe()||ct instanceof DOMException&&ct.name==="AbortError")return;y([]),ae(0),P(0),M(ct instanceof Error?ct.message:"No se pudieron cargar los reportes históricos. Intenta nuevamente."),n("No se pudieron cargar los reportes históricos.","error")}finally{Oe()&&(S(!1),ee.current===je&&(ee.current=null))}};D.useEffect(()=>{const J=window.setTimeout(()=>{ve()},c.trim()?300:0);return()=>{var je;window.clearTimeout(J),X.current+=1,(je=ee.current)==null||je.abort(),ee.current=null}},[l.code,v,c,d]);const Je=()=>{W(null),G(null),Q([]),me(new Set),V("")},Ye=async J=>{if(J.bankWithoutBamboo<=0){n("Este reporte no tiene casos de Banco sin Bamboo.","info");return}if(!lt){n("Supabase no está configurado en esta aplicación.","error");return}G(J),W(null),Q([]),me(new Set),V(""),Re(!0);try{const{data:je,error:Oe}=await lt.rpc("cruce_cuentas_get_banco_sin_bamboo_reporte",{p_report_id:J.id,p_country:l.code});if(Oe)throw Oe;const Ne=Array.isArray(je)?je[0]:je;if(!Ne||Ne.ok===!1)throw new Error(String((Ne==null?void 0:Ne.message)||"No se pudieron recuperar los casos de Banco sin Bamboo."));const ct=KR(Ne.cases),vt=YR(Ne.corrections),Pt=new Set(ct.filter(et=>vt.some(Ht=>XR(Ht,et))).map(et=>et.id));Q(ct),me(Pt),ct.length===0&&V("Este reporte fue creado antes de que se guardara el detalle de Banco sin Bamboo. Los reportes nuevos conservarán los casos para revisarlos desde Históricos.")}catch(je){const Oe=je instanceof Error?je.message:"No se pudieron recuperar los casos de Banco sin Bamboo.";V(Oe),n(Oe,"error")}finally{Re(!1)}},fn={label:(F==null?void 0:F.period)||"Período no especificado",start:(F==null?void 0:F.periodStart)||"",end:(F==null?void 0:F.periodEnd)||""},As=J=>{if(J.reportUrl){window.open(J.reportUrl,"_blank","noopener,noreferrer");return}n("Este reporte aún no tiene un Google Sheet disponible.","info")},ts=J=>{if(J.status==="Resuelto"){n("Este reporte ya fue marcado como resuelto.","info");return}z(J),Z("")},oi=async()=>{if(!N)return;const J=Y.trim();if(J.length<10){n("Debes escribir un comentario de resolución más descriptivo.","error");return}const je=FR(l.code);if(!je){n(`La resolución de reportes para ${l.name} estará disponible próximamente.`,"info");return}k(!0);try{const Oe=await fetch(je,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reportId:N.id,comentarioResolucion:J,resueltoPorNombre:i,resueltoPorEmail:a,country:l.code,countryName:l.name})}),Ne=await Oe.json().catch(()=>null);if(!Oe.ok||(Ne==null?void 0:Ne.ok)===!1){const Pt=Array.isArray(Ne==null?void 0:Ne.errors)?Ne.errors.join(` -`):(Ne==null?void 0:Ne.message)||"No se pudo marcar el reporte como resuelto.";throw new Error(Pt)}const ct=Ne.report??{},vt=xf(ct.resueltoEn??new Date().toISOString(),l.locale);y(Pt=>Pt.map(et=>et.id===N.id?{...et,status:"Resuelto",resolvedBy:String(ct.resueltoPorNombre??i),resolvedByEmail:String(ct.resueltoPorEmail??a),resolvedAt:vt,resolutionComment:String(ct.comentarioResolucion??J)}:et)),z(null),Z(""),n("Reporte marcado como resuelto correctamente.","success")}catch(Oe){const Ne=Oe instanceof Error?Oe.message:"No se pudo marcar el reporte como resuelto.";n(Ne,"error")}finally{k(!1)}};return g.jsxs("div",{className:"flex-1 flex flex-col min-h-screen bg-neutral-50 text-neutral-800 font-sans",children:[g.jsxs("header",{className:"bg-white border-b-4 border-[#6CC24A] flex justify-between items-center w-full px-4 sm:px-8 h-[84px] sticky top-0 z-40 shadow-sm gap-x-4",children:[g.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 min-w-0",children:[g.jsx("button",{onClick:e,className:"md:hidden p-2 -ml-2 text-neutral-600 hover:text-neutral-800 hover:bg-neutral-100 transition-colors flex-shrink-0","aria-label":"Abrir menú",children:g.jsx(hw,{className:"w-6 h-6"})}),g.jsx("h1",{className:"text-xs sm:text-sm md:text-lg lg:text-2xl font-black text-[#4F758B] tracking-tight uppercase flex items-center gap-1.5 sm:gap-2 min-w-0",children:g.jsxs("span",{className:"truncate",children:["Reportes Históricos - ",l.name]})})]}),g.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 flex-shrink-0",children:[g.jsx(zw,{country:l,operatorName:i,operatorEmail:a,section:"Reportes Históricos",onShowToast:n}),g.jsx("div",{className:"hidden sm:flex items-center border border-neutral-200 bg-white px-2.5 py-2 shadow-sm","aria-label":`Bandera de ${l.name}`,children:g.jsx("img",{src:l.flag,alt:`Bandera de ${l.name}`,className:"h-5 sm:h-6 w-auto object-contain"})}),g.jsxs("button",{onClick:ve,disabled:_,className:"hidden sm:inline-flex items-center gap-2 border border-[#4F758B]/20 text-[#4F758B] px-3 py-2 text-xs font-bold hover:border-[#4F758B]/50 disabled:opacity-50",children:[g.jsx(Wj,{className:`w-4 h-4 ${_?"animate-spin":""}`}),"Actualizar"]})]})]}),g.jsxs("main",{className:"flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 py-8 flex flex-col gap-6",children:[g.jsxs("div",{className:"bg-white border border-[#D0D0D0] p-4 flex flex-col lg:flex-row items-center justify-between gap-4 shadow-sm",children:[g.jsxs("div",{className:"relative w-full lg:max-w-md",children:[g.jsx(Jr,{className:"absolute left-3.5 top-1/2 transform -translate-y-1/2 text-neutral-400 w-4 h-4"}),g.jsx("input",{type:"text",placeholder:"Buscar por período, reporte, archivo o resolutor...",value:c,onChange:J=>u(J.target.value),className:"w-full pl-10 pr-4 py-2 border border-[#D0D0D0] text-xs font-medium placeholder-neutral-400 focus:outline-none focus:border-[#4F758B] transition-colors"})]}),g.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center gap-3 w-full lg:w-auto",children:[g.jsx("div",{className:"flex flex-wrap gap-2",children:[["todos","Todos"],["pendiente","Pendiente revisión"],["resuelto","Resuelto"]].map(([J,je])=>g.jsx("button",{onClick:()=>m(J),className:`px-3 py-1.5 text-[10px] uppercase tracking-wider font-black border transition-colors ${d===J?"bg-[#4F758B] text-white border-[#4F758B]":"bg-white text-neutral-500 border-neutral-300 hover:border-[#4F758B] hover:text-[#4F758B]"}`,children:je},J))}),g.jsxs("div",{className:"flex items-center gap-2 text-xs font-bold text-neutral-500",children:[g.jsx(Bj,{className:"w-4 h-4 text-neutral-400"}),g.jsxs("span",{children:["Mostrando: ",E," de ",pe," reportes"]})]})]})]}),C&&g.jsxs("div",{className:"border border-red-200 bg-red-50 text-[#ba1a1a] px-4 py-3 text-xs font-bold flex items-center gap-2",children:[g.jsx(fo,{className:"w-4 h-4"}),C]}),g.jsxs("div",{className:"bg-white border border-[#D0D0D0] shadow-sm overflow-hidden",children:[g.jsx("div",{className:"overflow-x-auto",children:g.jsxs("table",{className:"w-full text-left border-collapse min-w-[960px] xl:min-w-full",children:[g.jsx("thead",{children:g.jsxs("tr",{className:"bg-[#4F758B] text-white text-xs font-bold uppercase tracking-wider",children:[g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Fecha"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Período"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Archivo de Nómina / Bono 14"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Coincidencias"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Banco sin Bamboo"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Pendientes"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Estado"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Resuelto por"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Acciones"})]})}),g.jsx("tbody",{className:"divide-y divide-neutral-200 text-xs",children:_?g.jsx("tr",{children:g.jsx("td",{colSpan:9,className:"py-12 text-center text-neutral-400 font-medium",children:g.jsxs("div",{className:"inline-flex items-center gap-2",children:[g.jsx(qa,{className:"w-4 h-4 animate-spin"}),"Cargando reportes históricos..."]})})}):ue.length>0?ue.map(J=>g.jsxs(un.tr,{initial:{opacity:0},animate:{opacity:1},className:"hover:bg-neutral-50/70 transition-colors",children:[g.jsx("td",{className:"py-3 px-3 xl:px-4 font-medium text-neutral-700",children:g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx(vj,{className:"w-4 h-4 text-neutral-400"}),g.jsxs("div",{children:[g.jsx("span",{children:J.date}),J.executionDate&&g.jsx("p",{className:"text-[9px] text-neutral-400 mt-0.5",children:J.executionDate})]})]})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-bold text-[#4F758B]",children:J.period}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-mono text-neutral-500 truncate max-w-[260px]",title:J.payrollFile,children:g.jsxs("div",{className:"flex items-center gap-1.5",children:[g.jsx(Da,{className:"w-4 h-4 text-neutral-400 flex-shrink-0"}),g.jsx("span",{className:"truncate",children:J.payrollFile||"No especificado"})]})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center font-black text-emerald-800",children:J.matches}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:J.bankWithoutBamboo>0?g.jsxs("button",{type:"button",onClick:()=>void Ye(J),className:"inline-flex items-center gap-1.5 min-w-8 px-2.5 py-1 text-[10px] font-black border bg-orange-50 text-orange-800 border-orange-200 hover:bg-orange-100 hover:border-orange-300 transition-colors",title:"Revisar casos y reportar correcciones a RRHH",children:[g.jsx("span",{children:J.bankWithoutBamboo}),g.jsx("span",{className:"hidden 2xl:inline",children:"Revisar"})]}):g.jsx("span",{className:"inline-block min-w-8 px-2.5 py-1 text-[10px] font-black border bg-neutral-50 text-neutral-400 border-neutral-200",children:"0"})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsx("span",{className:`inline-block px-2.5 py-1 text-[10px] font-bold border ${J.totalPending>0?"bg-red-50 text-[#ba1a1a] border-red-200":"bg-emerald-50 text-emerald-800 border-emerald-200"}`,children:J.totalPending===0?"Sin pendientes":`${J.totalPending} pendiente${J.totalPending>1?"s":""}`})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsx("span",{className:`inline-block px-2.5 py-1 text-[9px] uppercase font-black tracking-wider border ${JR(J.status)}`,children:QR(J.status)})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-neutral-600 max-w-[220px]",children:J.resolvedBy?g.jsxs("div",{title:J.resolutionComment||"",children:[g.jsx("p",{className:"font-bold text-neutral-700 truncate",children:J.resolvedBy}),g.jsx("p",{className:"text-[10px] text-neutral-400",children:J.resolvedAt})]}):g.jsx("span",{className:"text-neutral-400",children:"Pendiente"})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsxs("div",{className:"flex justify-center gap-2",children:[g.jsxs("button",{onClick:()=>As(J),className:"inline-flex items-center gap-1.5 text-[#4F758B] hover:text-[#3d6378] font-bold text-xs border border-[#4F758B]/20 hover:border-[#4F758B]/50 px-3 py-1.5 bg-white hover:bg-neutral-50 transition-colors",children:[g.jsx(Rj,{className:"w-3.5 h-3.5"}),g.jsx("span",{children:"Ver"})]}),g.jsxs("button",{onClick:()=>ts(J),disabled:J.status==="Resuelto",className:"inline-flex items-center gap-1.5 text-emerald-700 hover:text-emerald-800 font-bold text-xs border border-emerald-200 hover:border-emerald-400 px-3 py-1.5 bg-emerald-50 hover:bg-emerald-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[g.jsx(Ac,{className:"w-3.5 h-3.5"}),g.jsx("span",{children:J.status==="Resuelto"?"Resuelto":"Marcar resuelto"})]})]})})]},J.id)):g.jsx("tr",{children:g.jsx("td",{colSpan:9,className:"py-12 text-center text-neutral-400 font-medium",children:"No se encontraron reportes históricos."})})})]})}),g.jsxs("div",{className:"p-4 border-t border-neutral-200 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 text-xs text-neutral-500",children:[g.jsxs("span",{children:["Mostrando ",ue.length," de ",E," reporte(s)."]}),g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("button",{onClick:()=>x(J=>Math.max(1,J-1)),disabled:v===1,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Anterior"}),g.jsxs("span",{className:"font-bold text-neutral-700",children:["Página ",v," de ",se]}),g.jsx("button",{onClick:()=>x(J=>Math.min(se,J+1)),disabled:v===se,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Siguiente"})]})]})]})]}),g.jsx(ei,{children:F&&g.jsx(un.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:"fixed inset-0 z-[80] flex items-center justify-center bg-black/45 px-4 py-6",children:g.jsxs(un.div,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},exit:{opacity:0,y:12},className:"w-full max-w-5xl bg-white border border-[#D0D0D0] shadow-2xl max-h-[92vh] flex flex-col",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A] flex-shrink-0"}),g.jsxs("div",{className:"border-b border-neutral-200 px-5 sm:px-6 py-4 flex items-start justify-between gap-4 flex-shrink-0",children:[g.jsxs("div",{children:[g.jsx("p",{className:"text-[10px] uppercase tracking-[0.18em] font-black text-[#6B8FA3]",children:"Histórico · Banco sin BambooHR"}),g.jsx("h2",{className:"text-lg sm:text-xl font-black text-[#4F758B] mt-1",children:F.period}),g.jsxs("p",{className:"text-xs text-neutral-500 mt-1",children:[l.name," · ",F.bankWithoutBamboo," caso",F.bankWithoutBamboo===1?"":"s"," guardado",F.bankWithoutBamboo===1?"":"s"," en este reporte"]})]}),g.jsx("button",{type:"button",onClick:Je,className:"p-2 text-neutral-400 hover:text-neutral-700 hover:bg-neutral-100","aria-label":"Cerrar revisión de Banco sin Bamboo",children:g.jsx(Ya,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-5 sm:px-6 py-4 overflow-y-auto",children:[g.jsxs("div",{className:"border border-[#D6E8F4] bg-[#F7FBFD] px-4 py-3 text-xs text-neutral-600 leading-relaxed mb-4",children:["Aquí puedes volver días después al reporte y enviar a RRHH únicamente los falsos positivos que ya verificaste manualmente. Los casos previamente enviados se muestran como ",g.jsx("strong",{children:"Reportado a RRHH"}),"."]}),_e?g.jsxs("div",{className:"py-14 text-center text-neutral-500 text-sm",children:[g.jsx(qa,{className:"w-5 h-5 animate-spin mx-auto mb-2 text-[#4F758B]"}),"Recuperando casos del reporte..."]}):Se?g.jsxs("div",{className:"border border-amber-200 bg-amber-50 text-amber-800 px-4 py-3 text-xs font-medium flex items-start gap-2",children:[g.jsx(fo,{className:"w-4 h-4 mt-0.5 flex-shrink-0"}),g.jsx("span",{children:Se})]}):ne.length>0?g.jsx("div",{className:"overflow-x-auto border border-neutral-200",children:g.jsxs("table",{className:"w-full text-left border-collapse min-w-[760px]",children:[g.jsx("thead",{children:g.jsxs("tr",{className:"bg-[#4F758B] text-white text-[10px] font-black uppercase tracking-wider",children:[g.jsx("th",{className:"px-3 py-3",children:"Nombre detectado"}),g.jsx("th",{className:"px-3 py-3",children:"Cuenta"}),g.jsx("th",{className:"px-3 py-3 text-right",children:"Monto banco"}),g.jsx("th",{className:"px-3 py-3",children:"Candidato BambooHR"}),g.jsx("th",{className:"px-3 py-3 text-center",children:"Acción"})]})}),g.jsx("tbody",{className:"divide-y divide-neutral-200 text-xs",children:ne.map(J=>{var Ne;const je=ce.has(J.id),Oe=J.amountBank;return g.jsxs("tr",{className:"hover:bg-neutral-50",children:[g.jsxs("td",{className:"px-3 py-3",children:[g.jsx("p",{className:"font-black text-neutral-800",children:J.employee}),(J.bankNameFile||J.bankAccountHolder)&&g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1",children:[J.bankNameFile,J.bankAccountHolder].filter(Boolean).join(" · ")})]}),g.jsx("td",{className:"px-3 py-3 font-mono font-bold text-neutral-600",children:J.account||"—"}),g.jsx("td",{className:"px-3 py-3 text-right font-black text-neutral-700",children:Oe===null?"—":new Intl.NumberFormat(l.locale,{style:"currency",currency:l.currency,maximumFractionDigits:2}).format(Oe)}),g.jsx("td",{className:"px-3 py-3 text-neutral-600 max-w-[260px]",children:(Ne=J.bestBambooCandidate)!=null&&Ne.employeeName?g.jsxs("div",{children:[g.jsx("p",{className:"font-bold text-[#4F758B]",children:J.bestBambooCandidate.employeeName}),J.bestBambooCandidate.employeeNumber&&g.jsxs("p",{className:"text-[10px] text-neutral-400 mt-0.5",children:["Employee Number ",J.bestBambooCandidate.employeeNumber]})]}):g.jsx("span",{className:"text-neutral-400",children:"Sin candidato confiable"})}),g.jsx("td",{className:"px-3 py-3 text-center",children:je?g.jsxs("span",{className:"inline-flex items-center gap-1.5 px-3 py-1.5 border border-emerald-200 bg-emerald-50 text-emerald-800 text-[10px] font-black uppercase tracking-wider",children:[g.jsx(Ac,{className:"w-3.5 h-3.5"}),"Reportado a RRHH"]}):g.jsx("button",{type:"button",onClick:()=>W(J),className:"inline-flex items-center gap-1.5 px-3 py-1.5 bg-[#6CC24A] hover:bg-[#5aaa3f] text-white text-[10px] font-black uppercase tracking-wider transition-colors",children:"Reportar a RRHH"})})]},J.id)})})]})}):g.jsx("div",{className:"py-12 text-center text-neutral-400 text-sm",children:"No hay casos guardados en este reporte."})]}),g.jsxs("div",{className:"border-t border-neutral-200 px-5 sm:px-6 py-4 flex items-center justify-between gap-3 flex-shrink-0",children:[g.jsxs("span",{className:"text-[10px] text-neutral-400",children:["ID del reporte: ",F.id]}),g.jsx("button",{type:"button",onClick:Je,className:"border border-neutral-300 bg-white px-4 py-2 text-xs font-bold text-neutral-600 hover:bg-neutral-50",children:"Cerrar"})]})]})})}),K&&F&&g.jsx(Lw,{row:K,country:l,periodRange:fn,operatorName:i,operatorEmail:a,executionId:F.id,reportUrl:F.reportUrl,onClose:()=>W(null),onSuccess:J=>{me(je=>{const Oe=new Set(je);return Oe.add(J),Oe})},onShowToast:n}),g.jsx(ei,{children:N&&g.jsx(un.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:"fixed inset-0 z-[80] flex items-center justify-center bg-black/45 px-4",children:g.jsxs(un.div,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},exit:{opacity:0,y:12},className:"w-full max-w-lg bg-white border border-[#D0D0D0] shadow-xl",children:[g.jsxs("div",{className:"border-b border-neutral-200 px-5 py-4 flex items-center justify-between",children:[g.jsxs("div",{children:[g.jsx("h2",{className:"text-base font-black text-[#4F758B] uppercase tracking-tight",children:"Marcar reporte como resuelto"}),g.jsx("p",{className:"text-xs text-neutral-500 mt-1",children:N.period})]}),g.jsx("button",{onClick:()=>z(null),disabled:T,className:"p-2 text-neutral-400 hover:text-neutral-700 disabled:opacity-50","aria-label":"Cerrar",children:g.jsx(Ya,{className:"w-4 h-4"})})]}),g.jsxs("div",{className:"p-5 space-y-4",children:[g.jsx("div",{className:"bg-amber-50 border border-amber-200 text-amber-800 p-3 text-xs leading-relaxed",children:"Antes de resolver, confirma que las diferencias fueron revisadas y explica cómo se cerró el caso."}),g.jsxs("label",{className:"block",children:[g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black text-neutral-500",children:"Comentario de resolución"}),g.jsx("textarea",{value:Y,onChange:J=>Z(J.target.value),placeholder:"Ejemplo: Se corrigió la cuenta bancaria en nómina y se validó contra el banco.",className:"mt-2 w-full min-h-[130px] border border-[#D0D0D0] px-3 py-2 text-sm focus:outline-none focus:border-[#4F758B]"})]})]}),g.jsxs("div",{className:"border-t border-neutral-200 px-5 py-4 flex justify-end gap-3",children:[g.jsx("button",{onClick:()=>z(null),disabled:T,className:"border border-neutral-300 bg-white px-4 py-2 text-xs font-bold text-neutral-600 hover:bg-neutral-50 disabled:opacity-50",children:"Cancelar"}),g.jsxs("button",{onClick:oi,disabled:T,className:"bg-[#4F758B] hover:bg-[#41677b] text-white px-4 py-2 text-xs font-black uppercase tracking-wider disabled:opacity-50 inline-flex items-center gap-2",children:[T&&g.jsx(qa,{className:"w-4 h-4 animate-spin"}),"Confirmar resolución"]})]})]})})})]})}function Ud({message:n,type:e,onClose:i}){D.useEffect(()=>{const c=setTimeout(()=>{i()},2e3);return()=>clearTimeout(c)},[i]);const a={success:"bg-[#6CC24A] text-white border-l-4 border-emerald-700",error:"bg-[#ba1a1a] text-white border-l-4 border-red-800",info:"bg-[#4F758B] text-white border-l-4 border-slate-700"},l={success:Ac,error:Sj,info:uw}[e];return g.jsx("div",{className:"fixed bottom-6 right-6 z-50 max-w-sm w-full",children:g.jsxs(un.div,{initial:{opacity:0,y:30,scale:.95},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:20,scale:.95},className:`flex items-center gap-3 p-4 shadow-lg rounded-md border border-neutral-200/10 ${a[e]}`,children:[g.jsx("span",{className:"flex-shrink-0",children:g.jsx(l,{className:"w-5 h-5"})}),g.jsx("div",{className:"flex-1 font-medium text-sm leading-tight pr-2",children:n}),g.jsx("button",{onClick:i,className:"flex-shrink-0 hover:bg-white/20 p-1 rounded transition-colors text-white",children:g.jsx(Ya,{className:"w-4 h-4"})})]})})}function WR({onShowToast:n}){const[e,i]=D.useState(!1),a=async()=>{if(!oo||!lt){n("El acceso con Google aún no está disponible. Contacta a IT.","error");return}i(!0),await pR();const{error:l}=await lt.auth.signInWithOAuth({provider:"google",options:{redirectTo:Uw(),queryParams:{access_type:"offline",prompt:"select_account"}}});l&&(i(!1),n("No se pudo iniciar sesión con Google. Intenta nuevamente.","error"))};return g.jsxs("div",{className:"min-h-screen w-full flex flex-col justify-between bg-[#F5F5F5] relative overflow-hidden font-sans",children:[g.jsx("div",{className:"h-1.5 w-full bg-[#4F758B]"}),g.jsx("div",{className:"flex-1 flex items-center justify-center p-4",children:g.jsxs(un.div,{initial:{opacity:0,y:15},animate:{opacity:1,y:0},transition:{duration:.5,ease:"easeOut"},className:"w-full max-w-md bg-white border border-[#D0D0D0] p-8 shadow-xl flex flex-col items-center gap-6",children:[g.jsx("div",{className:"w-full flex justify-center py-2",children:g.jsx("img",{alt:"Logo GOMEZLEE MARKETING",className:"h-14 object-contain",src:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png",referrerPolicy:"no-referrer"})}),g.jsx("div",{className:"text-center w-full",children:g.jsx("h1",{className:"text-2xl font-bold text-[#4F758B] tracking-tight",children:"Portal de Cruce de Cuentas GLM"})}),g.jsx("button",{onClick:a,disabled:e||!oo,className:"w-full relative overflow-hidden flex items-center justify-center gap-3 bg-[#6CC24A] hover:bg-[#5bb03c] disabled:bg-neutral-300 disabled:cursor-not-allowed text-white font-semibold py-3.5 px-6 transition-all duration-150 transform active:scale-[0.99] select-none text-sm tracking-wide",children:e?g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsxs("svg",{className:"animate-spin h-5 w-5 text-white",fill:"none",viewBox:"0 0 24 24",children:[g.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),g.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),g.jsx("span",{children:"Redirigiendo a Google..."})]}):g.jsxs(g.Fragment,{children:[g.jsxs("svg",{className:"w-5 h-5 flex-shrink-0 fill-current text-white",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",children:[g.jsx("path",{d:"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"}),g.jsx("path",{d:"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"}),g.jsx("path",{d:"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"}),g.jsx("path",{d:"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"})]}),g.jsx("span",{children:"Continuar con Google"})]})}),!oo&&g.jsxs("div",{className:"w-full flex items-start gap-2 border border-amber-200 bg-amber-50 px-3 py-2 text-[11px] text-amber-800 leading-relaxed",children:[g.jsx(fo,{className:"w-4 h-4 mt-0.5 flex-shrink-0"}),g.jsx("span",{children:"El acceso con Google está pendiente de activación."})]})]})})]})}const eO="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png";function tO({onSelect:n}){return g.jsxs("div",{className:"min-h-screen bg-[#F5F5F5] flex flex-col font-sans",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A]"}),g.jsx("main",{className:"flex-1 flex items-center justify-center px-4 py-10",children:g.jsxs(un.section,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},className:"w-full max-w-3xl bg-white border border-[#D0D0D0] shadow-xl p-6 sm:p-9",children:[g.jsx("img",{src:eO,alt:"GomezLee Marketing",className:"h-14 object-contain mb-7"}),g.jsxs("div",{className:"flex items-start gap-3 mb-7",children:[g.jsx("div",{className:"bg-[#4F758B]/10 p-2.5 rounded-full",children:g.jsx(cw,{className:"w-5 h-5 text-[#4F758B]"})}),g.jsxs("div",{children:[g.jsx("h1",{className:"text-xl sm:text-2xl font-black text-[#4F758B] uppercase tracking-tight",children:"Selecciona el país"}),g.jsx("p",{className:"text-sm text-neutral-500 mt-1",children:"El portal cargará las reglas, la moneda y los reportes correspondientes."})]})]}),g.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",children:mo.map(e=>g.jsxs("button",{type:"button",disabled:!e.enabled,onClick:()=>e.enabled&&n(e.code),className:`text-left border p-5 transition-all min-h-[150px] flex flex-col justify-between ${e.enabled?"border-[#4F758B]/30 hover:border-[#4F758B] hover:shadow-md bg-white":"border-neutral-200 bg-neutral-50 cursor-not-allowed opacity-70"}`,children:[g.jsxs("div",{className:"flex items-start justify-between gap-3",children:[g.jsx("img",{src:e.flag,alt:"",className:"w-12 h-8 sm:w-16 sm:h-10 object-cover shadow-sm border border-neutral-200","aria-hidden":"true"}),e.enabled?g.jsx(mj,{className:"w-5 h-5 text-[#6CC24A]"}):g.jsx($j,{className:"w-4 h-4 text-neutral-400"})]}),g.jsxs("div",{children:[g.jsx("p",{className:"font-black text-[#4F758B] uppercase tracking-wide",children:e.name}),g.jsx("p",{className:"text-xs text-neutral-500 mt-1",children:e.enabled?`Moneda ${e.currency}`:"Próximamente"})]})]},e.code))})]})})]})}const nO="cruce_cuentas_mi_acceso",vv=2;function Hw(n){return(n??"").trim().toLowerCase()}function sO(n){return Array.isArray(n)?Array.from(new Set(n.map(e=>String(e??"").trim().toUpperCase()).filter(Boolean))):[]}function iO(n){const e=Array.isArray(n)?n[0]:n;if(!e||typeof e!="object")throw new Error("Supabase no devolvió una respuesta válida al comprobar el acceso.");return{ok:e.ok===!0,authenticated:e.autenticado===!0,authorized:e.autorizado===!0,email:Hw(typeof e.email=="string"?e.email:""),name:typeof e.nombre=="string"?e.nombre.trim():"",active:e.activo===!0,appAccess:e.acceso_app===!0,historyAccess:e.acceso_historicos===!0,receivesGeneratedReports:e.recibe_reportes_generados===!0,receivesResolvedEmails:e.recibe_correos_resuelto===!0,countries:sO(e.paises),reason:typeof e.motivo=="string"?e.motivo.trim():""}}function aO(n){return new Promise(e=>window.setTimeout(e,n))}async function rO(){if(!lt)throw new Error("Supabase no está configurado.");let n=null;for(let i=1;i<=vv;i+=1){const{data:a,error:l}=await lt.rpc(nO);if(!l)return iO(a);n=l,i{if(typeof indexedDB>"u"){e(new Error("IndexedDB no está disponible en este navegador."));return}const i=indexedDB.open(oO,lO);i.onupgradeneeded=()=>{const a=i.result;a.objectStoreNames.contains(Wn)||a.createObjectStore(Wn,{keyPath:"sessionId"})},i.onsuccess=()=>n(i.result),i.onerror=()=>e(i.error??new Error("No se pudo abrir IndexedDB.")),i.onblocked=()=>e(new Error("La base local está bloqueada por otra pestaña."))}),Ra.catch(()=>{Ra=null}),Ra)}function uO(n){return new Promise((e,i)=>{n.onsuccess=()=>e(n.result),n.onerror=()=>i(n.error??new Error("Falló una operación local."))})}function Ic(n){return new Promise((e,i)=>{n.oncomplete=()=>e(),n.onerror=()=>i(n.error??new Error("Falló la transacción local.")),n.onabort=()=>i(n.error??new Error("La transacción local fue cancelada."))})}function hO(n){if(!n||typeof n!="object")return!1;const e=n;return e.version===$w&&typeof e.sessionId=="string"&&typeof e.updatedAt=="number"&&(e.currentView==="cruce"||e.currentView==="historial")&&Array.isArray(e.uploadedBankFiles)&&typeof e.isCruceExecuted=="boolean"}async function dO(n,e){const i=Date.now()-cO,a=n.transaction(Wn,"readwrite"),l=Ic(a),u=a.objectStore(Wn).openCursor();await new Promise((d,m)=>{u.onsuccess=()=>{const p=u.result;if(!p){d();return}const y=p.value;y.sessionId!==e&&typeof y.updatedAt=="number"&&y.updatedAtm(u.error??new Error("No se pudieron limpiar borradores antiguos."))}),await l}async function fO(){const n=nm();try{const e=await sm(),i=e.transaction(Wn,"readonly"),a=Ic(i),l=await uO(i.objectStore(Wn).get(n));return await a,dO(e,n).catch(()=>{}),hO(l)?l:null}catch{return null}}function mO(n){const e=nm(),i={...n,version:$w,sessionId:e,updatedAt:Date.now()};return Ia=Ia.catch(()=>{}).then(async()=>{const l=(await sm()).transaction(Wn,"readwrite"),c=Ic(l);l.objectStore(Wn).put(i),await c}).catch(()=>{}),Ia}function Sv(){const n=nm();return Ia=Ia.catch(()=>{}).then(async()=>{const i=(await sm()).transaction(Wn,"readwrite"),a=Ic(i);i.objectStore(Wn).delete(n),await a}).catch(()=>{}),Ia}const Tv=!0,Sf="glm_cruce_selected_country",pO={name:"Isaac Aracena",email:"iaracena@gomezleemarketing.com"};function gO(n){const e=(n==null?void 0:n.user_metadata)??{},i=e.full_name||e.name||e.display_name;if(i)return String(i);const a=String((n==null?void 0:n.email)??"Usuario GLM");return a.includes("@")?a.split("@")[0]:a}function yO(){try{const n=window.localStorage.getItem(Sf),e=mo.find(i=>i.code===n&&i.enabled);return(e==null?void 0:e.code)??null}catch{return null}}function bO(){const[n,e]=D.useState("cruce"),[i,a]=D.useState(!1),[l,c]=D.useState(null),[u,d]=D.useState([]),[m,p]=D.useState([]),[y,v]=D.useState(!1),[x,_]=D.useState(null),[S,T]=D.useState(0),[k,C]=D.useState(()=>yO()),[M,N]=D.useState(null),[z,Y]=D.useState(Tv),[Z,F]=D.useState(!1),[G,ne]=D.useState(null),Q=D.useRef(0),ce=D.useMemo(()=>oo&<,[]),me=k?_b(k):null,_e=(ae,E)=>{ne({message:ae,type:E})};D.useEffect(()=>{if(!ce||!lt){Y(!1);return}let ae=!0;const E=async ee=>{const se=++Q.current;if(!(ee!=null&&ee.email)){ae&&se===Q.current&&N(null);return}try{const ue=await rO();if(!ae||se!==Q.current)return;const ve=Hw(ee.email),Je=!!ue.email&&ue.email===ve;if(!ue.ok||!ue.authenticated||!ue.authorized||!ue.active||!ue.appAccess||!Je){N(null),_e(ue.reason||"Tu usuario no tiene acceso autorizado a este módulo.","error"),lt.auth.signOut();return}N({name:ue.name||gO(ee),email:ve})}catch(ue){if(!ae||se!==Q.current)return;console.error("No se pudo validar el acceso en Supabase:",ue),N(null),_e("No se pudo validar tu acceso en Supabase. Intenta iniciar sesión nuevamente.","error"),lt.auth.signOut()}};(async()=>{var ee;try{const{data:se,error:ue}=await lt.auth.getSession();if(ue)throw ue;if(!ae)return;await E((ee=se.session)==null?void 0:ee.user)}catch(se){if(!ae)return;console.error("No se pudo recuperar la sesión de Supabase:",se),N(null),_e("No se pudo recuperar tu sesión. Intenta iniciar sesión nuevamente.","error")}finally{ae&&Y(!1)}})();const{data:X}=lt.auth.onAuthStateChange((ee,se)=>{if(ae){if(se!=null&&se.user){window.setTimeout(()=>{ae&&E(se.user)},0);return}ee==="SIGNED_OUT"&&(Q.current+=1,N(null),Y(!1))}});return()=>{ae=!1,Q.current+=1,X.subscription.unsubscribe()}},[ce]),D.useEffect(()=>{if(!ce||!lt)return;let ae=0;const E=()=>{if(document.visibilityState!=="visible")return;const P=Date.now();P-ae<5e3||(ae=P,So(300).catch(X=>{console.warn("No se pudo revalidar la sesión al reanudar la pestaña:",X)}))};return document.addEventListener("visibilitychange",E),window.addEventListener("focus",E),()=>{document.removeEventListener("visibilitychange",E),window.removeEventListener("focus",E)}},[ce]),D.useEffect(()=>{let ae=!0;return(async()=>{const P=await fO();ae&&(P&&(!k||P.countryCode===k?(P.countryCode&&C(P.countryCode),e(P.currentView),c(P.uploadedPayroll),d(Array.isArray(P.uploadedPayrollFiles)?P.uploadedPayrollFiles:[]),p(P.uploadedBankFiles),v(P.isCruceExecuted),_(P.cruceResponse)):await Sv()),ae&&F(!0))})(),()=>{ae=!1}},[]),D.useEffect(()=>{Z&&mO({countryCode:k,currentView:n,uploadedPayroll:l,uploadedPayrollFiles:u,uploadedBankFiles:m,isCruceExecuted:y,cruceResponse:x})},[Z,k,n,l,u,m,y,x]);const Re=()=>{c(null),d([]),p([]),v(!1),_(null),T(ae=>ae+1)},Se=async()=>{await Sv(),lt&&await lt.auth.signOut(),N(null),C(null),window.localStorage.removeItem(Sf),Re(),e("cruce"),_e("Sesión cerrada correctamente.","success")},V=()=>{Re(),e("cruce"),_e("Nuevo cruce iniciado.","success")},K=ae=>{const E=_b(ae);if(!E.enabled){_e(`${E.name} estará disponible próximamente.`,"info");return}k&&k!==ae&&Re(),C(ae),window.localStorage.setItem(Sf,ae),e("cruce"),a(!1)},W=()=>{C(null),a(!1)};if(z)return g.jsx("div",{className:"min-h-screen bg-[#F5F5F5] flex items-center justify-center text-[#4F758B] font-black uppercase tracking-wider",children:"Validando acceso..."});if(!M)return g.jsxs(g.Fragment,{children:[g.jsx(WR,{onShowToast:_e}),g.jsx(ei,{children:G&&g.jsx(Ud,{message:G.message,type:G.type,onClose:()=>ne(null)})})]});if(!me)return g.jsxs(g.Fragment,{children:[g.jsx(tO,{onSelect:K}),g.jsx(ei,{children:G&&g.jsx(Ud,{message:G.message,type:G.type,onClose:()=>ne(null)})})]});const pe=M??pO;return g.jsxs("div",{className:"min-h-screen bg-[#F5F5F5] selection:bg-[#6CC24A] selection:text-white antialiased font-sans flex flex-col",children:[g.jsxs(un.div,{initial:{opacity:0},animate:{opacity:1},className:"min-h-screen flex w-full relative overflow-x-hidden",children:[g.jsx(ei,{children:i&&g.jsx(un.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},onClick:()=>a(!1),className:"fixed inset-0 bg-black/45 z-45 md:hidden"},"backdrop")}),g.jsx(oN,{currentView:n,onViewChange:ae=>{e(ae),a(!1)},onNewCruce:()=>{V(),a(!1)},isSidebarOpen:i,onCloseSidebar:()=>a(!1),user:pe,isAuthEnabled:Tv,onLogout:Se,country:me,onCountryChange:K,onChooseAnotherCountry:W}),g.jsx("div",{className:"pl-0 md:pl-64 flex-1 flex flex-col w-full min-h-screen transition-all duration-300",children:g.jsx(ei,{mode:"wait",children:n==="cruce"?g.jsx(un.div,{initial:{opacity:0,x:10},animate:{opacity:1,x:0},exit:{opacity:0,x:-10},transition:{duration:.2},className:"flex-1 flex flex-col",children:g.jsx($R,{onShowToast:_e,uploadedPayroll:l,setUploadedPayroll:c,uploadedPayrollFiles:u,setUploadedPayrollFiles:d,uploadedBankFiles:m,setUploadedBankFiles:p,isCruceExecuted:y,setIsCruceExecuted:v,response:x,setResponse:_,onOpenSidebar:()=>a(!0),operatorName:pe.name,operatorEmail:pe.email,resetToken:S,country:me})},`view-cruce-${me.code}`):g.jsx(un.div,{initial:{opacity:0,x:10},animate:{opacity:1,x:0},exit:{opacity:0,x:-10},transition:{duration:.2},className:"flex-1 flex flex-col",children:g.jsx(ZR,{onShowToast:_e,onOpenSidebar:()=>a(!0),operatorName:pe.name,operatorEmail:pe.email,country:me})},`view-history-${me.code}`)})})]},`interface-${me.code}`),g.jsx(ei,{children:G&&g.jsx(Ud,{message:G.message,type:G.type,onClose:()=>ne(null)})})]})}window.addEventListener("pageshow",n=>{n.persisted&&window.location.reload()});o1.createRoot(document.getElementById("root")).render(g.jsx(D.StrictMode,{children:g.jsx(bO,{})})); diff --git a/dist/assets/index-saVPcx_I.js b/dist/assets/index-saVPcx_I.js new file mode 100644 index 0000000..d201809 --- /dev/null +++ b/dist/assets/index-saVPcx_I.js @@ -0,0 +1,270 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))a(l);new MutationObserver(l=>{for(const c of l)if(c.type==="childList")for(const u of c.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&a(u)}).observe(document,{childList:!0,subtree:!0});function i(l){const c={};return l.integrity&&(c.integrity=l.integrity),l.referrerPolicy&&(c.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?c.credentials="include":l.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}function a(l){if(l.ep)return;l.ep=!0;const c=i(l);fetch(l.href,c)}})();var sd={exports:{}},qr={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Ky;function WS(){if(Ky)return qr;Ky=1;var n=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function i(a,l,c){var u=null;if(c!==void 0&&(u=""+c),l.key!==void 0&&(u=""+l.key),"key"in l){c={};for(var d in l)d!=="key"&&(c[d]=l[d])}else c=l;return l=c.ref,{$$typeof:n,type:a,key:u,ref:l!==void 0?l:null,props:c}}return qr.Fragment=e,qr.jsx=i,qr.jsxs=i,qr}var Yy;function e1(){return Yy||(Yy=1,sd.exports=WS()),sd.exports}var g=e1(),id={exports:{}},be={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Xy;function t1(){if(Xy)return be;Xy=1;var n=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),l=Symbol.for("react.profiler"),c=Symbol.for("react.consumer"),u=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),m=Symbol.for("react.suspense"),p=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),x=Symbol.iterator;function _(E){return E===null||typeof E!="object"?null:(E=x&&E[x]||E["@@iterator"],typeof E=="function"?E:null)}var S={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,k={};function R(E,H,W){this.props=E,this.context=H,this.refs=k,this.updater=W||S}R.prototype.isReactComponent={},R.prototype.setState=function(E,H){if(typeof E!="object"&&typeof E!="function"&&E!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,E,H,"setState")},R.prototype.forceUpdate=function(E){this.updater.enqueueForceUpdate(this,E,"forceUpdate")};function M(){}M.prototype=R.prototype;function N(E,H,W){this.props=E,this.context=H,this.refs=k,this.updater=W||S}var P=N.prototype=new M;P.constructor=N,T(P,R.prototype),P.isPureReactComponent=!0;var G=Array.isArray;function X(){}var q={H:null,A:null,T:null,S:null},Y=Object.prototype.hasOwnProperty;function se(E,H,W){var Q=W.ref;return{$$typeof:n,type:E,key:H,ref:Q!==void 0?Q:null,props:W}}function J(E,H){return se(E.type,H,E.props)}function he(E){return typeof E=="object"&&E!==null&&E.$$typeof===n}function fe(E){var H={"=":"=0",":":"=2"};return"$"+E.replace(/[=:]/g,function(W){return H[W]})}var Ae=/\/+/g;function ve(E,H){return typeof E=="object"&&E!==null&&E.key!=null?fe(""+E.key):H.toString(36)}function Ce(E){switch(E.status){case"fulfilled":return E.value;case"rejected":throw E.reason;default:switch(typeof E.status=="string"?E.then(X,X):(E.status="pending",E.then(function(H){E.status==="pending"&&(E.status="fulfilled",E.value=H)},function(H){E.status==="pending"&&(E.status="rejected",E.reason=H)})),E.status){case"fulfilled":return E.value;case"rejected":throw E.reason}}throw E}function L(E,H,W,Q,oe){var re=typeof E;(re==="undefined"||re==="boolean")&&(E=null);var _e=!1;if(E===null)_e=!0;else switch(re){case"bigint":case"string":case"number":_e=!0;break;case"object":switch(E.$$typeof){case n:case e:_e=!0;break;case y:return _e=E._init,L(_e(E._payload),H,W,Q,oe)}}if(_e)return oe=oe(E),_e=Q===""?"."+ve(E,0):Q,G(oe)?(W="",_e!=null&&(W=_e.replace(Ae,"$&/")+"/"),L(oe,H,W,"",function(en){return en})):oe!=null&&(he(oe)&&(oe=J(oe,W+(oe.key==null||E&&E.key===oe.key?"":(""+oe.key).replace(Ae,"$&/")+"/")+_e)),H.push(oe)),1;_e=0;var ke=Q===""?".":Q+":";if(G(E))for(var He=0;He>>1,ie=L[pe];if(0>>1;pel(W,Z))Ql(oe,W)?(L[pe]=oe,L[Q]=Z,pe=Q):(L[pe]=W,L[H]=Z,pe=H);else if(Ql(oe,Z))L[pe]=oe,L[Q]=Z,pe=Q;else break e}}return K}function l(L,K){var Z=L.sortIndex-K.sortIndex;return Z!==0?Z:L.id-K.id}if(n.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var c=performance;n.unstable_now=function(){return c.now()}}else{var u=Date,d=u.now();n.unstable_now=function(){return u.now()-d}}var m=[],p=[],y=1,v=null,x=3,_=!1,S=!1,T=!1,k=!1,R=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function P(L){for(var K=i(p);K!==null;){if(K.callback===null)a(p);else if(K.startTime<=L)a(p),K.sortIndex=K.expirationTime,e(m,K);else break;K=i(p)}}function G(L){if(T=!1,P(L),!S)if(i(m)!==null)S=!0,X||(X=!0,fe());else{var K=i(p);K!==null&&Ce(G,K.startTime-L)}}var X=!1,q=-1,Y=5,se=-1;function J(){return k?!0:!(n.unstable_now()-seL&&J());){var pe=v.callback;if(typeof pe=="function"){v.callback=null,x=v.priorityLevel;var ie=pe(v.expirationTime<=L);if(L=n.unstable_now(),typeof ie=="function"){v.callback=ie,P(L),K=!0;break t}v===i(m)&&a(m),P(L)}else a(m);v=i(m)}if(v!==null)K=!0;else{var E=i(p);E!==null&&Ce(G,E.startTime-L),K=!1}}break e}finally{v=null,x=Z,_=!1}K=void 0}}finally{K?fe():X=!1}}}var fe;if(typeof N=="function")fe=function(){N(he)};else if(typeof MessageChannel<"u"){var Ae=new MessageChannel,ve=Ae.port2;Ae.port1.onmessage=he,fe=function(){ve.postMessage(null)}}else fe=function(){R(he,0)};function Ce(L,K){q=R(function(){L(n.unstable_now())},K)}n.unstable_IdlePriority=5,n.unstable_ImmediatePriority=1,n.unstable_LowPriority=4,n.unstable_NormalPriority=3,n.unstable_Profiling=null,n.unstable_UserBlockingPriority=2,n.unstable_cancelCallback=function(L){L.callback=null},n.unstable_forceFrameRate=function(L){0>L||125pe?(L.sortIndex=Z,e(p,L),i(m)===null&&L===i(p)&&(T?(M(q),q=-1):T=!0,Ce(G,Z-pe))):(L.sortIndex=ie,e(m,L),S||_||(S=!0,X||(X=!0,fe()))),L},n.unstable_shouldYield=J,n.unstable_wrapCallback=function(L){var K=x;return function(){var Z=x;x=K;try{return L.apply(this,arguments)}finally{x=Z}}}})(od)),od}var Zy;function s1(){return Zy||(Zy=1,rd.exports=n1()),rd.exports}var ld={exports:{}},Pt={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Wy;function i1(){if(Wy)return Pt;Wy=1;var n=Tf();function e(m){var p="https://react.dev/errors/"+m;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}return n(),ld.exports=i1(),ld.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var t0;function r1(){if(t0)return Fr;t0=1;var n=s1(),e=Tf(),i=a1();function a(t){var s="https://react.dev/errors/"+t;if(1ie||(t.current=pe[ie],pe[ie]=null,ie--)}function W(t,s){ie++,pe[ie]=t.current,t.current=s}var Q=E(null),oe=E(null),re=E(null),_e=E(null);function ke(t,s){switch(W(re,s),W(oe,t),W(Q,null),s.nodeType){case 9:case 11:t=(t=s.documentElement)&&(t=t.namespaceURI)?gy(t):0;break;default:if(t=s.tagName,s=s.namespaceURI)s=gy(s),t=yy(s,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}H(Q),W(Q,t)}function He(){H(Q),H(oe),H(re)}function en(t){t.memoizedState!==null&&W(_e,t);var s=Q.current,r=yy(s,t.type);s!==r&&(W(oe,t),W(Q,r))}function jn(t){oe.current===t&&(H(Q),H(oe)),_e.current===t&&(H(_e),Pr._currentValue=Z)}var Rs,is;function te(t){if(Rs===void 0)try{throw Error()}catch(r){var s=r.stack.trim().match(/\n( *(at )?)/);Rs=s&&s[1]||"",is=-1)":-1h||A[o]!==U[h]){var $=` +`+A[o].replace(" at new "," at ");return t.displayName&&$.includes("")&&($=$.replace("",t.displayName)),$}while(1<=o&&0<=h);break}}}finally{Be=!1,Error.prepareStackTrace=r}return(r=t?t.displayName||t.name:"")?te(r):""}function xe(t,s){switch(t.tag){case 26:case 27:case 5:return te(t.type);case 16:return te("Lazy");case 13:return t.child!==s&&s!==null?te("Suspense Fallback"):te("Suspense");case 19:return te("SuspenseList");case 0:case 15:return Ue(t.type,!1);case 11:return Ue(t.type.render,!1);case 1:return Ue(t.type,!0);case 31:return te("Activity");default:return""}}function $e(t){try{var s="",r=null;do s+=xe(t,r),r=t,t=t.return;while(t);return s}catch(o){return` +Error generating stack: `+o.message+` +`+o.stack}}var kt=Object.prototype.hasOwnProperty,xt=n.unstable_scheduleCallback,Et=n.unstable_cancelCallback,Ht=n.unstable_shouldYield,Nn=n.unstable_requestPaint,We=n.unstable_now,tn=n.unstable_getCurrentPriorityLevel,Gt=n.unstable_ImmediatePriority,kn=n.unstable_UserBlockingPriority,fn=n.unstable_NormalPriority,Rn=n.unstable_LowPriority,Cs=n.unstable_IdlePriority,Os=n.log,qn=n.unstable_setDisableYieldValue,as=null,$t=null;function et(t){if(typeof Os=="function"&&qn(t),$t&&typeof $t.setStrictMode=="function")try{$t.setStrictMode(as,t)}catch{}}var Lt=Math.clz32?Math.clz32:ui,ko=Math.log,Ro=Math.LN2;function ui(t){return t>>>=0,t===0?32:31-(ko(t)/Ro|0)|0}var hi=256,Hi=262144,$i=4194304;function rs(t){var s=t&42;if(s!==0)return s;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function qi(t,s,r){var o=t.pendingLanes;if(o===0)return 0;var h=0,f=t.suspendedLanes,b=t.pingedLanes;t=t.warmLanes;var w=o&134217727;return w!==0?(o=w&~f,o!==0?h=rs(o):(b&=w,b!==0?h=rs(b):r||(r=w&~t,r!==0&&(h=rs(r))))):(w=o&~f,w!==0?h=rs(w):b!==0?h=rs(b):r||(r=o&~t,r!==0&&(h=rs(r)))),h===0?0:s!==0&&s!==h&&(s&f)===0&&(f=h&-h,r=s&-s,f>=r||f===32&&(r&4194048)!==0)?s:h}function di(t,s){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&s)===0}function Xc(t,s){switch(t){case 1:case 2:case 4:case 8:case 64:return s+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return s+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Za(){var t=$i;return $i<<=1,($i&62914560)===0&&($i=4194304),t}function Wa(t){for(var s=[],r=0;31>r;r++)s.push(t);return s}function fi(t,s){t.pendingLanes|=s,s!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Co(t,s,r,o,h,f){var b=t.pendingLanes;t.pendingLanes=r,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=r,t.entangledLanes&=r,t.errorRecoveryDisabledLanes&=r,t.shellSuspendCounter=0;var w=t.entanglements,A=t.expirationTimes,U=t.hiddenUpdates;for(r=b&~r;0"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var Kw=/[\n"\\]/g;function pn(t){return t.replace(Kw,function(s){return"\\"+s.charCodeAt(0).toString(16)+" "})}function Qc(t,s,r,o,h,f,b,w){t.name="",b!=null&&typeof b!="function"&&typeof b!="symbol"&&typeof b!="boolean"?t.type=b:t.removeAttribute("type"),s!=null?b==="number"?(s===0&&t.value===""||t.value!=s)&&(t.value=""+mn(s)):t.value!==""+mn(s)&&(t.value=""+mn(s)):b!=="submit"&&b!=="reset"||t.removeAttribute("value"),s!=null?Zc(t,b,mn(s)):r!=null?Zc(t,b,mn(r)):o!=null&&t.removeAttribute("value"),h==null&&f!=null&&(t.defaultChecked=!!f),h!=null&&(t.checked=h&&typeof h!="function"&&typeof h!="symbol"),w!=null&&typeof w!="function"&&typeof w!="symbol"&&typeof w!="boolean"?t.name=""+mn(w):t.removeAttribute("name")}function um(t,s,r,o,h,f,b,w){if(f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"&&(t.type=f),s!=null||r!=null){if(!(f!=="submit"&&f!=="reset"||s!=null)){Jc(t);return}r=r!=null?""+mn(r):"",s=s!=null?""+mn(s):r,w||s===t.value||(t.value=s),t.defaultValue=s}o=o??h,o=typeof o!="function"&&typeof o!="symbol"&&!!o,t.checked=w?t.checked:!!o,t.defaultChecked=!!o,b!=null&&typeof b!="function"&&typeof b!="symbol"&&typeof b!="boolean"&&(t.name=b),Jc(t)}function Zc(t,s,r){s==="number"&&zo(t.ownerDocument)===t||t.defaultValue===""+r||(t.defaultValue=""+r)}function Gi(t,s,r,o){if(t=t.options,s){s={};for(var h=0;h"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),su=!1;if(cs)try{var sr={};Object.defineProperty(sr,"passive",{get:function(){su=!0}}),window.addEventListener("test",sr,sr),window.removeEventListener("test",sr,sr)}catch{su=!1}var Ds=null,iu=null,Vo=null;function ym(){if(Vo)return Vo;var t,s=iu,r=s.length,o,h="value"in Ds?Ds.value:Ds.textContent,f=h.length;for(t=0;t=rr),Sm=" ",Tm=!1;function Em(t,s){switch(t){case"keyup":return w_.indexOf(s.keyCode)!==-1;case"keydown":return s.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Am(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Ji=!1;function S_(t,s){switch(t){case"compositionend":return Am(s);case"keypress":return s.which!==32?null:(Tm=!0,Sm);case"textInput":return t=s.data,t===Sm&&Tm?null:t;default:return null}}function T_(t,s){if(Ji)return t==="compositionend"||!cu&&Em(t,s)?(t=ym(),Vo=iu=Ds=null,Ji=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(s.ctrlKey||s.altKey||s.metaKey)||s.ctrlKey&&s.altKey){if(s.char&&1=s)return{node:r,offset:s-t};t=o}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=Mm(r)}}function Um(t,s){return t&&s?t===s?!0:t&&t.nodeType===3?!1:s&&s.nodeType===3?Um(t,s.parentNode):"contains"in t?t.contains(s):t.compareDocumentPosition?!!(t.compareDocumentPosition(s)&16):!1:!1}function Lm(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var s=zo(t.document);s instanceof t.HTMLIFrameElement;){try{var r=typeof s.contentWindow.location.href=="string"}catch{r=!1}if(r)t=s.contentWindow;else break;s=zo(t.document)}return s}function du(t){var s=t&&t.nodeName&&t.nodeName.toLowerCase();return s&&(s==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||s==="textarea"||t.contentEditable==="true")}var O_=cs&&"documentMode"in document&&11>=document.documentMode,Qi=null,fu=null,ur=null,mu=!1;function zm(t,s,r){var o=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;mu||Qi==null||Qi!==zo(o)||(o=Qi,"selectionStart"in o&&du(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),ur&&cr(ur,o)||(ur=o,o=Dl(fu,"onSelect"),0>=b,h-=b,In=1<<32-Lt(s)+h|r<Se?(Ne=de,de=null):Ne=de.sibling;var De=z(O,de,B[Se],F);if(De===null){de===null&&(de=Ne);break}t&&de&&De.alternate===null&&s(O,de),j=f(De,j,Se),Oe===null?me=De:Oe.sibling=De,Oe=De,de=Ne}if(Se===B.length)return r(O,de),Re&&hs(O,Se),me;if(de===null){for(;SeSe?(Ne=de,de=null):Ne=de.sibling;var ti=z(O,de,De.value,F);if(ti===null){de===null&&(de=Ne);break}t&&de&&ti.alternate===null&&s(O,de),j=f(ti,j,Se),Oe===null?me=ti:Oe.sibling=ti,Oe=ti,de=Ne}if(De.done)return r(O,de),Re&&hs(O,Se),me;if(de===null){for(;!De.done;Se++,De=B.next())De=I(O,De.value,F),De!==null&&(j=f(De,j,Se),Oe===null?me=De:Oe.sibling=De,Oe=De);return Re&&hs(O,Se),me}for(de=o(de);!De.done;Se++,De=B.next())De=V(de,O,Se,De.value,F),De!==null&&(t&&De.alternate!==null&&de.delete(De.key===null?Se:De.key),j=f(De,j,Se),Oe===null?me=De:Oe.sibling=De,Oe=De);return t&&de.forEach(function(ZS){return s(O,ZS)}),Re&&hs(O,Se),me}function Ie(O,j,B,F){if(typeof B=="object"&&B!==null&&B.type===T&&B.key===null&&(B=B.props.children),typeof B=="object"&&B!==null){switch(B.$$typeof){case _:e:{for(var me=B.key;j!==null;){if(j.key===me){if(me=B.type,me===T){if(j.tag===7){r(O,j.sibling),F=h(j,B.props.children),F.return=O,O=F;break e}}else if(j.elementType===me||typeof me=="object"&&me!==null&&me.$$typeof===Y&&Ti(me)===j.type){r(O,j.sibling),F=h(j,B.props),gr(F,B),F.return=O,O=F;break e}r(O,j);break}else s(O,j);j=j.sibling}B.type===T?(F=vi(B.props.children,O.mode,F,B.key),F.return=O,O=F):(F=Jo(B.type,B.key,B.props,null,O.mode,F),gr(F,B),F.return=O,O=F)}return b(O);case S:e:{for(me=B.key;j!==null;){if(j.key===me)if(j.tag===4&&j.stateNode.containerInfo===B.containerInfo&&j.stateNode.implementation===B.implementation){r(O,j.sibling),F=h(j,B.children||[]),F.return=O,O=F;break e}else{r(O,j);break}else s(O,j);j=j.sibling}F=wu(B,O.mode,F),F.return=O,O=F}return b(O);case Y:return B=Ti(B),Ie(O,j,B,F)}if(Ce(B))return le(O,j,B,F);if(fe(B)){if(me=fe(B),typeof me!="function")throw Error(a(150));return B=me.call(B),ye(O,j,B,F)}if(typeof B.then=="function")return Ie(O,j,sl(B),F);if(B.$$typeof===N)return Ie(O,j,Wo(O,B),F);il(O,B)}return typeof B=="string"&&B!==""||typeof B=="number"||typeof B=="bigint"?(B=""+B,j!==null&&j.tag===6?(r(O,j.sibling),F=h(j,B),F.return=O,O=F):(r(O,j),F=xu(B,O.mode,F),F.return=O,O=F),b(O)):r(O,j)}return function(O,j,B,F){try{pr=0;var me=Ie(O,j,B,F);return la=null,me}catch(de){if(de===oa||de===tl)throw de;var Oe=sn(29,de,null,O.mode);return Oe.lanes=F,Oe.return=O,Oe}finally{}}}var Ai=op(!0),lp=op(!1),zs=!1;function Du(t){t.updateQueue={baseState:t.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Mu(t,s){t=t.updateQueue,s.updateQueue===t&&(s.updateQueue={baseState:t.baseState,firstBaseUpdate:t.firstBaseUpdate,lastBaseUpdate:t.lastBaseUpdate,shared:t.shared,callbacks:null})}function Ps(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function Vs(t,s,r){var o=t.updateQueue;if(o===null)return null;if(o=o.shared,(ze&2)!==0){var h=o.pending;return h===null?s.next=s:(s.next=h.next,h.next=s),o.pending=s,s=Xo(t),Im(t,null,r),s}return Yo(t,o,s,r),Xo(t)}function yr(t,s,r){if(s=s.updateQueue,s!==null&&(s=s.shared,(r&4194048)!==0)){var o=s.lanes;o&=t.pendingLanes,r|=o,s.lanes=r,Do(t,r)}}function Bu(t,s){var r=t.updateQueue,o=t.alternate;if(o!==null&&(o=o.updateQueue,r===o)){var h=null,f=null;if(r=r.firstBaseUpdate,r!==null){do{var b={lane:r.lane,tag:r.tag,payload:r.payload,callback:null,next:null};f===null?h=f=b:f=f.next=b,r=r.next}while(r!==null);f===null?h=f=s:f=f.next=s}else h=f=s;r={baseState:o.baseState,firstBaseUpdate:h,lastBaseUpdate:f,shared:o.shared,callbacks:o.callbacks},t.updateQueue=r;return}t=r.lastBaseUpdate,t===null?r.firstBaseUpdate=s:t.next=s,r.lastBaseUpdate=s}var Uu=!1;function br(){if(Uu){var t=ra;if(t!==null)throw t}}function vr(t,s,r,o){Uu=!1;var h=t.updateQueue;zs=!1;var f=h.firstBaseUpdate,b=h.lastBaseUpdate,w=h.shared.pending;if(w!==null){h.shared.pending=null;var A=w,U=A.next;A.next=null,b===null?f=U:b.next=U,b=A;var $=t.alternate;$!==null&&($=$.updateQueue,w=$.lastBaseUpdate,w!==b&&(w===null?$.firstBaseUpdate=U:w.next=U,$.lastBaseUpdate=A))}if(f!==null){var I=h.baseState;b=0,$=U=A=null,w=f;do{var z=w.lane&-536870913,V=z!==w.lane;if(V?(je&z)===z:(o&z)===z){z!==0&&z===aa&&(Uu=!0),$!==null&&($=$.next={lane:0,tag:w.tag,payload:w.payload,callback:null,next:null});e:{var le=t,ye=w;z=s;var Ie=r;switch(ye.tag){case 1:if(le=ye.payload,typeof le=="function"){I=le.call(Ie,I,z);break e}I=le;break e;case 3:le.flags=le.flags&-65537|128;case 0:if(le=ye.payload,z=typeof le=="function"?le.call(Ie,I,z):le,z==null)break e;I=v({},I,z);break e;case 2:zs=!0}}z=w.callback,z!==null&&(t.flags|=64,V&&(t.flags|=8192),V=h.callbacks,V===null?h.callbacks=[z]:V.push(z))}else V={lane:z,tag:w.tag,payload:w.payload,callback:w.callback,next:null},$===null?(U=$=V,A=I):$=$.next=V,b|=z;if(w=w.next,w===null){if(w=h.shared.pending,w===null)break;V=w,w=V.next,V.next=null,h.lastBaseUpdate=V,h.shared.pending=null}}while(!0);$===null&&(A=I),h.baseState=A,h.firstBaseUpdate=U,h.lastBaseUpdate=$,f===null&&(h.shared.lanes=0),Is|=b,t.lanes=b,t.memoizedState=I}}function cp(t,s){if(typeof t!="function")throw Error(a(191,t));t.call(s)}function up(t,s){var r=t.callbacks;if(r!==null)for(t.callbacks=null,t=0;tf?f:8;var b=L.T,w={};L.T=w,th(t,!1,s,r);try{var A=h(),U=L.S;if(U!==null&&U(w,A),A!==null&&typeof A=="object"&&typeof A.then=="function"){var $=H_(A,o);_r(t,s,$,cn(t))}else _r(t,s,o,cn(t))}catch(I){_r(t,s,{then:function(){},status:"rejected",reason:I},cn())}finally{K.p=f,b!==null&&w.types!==null&&(b.types=w.types),L.T=b}}function K_(){}function Wu(t,s,r,o){if(t.tag!==5)throw Error(a(476));var h=$p(t).queue;Hp(t,h,s,Z,r===null?K_:function(){return qp(t),r(o)})}function $p(t){var s=t.memoizedState;if(s!==null)return s;s={memoizedState:Z,baseState:Z,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ps,lastRenderedState:Z},next:null};var r={};return s.next={memoizedState:r,baseState:r,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ps,lastRenderedState:r},next:null},t.memoizedState=s,t=t.alternate,t!==null&&(t.memoizedState=s),s}function qp(t){var s=$p(t);s.next===null&&(s=t.alternate.memoizedState),_r(t,s.next.queue,{},cn())}function eh(){return Ot(Pr)}function Fp(){return dt().memoizedState}function Ip(){return dt().memoizedState}function Y_(t){for(var s=t.return;s!==null;){switch(s.tag){case 24:case 3:var r=cn();t=Ps(r);var o=Vs(s,t,r);o!==null&&(Zt(o,s,r),yr(o,s,r)),s={cache:ku()},t.payload=s;return}s=s.return}}function X_(t,s,r){var o=cn();r={lane:o,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},ml(t)?Kp(s,r):(r=bu(t,s,r,o),r!==null&&(Zt(r,t,o),Yp(r,s,o)))}function Gp(t,s,r){var o=cn();_r(t,s,r,o)}function _r(t,s,r,o){var h={lane:o,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null};if(ml(t))Kp(s,h);else{var f=t.alternate;if(t.lanes===0&&(f===null||f.lanes===0)&&(f=s.lastRenderedReducer,f!==null))try{var b=s.lastRenderedState,w=f(b,r);if(h.hasEagerState=!0,h.eagerState=w,nn(w,b))return Yo(t,s,h,0),Ke===null&&Ko(),!1}catch{}finally{}if(r=bu(t,s,h,o),r!==null)return Zt(r,t,o),Yp(r,s,o),!0}return!1}function th(t,s,r,o){if(o={lane:2,revertLane:Dh(),gesture:null,action:o,hasEagerState:!1,eagerState:null,next:null},ml(t)){if(s)throw Error(a(479))}else s=bu(t,r,o,2),s!==null&&Zt(s,t,2)}function ml(t){var s=t.alternate;return t===we||s!==null&&s===we}function Kp(t,s){ua=ol=!0;var r=t.pending;r===null?s.next=s:(s.next=r.next,r.next=s),t.pending=s}function Yp(t,s,r){if((r&4194048)!==0){var o=s.lanes;o&=t.pendingLanes,r|=o,s.lanes=r,Do(t,r)}}var Sr={readContext:Ot,use:ul,useCallback:rt,useContext:rt,useEffect:rt,useImperativeHandle:rt,useLayoutEffect:rt,useInsertionEffect:rt,useMemo:rt,useReducer:rt,useRef:rt,useState:rt,useDebugValue:rt,useDeferredValue:rt,useTransition:rt,useSyncExternalStore:rt,useId:rt,useHostTransitionStatus:rt,useFormState:rt,useActionState:rt,useOptimistic:rt,useMemoCache:rt,useCacheRefresh:rt};Sr.useEffectEvent=rt;var Xp={readContext:Ot,use:ul,useCallback:function(t,s){return qt().memoizedState=[t,s===void 0?null:s],t},useContext:Ot,useEffect:Op,useImperativeHandle:function(t,s,r){r=r!=null?r.concat([t]):null,dl(4194308,4,Up.bind(null,s,t),r)},useLayoutEffect:function(t,s){return dl(4194308,4,t,s)},useInsertionEffect:function(t,s){dl(4,2,t,s)},useMemo:function(t,s){var r=qt();s=s===void 0?null:s;var o=t();if(ji){et(!0);try{t()}finally{et(!1)}}return r.memoizedState=[o,s],o},useReducer:function(t,s,r){var o=qt();if(r!==void 0){var h=r(s);if(ji){et(!0);try{r(s)}finally{et(!1)}}}else h=s;return o.memoizedState=o.baseState=h,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:h},o.queue=t,t=t.dispatch=X_.bind(null,we,t),[o.memoizedState,t]},useRef:function(t){var s=qt();return t={current:t},s.memoizedState=t},useState:function(t){t=Yu(t);var s=t.queue,r=Gp.bind(null,we,s);return s.dispatch=r,[t.memoizedState,r]},useDebugValue:Qu,useDeferredValue:function(t,s){var r=qt();return Zu(r,t,s)},useTransition:function(){var t=Yu(!1);return t=Hp.bind(null,we,t.queue,!0,!1),qt().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,s,r){var o=we,h=qt();if(Re){if(r===void 0)throw Error(a(407));r=r()}else{if(r=s(),Ke===null)throw Error(a(349));(je&127)!==0||gp(o,s,r)}h.memoizedState=r;var f={value:r,getSnapshot:s};return h.queue=f,Op(bp.bind(null,o,f,t),[t]),o.flags|=2048,da(9,{destroy:void 0},yp.bind(null,o,f,r,s),null),r},useId:function(){var t=qt(),s=Ke.identifierPrefix;if(Re){var r=Gn,o=In;r=(o&~(1<<32-Lt(o)-1)).toString(32)+r,s="_"+s+"R_"+r,r=ll++,0<\/script>",f=f.removeChild(f.firstChild);break;case"select":f=typeof o.is=="string"?b.createElement("select",{is:o.is}):b.createElement("select"),o.multiple?f.multiple=!0:o.size&&(f.size=o.size);break;default:f=typeof o.is=="string"?b.createElement(h,{is:o.is}):b.createElement(h)}}f[ae]=s,f[nt]=o;e:for(b=s.child;b!==null;){if(b.tag===5||b.tag===6)f.appendChild(b.stateNode);else if(b.tag!==4&&b.tag!==27&&b.child!==null){b.child.return=b,b=b.child;continue}if(b===s)break e;for(;b.sibling===null;){if(b.return===null||b.return===s)break e;b=b.return}b.sibling.return=b.return,b=b.sibling}s.stateNode=f;e:switch(Mt(f,h,o),h){case"button":case"input":case"select":case"textarea":o=!!o.autoFocus;break e;case"img":o=!0;break e;default:o=!1}o&&ys(s)}}return Ze(s),ph(s,s.type,t===null?null:t.memoizedProps,s.pendingProps,r),null;case 6:if(t&&s.stateNode!=null)t.memoizedProps!==o&&ys(s);else{if(typeof o!="string"&&s.stateNode===null)throw Error(a(166));if(t=re.current,sa(s)){if(t=s.stateNode,r=s.memoizedProps,o=null,h=Ct,h!==null)switch(h.tag){case 27:case 5:o=h.memoizedProps}t[ae]=s,t=!!(t.nodeValue===r||o!==null&&o.suppressHydrationWarning===!0||my(t.nodeValue,r)),t||Us(s,!0)}else t=Ml(t).createTextNode(o),t[ae]=s,s.stateNode=t}return Ze(s),null;case 31:if(r=s.memoizedState,t===null||t.memoizedState!==null){if(o=sa(s),r!==null){if(t===null){if(!o)throw Error(a(318));if(t=s.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(a(557));t[ae]=s}else xi(),(s.flags&128)===0&&(s.memoizedState=null),s.flags|=4;Ze(s),t=!1}else r=Eu(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=r),t=!0;if(!t)return s.flags&256?(rn(s),s):(rn(s),null);if((s.flags&128)!==0)throw Error(a(558))}return Ze(s),null;case 13:if(o=s.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(h=sa(s),o!==null&&o.dehydrated!==null){if(t===null){if(!h)throw Error(a(318));if(h=s.memoizedState,h=h!==null?h.dehydrated:null,!h)throw Error(a(317));h[ae]=s}else xi(),(s.flags&128)===0&&(s.memoizedState=null),s.flags|=4;Ze(s),h=!1}else h=Eu(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=h),h=!0;if(!h)return s.flags&256?(rn(s),s):(rn(s),null)}return rn(s),(s.flags&128)!==0?(s.lanes=r,s):(r=o!==null,t=t!==null&&t.memoizedState!==null,r&&(o=s.child,h=null,o.alternate!==null&&o.alternate.memoizedState!==null&&o.alternate.memoizedState.cachePool!==null&&(h=o.alternate.memoizedState.cachePool.pool),f=null,o.memoizedState!==null&&o.memoizedState.cachePool!==null&&(f=o.memoizedState.cachePool.pool),f!==h&&(o.flags|=2048)),r!==t&&r&&(s.child.flags|=8192),vl(s,s.updateQueue),Ze(s),null);case 4:return He(),t===null&&Lh(s.stateNode.containerInfo),Ze(s),null;case 10:return fs(s.type),Ze(s),null;case 19:if(H(ht),o=s.memoizedState,o===null)return Ze(s),null;if(h=(s.flags&128)!==0,f=o.rendering,f===null)if(h)Er(o,!1);else{if(ot!==0||t!==null&&(t.flags&128)!==0)for(t=s.child;t!==null;){if(f=rl(t),f!==null){for(s.flags|=128,Er(o,!1),t=f.updateQueue,s.updateQueue=t,vl(s,t),s.subtreeFlags=0,t=r,r=s.child;r!==null;)Gm(r,t),r=r.sibling;return W(ht,ht.current&1|2),Re&&hs(s,o.treeForkCount),s.child}t=t.sibling}o.tail!==null&&We()>Tl&&(s.flags|=128,h=!0,Er(o,!1),s.lanes=4194304)}else{if(!h)if(t=rl(f),t!==null){if(s.flags|=128,h=!0,t=t.updateQueue,s.updateQueue=t,vl(s,t),Er(o,!0),o.tail===null&&o.tailMode==="hidden"&&!f.alternate&&!Re)return Ze(s),null}else 2*We()-o.renderingStartTime>Tl&&r!==536870912&&(s.flags|=128,h=!0,Er(o,!1),s.lanes=4194304);o.isBackwards?(f.sibling=s.child,s.child=f):(t=o.last,t!==null?t.sibling=f:s.child=f,o.last=f)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=We(),t.sibling=null,r=ht.current,W(ht,h?r&1|2:r&1),Re&&hs(s,o.treeForkCount),t):(Ze(s),null);case 22:case 23:return rn(s),zu(),o=s.memoizedState!==null,t!==null?t.memoizedState!==null!==o&&(s.flags|=8192):o&&(s.flags|=8192),o?(r&536870912)!==0&&(s.flags&128)===0&&(Ze(s),s.subtreeFlags&6&&(s.flags|=8192)):Ze(s),r=s.updateQueue,r!==null&&vl(s,r.retryQueue),r=null,t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(r=t.memoizedState.cachePool.pool),o=null,s.memoizedState!==null&&s.memoizedState.cachePool!==null&&(o=s.memoizedState.cachePool.pool),o!==r&&(s.flags|=2048),t!==null&&H(Si),null;case 24:return r=null,t!==null&&(r=t.memoizedState.cache),s.memoizedState.cache!==r&&(s.flags|=2048),fs(mt),Ze(s),null;case 25:return null;case 30:return null}throw Error(a(156,s.tag))}function eS(t,s){switch(Su(s),s.tag){case 1:return t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 3:return fs(mt),He(),t=s.flags,(t&65536)!==0&&(t&128)===0?(s.flags=t&-65537|128,s):null;case 26:case 27:case 5:return jn(s),null;case 31:if(s.memoizedState!==null){if(rn(s),s.alternate===null)throw Error(a(340));xi()}return t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 13:if(rn(s),t=s.memoizedState,t!==null&&t.dehydrated!==null){if(s.alternate===null)throw Error(a(340));xi()}return t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 19:return H(ht),null;case 4:return He(),null;case 10:return fs(s.type),null;case 22:case 23:return rn(s),zu(),t!==null&&H(Si),t=s.flags,t&65536?(s.flags=t&-65537|128,s):null;case 24:return fs(mt),null;case 25:return null;default:return null}}function vg(t,s){switch(Su(s),s.tag){case 3:fs(mt),He();break;case 26:case 27:case 5:jn(s);break;case 4:He();break;case 31:s.memoizedState!==null&&rn(s);break;case 13:rn(s);break;case 19:H(ht);break;case 10:fs(s.type);break;case 22:case 23:rn(s),zu(),t!==null&&H(Si);break;case 24:fs(mt)}}function Ar(t,s){try{var r=s.updateQueue,o=r!==null?r.lastEffect:null;if(o!==null){var h=o.next;r=h;do{if((r.tag&t)===t){o=void 0;var f=r.create,b=r.inst;o=f(),b.destroy=o}r=r.next}while(r!==h)}}catch(w){Ve(s,s.return,w)}}function qs(t,s,r){try{var o=s.updateQueue,h=o!==null?o.lastEffect:null;if(h!==null){var f=h.next;o=f;do{if((o.tag&t)===t){var b=o.inst,w=b.destroy;if(w!==void 0){b.destroy=void 0,h=s;var A=r,U=w;try{U()}catch($){Ve(h,A,$)}}}o=o.next}while(o!==f)}}catch($){Ve(s,s.return,$)}}function xg(t){var s=t.updateQueue;if(s!==null){var r=t.stateNode;try{up(s,r)}catch(o){Ve(t,t.return,o)}}}function wg(t,s,r){r.props=Ni(t.type,t.memoizedProps),r.state=t.memoizedState;try{r.componentWillUnmount()}catch(o){Ve(t,s,o)}}function jr(t,s){try{var r=t.ref;if(r!==null){switch(t.tag){case 26:case 27:case 5:var o=t.stateNode;break;case 30:o=t.stateNode;break;default:o=t.stateNode}typeof r=="function"?t.refCleanup=r(o):r.current=o}}catch(h){Ve(t,s,h)}}function Kn(t,s){var r=t.ref,o=t.refCleanup;if(r!==null)if(typeof o=="function")try{o()}catch(h){Ve(t,s,h)}finally{t.refCleanup=null,t=t.alternate,t!=null&&(t.refCleanup=null)}else if(typeof r=="function")try{r(null)}catch(h){Ve(t,s,h)}else r.current=null}function _g(t){var s=t.type,r=t.memoizedProps,o=t.stateNode;try{e:switch(s){case"button":case"input":case"select":case"textarea":r.autoFocus&&o.focus();break e;case"img":r.src?o.src=r.src:r.srcSet&&(o.srcset=r.srcSet)}}catch(h){Ve(t,t.return,h)}}function gh(t,s,r){try{var o=t.stateNode;_S(o,t.type,r,s),o[nt]=s}catch(h){Ve(t,t.return,h)}}function Sg(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&Js(t.type)||t.tag===4}function yh(t){e:for(;;){for(;t.sibling===null;){if(t.return===null||Sg(t.return))return null;t=t.return}for(t.sibling.return=t.return,t=t.sibling;t.tag!==5&&t.tag!==6&&t.tag!==18;){if(t.tag===27&&Js(t.type)||t.flags&2||t.child===null||t.tag===4)continue e;t.child.return=t,t=t.child}if(!(t.flags&2))return t.stateNode}}function bh(t,s,r){var o=t.tag;if(o===5||o===6)t=t.stateNode,s?(r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r).insertBefore(t,s):(s=r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r,s.appendChild(t),r=r._reactRootContainer,r!=null||s.onclick!==null||(s.onclick=ls));else if(o!==4&&(o===27&&Js(t.type)&&(r=t.stateNode,s=null),t=t.child,t!==null))for(bh(t,s,r),t=t.sibling;t!==null;)bh(t,s,r),t=t.sibling}function xl(t,s,r){var o=t.tag;if(o===5||o===6)t=t.stateNode,s?r.insertBefore(t,s):r.appendChild(t);else if(o!==4&&(o===27&&Js(t.type)&&(r=t.stateNode),t=t.child,t!==null))for(xl(t,s,r),t=t.sibling;t!==null;)xl(t,s,r),t=t.sibling}function Tg(t){var s=t.stateNode,r=t.memoizedProps;try{for(var o=t.type,h=s.attributes;h.length;)s.removeAttributeNode(h[0]);Mt(s,o,r),s[ae]=t,s[nt]=r}catch(f){Ve(t,t.return,f)}}var bs=!1,yt=!1,vh=!1,Eg=typeof WeakSet=="function"?WeakSet:Set,jt=null;function tS(t,s){if(t=t.containerInfo,Vh=Hl,t=Lm(t),du(t)){if("selectionStart"in t)var r={start:t.selectionStart,end:t.selectionEnd};else e:{r=(r=t.ownerDocument)&&r.defaultView||window;var o=r.getSelection&&r.getSelection();if(o&&o.rangeCount!==0){r=o.anchorNode;var h=o.anchorOffset,f=o.focusNode;o=o.focusOffset;try{r.nodeType,f.nodeType}catch{r=null;break e}var b=0,w=-1,A=-1,U=0,$=0,I=t,z=null;t:for(;;){for(var V;I!==r||h!==0&&I.nodeType!==3||(w=b+h),I!==f||o!==0&&I.nodeType!==3||(A=b+o),I.nodeType===3&&(b+=I.nodeValue.length),(V=I.firstChild)!==null;)z=I,I=V;for(;;){if(I===t)break t;if(z===r&&++U===h&&(w=b),z===f&&++$===o&&(A=b),(V=I.nextSibling)!==null)break;I=z,z=I.parentNode}I=V}r=w===-1||A===-1?null:{start:w,end:A}}else r=null}r=r||{start:0,end:0}}else r=null;for(Hh={focusedElem:t,selectionRange:r},Hl=!1,jt=s;jt!==null;)if(s=jt,t=s.child,(s.subtreeFlags&1028)!==0&&t!==null)t.return=s,jt=t;else for(;jt!==null;){switch(s=jt,f=s.alternate,t=s.flags,s.tag){case 0:if((t&4)!==0&&(t=s.updateQueue,t=t!==null?t.events:null,t!==null))for(r=0;r title"))),Mt(f,o,r),f[ae]=t,At(f),o=f;break e;case"link":var b=Cy("link","href",h).get(o+(r.href||""));if(b){for(var w=0;wIe&&(b=Ie,Ie=ye,ye=b);var O=Bm(w,ye),j=Bm(w,Ie);if(O&&j&&(V.rangeCount!==1||V.anchorNode!==O.node||V.anchorOffset!==O.offset||V.focusNode!==j.node||V.focusOffset!==j.offset)){var B=I.createRange();B.setStart(O.node,O.offset),V.removeAllRanges(),ye>Ie?(V.addRange(B),V.extend(j.node,j.offset)):(B.setEnd(j.node,j.offset),V.addRange(B))}}}}for(I=[],V=w;V=V.parentNode;)V.nodeType===1&&I.push({element:V,left:V.scrollLeft,top:V.scrollTop});for(typeof w.focus=="function"&&w.focus(),w=0;wr?32:r,L.T=null,r=Ah,Ah=null;var f=Ks,b=Ss;if(_t=0,ya=Ks=null,Ss=0,(ze&6)!==0)throw Error(a(331));var w=ze;if(ze|=4,Ug(f.current),Dg(f,f.current,b,r),ze=w,Dr(0,!1),$t&&typeof $t.onPostCommitFiberRoot=="function")try{$t.onPostCommitFiberRoot(as,f)}catch{}return!0}finally{K.p=h,L.T=o,ey(t,s)}}function ny(t,s,r){s=yn(r,s),s=ah(t.stateNode,s,2),t=Vs(t,s,2),t!==null&&(fi(t,2),Yn(t))}function Ve(t,s,r){if(t.tag===3)ny(t,t,r);else for(;s!==null;){if(s.tag===3){ny(s,t,r);break}else if(s.tag===1){var o=s.stateNode;if(typeof s.type.getDerivedStateFromError=="function"||typeof o.componentDidCatch=="function"&&(Gs===null||!Gs.has(o))){t=yn(r,t),r=sg(2),o=Vs(s,r,2),o!==null&&(ig(r,o,s,t),fi(o,2),Yn(o));break}}s=s.return}}function Rh(t,s,r){var o=t.pingCache;if(o===null){o=t.pingCache=new iS;var h=new Set;o.set(s,h)}else h=o.get(s),h===void 0&&(h=new Set,o.set(s,h));h.has(r)||(_h=!0,h.add(r),t=cS.bind(null,t,s,r),s.then(t,t))}function cS(t,s,r){var o=t.pingCache;o!==null&&o.delete(s),t.pingedLanes|=t.suspendedLanes&r,t.warmLanes&=~r,Ke===t&&(je&r)===r&&(ot===4||ot===3&&(je&62914560)===je&&300>We()-Sl?(ze&2)===0&&ba(t,0):Sh|=r,ga===je&&(ga=0)),Yn(t)}function sy(t,s){s===0&&(s=Za()),t=bi(t,s),t!==null&&(fi(t,s),Yn(t))}function uS(t){var s=t.memoizedState,r=0;s!==null&&(r=s.retryLane),sy(t,r)}function hS(t,s){var r=0;switch(t.tag){case 31:case 13:var o=t.stateNode,h=t.memoizedState;h!==null&&(r=h.retryLane);break;case 19:o=t.stateNode;break;case 22:o=t.stateNode._retryCache;break;default:throw Error(a(314))}o!==null&&o.delete(s),sy(t,r)}function dS(t,s){return xt(t,s)}var Rl=null,xa=null,Ch=!1,Cl=!1,Oh=!1,Xs=0;function Yn(t){t!==xa&&t.next===null&&(xa===null?Rl=xa=t:xa=xa.next=t),Cl=!0,Ch||(Ch=!0,mS())}function Dr(t,s){if(!Oh&&Cl){Oh=!0;do for(var r=!1,o=Rl;o!==null;){if(t!==0){var h=o.pendingLanes;if(h===0)var f=0;else{var b=o.suspendedLanes,w=o.pingedLanes;f=(1<<31-Lt(42|t)+1)-1,f&=h&~(b&~w),f=f&201326741?f&201326741|1:f?f|2:0}f!==0&&(r=!0,oy(o,f))}else f=je,f=qi(o,o===Ke?f:0,o.cancelPendingCommit!==null||o.timeoutHandle!==-1),(f&3)===0||di(o,f)||(r=!0,oy(o,f));o=o.next}while(r);Oh=!1}}function fS(){iy()}function iy(){Cl=Ch=!1;var t=0;Xs!==0&&TS()&&(t=Xs);for(var s=We(),r=null,o=Rl;o!==null;){var h=o.next,f=ay(o,s);f===0?(o.next=null,r===null?Rl=h:r.next=h,h===null&&(xa=r)):(r=o,(t!==0||(f&3)!==0)&&(Cl=!0)),o=h}_t!==0&&_t!==5||Dr(t),Xs!==0&&(Xs=0)}function ay(t,s){for(var r=t.suspendedLanes,o=t.pingedLanes,h=t.expirationTimes,f=t.pendingLanes&-62914561;0w)break;var $=A.transferSize,I=A.initiatorType;$&&py(I)&&(A=A.responseEnd,b+=$*(A"u"?null:document;function jy(t,s,r){var o=wa;if(o&&typeof s=="string"&&s){var h=pn(s);h='link[rel="'+t+'"][href="'+h+'"]',typeof r=="string"&&(h+='[crossorigin="'+r+'"]'),Ay.has(h)||(Ay.add(h),t={rel:t,crossOrigin:r,href:s},o.querySelector(h)===null&&(s=o.createElement("link"),Mt(s,"link",t),At(s),o.head.appendChild(s)))}}function DS(t){Ts.D(t),jy("dns-prefetch",t,null)}function MS(t,s){Ts.C(t,s),jy("preconnect",t,s)}function BS(t,s,r){Ts.L(t,s,r);var o=wa;if(o&&t&&s){var h='link[rel="preload"][as="'+pn(s)+'"]';s==="image"&&r&&r.imageSrcSet?(h+='[imagesrcset="'+pn(r.imageSrcSet)+'"]',typeof r.imageSizes=="string"&&(h+='[imagesizes="'+pn(r.imageSizes)+'"]')):h+='[href="'+pn(t)+'"]';var f=h;switch(s){case"style":f=_a(t);break;case"script":f=Sa(t)}Sn.has(f)||(t=v({rel:"preload",href:s==="image"&&r&&r.imageSrcSet?void 0:t,as:s},r),Sn.set(f,t),o.querySelector(h)!==null||s==="style"&&o.querySelector(Lr(f))||s==="script"&&o.querySelector(zr(f))||(s=o.createElement("link"),Mt(s,"link",t),At(s),o.head.appendChild(s)))}}function US(t,s){Ts.m(t,s);var r=wa;if(r&&t){var o=s&&typeof s.as=="string"?s.as:"script",h='link[rel="modulepreload"][as="'+pn(o)+'"][href="'+pn(t)+'"]',f=h;switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":f=Sa(t)}if(!Sn.has(f)&&(t=v({rel:"modulepreload",href:t},s),Sn.set(f,t),r.querySelector(h)===null)){switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(r.querySelector(zr(f)))return}o=r.createElement("link"),Mt(o,"link",t),At(o),r.head.appendChild(o)}}}function LS(t,s,r){Ts.S(t,s,r);var o=wa;if(o&&t){var h=Fi(o).hoistableStyles,f=_a(t);s=s||"default";var b=h.get(f);if(!b){var w={loading:0,preload:null};if(b=o.querySelector(Lr(f)))w.loading=5;else{t=v({rel:"stylesheet",href:t,"data-precedence":s},r),(r=Sn.get(f))&&Yh(t,r);var A=b=o.createElement("link");At(A),Mt(A,"link",t),A._p=new Promise(function(U,$){A.onload=U,A.onerror=$}),A.addEventListener("load",function(){w.loading|=1}),A.addEventListener("error",function(){w.loading|=2}),w.loading|=4,Ul(b,s,o)}b={type:"stylesheet",instance:b,count:1,state:w},h.set(f,b)}}}function zS(t,s){Ts.X(t,s);var r=wa;if(r&&t){var o=Fi(r).hoistableScripts,h=Sa(t),f=o.get(h);f||(f=r.querySelector(zr(h)),f||(t=v({src:t,async:!0},s),(s=Sn.get(h))&&Xh(t,s),f=r.createElement("script"),At(f),Mt(f,"link",t),r.head.appendChild(f)),f={type:"script",instance:f,count:1,state:null},o.set(h,f))}}function PS(t,s){Ts.M(t,s);var r=wa;if(r&&t){var o=Fi(r).hoistableScripts,h=Sa(t),f=o.get(h);f||(f=r.querySelector(zr(h)),f||(t=v({src:t,async:!0,type:"module"},s),(s=Sn.get(h))&&Xh(t,s),f=r.createElement("script"),At(f),Mt(f,"link",t),r.head.appendChild(f)),f={type:"script",instance:f,count:1,state:null},o.set(h,f))}}function Ny(t,s,r,o){var h=(h=re.current)?Bl(h):null;if(!h)throw Error(a(446));switch(t){case"meta":case"title":return null;case"style":return typeof r.precedence=="string"&&typeof r.href=="string"?(s=_a(r.href),r=Fi(h).hoistableStyles,o=r.get(s),o||(o={type:"style",instance:null,count:0,state:null},r.set(s,o)),o):{type:"void",instance:null,count:0,state:null};case"link":if(r.rel==="stylesheet"&&typeof r.href=="string"&&typeof r.precedence=="string"){t=_a(r.href);var f=Fi(h).hoistableStyles,b=f.get(t);if(b||(h=h.ownerDocument||h,b={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},f.set(t,b),(f=h.querySelector(Lr(t)))&&!f._p&&(b.instance=f,b.state.loading=5),Sn.has(t)||(r={rel:"preload",as:"style",href:r.href,crossOrigin:r.crossOrigin,integrity:r.integrity,media:r.media,hrefLang:r.hrefLang,referrerPolicy:r.referrerPolicy},Sn.set(t,r),f||VS(h,t,r,b.state))),s&&o===null)throw Error(a(528,""));return b}if(s&&o!==null)throw Error(a(529,""));return null;case"script":return s=r.async,r=r.src,typeof r=="string"&&s&&typeof s!="function"&&typeof s!="symbol"?(s=Sa(r),r=Fi(h).hoistableScripts,o=r.get(s),o||(o={type:"script",instance:null,count:0,state:null},r.set(s,o)),o):{type:"void",instance:null,count:0,state:null};default:throw Error(a(444,t))}}function _a(t){return'href="'+pn(t)+'"'}function Lr(t){return'link[rel="stylesheet"]['+t+"]"}function ky(t){return v({},t,{"data-precedence":t.precedence,precedence:null})}function VS(t,s,r,o){t.querySelector('link[rel="preload"][as="style"]['+s+"]")?o.loading=1:(s=t.createElement("link"),o.preload=s,s.addEventListener("load",function(){return o.loading|=1}),s.addEventListener("error",function(){return o.loading|=2}),Mt(s,"link",r),At(s),t.head.appendChild(s))}function Sa(t){return'[src="'+pn(t)+'"]'}function zr(t){return"script[async]"+t}function Ry(t,s,r){if(s.count++,s.instance===null)switch(s.type){case"style":var o=t.querySelector('style[data-href~="'+pn(r.href)+'"]');if(o)return s.instance=o,At(o),o;var h=v({},r,{"data-href":r.href,"data-precedence":r.precedence,href:null,precedence:null});return o=(t.ownerDocument||t).createElement("style"),At(o),Mt(o,"style",h),Ul(o,r.precedence,t),s.instance=o;case"stylesheet":h=_a(r.href);var f=t.querySelector(Lr(h));if(f)return s.state.loading|=4,s.instance=f,At(f),f;o=ky(r),(h=Sn.get(h))&&Yh(o,h),f=(t.ownerDocument||t).createElement("link"),At(f);var b=f;return b._p=new Promise(function(w,A){b.onload=w,b.onerror=A}),Mt(f,"link",o),s.state.loading|=4,Ul(f,r.precedence,t),s.instance=f;case"script":return f=Sa(r.src),(h=t.querySelector(zr(f)))?(s.instance=h,At(h),h):(o=r,(h=Sn.get(f))&&(o=v({},r),Xh(o,h)),t=t.ownerDocument||t,h=t.createElement("script"),At(h),Mt(h,"link",o),t.head.appendChild(h),s.instance=h);case"void":return null;default:throw Error(a(443,s.type))}else s.type==="stylesheet"&&(s.state.loading&4)===0&&(o=s.instance,s.state.loading|=4,Ul(o,r.precedence,t));return s.instance}function Ul(t,s,r){for(var o=r.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),h=o.length?o[o.length-1]:null,f=h,b=0;b title"):null)}function HS(t,s,r){if(r===1||s.itemProp!=null)return!1;switch(t){case"meta":case"title":return!0;case"style":if(typeof s.precedence!="string"||typeof s.href!="string"||s.href==="")break;return!0;case"link":if(typeof s.rel!="string"||typeof s.href!="string"||s.href===""||s.onLoad||s.onError)break;switch(s.rel){case"stylesheet":return t=s.disabled,typeof s.precedence=="string"&&t==null;default:return!0}case"script":if(s.async&&typeof s.async!="function"&&typeof s.async!="symbol"&&!s.onLoad&&!s.onError&&s.src&&typeof s.src=="string")return!0}return!1}function Dy(t){return!(t.type==="stylesheet"&&(t.state.loading&3)===0)}function $S(t,s,r,o){if(r.type==="stylesheet"&&(typeof o.media!="string"||matchMedia(o.media).matches!==!1)&&(r.state.loading&4)===0){if(r.instance===null){var h=_a(o.href),f=s.querySelector(Lr(h));if(f){s=f._p,s!==null&&typeof s=="object"&&typeof s.then=="function"&&(t.count++,t=zl.bind(t),s.then(t,t)),r.state.loading|=4,r.instance=f,At(f);return}f=s.ownerDocument||s,o=ky(o),(h=Sn.get(h))&&Yh(o,h),f=f.createElement("link"),At(f);var b=f;b._p=new Promise(function(w,A){b.onload=w,b.onerror=A}),Mt(f,"link",o),r.instance=f}t.stylesheets===null&&(t.stylesheets=new Map),t.stylesheets.set(r,s),(s=r.state.preload)&&(r.state.loading&3)===0&&(t.count++,r=zl.bind(t),s.addEventListener("load",r),s.addEventListener("error",r))}}var Jh=0;function qS(t,s){return t.stylesheets&&t.count===0&&Vl(t,t.stylesheets),0Jh?50:800)+s);return t.unsuspend=r,function(){t.unsuspend=null,clearTimeout(o),clearTimeout(h)}}:null}function zl(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Vl(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var Pl=null;function Vl(t,s){t.stylesheets=null,t.unsuspend!==null&&(t.count++,Pl=new Map,s.forEach(FS,t),Pl=null,zl.call(t))}function FS(t,s){if(!(s.state.loading&4)){var r=Pl.get(t);if(r)var o=r.get(null);else{r=new Map,Pl.set(t,r);for(var h=t.querySelectorAll("link[data-precedence],style[data-precedence]"),f=0;f"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(e){console.error(e)}}return n(),ad.exports=r1(),ad.exports}var l1=o1();const Ef=D.createContext({});function Af(n){const e=D.useRef(null);return e.current===null&&(e.current=n()),e.current}const c1=typeof window<"u",jf=c1?D.useLayoutEffect:D.useEffect,Pc=D.createContext(null);function Nf(n,e){n.indexOf(e)===-1&&n.push(e)}function vc(n,e){const i=n.indexOf(e);i>-1&&n.splice(i,1)}const ts=(n,e,i)=>i>e?e:i{};const oi={},Av=n=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(n),jv=n=>typeof n=="object"&&n!==null,Nv=n=>/^0[^.\s]+$/u.test(n);function kv(n){let e;return()=>(e===void 0&&(e=n()),e)}const An=n=>n,To=(...n)=>n.reduce((e,i)=>a=>i(e(a))),lo=(n,e,i)=>{const a=e-n;return a?(i-n)/a:1};class Rf{constructor(){this.subscriptions=[]}add(e){return Nf(this.subscriptions,e),()=>vc(this.subscriptions,e)}notify(e,i,a){const l=this.subscriptions.length;if(l)if(l===1)this.subscriptions[0](e,i,a);else for(let c=0;cn*1e3,En=n=>n/1e3,Rv=(n,e)=>e?n*(1e3/e):0,Cv=(n,e,i)=>(((1-3*i+3*e)*n+(3*i-6*e))*n+3*e)*n,u1=1e-7,h1=12;function d1(n,e,i,a,l){let c,u,d=0;do u=e+(i-e)/2,c=Cv(u,a,l)-n,c>0?i=u:e=u;while(Math.abs(c)>u1&&++dd1(c,0,1,n,i);return c=>c===0||c===1?c:Cv(l(c),e,a)}const Ov=n=>e=>e<=.5?n(2*e)/2:(2-n(2*(1-e)))/2,Dv=n=>e=>1-n(1-e),Mv=Eo(.33,1.53,.69,.99),Cf=Dv(Mv),Bv=Ov(Cf),Uv=n=>n>=1?1:(n*=2)<1?.5*Cf(n):.5*(2-Math.pow(2,-10*(n-1))),Of=n=>1-Math.sin(Math.acos(n)),Lv=Dv(Of),zv=Ov(Of),f1=Eo(.42,0,1,1),m1=Eo(0,0,.58,1),Pv=Eo(.42,0,.58,1),p1=n=>Array.isArray(n)&&typeof n[0]!="number",Vv=n=>Array.isArray(n)&&typeof n[0]=="number",g1={linear:An,easeIn:f1,easeInOut:Pv,easeOut:m1,circIn:Of,circInOut:zv,circOut:Lv,backIn:Cf,backInOut:Bv,backOut:Mv,anticipate:Uv},y1=n=>typeof n=="string",s0=n=>{if(Vv(n)){kf(n.length===4);const[e,i,a,l]=n;return Eo(e,i,a,l)}else if(y1(n))return g1[n];return n},Yl=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"];function b1(n){let e=new Set,i=new Set,a=!1,l=!1;const c=new WeakSet;let u={delta:0,timestamp:0,isProcessing:!1};function d(p){c.has(p)&&(m.schedule(p),n()),p(u)}const m={schedule:(p,y=!1,v=!1)=>{const _=v&&a?e:i;return y&&c.add(p),_.add(p),p},cancel:p=>{i.delete(p),c.delete(p)},process:p=>{if(u=p,a){l=!0;return}a=!0;const y=e;e=i,i=y,e.forEach(d),e.clear(),a=!1,l&&(l=!1,m.process(p))}};return m}const v1=40;function Hv(n,e){let i=!1,a=!0;const l={delta:0,timestamp:0,isProcessing:!1},c=()=>i=!0,u=Yl.reduce((N,P)=>(N[P]=b1(c),N),{}),{setup:d,read:m,resolveKeyframes:p,preUpdate:y,update:v,preRender:x,render:_,postRender:S}=u,T=()=>{const N=oi.useManualTiming,P=N?l.timestamp:performance.now();i=!1,N||(l.delta=a?1e3/60:Math.max(Math.min(P-l.timestamp,v1),1)),l.timestamp=P,l.isProcessing=!0,d.process(l),m.process(l),p.process(l),y.process(l),v.process(l),x.process(l),_.process(l),S.process(l),l.isProcessing=!1,i&&e&&(a=!1,n(T))},k=()=>{i=!0,a=!0,l.isProcessing||n(T)};return{schedule:Yl.reduce((N,P)=>{const G=u[P];return N[P]=(X,q=!1,Y=!1)=>(i||k(),G.schedule(X,q,Y)),N},{}),cancel:N=>{for(let P=0;P(uc===void 0&&Ft.set(Bt.isProcessing||oi.useManualTiming?Bt.timestamp:performance.now()),uc),set:n=>{uc=n,queueMicrotask(x1)}},$v=n=>e=>typeof e=="string"&&e.startsWith(n),qv=$v("--"),w1=$v("var(--"),Df=n=>w1(n)?_1.test(n.split("/*")[0].trim()):!1,_1=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;function i0(n){return typeof n!="string"?!1:n.split("/*")[0].includes("var(--")}const Ya={test:n=>typeof n=="number",parse:parseFloat,transform:n=>n},co={...Ya,transform:n=>ts(0,1,n)},Xl={...Ya,default:1},eo=n=>Math.round(n*1e5)/1e5,Mf=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;function S1(n){return n==null}const T1=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,Bf=(n,e)=>i=>!!(typeof i=="string"&&T1.test(i)&&i.startsWith(n)||e&&!S1(i)&&Object.prototype.hasOwnProperty.call(i,e)),Fv=(n,e,i)=>a=>{if(typeof a!="string")return a;const[l,c,u,d]=a.match(Mf);return{[n]:parseFloat(l),[e]:parseFloat(c),[i]:parseFloat(u),alpha:d!==void 0?parseFloat(d):1}},E1=n=>ts(0,255,n),ud={...Ya,transform:n=>Math.round(E1(n))},Ui={test:Bf("rgb","red"),parse:Fv("red","green","blue"),transform:({red:n,green:e,blue:i,alpha:a=1})=>"rgba("+ud.transform(n)+", "+ud.transform(e)+", "+ud.transform(i)+", "+eo(co.transform(a))+")"};function A1(n){let e="",i="",a="",l="";return n.length>5?(e=n.substring(1,3),i=n.substring(3,5),a=n.substring(5,7),l=n.substring(7,9)):(e=n.substring(1,2),i=n.substring(2,3),a=n.substring(3,4),l=n.substring(4,5),e+=e,i+=i,a+=a,l+=l),{red:parseInt(e,16),green:parseInt(i,16),blue:parseInt(a,16),alpha:l?parseInt(l,16)/255:1}}const Ld={test:Bf("#"),parse:A1,transform:Ui.transform},Ao=n=>({test:e=>typeof e=="string"&&e.endsWith(n)&&e.split(" ").length===1,parse:parseFloat,transform:e=>`${e}${n}`}),As=Ao("deg"),es=Ao("%"),ce=Ao("px"),j1=Ao("vh"),N1=Ao("vw"),a0={...es,parse:n=>es.parse(n)/100,transform:n=>es.transform(n*100)},La={test:Bf("hsl","hue"),parse:Fv("hue","saturation","lightness"),transform:({hue:n,saturation:e,lightness:i,alpha:a=1})=>"hsla("+Math.round(n)+", "+es.transform(eo(e))+", "+es.transform(eo(i))+", "+eo(co.transform(a))+")"},vt={test:n=>Ui.test(n)||Ld.test(n)||La.test(n),parse:n=>Ui.test(n)?Ui.parse(n):La.test(n)?La.parse(n):Ld.parse(n),transform:n=>typeof n=="string"?n:n.hasOwnProperty("red")?Ui.transform(n):La.transform(n),getAnimatableNone:n=>{const e=vt.parse(n);return e.alpha=0,vt.transform(e)}},k1=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;function R1(n){var e,i;return isNaN(n)&&typeof n=="string"&&(((e=n.match(Mf))==null?void 0:e.length)||0)+(((i=n.match(k1))==null?void 0:i.length)||0)>0}const Iv="number",Gv="color",C1="var",O1="var(",r0="${}",D1=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function Ia(n){const e=n.toString(),i=[],a={color:[],number:[],var:[]},l=[];let c=0;const d=e.replace(D1,m=>(vt.test(m)?(a.color.push(c),l.push(Gv),i.push(vt.parse(m))):m.startsWith(O1)?(a.var.push(c),l.push(C1),i.push(m)):(a.number.push(c),l.push(Iv),i.push(parseFloat(m))),++c,r0)).split(r0);return{values:i,split:d,indexes:a,types:l}}function M1(n){return Ia(n).values}function Kv({split:n,types:e}){const i=n.length;return a=>{let l="";for(let c=0;ctypeof n=="number"?0:vt.test(n)?vt.getAnimatableNone(n):n,L1=(n,e)=>typeof n=="number"?e!=null&&e.trim().endsWith("/")?n:0:U1(n);function z1(n){const e=Ia(n);return Kv(e)(e.values.map((a,l)=>L1(a,e.split[l])))}const $n={test:R1,parse:M1,createTransformer:B1,getAnimatableNone:z1};function hd(n,e,i){return i<0&&(i+=1),i>1&&(i-=1),i<1/6?n+(e-n)*6*i:i<1/2?e:i<2/3?n+(e-n)*(2/3-i)*6:n}function P1({hue:n,saturation:e,lightness:i,alpha:a}){n/=360,e/=100,i/=100;let l=0,c=0,u=0;if(!e)l=c=u=i;else{const d=i<.5?i*(1+e):i+e-i*e,m=2*i-d;l=hd(m,d,n+1/3),c=hd(m,d,n),u=hd(m,d,n-1/3)}return{red:Math.round(l*255),green:Math.round(c*255),blue:Math.round(u*255),alpha:a}}function xc(n,e){return i=>i>0?e:n}const Ye=(n,e,i)=>n+(e-n)*i,dd=(n,e,i)=>{const a=n*n,l=i*(e*e-a)+a;return l<0?0:Math.sqrt(l)},V1=[Ld,Ui,La],H1=n=>V1.find(e=>e.test(n));function o0(n){const e=H1(n);if(!e)return!1;let i=e.parse(n);return e===La&&(i=P1(i)),i}const l0=(n,e)=>{const i=o0(n),a=o0(e);if(!i||!a)return xc(n,e);const l={...i};return c=>(l.red=dd(i.red,a.red,c),l.green=dd(i.green,a.green,c),l.blue=dd(i.blue,a.blue,c),l.alpha=Ye(i.alpha,a.alpha,c),Ui.transform(l))},zd=new Set(["none","hidden"]);function $1(n,e){return zd.has(n)?i=>i<=0?n:e:i=>i>=1?e:n}function q1(n,e){return i=>Ye(n,e,i)}function Uf(n){return typeof n=="number"?q1:typeof n=="string"?Df(n)?xc:vt.test(n)?l0:G1:Array.isArray(n)?Yv:typeof n=="object"?vt.test(n)?l0:F1:xc}function Yv(n,e){const i=[...n],a=i.length,l=n.map((c,u)=>Uf(c)(c,e[u]));return c=>{for(let u=0;u{for(const c in a)i[c]=a[c](l);return i}}function I1(n,e){const i=[],a={color:0,var:0,number:0};for(let l=0;l{const i=$n.createTransformer(e),a=Ia(n),l=Ia(e);return a.indexes.var.length===l.indexes.var.length&&a.indexes.color.length===l.indexes.color.length&&a.indexes.number.length>=l.indexes.number.length?zd.has(n)&&!l.values.length||zd.has(e)&&!a.values.length?$1(n,e):To(Yv(I1(a,l),l.values),i):xc(n,e)};function Xv(n,e,i){return typeof n=="number"&&typeof e=="number"&&typeof i=="number"?Ye(n,e,i):Uf(n)(n,e)}const K1=n=>{const e=({timestamp:i})=>n(i);return{start:(i=!0)=>Xe.update(e,i),stop:()=>li(e),now:()=>Bt.isProcessing?Bt.timestamp:Ft.now()}},Jv=(n,e,i=10)=>{let a="";const l=Math.max(Math.round(e/i),2);for(let c=0;c=wc?1/0:e}function Y1(n,e=100,i){const a=i({...n,keyframes:[0,e]}),l=Math.min(Lf(a),wc);return{type:"keyframes",ease:c=>a.next(l*c).value/e,duration:En(l)}}const ct={stiffness:100,damping:10,mass:1,velocity:0,duration:800,bounce:.3,visualDuration:.3,restSpeed:{granular:.01,default:2},restDelta:{granular:.005,default:.5},minDuration:.01,maxDuration:10,minDamping:.05,maxDamping:1};function Pd(n,e){return n*Math.sqrt(1-e*e)}const X1=12;function J1(n,e,i){let a=i;for(let l=1;l{const y=p*u,v=y*n,x=y-i,_=Pd(p,u),S=Math.exp(-v);return fd-x/_*S},c=p=>{const v=p*u*n,x=v*i+i,_=Math.pow(u,2)*Math.pow(p,2)*n,S=Math.exp(-v),T=Pd(Math.pow(p,2),u);return(-l(p)+fd>0?-1:1)*((x-_)*S)/T}):(l=p=>{const y=Math.exp(-p*n),v=(p-i)*n+1;return-fd+y*v},c=p=>{const y=Math.exp(-p*n),v=(i-p)*(n*n);return y*v});const d=5/n,m=J1(l,c,d);if(n=dn(n),isNaN(m))return{stiffness:ct.stiffness,damping:ct.damping,duration:n};{const p=Math.pow(m,2)*a;return{stiffness:p,damping:u*2*Math.sqrt(a*p),duration:n}}}const Z1=["duration","bounce"],W1=["stiffness","damping","mass"];function c0(n,e){return e.some(i=>n[i]!==void 0)}function eT(n){let e={velocity:ct.velocity,stiffness:ct.stiffness,damping:ct.damping,mass:ct.mass,isResolvedFromDuration:!1,...n};if(!c0(n,W1)&&c0(n,Z1))if(e.velocity=0,n.visualDuration){const i=n.visualDuration,a=2*Math.PI/(i*1.2),l=a*a,c=2*ts(.05,1,1-(n.bounce||0))*Math.sqrt(l);e={...e,mass:ct.mass,stiffness:l,damping:c}}else{const i=Q1({...n,velocity:0});e={...e,...i,mass:ct.mass},e.isResolvedFromDuration=!0}return e}function _c(n=ct.visualDuration,e=ct.bounce){const i=typeof n!="object"?{visualDuration:n,keyframes:[0,1],bounce:e}:n;let{restSpeed:a,restDelta:l}=i;const c=i.keyframes[0],u=i.keyframes[i.keyframes.length-1],d={done:!1,value:c},{stiffness:m,damping:p,mass:y,duration:v,velocity:x,isResolvedFromDuration:_}=eT({...i,velocity:-En(i.velocity||0)}),S=x||0,T=p/(2*Math.sqrt(m*y)),k=u-c,R=En(Math.sqrt(m/y)),M=Math.abs(k)<5;a||(a=M?ct.restSpeed.granular:ct.restSpeed.default),l||(l=M?ct.restDelta.granular:ct.restDelta.default);let N,P,G,X,q,Y;if(T<1)G=Pd(R,T),X=(S+T*R*k)/G,N=J=>{const he=Math.exp(-T*R*J);return u-he*(X*Math.sin(G*J)+k*Math.cos(G*J))},q=T*R*X+k*G,Y=T*R*k-X*G,P=J=>Math.exp(-T*R*J)*(q*Math.sin(G*J)+Y*Math.cos(G*J));else if(T===1){N=he=>u-Math.exp(-R*he)*(k+(S+R*k)*he);const J=S+R*k;P=he=>Math.exp(-R*he)*(R*J*he-S)}else{const J=R*Math.sqrt(T*T-1);N=ve=>{const Ce=Math.exp(-T*R*ve),L=Math.min(J*ve,300);return u-Ce*((S+T*R*k)*Math.sinh(L)+J*k*Math.cosh(L))/J};const he=(S+T*R*k)/J,fe=T*R*he-k*J,Ae=T*R*k-he*J;P=ve=>{const Ce=Math.exp(-T*R*ve),L=Math.min(J*ve,300);return Ce*(fe*Math.sinh(L)+Ae*Math.cosh(L))}}const se={calculatedDuration:_&&v||null,velocity:J=>dn(P(J)),next:J=>{if(!_&&T<1){const fe=Math.exp(-T*R*J),Ae=Math.sin(G*J),ve=Math.cos(G*J),Ce=u-fe*(X*Ae+k*ve),L=dn(fe*(q*Ae+Y*ve));return d.done=Math.abs(L)<=a&&Math.abs(u-Ce)<=l,d.value=d.done?u:Ce,d}const he=N(J);if(_)d.done=J>=v;else{const fe=dn(P(J));d.done=Math.abs(fe)<=a&&Math.abs(u-he)<=l}return d.value=d.done?u:he,d},toString:()=>{const J=Math.min(Lf(se),wc),he=Jv(fe=>se.next(J*fe).value,J,30);return J+"ms "+he},toTransition:()=>{}};return se}_c.applyToOptions=n=>{const e=Y1(n,100,_c);return n.ease=e.ease,n.duration=dn(e.duration),n.type="keyframes",n};const tT=5;function Qv(n,e,i){const a=Math.max(e-tT,0);return Rv(i-n(a),e-a)}function Vd({keyframes:n,velocity:e=0,power:i=.8,timeConstant:a=325,bounceDamping:l=10,bounceStiffness:c=500,modifyTarget:u,min:d,max:m,restDelta:p=.5,restSpeed:y}){const v=n[0],x={done:!1,value:v},_=Y=>d!==void 0&&Ym,S=Y=>d===void 0?m:m===void 0||Math.abs(d-Y)-T*Math.exp(-Y/a),N=Y=>R+M(Y),P=Y=>{const se=M(Y),J=N(Y);x.done=Math.abs(se)<=p,x.value=x.done?R:J};let G,X;const q=Y=>{_(x.value)&&(G=Y,X=_c({keyframes:[x.value,S(x.value)],velocity:Qv(N,Y,x.value),damping:l,stiffness:c,restDelta:p,restSpeed:y}))};return q(0),{calculatedDuration:null,next:Y=>{let se=!1;return!X&&G===void 0&&(se=!0,P(Y),q(Y)),G!==void 0&&Y>=G?X.next(Y-G):(!se&&P(Y),x)}}}function nT(n,e,i){const a=[],l=i||oi.mix||Xv,c=n.length-1;for(let u=0;ue[0];if(c===2&&e[0]===e[1])return()=>e[1];const u=n[0]===n[1];n[0]>n[c-1]&&(n=[...n].reverse(),e=[...e].reverse());const d=nT(e,a,l),m=d.length,p=y=>{if(u&&y1)for(;vp(ts(n[0],n[c-1],y)):p}function iT(n,e){const i=n[n.length-1];for(let a=1;a<=e;a++){const l=lo(0,e,a);n.push(Ye(i,1,l))}}function aT(n){const e=[0];return iT(e,n.length-1),e}function rT(n,e){return n.map(i=>i*e)}function oT(n,e){return n.map(()=>e||Pv).splice(0,n.length-1)}function to({duration:n=300,keyframes:e,times:i,ease:a="easeInOut"}){const l=p1(a)?a.map(s0):s0(a),c={done:!1,value:e[0]},u=rT(i&&i.length===e.length?i:aT(e),n),d=sT(u,e,{ease:Array.isArray(l)?l:oT(e,l)});return{calculatedDuration:n,next:m=>(c.value=d(m),c.done=m>=n,c)}}const lT=n=>n!==null;function Vc(n,{repeat:e,repeatType:i="loop"},a,l=1){const c=n.filter(lT),d=l<0||e&&i!=="loop"&&e%2===1?0:c.length-1;return!d||a===void 0?c[d]:a}const cT={decay:Vd,inertia:Vd,tween:to,keyframes:to,spring:_c};function Zv(n){typeof n.type=="string"&&(n.type=cT[n.type])}class zf{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(e=>{this.resolve=e})}notifyFinished(){this.resolve()}then(e,i){return this.finished.then(e,i)}}const uT=n=>n/100;class Sc extends zf{constructor(e){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.delayState={done:!1,value:void 0},this.stop=()=>{var a,l;const{motionValue:i}=this.options;i&&i.updatedAt!==Ft.now()&&this.tick(Ft.now()),this.isStopped=!0,this.state!=="idle"&&(this.teardown(),(l=(a=this.options).onStop)==null||l.call(a))},this.options=e,this.initAnimation(),this.play(),e.autoplay===!1&&this.pause()}initAnimation(){const{options:e}=this;Zv(e);const{type:i=to,repeat:a=0,repeatDelay:l=0,repeatType:c,velocity:u=0}=e;let{keyframes:d}=e;const m=i||to;m!==to&&typeof d[0]!="number"&&(this.mixKeyframes=To(uT,Xv(d[0],d[1])),d=[0,100]);const p=m({...e,keyframes:d});c==="mirror"&&(this.mirroredGenerator=m({...e,keyframes:[...d].reverse(),velocity:-u})),p.calculatedDuration===null&&(p.calculatedDuration=Lf(p));const{calculatedDuration:y}=p;this.calculatedDuration=y,this.resolvedDuration=y+l,this.totalDuration=this.resolvedDuration*(a+1)-l,this.generator=p}updateTime(e){const i=Math.round(e-this.startTime)*this.playbackSpeed;this.holdTime!==null?this.currentTime=this.holdTime:this.currentTime=i}tick(e,i=!1){const{generator:a,totalDuration:l,mixKeyframes:c,mirroredGenerator:u,resolvedDuration:d,calculatedDuration:m}=this;if(this.startTime===null)return a.next(0);const{delay:p=0,keyframes:y,repeat:v,repeatType:x,repeatDelay:_,type:S,onUpdate:T,finalKeyframe:k}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,e):this.speed<0&&(this.startTime=Math.min(e-l/this.speed,this.startTime)),i?this.currentTime=e:this.updateTime(e);const R=this.currentTime-p*(this.playbackSpeed>=0?1:-1),M=this.playbackSpeed>=0?R<0:R>l;this.currentTime=Math.max(R,0),this.state==="finished"&&this.holdTime===null&&(this.currentTime=l);let N=this.currentTime,P=a;if(v){const Y=Math.min(this.currentTime,l)/d;let se=Math.floor(Y),J=Y%1;!J&&Y>=1&&(J=1),J===1&&se--,se=Math.min(se,v+1),!!(se%2)&&(x==="reverse"?(J=1-J,_&&(J-=_/d)):x==="mirror"&&(P=u)),N=ts(0,1,J)*d}let G;M?(this.delayState.value=y[0],G=this.delayState):G=P.next(N),c&&!M&&(G.value=c(G.value));let{done:X}=G;!M&&m!==null&&(X=this.playbackSpeed>=0?this.currentTime>=l:this.currentTime<=0);const q=this.holdTime===null&&(this.state==="finished"||this.state==="running"&&X);return q&&S!==Vd&&(G.value=Vc(y,this.options,k,this.speed)),T&&T(G.value),q&&this.finish(),G}then(e,i){return this.finished.then(e,i)}get duration(){return En(this.calculatedDuration)}get iterationDuration(){const{delay:e=0}=this.options||{};return this.duration+En(e)}get time(){return En(this.currentTime)}set time(e){e=dn(e),this.currentTime=e,this.startTime===null||this.holdTime!==null||this.playbackSpeed===0?this.holdTime=e:this.driver&&(this.startTime=this.driver.now()-e/this.playbackSpeed),this.driver?this.driver.start(!1):(this.startTime=0,this.state="paused",this.holdTime=e,this.tick(e))}getGeneratorVelocity(){const e=this.currentTime;if(e<=0)return this.options.velocity||0;if(this.generator.velocity)return this.generator.velocity(e);const i=this.generator.next(e).value;return Qv(a=>this.generator.next(a).value,e,i)}get speed(){return this.playbackSpeed}set speed(e){const i=this.playbackSpeed!==e;i&&this.driver&&this.updateTime(Ft.now()),this.playbackSpeed=e,i&&this.driver&&(this.time=En(this.currentTime))}play(){var l,c;if(this.isStopped)return;const{driver:e=K1,startTime:i}=this.options;this.driver||(this.driver=e(u=>this.tick(u))),(c=(l=this.options).onPlay)==null||c.call(l);const a=this.driver.now();this.state==="finished"?(this.updateFinished(),this.startTime=a):this.holdTime!==null?this.startTime=a-this.holdTime:this.startTime||(this.startTime=i??a),this.state==="finished"&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(Ft.now()),this.holdTime=this.currentTime}complete(){this.state!=="running"&&this.play(),this.state="finished",this.holdTime=null}finish(){var e,i;this.notifyFinished(),this.teardown(),this.state="finished",(i=(e=this.options).onComplete)==null||i.call(e)}cancel(){var e,i;this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),(i=(e=this.options).onCancel)==null||i.call(e)}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(e){return this.startTime=0,this.tick(e,!0)}attachTimeline(e){var i;return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),(i=this.driver)==null||i.stop(),e.observe(this)}}function hT(n){for(let e=1;en*180/Math.PI,Hd=n=>{const e=Li(Math.atan2(n[1],n[0]));return $d(e)},dT={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:n=>(Math.abs(n[0])+Math.abs(n[3]))/2,rotate:Hd,rotateZ:Hd,skewX:n=>Li(Math.atan(n[1])),skewY:n=>Li(Math.atan(n[2])),skew:n=>(Math.abs(n[1])+Math.abs(n[2]))/2},$d=n=>(n=n%360,n<0&&(n+=360),n),u0=Hd,h0=n=>Math.sqrt(n[0]*n[0]+n[1]*n[1]),d0=n=>Math.sqrt(n[4]*n[4]+n[5]*n[5]),fT={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:h0,scaleY:d0,scale:n=>(h0(n)+d0(n))/2,rotateX:n=>$d(Li(Math.atan2(n[6],n[5]))),rotateY:n=>$d(Li(Math.atan2(-n[2],n[0]))),rotateZ:u0,rotate:u0,skewX:n=>Li(Math.atan(n[4])),skewY:n=>Li(Math.atan(n[1])),skew:n=>(Math.abs(n[1])+Math.abs(n[4]))/2};function qd(n){return n.includes("scale")?1:0}function Fd(n,e){if(!n||n==="none")return qd(e);const i=n.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let a,l;if(i)a=fT,l=i;else{const d=n.match(/^matrix\(([-\d.e\s,]+)\)$/u);a=dT,l=d}if(!l)return qd(e);const c=a[e],u=l[1].split(",").map(pT);return typeof c=="function"?c(u):u[c]}const mT=(n,e)=>{const{transform:i="none"}=getComputedStyle(n);return Fd(i,e)};function pT(n){return parseFloat(n.trim())}const Xa=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],Ja=new Set([...Xa,"pathRotation"]),f0=n=>n===Ya||n===ce,gT=new Set(["x","y","z"]),yT=Xa.filter(n=>!gT.has(n));function bT(n){const e=[];return yT.forEach(i=>{const a=n.getValue(i);a!==void 0&&(e.push([i,a.get()]),a.set(i.startsWith("scale")?1:0))}),e}const ri={width:({x:n},{paddingLeft:e="0",paddingRight:i="0",boxSizing:a})=>{const l=n.max-n.min;return a==="border-box"?l:l-parseFloat(e)-parseFloat(i)},height:({y:n},{paddingTop:e="0",paddingBottom:i="0",boxSizing:a})=>{const l=n.max-n.min;return a==="border-box"?l:l-parseFloat(e)-parseFloat(i)},top:(n,{top:e})=>parseFloat(e),left:(n,{left:e})=>parseFloat(e),bottom:({y:n},{top:e})=>parseFloat(e)+(n.max-n.min),right:({x:n},{left:e})=>parseFloat(e)+(n.max-n.min),x:(n,{transform:e})=>Fd(e,"x"),y:(n,{transform:e})=>Fd(e,"y")};ri.translateX=ri.x;ri.translateY=ri.y;const Pi=new Set;let Id=!1,Gd=!1,Kd=!1;function Wv(){if(Gd){const n=Array.from(Pi).filter(a=>a.needsMeasurement),e=new Set(n.map(a=>a.element)),i=new Map;e.forEach(a=>{const l=bT(a);l.length&&(i.set(a,l),a.render())}),n.forEach(a=>a.measureInitialState()),e.forEach(a=>{a.render();const l=i.get(a);l&&l.forEach(([c,u])=>{var d;(d=a.getValue(c))==null||d.set(u)})}),n.forEach(a=>a.measureEndState()),n.forEach(a=>{a.suspendedScrollY!==void 0&&window.scrollTo(0,a.suspendedScrollY)})}Gd=!1,Id=!1,Pi.forEach(n=>n.complete(Kd)),Pi.clear()}function ex(){Pi.forEach(n=>{n.readKeyframes(),n.needsMeasurement&&(Gd=!0)})}function vT(){Kd=!0,ex(),Wv(),Kd=!1}class Pf{constructor(e,i,a,l,c,u=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...e],this.onComplete=i,this.name=a,this.motionValue=l,this.element=c,this.isAsync=u}scheduleResolve(){this.state="scheduled",this.isAsync?(Pi.add(this),Id||(Id=!0,Xe.read(ex),Xe.resolveKeyframes(Wv))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:e,name:i,element:a,motionValue:l}=this;if(e[0]===null){const c=l==null?void 0:l.get(),u=e[e.length-1];if(c!==void 0)e[0]=c;else if(a&&i){const d=a.readValue(i,u);d!=null&&(e[0]=d)}e[0]===void 0&&(e[0]=u),l&&c===void 0&&l.set(e[0])}hT(e)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(e=!1){this.state="complete",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,e),Pi.delete(this)}cancel(){this.state==="scheduled"&&(Pi.delete(this),this.state="pending")}resume(){this.state==="pending"&&this.scheduleResolve()}}const xT=n=>n.startsWith("--");function tx(n,e,i){xT(e)?n.style.setProperty(e,i):n.style[e]=i}const wT={};function nx(n,e){const i=kv(n);return()=>wT[e]??i()}const _T=nx(()=>window.ScrollTimeline!==void 0,"scrollTimeline"),sx=nx(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch{return!1}return!0},"linearEasing"),Xr=([n,e,i,a])=>`cubic-bezier(${n}, ${e}, ${i}, ${a})`,m0={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:Xr([0,.65,.55,1]),circOut:Xr([.55,0,1,.45]),backIn:Xr([.31,.01,.66,-.59]),backOut:Xr([.33,1.53,.69,.99])};function ix(n,e){if(n)return typeof n=="function"?sx()?Jv(n,e):"ease-out":Vv(n)?Xr(n):Array.isArray(n)?n.map(i=>ix(i,e)||m0.easeOut):m0[n]}function ST(n,e,i,{delay:a=0,duration:l=300,repeat:c=0,repeatType:u="loop",ease:d="easeOut",times:m}={},p=void 0){const y={[e]:i};m&&(y.offset=m);const v=ix(d,l);Array.isArray(v)&&(y.easing=v);const x={delay:a,duration:l,easing:Array.isArray(v)?"linear":v,fill:"both",iterations:c+1,direction:u==="reverse"?"alternate":"normal"};return p&&(x.pseudoElement=p),n.animate(y,x)}function ax(n){return typeof n=="function"&&"applyToOptions"in n}function TT({type:n,...e}){return ax(n)&&sx()?n.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease="easeOut"),e)}class rx extends zf{constructor(e){if(super(),this.finishedTime=null,this.isStopped=!1,this.manualStartTime=null,!e)return;const{element:i,name:a,keyframes:l,pseudoElement:c,allowFlatten:u=!1,finalKeyframe:d,onComplete:m}=e;this.isPseudoElement=!!c,this.allowFlatten=u,this.options=e,kf(typeof e.type!="string");const p=TT(e);this.animation=ST(i,a,l,p,c),p.autoplay===!1&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!c){const y=Vc(l,this.options,d,this.speed);this.updateMotionValue&&this.updateMotionValue(y),tx(i,a,y),this.animation.cancel()}m==null||m(),this.notifyFinished()}}play(){this.isStopped||(this.manualStartTime=null,this.animation.play(),this.state==="finished"&&this.updateFinished())}pause(){this.animation.pause()}complete(){var e,i;(i=(e=this.animation).finish)==null||i.call(e)}cancel(){try{this.animation.cancel()}catch{}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:e}=this;e==="idle"||e==="finished"||(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){var i,a,l;const e=(i=this.options)==null?void 0:i.element;!this.isPseudoElement&&(e!=null&&e.isConnected)&&((l=(a=this.animation).commitStyles)==null||l.call(a))}get duration(){var i,a;const e=((a=(i=this.animation.effect)==null?void 0:i.getComputedTiming)==null?void 0:a.call(i).duration)||0;return En(Number(e))}get iterationDuration(){const{delay:e=0}=this.options||{};return this.duration+En(e)}get time(){return En(Number(this.animation.currentTime)||0)}set time(e){const i=this.finishedTime!==null;this.manualStartTime=null,this.finishedTime=null,this.animation.currentTime=dn(e),i&&this.animation.pause()}get speed(){return this.animation.playbackRate}set speed(e){e<0&&(this.finishedTime=null),this.animation.playbackRate=e}get state(){return this.finishedTime!==null?"finished":this.animation.playState}get startTime(){return this.manualStartTime??Number(this.animation.startTime)}set startTime(e){this.manualStartTime=this.animation.startTime=e}attachTimeline({timeline:e,rangeStart:i,rangeEnd:a,observe:l}){var c;return this.allowFlatten&&((c=this.animation.effect)==null||c.updateTiming({easing:"linear"})),this.animation.onfinish=null,e&&_T()?(this.animation.timeline=e,i&&(this.animation.rangeStart=i),a&&(this.animation.rangeEnd=a),An):l(this)}}const ox={anticipate:Uv,backInOut:Bv,circInOut:zv};function ET(n){return n in ox}function AT(n){typeof n.ease=="string"&&ET(n.ease)&&(n.ease=ox[n.ease])}const md=10;class jT extends rx{constructor(e){AT(e),Zv(e),super(e),e.startTime!==void 0&&e.autoplay!==!1&&(this.startTime=e.startTime),this.options=e}updateMotionValue(e){const{motionValue:i,onUpdate:a,onComplete:l,element:c,...u}=this.options;if(!i)return;if(e!==void 0){i.set(e);return}const d=new Sc({...u,autoplay:!1}),m=Math.max(md,Ft.now()-this.startTime),p=ts(0,md,m-md),y=d.sample(m).value,{name:v}=this.options;c&&v&&tx(c,v,y),i.setWithVelocity(d.sample(Math.max(0,m-p)).value,y,p),d.stop()}}const p0=(n,e)=>e==="zIndex"?!1:!!(typeof n=="number"||Array.isArray(n)||typeof n=="string"&&($n.test(n)||n==="0")&&!n.startsWith("url("));function NT(n){const e=n[0];if(n.length===1)return!0;for(let i=0;iObject.hasOwnProperty.call(Element.prototype,"animate"));function MT(n){var v;const{motionValue:e,name:i,repeatDelay:a,repeatType:l,damping:c,type:u,keyframes:d}=n;if(!(((v=e==null?void 0:e.owner)==null?void 0:v.current)instanceof HTMLElement))return!1;const{onUpdate:p,transformTemplate:y}=e.owner.getProps();return DT()&&i&&(lx.has(i)||OT.has(i)&&CT(d))&&(i!=="transform"||!y)&&!p&&!a&&l!=="mirror"&&c!==0&&u!=="inertia"}const BT=40;class UT extends zf{constructor({autoplay:e=!0,delay:i=0,type:a="keyframes",repeat:l=0,repeatDelay:c=0,repeatType:u="loop",keyframes:d,name:m,motionValue:p,element:y,...v}){var S;super(),this.stop=()=>{var T,k;this._animation&&(this._animation.stop(),(T=this.stopTimeline)==null||T.call(this)),(k=this.keyframeResolver)==null||k.cancel()},this.createdAt=Ft.now();const x={autoplay:e,delay:i,type:a,repeat:l,repeatDelay:c,repeatType:u,name:m,motionValue:p,element:y,...v},_=(y==null?void 0:y.KeyframeResolver)||Pf;this.keyframeResolver=new _(d,(T,k,R)=>this.onKeyframesResolved(T,k,x,!R),m,p,y),(S=this.keyframeResolver)==null||S.scheduleResolve()}onKeyframesResolved(e,i,a,l){var R,M;this.keyframeResolver=void 0;const{name:c,type:u,velocity:d,delay:m,isHandoff:p,onUpdate:y}=a;this.resolvedAt=Ft.now();let v=!0;kT(e,c,u,d)||(v=!1,(oi.instantAnimations||!m)&&(y==null||y(Vc(e,a,i))),e[0]=e[e.length-1],Yd(a),a.repeat=0);const _={startTime:l?this.resolvedAt?this.resolvedAt-this.createdAt>BT?this.resolvedAt:this.createdAt:this.createdAt:void 0,finalKeyframe:i,...a,keyframes:e},S=v&&!p&&MT(_),T=(M=(R=_.motionValue)==null?void 0:R.owner)==null?void 0:M.current;let k;if(S)try{k=new jT({..._,element:T})}catch{k=new Sc(_)}else k=new Sc(_);k.finished.then(()=>{this.notifyFinished()}).catch(An),this.pendingTimeline&&(this.stopTimeline=k.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=k}get finished(){return this._animation?this.animation.finished:this._finished}then(e,i){return this.finished.finally(e).then(()=>{})}get animation(){var e;return this._animation||((e=this.keyframeResolver)==null||e.resume(),vT()),this._animation}get duration(){return this.animation.duration}get iterationDuration(){return this.animation.iterationDuration}get time(){return this.animation.time}set time(e){this.animation.time=e}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(e){this.animation.speed=e}get startTime(){return this.animation.startTime}attachTimeline(e){return this._animation?this.stopTimeline=this.animation.attachTimeline(e):this.pendingTimeline=e,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){var e;this._animation&&this.animation.cancel(),(e=this.keyframeResolver)==null||e.cancel()}}function cx(n,e,i,a=0,l=1){const c=Array.from(n).sort((p,y)=>p.sortNodePosition(y)).indexOf(e),u=n.size,d=(u-1)*a;return typeof i=="function"?i(c,u):l===1?c*a:d-c*a}const g0=30,LT=n=>!isNaN(parseFloat(n));class zT{constructor(e,i={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=a=>{var c;const l=Ft.now();if(this.updatedAt!==l&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(a),this.current!==this.prev&&((c=this.events.change)==null||c.notify(this.current),this.dependents))for(const u of this.dependents)u.dirty()},this.hasAnimated=!1,this.setCurrent(e),this.owner=i.owner}setCurrent(e){this.current=e,this.updatedAt=Ft.now(),this.canTrackVelocity===null&&e!==void 0&&(this.canTrackVelocity=LT(this.current))}setPrevFrameValue(e=this.current){this.prevFrameValue=e,this.prevUpdatedAt=this.updatedAt}onChange(e){return this.on("change",e)}on(e,i){this.events[e]||(this.events[e]=new Rf);const a=this.events[e].add(i);return e==="change"?()=>{a(),Xe.read(()=>{this.events.change.getSize()||this.stop()})}:a}clearListeners(){for(const e in this.events)this.events[e].clear()}attach(e,i){this.passiveEffect=e,this.stopPassiveEffect=i}set(e){this.passiveEffect?this.passiveEffect(e,this.updateAndNotify):this.updateAndNotify(e)}setWithVelocity(e,i,a){this.set(i),this.prev=void 0,this.prevFrameValue=e,this.prevUpdatedAt=this.updatedAt-a}jump(e,i=!0){this.updateAndNotify(e),this.prev=e,this.prevUpdatedAt=this.prevFrameValue=void 0,i&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){var e;(e=this.events.change)==null||e.notify(this.current)}addDependent(e){this.dependents||(this.dependents=new Set),this.dependents.add(e)}removeDependent(e){this.dependents&&this.dependents.delete(e)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const e=Ft.now();if(!this.canTrackVelocity||this.prevFrameValue===void 0||e-this.updatedAt>g0)return 0;const i=Math.min(this.updatedAt-this.prevUpdatedAt,g0);return Rv(parseFloat(this.current)-parseFloat(this.prevFrameValue),i)}start(e){return this.stop(),new Promise(i=>{this.hasAnimated=!0,this.animation=e(i),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){var e,i;(e=this.dependents)==null||e.clear(),(i=this.events.destroy)==null||i.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function Ga(n,e){return new zT(n,e)}function ux(n,e){if(n!=null&&n.inherit&&e){const{inherit:i,...a}=n;return{...e,...a}}return n}function Vf(n,e){const i=(n==null?void 0:n[e])??(n==null?void 0:n.default)??n;return i!==n?ux(i,n):i}const PT={type:"spring",stiffness:500,damping:25,restSpeed:10},VT=n=>({type:"spring",stiffness:550,damping:n===0?2*Math.sqrt(550):30,restSpeed:10}),HT={type:"keyframes",duration:.8},$T={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},qT=(n,{keyframes:e})=>e.length>2?HT:Ja.has(n)?n.startsWith("scale")?VT(e[1]):PT:$T,FT=new Set(["when","delay","delayChildren","staggerChildren","staggerDirection","repeat","repeatType","repeatDelay","from","elapsed"]);function IT(n){for(const e in n)if(!FT.has(e))return!0;return!1}const Hf=(n,e,i,a={},l,c)=>u=>{const d=Vf(a,n)||{},m=d.delay||a.delay||0;let{elapsed:p=0}=a;p=p-dn(m);const y={keyframes:Array.isArray(i)?i:[null,i],ease:"easeOut",velocity:e.getVelocity(),...d,delay:-p,onUpdate:x=>{e.set(x),d.onUpdate&&d.onUpdate(x)},onComplete:()=>{u(),d.onComplete&&d.onComplete()},name:n,motionValue:e,element:c?void 0:l};IT(d)||Object.assign(y,qT(n,y)),y.duration&&(y.duration=dn(y.duration)),y.repeatDelay&&(y.repeatDelay=dn(y.repeatDelay)),y.from!==void 0&&(y.keyframes[0]=y.from);let v=!1;if((y.type===!1||y.duration===0&&!y.repeatDelay)&&(Yd(y),y.delay===0&&(v=!0)),(oi.instantAnimations||oi.skipAnimations||l!=null&&l.shouldSkipAnimations||d.skipAnimations)&&(v=!0,Yd(y),y.delay=0),y.allowFlatten=!d.type&&!d.ease,v&&!c&&e.get()!==void 0){const x=Vc(y.keyframes,d);if(x!==void 0){Xe.update(()=>{y.onUpdate(x),y.onComplete()});return}}return d.isSync?new Sc(y):new UT(y)},GT=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function KT(n){const e=GT.exec(n);if(!e)return[,];const[,i,a,l]=e;return[`--${i??a}`,l]}function hx(n,e,i=1){const[a,l]=KT(n);if(!a)return;const c=window.getComputedStyle(e).getPropertyValue(a);if(c){const u=c.trim();return Av(u)?parseFloat(u):u}return Df(l)?hx(l,e,i+1):l}function y0(n){const e=[{},{}];return n==null||n.values.forEach((i,a)=>{e[0][a]=i.get(),e[1][a]=i.getVelocity()}),e}function $f(n,e,i,a){if(typeof e=="function"){const[l,c]=y0(a);e=e(i!==void 0?i:n.custom,l,c)}if(typeof e=="string"&&(e=n.variants&&n.variants[e]),typeof e=="function"){const[l,c]=y0(a);e=e(i!==void 0?i:n.custom,l,c)}return e}function Vi(n,e,i){const a=n.getProps();return $f(a,e,i!==void 0?i:a.custom,n)}const dx=new Set(["width","height","top","left","right","bottom",...Xa]),Xd=n=>Array.isArray(n);function YT(n,e,i){n.hasValue(e)?n.getValue(e).set(i):n.addValue(e,Ga(i))}function XT(n){return Xd(n)?n[n.length-1]||0:n}function JT(n,e){const i=Vi(n,e);let{transitionEnd:a={},transition:l={},...c}=i||{};c={...c,...a};for(const u in c){const d=XT(c[u]);YT(n,u,d)}}const Ut=n=>!!(n&&n.getVelocity);function QT(n){return!!(Ut(n)&&n.add)}function Jd(n,e){const i=n.getValue("willChange");if(QT(i))return i.add(e);if(!i&&oi.WillChange){const a=new oi.WillChange("auto");n.addValue("willChange",a),a.add(e)}}function qf(n){return n.replace(/([A-Z])/g,e=>`-${e.toLowerCase()}`)}const ZT="framerAppearId",fx="data-"+qf(ZT);function mx(n){return n.props[fx]}function WT({protectedKeys:n,needsAnimating:e},i){const a=n.hasOwnProperty(i)&&e[i]!==!0;return e[i]=!1,a}function px(n,e,{delay:i=0,transitionOverride:a,type:l}={}){let{transition:c,transitionEnd:u,...d}=e;const m=n.getDefaultTransition();c=c?ux(c,m):m;const p=c==null?void 0:c.reduceMotion,y=c==null?void 0:c.skipAnimations;a&&(c=a);const v=[],x=l&&n.animationState&&n.animationState.getState()[l],_=c==null?void 0:c.path;_&&_.animateVisualElement(n,d,c,i,v);for(const S in d){const T=n.getValue(S,n.latestValues[S]??null),k=d[S];if(k===void 0||x&&WT(x,S))continue;const R={delay:i,...Vf(c||{},S)};y&&(R.skipAnimations=!0);const M=T.get();if(M!==void 0&&!T.isAnimating()&&!Array.isArray(k)&&k===M&&!R.velocity){Xe.update(()=>T.set(k));continue}let N=!1;if(window.MotionHandoffAnimation){const X=mx(n);if(X){const q=window.MotionHandoffAnimation(X,S,Xe);q!==null&&(R.startTime=q,N=!0)}}Jd(n,S);const P=p??n.shouldReduceMotion;T.start(Hf(S,T,k,P&&dx.has(S)?{type:!1}:R,n,N));const G=T.animation;G&&v.push(G)}if(u){const S=()=>Xe.update(()=>{u&&JT(n,u)});v.length?Promise.all(v).then(S):S()}return v}function Qd(n,e,i={}){var m;const a=Vi(n,e,i.type==="exit"?(m=n.presenceContext)==null?void 0:m.custom:void 0);let{transition:l=n.getDefaultTransition()||{}}=a||{};i.transitionOverride&&(l=i.transitionOverride);const c=a?()=>Promise.all(px(n,a,i)):()=>Promise.resolve(),u=n.variantChildren&&n.variantChildren.size?(p=0)=>{const{delayChildren:y=0,staggerChildren:v,staggerDirection:x}=l;return eE(n,e,p,y,v,x,i)}:()=>Promise.resolve(),{when:d}=l;if(d){const[p,y]=d==="beforeChildren"?[c,u]:[u,c];return p().then(()=>y())}else return Promise.all([c(),u(i.delay)])}function eE(n,e,i=0,a=0,l=0,c=1,u){const d=[];for(const m of n.variantChildren)m.notify("AnimationStart",e),d.push(Qd(m,e,{...u,delay:i+(typeof a=="function"?0:a)+cx(n.variantChildren,m,a,l,c)}).then(()=>m.notify("AnimationComplete",e)));return Promise.all(d)}function tE(n,e,i={}){n.notify("AnimationStart",e);let a;if(Array.isArray(e)){const l=e.map(c=>Qd(n,c,i));a=Promise.all(l)}else if(typeof e=="string")a=Qd(n,e,i);else{const l=typeof e=="function"?Vi(n,e,i.custom):e;a=Promise.all(px(n,l,i))}return a.then(()=>{n.notify("AnimationComplete",e)})}const nE={test:n=>n==="auto",parse:n=>n},gx=n=>e=>e.test(n),yx=[Ya,ce,es,As,N1,j1,nE],b0=n=>yx.find(gx(n));function sE(n){return typeof n=="number"?n===0:n!==null?n==="none"||n==="0"||Nv(n):!0}const iE=new Set(["brightness","contrast","saturate","opacity"]);function aE(n){const[e,i]=n.slice(0,-1).split("(");if(e==="drop-shadow")return n;const[a]=i.match(Mf)||[];if(!a)return n;const l=i.replace(a,"");let c=iE.has(e)?1:0;return a!==i&&(c*=100),e+"("+c+l+")"}const rE=/\b([a-z-]*)\(.*?\)/gu,Zd={...$n,getAnimatableNone:n=>{const e=n.match(rE);return e?e.map(aE).join(" "):n}},Wd={...$n,getAnimatableNone:n=>{const e=$n.parse(n);return $n.createTransformer(n)(e.map(a=>typeof a=="number"?0:typeof a=="object"?{...a,alpha:1}:a))}},v0={...Ya,transform:Math.round},oE={rotate:As,pathRotation:As,rotateX:As,rotateY:As,rotateZ:As,scale:Xl,scaleX:Xl,scaleY:Xl,scaleZ:Xl,skew:As,skewX:As,skewY:As,distance:ce,translateX:ce,translateY:ce,translateZ:ce,x:ce,y:ce,z:ce,perspective:ce,transformPerspective:ce,opacity:co,originX:a0,originY:a0,originZ:ce},Tc={borderWidth:ce,borderTopWidth:ce,borderRightWidth:ce,borderBottomWidth:ce,borderLeftWidth:ce,borderRadius:ce,borderTopLeftRadius:ce,borderTopRightRadius:ce,borderBottomRightRadius:ce,borderBottomLeftRadius:ce,width:ce,maxWidth:ce,height:ce,maxHeight:ce,top:ce,right:ce,bottom:ce,left:ce,inset:ce,insetBlock:ce,insetBlockStart:ce,insetBlockEnd:ce,insetInline:ce,insetInlineStart:ce,insetInlineEnd:ce,padding:ce,paddingTop:ce,paddingRight:ce,paddingBottom:ce,paddingLeft:ce,paddingBlock:ce,paddingBlockStart:ce,paddingBlockEnd:ce,paddingInline:ce,paddingInlineStart:ce,paddingInlineEnd:ce,margin:ce,marginTop:ce,marginRight:ce,marginBottom:ce,marginLeft:ce,marginBlock:ce,marginBlockStart:ce,marginBlockEnd:ce,marginInline:ce,marginInlineStart:ce,marginInlineEnd:ce,fontSize:ce,backgroundPositionX:ce,backgroundPositionY:ce,...oE,zIndex:v0,fillOpacity:co,strokeOpacity:co,numOctaves:v0},lE={...Tc,color:vt,backgroundColor:vt,outlineColor:vt,fill:vt,stroke:vt,borderColor:vt,borderTopColor:vt,borderRightColor:vt,borderBottomColor:vt,borderLeftColor:vt,filter:Zd,WebkitFilter:Zd,mask:Wd,WebkitMask:Wd},bx=n=>lE[n],cE=new Set([Zd,Wd]);function vx(n,e){let i=bx(n);return cE.has(i)||(i=$n),i.getAnimatableNone?i.getAnimatableNone(e):void 0}const uE=new Set(["auto","none","0"]);function hE(n,e,i){let a=0,l;for(;a{e.getValue(m).set(p)}),this.resolveNoneKeyframes()}}const Ff=["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"];function xx(n,e,i){if(n==null)return[];if(n instanceof EventTarget)return[n];if(typeof n=="string"){let a=document;const l=(i==null?void 0:i[n])??a.querySelectorAll(n);return l?Array.from(l):[]}return Array.from(n).filter(a=>a!=null)}const ef=(n,e)=>e&&typeof n=="number"?e.transform(n):n;function hc(n){return jv(n)&&"offsetHeight"in n&&!("ownerSVGElement"in n)}const{schedule:If}=Hv(queueMicrotask,!1),Ln={x:!1,y:!1};function wx(){return Ln.x||Ln.y}function fE(n){return n==="x"||n==="y"?Ln[n]?null:(Ln[n]=!0,()=>{Ln[n]=!1}):Ln.x||Ln.y?null:(Ln.x=Ln.y=!0,()=>{Ln.x=Ln.y=!1})}function _x(n,e){const i=xx(n),a=new AbortController,l={passive:!0,...e,signal:a.signal};return[i,l,()=>a.abort()]}function mE(n){return!(n.pointerType==="touch"||wx())}function pE(n,e,i={}){const[a,l,c]=_x(n,i);return a.forEach(u=>{let d=!1,m=!1,p;const y=()=>{u.removeEventListener("pointerleave",S)},v=k=>{p&&(p(k),p=void 0),y()},x=k=>{d=!1,window.removeEventListener("pointerup",x),window.removeEventListener("pointercancel",x),m&&(m=!1,v(k))},_=()=>{d=!0,window.addEventListener("pointerup",x,l),window.addEventListener("pointercancel",x,l)},S=k=>{if(k.pointerType!=="touch"){if(d){m=!0;return}v(k)}},T=k=>{if(!mE(k))return;m=!1;const R=e(u,k);typeof R=="function"&&(p=R,u.addEventListener("pointerleave",S,l))};u.addEventListener("pointerenter",T,l),u.addEventListener("pointerdown",_,l)}),c}const Sx=(n,e)=>e?n===e?!0:Sx(n,e.parentElement):!1,Gf=n=>n.pointerType==="mouse"?typeof n.button!="number"||n.button<=0:n.isPrimary!==!1,gE=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);function yE(n){return gE.has(n.tagName)||n.isContentEditable===!0}const bE=new Set(["INPUT","SELECT","TEXTAREA"]);function vE(n){return bE.has(n.tagName)||n.isContentEditable===!0}const dc=new WeakSet;function x0(n){return e=>{e.key==="Enter"&&n(e)}}function pd(n,e){n.dispatchEvent(new PointerEvent("pointer"+e,{isPrimary:!0,bubbles:!0}))}const xE=(n,e)=>{const i=n.currentTarget;if(!i)return;const a=x0(()=>{if(dc.has(i))return;pd(i,"down");const l=x0(()=>{pd(i,"up")}),c=()=>pd(i,"cancel");i.addEventListener("keyup",l,e),i.addEventListener("blur",c,e)});i.addEventListener("keydown",a,e),i.addEventListener("blur",()=>i.removeEventListener("keydown",a),e)};function w0(n){return Gf(n)&&!wx()}const _0=new WeakSet;function wE(n,e,i={}){const[a,l,c]=_x(n,i),u=d=>{const m=d.currentTarget;if(!w0(d)||_0.has(d))return;dc.add(m),i.stopPropagation&&_0.add(d);const p=e(m,d),y={...l,capture:!0},v=(S,T)=>{window.removeEventListener("pointerup",x,y),window.removeEventListener("pointercancel",_,y),dc.has(m)&&dc.delete(m),w0(S)&&typeof p=="function"&&p(S,{success:T})},x=S=>{v(S,m===window||m===document||i.useGlobalTarget||Sx(m,S.target))},_=S=>{v(S,!1)};window.addEventListener("pointerup",x,y),window.addEventListener("pointercancel",_,y)};return a.forEach(d=>{(i.useGlobalTarget?window:d).addEventListener("pointerdown",u,l),hc(d)&&(d.addEventListener("focus",p=>xE(p,l)),!yE(d)&&!d.hasAttribute("tabindex")&&(d.tabIndex=0))}),c}function Kf(n){return jv(n)&&"ownerSVGElement"in n}const fc=new WeakMap;let ni;const Tx=(n,e,i)=>(a,l)=>l&&l[0]?l[0][n+"Size"]:Kf(a)&&"getBBox"in a?a.getBBox()[e]:a[i],_E=Tx("inline","width","offsetWidth"),SE=Tx("block","height","offsetHeight");function TE({target:n,borderBoxSize:e}){var i;(i=fc.get(n))==null||i.forEach(a=>{a(n,{get width(){return _E(n,e)},get height(){return SE(n,e)}})})}function EE(n){n.forEach(TE)}function AE(){typeof ResizeObserver>"u"||(ni=new ResizeObserver(EE))}function jE(n,e){ni||AE();const i=xx(n);return i.forEach(a=>{let l=fc.get(a);l||(l=new Set,fc.set(a,l)),l.add(e),ni==null||ni.observe(a)}),()=>{i.forEach(a=>{const l=fc.get(a);l==null||l.delete(e),l!=null&&l.size||ni==null||ni.unobserve(a)})}}const mc=new Set;let za;function NE(){za=()=>{const n={get width(){return window.innerWidth},get height(){return window.innerHeight}};mc.forEach(e=>e(n))},window.addEventListener("resize",za)}function kE(n){return mc.add(n),za||NE(),()=>{mc.delete(n),!mc.size&&typeof za=="function"&&(window.removeEventListener("resize",za),za=void 0)}}function S0(n,e){return typeof n=="function"?kE(n):jE(n,e)}function RE(n){return Kf(n)&&n.tagName==="svg"}const CE=[...yx,vt,$n],OE=n=>CE.find(gx(n)),T0=()=>({translate:0,scale:1,origin:0,originPoint:0}),Pa=()=>({x:T0(),y:T0()}),E0=()=>({min:0,max:0}),St=()=>({x:E0(),y:E0()}),DE=new WeakMap;function Hc(n){return n!==null&&typeof n=="object"&&typeof n.start=="function"}function uo(n){return typeof n=="string"||Array.isArray(n)}const Yf=["animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"],Xf=["initial",...Yf];function $c(n){return Hc(n.animate)||Xf.some(e=>uo(n[e]))}function Ex(n){return!!($c(n)||n.variants)}function ME(n,e,i){for(const a in e){const l=e[a],c=i[a];if(Ut(l))n.addValue(a,l);else if(Ut(c))n.addValue(a,Ga(l,{owner:n}));else if(c!==l)if(n.hasValue(a)){const u=n.getValue(a);u.liveStyle===!0?u.jump(l):u.hasAnimated||u.set(l)}else{const u=n.getStaticValue(a);n.addValue(a,Ga(u!==void 0?u:l,{owner:n}))}}for(const a in i)e[a]===void 0&&n.removeValue(a);return e}const tf={current:null},Ax={current:!1},BE=typeof window<"u";function UE(){if(Ax.current=!0,!!BE)if(window.matchMedia){const n=window.matchMedia("(prefers-reduced-motion)"),e=()=>tf.current=n.matches;n.addEventListener("change",e),e()}else tf.current=!1}const A0=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];let Ec={};function jx(n){Ec=n}function LE(){return Ec}class zE{scrapeMotionValuesFromProps(e,i,a){return{}}constructor({parent:e,props:i,presenceContext:a,reducedMotionConfig:l,skipAnimations:c,blockInitialAnimation:u,visualState:d},m={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.shouldSkipAnimations=!1,this.values=new Map,this.KeyframeResolver=Pf,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.hasBeenMounted=!1,this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const _=Ft.now();this.renderScheduledAt<_&&(this.renderScheduledAt=_,Xe.render(this.render,!1,!0))};const{latestValues:p,renderState:y}=d;this.latestValues=p,this.baseTarget={...p},this.initialValues=i.initial?{...p}:{},this.renderState=y,this.parent=e,this.props=i,this.presenceContext=a,this.depth=e?e.depth+1:0,this.reducedMotionConfig=l,this.skipAnimationsConfig=c,this.options=m,this.blockInitialAnimation=!!u,this.isControllingVariants=$c(i),this.isVariantNode=Ex(i),this.isVariantNode&&(this.variantChildren=new Set),this.manuallyAnimateOnMount=!!(e&&e.current);const{willChange:v,...x}=this.scrapeMotionValuesFromProps(i,{},this);for(const _ in x){const S=x[_];p[_]!==void 0&&Ut(S)&&S.set(p[_])}}mount(e){var i,a;if(this.hasBeenMounted)for(const l in this.initialValues)(i=this.values.get(l))==null||i.jump(this.initialValues[l]),this.latestValues[l]=this.initialValues[l];this.current=e,DE.set(e,this),this.projection&&!this.projection.instance&&this.projection.mount(e),this.parent&&this.isVariantNode&&!this.isControllingVariants&&(this.removeFromVariantTree=this.parent.addVariantChild(this)),this.values.forEach((l,c)=>this.bindToMotionValue(c,l)),this.reducedMotionConfig==="never"?this.shouldReduceMotion=!1:this.reducedMotionConfig==="always"?this.shouldReduceMotion=!0:(Ax.current||UE(),this.shouldReduceMotion=tf.current),this.shouldSkipAnimations=this.skipAnimationsConfig??!1,(a=this.parent)==null||a.addChild(this),this.update(this.props,this.presenceContext),this.hasBeenMounted=!0}unmount(){var e;this.projection&&this.projection.unmount(),li(this.notifyUpdate),li(this.render),this.valueSubscriptions.forEach(i=>i()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),(e=this.parent)==null||e.removeChild(this);for(const i in this.events)this.events[i].clear();for(const i in this.features){const a=this.features[i];a&&(a.unmount(),a.isMounted=!1)}this.current=null}addChild(e){this.children.add(e),this.enteringChildren??(this.enteringChildren=new Set),this.enteringChildren.add(e)}removeChild(e){this.children.delete(e),this.enteringChildren&&this.enteringChildren.delete(e)}bindToMotionValue(e,i){if(this.valueSubscriptions.has(e)&&this.valueSubscriptions.get(e)(),i.accelerate&&lx.has(e)&&this.current instanceof HTMLElement){const{factory:u,keyframes:d,times:m,ease:p,duration:y}=i.accelerate,v=new rx({element:this.current,name:e,keyframes:d,times:m,ease:p,duration:dn(y)}),x=u(v);this.valueSubscriptions.set(e,()=>{x(),v.cancel()});return}const a=Ja.has(e);a&&this.onBindTransform&&this.onBindTransform();const l=i.on("change",u=>{this.latestValues[e]=u,this.props.onUpdate&&Xe.preRender(this.notifyUpdate),a&&this.projection&&(this.projection.isTransformDirty=!0),this.scheduleRender()});let c;typeof window<"u"&&window.MotionCheckAppearSync&&(c=window.MotionCheckAppearSync(this,e,i)),this.valueSubscriptions.set(e,()=>{l(),c&&c()})}sortNodePosition(e){return!this.current||!this.sortInstanceNodePosition||this.type!==e.type?0:this.sortInstanceNodePosition(this.current,e.current)}updateFeatures(){let e="animation";for(e in Ec){const i=Ec[e];if(!i)continue;const{isEnabled:a,Feature:l}=i;if(!this.features[e]&&l&&a(this.props)&&(this.features[e]=new l(this)),this.features[e]){const c=this.features[e];c.isMounted?c.update():(c.mount(),c.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):St()}getStaticValue(e){return this.latestValues[e]}setStaticValue(e,i){this.latestValues[e]=i}update(e,i){(e.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=e,this.prevPresenceContext=this.presenceContext,this.presenceContext=i;for(let a=0;ai.variantChildren.delete(e)}addValue(e,i){const a=this.values.get(e);i!==a&&(a&&this.removeValue(e),this.bindToMotionValue(e,i),this.values.set(e,i),this.latestValues[e]=i.get())}removeValue(e){this.values.delete(e);const i=this.valueSubscriptions.get(e);i&&(i(),this.valueSubscriptions.delete(e)),delete this.latestValues[e],this.removeValueFromRenderState(e,this.renderState)}hasValue(e){return this.values.has(e)}getValue(e,i){if(this.props.values&&this.props.values[e])return this.props.values[e];let a=this.values.get(e);return a===void 0&&i!==void 0&&(a=Ga(i===null?void 0:i,{owner:this}),this.addValue(e,a)),a}readValue(e,i){let a=this.latestValues[e]!==void 0||!this.current?this.latestValues[e]:this.getBaseTargetFromProps(this.props,e)??this.readValueFromInstance(this.current,e,this.options);return a!=null&&(typeof a=="string"&&(Av(a)||Nv(a))?a=parseFloat(a):!OE(a)&&$n.test(i)&&(a=vx(e,i)),this.setBaseTarget(e,Ut(a)?a.get():a)),Ut(a)?a.get():a}setBaseTarget(e,i){this.baseTarget[e]=i}getBaseTarget(e){var c;const{initial:i}=this.props;let a;if(typeof i=="string"||typeof i=="object"){const u=$f(this.props,i,(c=this.presenceContext)==null?void 0:c.custom);u&&(a=u[e])}if(i&&a!==void 0)return a;const l=this.getBaseTargetFromProps(this.props,e);return l!==void 0&&!Ut(l)?l:this.initialValues[e]!==void 0&&a===void 0?void 0:this.baseTarget[e]}on(e,i){return this.events[e]||(this.events[e]=new Rf),this.events[e].add(i)}notify(e,...i){this.events[e]&&this.events[e].notify(...i)}scheduleRenderMicrotask(){If.render(this.render)}}class Nx extends zE{constructor(){super(...arguments),this.KeyframeResolver=dE}sortInstanceNodePosition(e,i){return e.compareDocumentPosition(i)&2?1:-1}getBaseTargetFromProps(e,i){const a=e.style;return a?a[i]:void 0}removeValueFromRenderState(e,{vars:i,style:a}){delete i[e],delete a[e]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:e}=this.props;Ut(e)&&(this.childSubscription=e.on("change",i=>{this.current&&(this.current.textContent=`${i}`)}))}}class ci{constructor(e){this.isMounted=!1,this.node=e}update(){}}function kx({top:n,left:e,right:i,bottom:a}){return{x:{min:e,max:i},y:{min:n,max:a}}}function PE({x:n,y:e}){return{top:e.min,right:n.max,bottom:e.max,left:n.min}}function VE(n,e){if(!e)return n;const i=e({x:n.left,y:n.top}),a=e({x:n.right,y:n.bottom});return{top:i.y,left:i.x,bottom:a.y,right:a.x}}function gd(n){return n===void 0||n===1}function nf({scale:n,scaleX:e,scaleY:i}){return!gd(n)||!gd(e)||!gd(i)}function Mi(n){return nf(n)||Rx(n)||n.z||n.rotate||n.rotateX||n.rotateY||n.skewX||n.skewY}function Rx(n){return j0(n.x)||j0(n.y)}function j0(n){return n&&n!=="0%"}function Ac(n,e,i){const a=n-i,l=e*a;return i+l}function N0(n,e,i,a,l){return l!==void 0&&(n=Ac(n,l,a)),Ac(n,i,a)+e}function sf(n,e=0,i=1,a,l){n.min=N0(n.min,e,i,a,l),n.max=N0(n.max,e,i,a,l)}function Cx(n,{x:e,y:i}){sf(n.x,e.translate,e.scale,e.originPoint),sf(n.y,i.translate,i.scale,i.originPoint)}const k0=.999999999999,R0=1.0000000000001;function HE(n,e,i,a=!1){var d;const l=i.length;if(!l)return;e.x=e.y=1;let c,u;for(let m=0;mk0&&(e.x=1),e.yk0&&(e.y=1)}function Zn(n,e){n.min+=e,n.max+=e}function C0(n,e,i,a,l=.5){const c=Ye(n.min,n.max,l);sf(n,e,i,c,a)}function O0(n,e){return typeof n=="string"?parseFloat(n)/100*(e.max-e.min):n}function pc(n,e,i){const a=i??n;C0(n.x,O0(e.x,a.x),e.scaleX,e.scale,e.originX),C0(n.y,O0(e.y,a.y),e.scaleY,e.scale,e.originY)}function Ox(n,e){return kx(VE(n.getBoundingClientRect(),e))}function $E(n,e,i){const a=Ox(n,i),{scroll:l}=e;return l&&(Zn(a.x,l.offset.x),Zn(a.y,l.offset.y)),a}const qE={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},FE=Xa.length;function IE(n,e,i){let a="",l=!0;for(let u=0;u{if(!e.target)return n;if(typeof n=="string")if(ce.test(n))n=parseFloat(n);else return n;const i=D0(n,e.target.x),a=D0(n,e.target.y);return`${i}% ${a}%`}},GE={correct:(n,{treeScale:e,projectionDelta:i})=>{const a=n,l=$n.parse(n);if(l.length>5)return a;const c=$n.createTransformer(n),u=typeof l[0]!="number"?1:0,d=i.x.scale*e.x,m=i.y.scale*e.y;l[0+u]/=d,l[1+u]/=m;const p=Ye(d,m,.5);return typeof l[2+u]=="number"&&(l[2+u]/=p),typeof l[3+u]=="number"&&(l[3+u]/=p),c(l)}},af={borderRadius:{...Ir,applyTo:[...Ff]},borderTopLeftRadius:Ir,borderTopRightRadius:Ir,borderBottomLeftRadius:Ir,borderBottomRightRadius:Ir,boxShadow:GE};function Mx(n,{layout:e,layoutId:i}){return Ja.has(n)||n.startsWith("origin")||(e||i!==void 0)&&(!!af[n]||n==="opacity")}function Qf(n,e,i){var u;const a=n.style,l=e==null?void 0:e.style,c={};if(!a)return c;for(const d in a)(Ut(a[d])||l&&Ut(l[d])||Mx(d,n)||((u=i==null?void 0:i.getValue(d))==null?void 0:u.liveStyle)!==void 0)&&(c[d]=a[d]);return c}function KE(n){return window.getComputedStyle(n)}class YE extends Nx{constructor(){super(...arguments),this.type="html",this.renderInstance=Dx}readValueFromInstance(e,i){var a;if(Ja.has(i))return(a=this.projection)!=null&&a.isProjecting?qd(i):mT(e,i);{const l=KE(e),c=(qv(i)?l.getPropertyValue(i):l[i])||0;return typeof c=="string"?c.trim():c}}measureInstanceViewportBox(e,{transformPagePoint:i}){return Ox(e,i)}build(e,i,a){Jf(e,i,a.transformTemplate)}scrapeMotionValuesFromProps(e,i,a){return Qf(e,i,a)}}const XE={offset:"stroke-dashoffset",array:"stroke-dasharray"},JE={offset:"strokeDashoffset",array:"strokeDasharray"};function QE(n,e,i=1,a=0,l=!0){n.pathLength=1;const c=l?XE:JE;n[c.offset]=`${-a}`,n[c.array]=`${e} ${i}`}const ZE=["offsetDistance","offsetPath","offsetRotate","offsetAnchor"];function Bx(n,{attrX:e,attrY:i,attrScale:a,pathLength:l,pathSpacing:c=1,pathOffset:u=0,...d},m,p,y){if(Jf(n,d,p),m){n.style.viewBox&&(n.attrs.viewBox=n.style.viewBox);return}n.attrs=n.style,n.style={};const{attrs:v,style:x}=n;v.transform&&(x.transform=v.transform,delete v.transform),(x.transform||v.transformOrigin)&&(x.transformOrigin=v.transformOrigin??"50% 50%",delete v.transformOrigin),x.transform&&(x.transformBox=(y==null?void 0:y.transformBox)??"fill-box",delete v.transformBox);for(const _ of ZE)v[_]!==void 0&&(x[_]=v[_],delete v[_]);e!==void 0&&(v.x=e),i!==void 0&&(v.y=i),a!==void 0&&(v.scale=a),l!==void 0&&QE(v,l,c,u,!1)}const Ux=new Set(["baseFrequency","diffuseConstant","kernelMatrix","kernelUnitLength","keySplines","keyTimes","limitingConeAngle","markerHeight","markerWidth","numOctaves","targetX","targetY","surfaceScale","specularConstant","specularExponent","stdDeviation","tableValues","viewBox","gradientTransform","pathLength","startOffset","textLength","lengthAdjust"]),Lx=n=>typeof n=="string"&&n.toLowerCase()==="svg";function WE(n,e,i,a){Dx(n,e,void 0,a);for(const l in e.attrs)n.setAttribute(Ux.has(l)?l:qf(l),e.attrs[l])}function zx(n,e,i){const a=Qf(n,e,i);for(const l in n)if(Ut(n[l])||Ut(e[l])){const c=Xa.indexOf(l)!==-1?"attr"+l.charAt(0).toUpperCase()+l.substring(1):l;a[c]=n[l]}return a}class eA extends Nx{constructor(){super(...arguments),this.type="svg",this.isSVGTag=!1,this.measureInstanceViewportBox=St}getBaseTargetFromProps(e,i){return e[i]}readValueFromInstance(e,i){if(Ja.has(i)){const a=bx(i);return a&&a.default||0}return i=Ux.has(i)?i:qf(i),e.getAttribute(i)}scrapeMotionValuesFromProps(e,i,a){return zx(e,i,a)}build(e,i,a){Bx(e,i,this.isSVGTag,a.transformTemplate,a.style)}renderInstance(e,i,a,l){WE(e,i,a,l)}mount(e){this.isSVGTag=Lx(e.tagName),super.mount(e)}}const tA=Xf.length;function Px(n){if(!n)return;if(!n.isControllingVariants){const i=n.parent?Px(n.parent)||{}:{};return n.props.initial!==void 0&&(i.initial=n.props.initial),i}const e={};for(let i=0;iPromise.all(e.map(({animation:i,options:a})=>tE(n,i,a)))}function aA(n){let e=iA(n),i=M0(),a=!0,l=!1;const c=p=>(y,v)=>{var _;const x=Vi(n,v,p==="exit"?(_=n.presenceContext)==null?void 0:_.custom:void 0);if(x){const{transition:S,transitionEnd:T,...k}=x;y={...y,...k,...T}}return y};function u(p){e=p(n)}function d(p){const{props:y}=n,v=Px(n.parent)||{},x=[],_=new Set;let S={},T=1/0;for(let R=0;RT&&G,J=!1;const he=Array.isArray(P)?P:[P];let fe=he.reduce(c(M),{});X===!1&&(fe={});const{prevResolvedValues:Ae={}}=N,ve={...Ae,...fe},Ce=Z=>{se=!0,_.has(Z)&&(J=!0,_.delete(Z)),N.needsAnimating[Z]=!0;const pe=n.getValue(Z);pe&&(pe.liveStyle=!1)};for(const Z in ve){const pe=fe[Z],ie=Ae[Z];if(S.hasOwnProperty(Z))continue;let E=!1;Xd(pe)&&Xd(ie)?E=!Vx(pe,ie)||Y:E=pe!==ie,E?pe!=null?Ce(Z):_.add(Z):pe!==void 0&&_.has(Z)?Ce(Z):N.protectedKeys[Z]=!0}N.prevProp=P,N.prevResolvedValues=fe,N.isActive&&(S={...S,...fe}),(a||l)&&n.blockInitialAnimation&&(se=!1);const L=q&&Y;se&&(!L||J)&&x.push(...he.map(Z=>{const pe={type:M};if(typeof Z=="string"&&(a||l)&&!L&&n.manuallyAnimateOnMount&&n.parent){const{parent:ie}=n,E=Vi(ie,Z);if(ie.enteringChildren&&E){const{delayChildren:H}=E.transition||{};pe.delay=cx(ie.enteringChildren,n,H)}}return{animation:Z,options:pe}}))}if(_.size){const R={};if(typeof y.initial!="boolean"){const M=Vi(n,Array.isArray(y.initial)?y.initial[0]:y.initial);M&&M.transition&&(R.transition=M.transition)}_.forEach(M=>{const N=n.getBaseTarget(M),P=n.getValue(M);P&&(P.liveStyle=!0),R[M]=N??null}),x.push({animation:R})}let k=!!x.length;return a&&(y.initial===!1||y.initial===y.animate)&&!n.manuallyAnimateOnMount&&(k=!1),a=!1,l=!1,k?e(x):Promise.resolve()}function m(p,y){var x;if(i[p].isActive===y)return Promise.resolve();(x=n.variantChildren)==null||x.forEach(_=>{var S;return(S=_.animationState)==null?void 0:S.setActive(p,y)}),i[p].isActive=y;const v=d(p);for(const _ in i)i[_].protectedKeys={};return v}return{animateChanges:d,setActive:m,setAnimateFunction:u,getState:()=>i,reset:()=>{i=M0(),l=!0}}}function rA(n,e){return typeof e=="string"?e!==n:Array.isArray(e)?!Vx(e,n):!1}function Ci(n=!1){return{isActive:n,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function M0(){return{animate:Ci(!0),whileInView:Ci(),whileHover:Ci(),whileTap:Ci(),whileDrag:Ci(),whileFocus:Ci(),exit:Ci()}}function rf(n,e){n.min=e.min,n.max=e.max}function Bn(n,e){rf(n.x,e.x),rf(n.y,e.y)}function B0(n,e){n.translate=e.translate,n.scale=e.scale,n.originPoint=e.originPoint,n.origin=e.origin}const Hx=1e-4,oA=1-Hx,lA=1+Hx,$x=.01,cA=0-$x,uA=0+$x;function It(n){return n.max-n.min}function hA(n,e,i){return Math.abs(n-e)<=i}function U0(n,e,i,a=.5){n.origin=a,n.originPoint=Ye(e.min,e.max,n.origin),n.scale=It(i)/It(e),n.translate=Ye(i.min,i.max,n.origin)-n.originPoint,(n.scale>=oA&&n.scale<=lA||isNaN(n.scale))&&(n.scale=1),(n.translate>=cA&&n.translate<=uA||isNaN(n.translate))&&(n.translate=0)}function no(n,e,i,a){U0(n.x,e.x,i.x,a?a.originX:void 0),U0(n.y,e.y,i.y,a?a.originY:void 0)}function L0(n,e,i,a=0){const l=a?Ye(i.min,i.max,a):i.min;n.min=l+e.min,n.max=n.min+It(e)}function dA(n,e,i,a){L0(n.x,e.x,i.x,a==null?void 0:a.x),L0(n.y,e.y,i.y,a==null?void 0:a.y)}function z0(n,e,i,a=0){const l=a?Ye(i.min,i.max,a):i.min;n.min=e.min-l,n.max=n.min+It(e)}function jc(n,e,i,a){z0(n.x,e.x,i.x,a==null?void 0:a.x),z0(n.y,e.y,i.y,a==null?void 0:a.y)}function P0(n,e,i,a,l){return n-=e,n=Ac(n,1/i,a),l!==void 0&&(n=Ac(n,1/l,a)),n}function fA(n,e=0,i=1,a=.5,l,c=n,u=n){if(es.test(e)&&(e=parseFloat(e),e=Ye(u.min,u.max,e/100)-u.min),typeof e!="number")return;let d=Ye(c.min,c.max,a);n===c&&(d-=e),n.min=P0(n.min,e,i,d,l),n.max=P0(n.max,e,i,d,l)}function V0(n,e,[i,a,l],c,u){fA(n,e[i],e[a],e[l],e.scale,c,u)}const mA=["x","scaleX","originX"],pA=["y","scaleY","originY"];function H0(n,e,i,a){V0(n.x,e,mA,i?i.x:void 0,a?a.x:void 0),V0(n.y,e,pA,i?i.y:void 0,a?a.y:void 0)}function $0(n){return n.translate===0&&n.scale===1}function qx(n){return $0(n.x)&&$0(n.y)}function q0(n,e){return n.min===e.min&&n.max===e.max}function gA(n,e){return q0(n.x,e.x)&&q0(n.y,e.y)}function F0(n,e){return Math.round(n.min)===Math.round(e.min)&&Math.round(n.max)===Math.round(e.max)}function Fx(n,e){return F0(n.x,e.x)&&F0(n.y,e.y)}function I0(n){return It(n.x)/It(n.y)}function G0(n,e){return n.translate===e.translate&&n.scale===e.scale&&n.originPoint===e.originPoint}function Jn(n){return[n("x"),n("y")]}function yA(n,e,i){let a="";const l=n.x.translate/e.x,c=n.y.translate/e.y,u=(i==null?void 0:i.z)||0;if((l||c||u)&&(a=`translate3d(${l}px, ${c}px, ${u}px) `),(e.x!==1||e.y!==1)&&(a+=`scale(${1/e.x}, ${1/e.y}) `),i){const{transformPerspective:p,rotate:y,pathRotation:v,rotateX:x,rotateY:_,skewX:S,skewY:T}=i;p&&(a=`perspective(${p}px) ${a}`),y&&(a+=`rotate(${y}deg) `),v&&(a+=`rotate(${v}deg) `),x&&(a+=`rotateX(${x}deg) `),_&&(a+=`rotateY(${_}deg) `),S&&(a+=`skewX(${S}deg) `),T&&(a+=`skewY(${T}deg) `)}const d=n.x.scale*e.x,m=n.y.scale*e.y;return(d!==1||m!==1)&&(a+=`scale(${d}, ${m})`),a||"none"}const bA=Ff.length,K0=n=>typeof n=="string"?parseFloat(n):n,Y0=n=>typeof n=="number"||ce.test(n);function vA(n,e,i,a,l,c){l?(n.opacity=Ye(0,i.opacity??1,xA(a)),n.opacityExit=Ye(e.opacity??1,0,wA(a))):c&&(n.opacity=Ye(e.opacity??1,i.opacity??1,a));for(let u=0;uae?1:i(lo(n,e,a))}function _A(n,e,i){const a=Ut(n)?n:Ga(n);return a.start(Hf("",a,e,i)),a.animation}function ho(n,e,i,a={passive:!0}){return n.addEventListener(e,i,a),()=>n.removeEventListener(e,i,a)}const SA=(n,e)=>n.depth-e.depth;class TA{constructor(){this.children=[],this.isDirty=!1}add(e){Nf(this.children,e),this.isDirty=!0}remove(e){vc(this.children,e),this.isDirty=!0}forEach(e){this.isDirty&&this.children.sort(SA),this.isDirty=!1,this.children.forEach(e)}}function EA(n,e){const i=Ft.now(),a=({timestamp:l})=>{const c=l-i;c>=e&&(li(a),n(c-e))};return Xe.setup(a,!0),()=>li(a)}function gc(n){return Ut(n)?n.get():n}class AA{constructor(){this.members=[]}add(e){Nf(this.members,e);for(let i=this.members.length-1;i>=0;i--){const a=this.members[i];if(a===e||a===this.lead||a===this.prevLead)continue;const l=a.instance;(!l||l.isConnected===!1)&&!a.snapshot&&(vc(this.members,a),a.unmount())}e.scheduleRender()}remove(e){if(vc(this.members,e),e===this.prevLead&&(this.prevLead=void 0),e===this.lead){const i=this.members[this.members.length-1];i&&this.promote(i)}}relegate(e){var i;for(let a=this.members.indexOf(e)-1;a>=0;a--){const l=this.members[a];if(l.isPresent!==!1&&((i=l.instance)==null?void 0:i.isConnected)!==!1)return this.promote(l),!0}return!1}promote(e,i){var l;const a=this.lead;if(e!==a&&(this.prevLead=a,this.lead=e,e.show(),a)){a.updateSnapshot(),e.scheduleRender();const{layoutDependency:c}=a.options,{layoutDependency:u}=e.options;(c===void 0||c!==u)&&(e.resumeFrom=a,i&&(a.preserveOpacity=!0),a.snapshot&&(e.snapshot=a.snapshot,e.snapshot.latestValues=a.animationValues||a.latestValues),(l=e.root)!=null&&l.isUpdating&&(e.isLayoutDirty=!0)),e.options.crossfade===!1&&a.hide()}}exitAnimationComplete(){this.members.forEach(e=>{var i,a,l,c,u;(a=(i=e.options).onExitComplete)==null||a.call(i),(u=(l=e.resumingFrom)==null?void 0:(c=l.options).onExitComplete)==null||u.call(c)})}scheduleRender(){this.members.forEach(e=>e.instance&&e.scheduleRender(!1))}removeLeadSnapshot(){var e;(e=this.lead)!=null&&e.snapshot&&(this.lead.snapshot=void 0)}}const yc={hasAnimatedSinceResize:!0,hasEverUpdated:!1},yd=["","X","Y","Z"],jA=1e3;let NA=0;function bd(n,e,i,a){const{latestValues:l}=e;l[n]&&(i[n]=l[n],e.setStaticValue(n,0),a&&(a[n]=0))}function Gx(n){if(n.hasCheckedOptimisedAppear=!0,n.root===n)return;const{visualElement:e}=n.options;if(!e)return;const i=mx(e);if(window.MotionHasOptimisedAnimation(i,"transform")){const{layout:l,layoutId:c}=n.options;window.MotionCancelOptimisedAnimation(i,"transform",Xe,!(l||c))}const{parent:a}=n;a&&!a.hasCheckedOptimisedAppear&&Gx(a)}function Kx({attachResizeListener:n,defaultParent:e,measureScroll:i,checkIsScrollRoot:a,resetTransform:l}){return class{constructor(u={},d=e==null?void 0:e()){this.id=NA++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.layoutVersion=0,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,this.nodes.forEach(CA),this.nodes.forEach(LA),this.nodes.forEach(zA),this.nodes.forEach(OA)},this.resolvedRelativeTargetAt=0,this.linkedParentVersion=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=u,this.root=d?d.root||d:this,this.path=d?[...d.path,d]:[],this.parent=d,this.depth=d?d.depth+1:0;for(let m=0;mthis.root.updateBlockedByResize=!1;Xe.read(()=>{v=window.innerWidth}),n(u,()=>{const _=window.innerWidth;_!==v&&(v=_,this.root.updateBlockedByResize=!0,y&&y(),y=EA(x,250),yc.hasAnimatedSinceResize&&(yc.hasAnimatedSinceResize=!1,this.nodes.forEach(Z0)))})}d&&this.root.registerSharedNode(d,this),this.options.animate!==!1&&p&&(d||m)&&this.addEventListener("didUpdate",({delta:y,hasLayoutChanged:v,hasRelativeLayoutChanged:x,layout:_})=>{if(this.isTreeAnimationBlocked()){this.target=void 0,this.relativeTarget=void 0;return}const S=this.options.transition||p.getDefaultTransition()||qA,{onLayoutAnimationStart:T,onLayoutAnimationComplete:k}=p.getProps(),R=!this.targetLayout||!Fx(this.targetLayout,_),M=!v&&x;if(this.options.layoutRoot||this.resumeFrom||M||v&&(R||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const N={...Vf(S,"layout"),onPlay:T,onComplete:k};(p.shouldReduceMotion||this.options.layoutRoot)&&(N.delay=0,N.type=!1),this.startAnimation(N),this.setAnimationOrigin(y,M,N.path)}else v||Z0(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=_})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const u=this.getStack();u&&u.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),li(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(PA),this.animationId++)}getTransformTemplate(){const{visualElement:u}=this.options;return u&&u.getProps().transformTemplate}willUpdate(u=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked()){this.options.onExitComplete&&this.options.onExitComplete();return}if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&Gx(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let y=0;y{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){this.snapshot||!this.instance||(this.snapshot=this.measure(),this.snapshot&&!It(this.snapshot.measuredBox.x)&&!It(this.snapshot.measuredBox.y)&&(this.snapshot=void 0))}updateLayout(){if(!this.instance||(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead())&&!this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let m=0;m{const q=X/1e3,Y=G==null?void 0:G(q);Y?(x.x.translate=Y.x,x.x.scale=Ye(u.x.scale,1,q),x.x.origin=u.x.origin,x.x.originPoint=u.x.originPoint,x.y.translate=Y.y,x.y.scale=Ye(u.y.scale,1,q),x.y.origin=u.y.origin,x.y.originPoint=u.y.originPoint):(W0(x.x,u.x,q),W0(x.y,u.y,q)),this.setTargetDelta(x),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(jc(_,this.layout.layoutBox,this.relativeParent.layout.layoutBox,this.options.layoutAnchor||void 0),HA(this.relativeTarget,this.relativeTargetOrigin,_,q),P&&gA(this.relativeTarget,P)&&(this.isProjectionDirty=!1),P||(P=St()),Bn(P,this.relativeTarget)),k&&(this.animationValues=v,vA(v,y,this.latestValues,q,N,M)),Y&&Y.rotate!==void 0&&(this.animationValues||(this.animationValues=v),this.animationValues.pathRotation=Y.rotate),this.root.scheduleUpdateProjection(),this.scheduleRender(),this.animationProgress=q},this.mixTargetDelta(this.options.layoutRoot?1e3:0)}startAnimation(u){var d,m,p;this.notifyListeners("animationStart"),(d=this.currentAnimation)==null||d.stop(),(p=(m=this.resumingFrom)==null?void 0:m.currentAnimation)==null||p.stop(),this.pendingAnimation&&(li(this.pendingAnimation),this.pendingAnimation=void 0),this.pendingAnimation=Xe.update(()=>{yc.hasAnimatedSinceResize=!0,this.motionValue||(this.motionValue=Ga(0)),this.motionValue.jump(0,!1),this.currentAnimation=_A(this.motionValue,[0,1e3],{...u,velocity:0,isSync:!0,onUpdate:y=>{this.mixTargetDelta(y),u.onUpdate&&u.onUpdate(y)},onComplete:()=>{u.onComplete&&u.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const u=this.getStack();u&&u.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners("animationComplete")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(jA),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const u=this.getLead();let{targetWithTransforms:d,target:m,layout:p,latestValues:y}=u;if(!(!d||!m||!p)){if(this!==u&&this.layout&&p&&Yx(this.options.animationType,this.layout.layoutBox,p.layoutBox)){m=this.target||St();const v=It(this.layout.layoutBox.x);m.x.min=u.target.x.min,m.x.max=m.x.min+v;const x=It(this.layout.layoutBox.y);m.y.min=u.target.y.min,m.y.max=m.y.min+x}Bn(d,m),pc(d,y),no(this.projectionDeltaWithTransform,this.layoutCorrected,d,y)}}registerSharedNode(u,d){this.sharedNodes.has(u)||this.sharedNodes.set(u,new AA),this.sharedNodes.get(u).add(d);const p=d.options.initialPromotionConfig;d.promote({transition:p?p.transition:void 0,preserveFollowOpacity:p&&p.shouldPreserveFollowOpacity?p.shouldPreserveFollowOpacity(d):void 0})}isLead(){const u=this.getStack();return u?u.lead===this:!0}getLead(){var d;const{layoutId:u}=this.options;return u?((d=this.getStack())==null?void 0:d.lead)||this:this}getPrevLead(){var d;const{layoutId:u}=this.options;return u?(d=this.getStack())==null?void 0:d.prevLead:void 0}getStack(){const{layoutId:u}=this.options;if(u)return this.root.sharedNodes.get(u)}promote({needsReset:u,transition:d,preserveFollowOpacity:m}={}){const p=this.getStack();p&&p.promote(this,m),u&&(this.projectionDelta=void 0,this.needsReset=!0),d&&this.setOptions({transition:d})}relegate(){const u=this.getStack();return u?u.relegate(this):!1}resetSkewAndRotation(){const{visualElement:u}=this.options;if(!u)return;let d=!1;const{latestValues:m}=u;if((m.z||m.rotate||m.rotateX||m.rotateY||m.rotateZ||m.skewX||m.skewY)&&(d=!0),!d)return;const p={};m.z&&bd("z",u,p,this.animationValues);for(let y=0;y{var d;return(d=u.currentAnimation)==null?void 0:d.stop()}),this.root.nodes.forEach(J0),this.root.sharedNodes.clear()}}}function kA(n){n.updateLayout()}function RA(n){var i;const e=((i=n.resumeFrom)==null?void 0:i.snapshot)||n.snapshot;if(n.isLead()&&n.layout&&e&&n.hasListeners("didUpdate")){const{layoutBox:a,measuredBox:l}=n.layout,{animationType:c}=n.options,u=e.source!==n.layout.source;if(c==="size")Jn(v=>{const x=u?e.measuredBox[v]:e.layoutBox[v],_=It(x);x.min=a[v].min,x.max=x.min+_});else if(c==="x"||c==="y"){const v=c==="x"?"y":"x";rf(u?e.measuredBox[v]:e.layoutBox[v],a[v])}else Yx(c,e.layoutBox,a)&&Jn(v=>{const x=u?e.measuredBox[v]:e.layoutBox[v],_=It(a[v]);x.max=x.min+_,n.relativeTarget&&!n.currentAnimation&&(n.isProjectionDirty=!0,n.relativeTarget[v].max=n.relativeTarget[v].min+_)});const d=Pa();no(d,a,e.layoutBox);const m=Pa();u?no(m,n.applyTransform(l,!0),e.measuredBox):no(m,a,e.layoutBox);const p=!qx(d);let y=!1;if(!n.resumeFrom){const v=n.getClosestProjectingParent();if(v&&!v.resumeFrom){const{snapshot:x,layout:_}=v;if(x&&_){const S=n.options.layoutAnchor||void 0,T=St();jc(T,e.layoutBox,x.layoutBox,S);const k=St();jc(k,a,_.layoutBox,S),Fx(T,k)||(y=!0),v.options.layoutRoot&&(n.relativeTarget=k,n.relativeTargetOrigin=T,n.relativeParent=v)}}}n.notifyListeners("didUpdate",{layout:a,snapshot:e,delta:m,layoutDelta:d,hasLayoutChanged:p,hasRelativeLayoutChanged:y})}else if(n.isLead()){const{onExitComplete:a}=n.options;a&&a()}n.options.transition=void 0}function CA(n){n.parent&&(n.isProjecting()||(n.isProjectionDirty=n.parent.isProjectionDirty),n.isSharedProjectionDirty||(n.isSharedProjectionDirty=!!(n.isProjectionDirty||n.parent.isProjectionDirty||n.parent.isSharedProjectionDirty)),n.isTransformDirty||(n.isTransformDirty=n.parent.isTransformDirty))}function OA(n){n.isProjectionDirty=n.isSharedProjectionDirty=n.isTransformDirty=!1}function DA(n){n.clearSnapshot()}function J0(n){n.clearMeasurements()}function MA(n){n.isLayoutDirty=!0,n.updateLayout()}function Q0(n){n.isLayoutDirty=!1}function BA(n){n.isAnimationBlocked&&n.layout&&!n.isLayoutDirty&&(n.snapshot=n.layout,n.isLayoutDirty=!0)}function UA(n){const{visualElement:e}=n.options;e&&e.getProps().onBeforeLayoutMeasure&&e.notify("BeforeLayoutMeasure"),n.resetTransform()}function Z0(n){n.finishAnimation(),n.targetDelta=n.relativeTarget=n.target=void 0,n.isProjectionDirty=!0}function LA(n){n.resolveTargetDelta()}function zA(n){n.calcProjection()}function PA(n){n.resetSkewAndRotation()}function VA(n){n.removeLeadSnapshot()}function W0(n,e,i){n.translate=Ye(e.translate,0,i),n.scale=Ye(e.scale,1,i),n.origin=e.origin,n.originPoint=e.originPoint}function eb(n,e,i,a){n.min=Ye(e.min,i.min,a),n.max=Ye(e.max,i.max,a)}function HA(n,e,i,a){eb(n.x,e.x,i.x,a),eb(n.y,e.y,i.y,a)}function $A(n){return n.animationValues&&n.animationValues.opacityExit!==void 0}const qA={duration:.45,ease:[.4,0,.1,1]},tb=n=>typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(n),nb=tb("applewebkit/")&&!tb("chrome/")?Math.round:An;function sb(n){n.min=nb(n.min),n.max=nb(n.max)}function FA(n){sb(n.x),sb(n.y)}function Yx(n,e,i){return n==="position"||n==="preserve-aspect"&&!hA(I0(e),I0(i),.2)}function IA(n){var e;return n!==n.root&&((e=n.scroll)==null?void 0:e.wasRoot)}const GA=Kx({attachResizeListener:(n,e)=>ho(n,"resize",e),measureScroll:()=>{var n,e;return{x:document.documentElement.scrollLeft||((n=document.body)==null?void 0:n.scrollLeft)||0,y:document.documentElement.scrollTop||((e=document.body)==null?void 0:e.scrollTop)||0}},checkIsScrollRoot:()=>!0}),vd={current:void 0},Xx=Kx({measureScroll:n=>({x:n.scrollLeft,y:n.scrollTop}),defaultParent:()=>{if(!vd.current){const n=new GA({});n.mount(window),n.setOptions({layoutScroll:!0}),vd.current=n}return vd.current},resetTransform:(n,e)=>{n.style.transform=e!==void 0?e:"none"},checkIsScrollRoot:n=>window.getComputedStyle(n).position==="fixed"}),Zf=D.createContext({transformPagePoint:n=>n,isStatic:!1,reducedMotion:"never"});function ib(n,e){if(typeof n=="function")return n(e);n!=null&&(n.current=e)}function KA(...n){return e=>{let i=!1;const a=n.map(l=>{const c=ib(l,e);return!i&&typeof c=="function"&&(i=!0),c});if(i)return()=>{for(let l=0;l{const{width:_,height:S,top:T,left:k,right:R,bottom:M,direction:N}=m.current;if(e||c===!1||!d.current||!_||!S)return;const P=N==="rtl",G=i==="left"?P?`right: ${R}`:`left: ${k}`:P?`left: ${k}`:`right: ${R}`,X=a==="bottom"?`bottom: ${M}`:`top: ${T}`;d.current.dataset.motionPopId=u;const q=document.createElement("style");p&&(q.nonce=p);const Y=l??document.head;return Y.appendChild(q),q.sheet&&q.sheet.insertRule(` + [data-motion-pop-id="${u}"] { + position: absolute !important; + width: ${_}px !important; + height: ${S}px !important; + ${G}px !important; + ${X}px !important; + } + `),()=>{var se;(se=d.current)==null||se.removeAttribute("data-motion-pop-id"),Y.contains(q)&&Y.removeChild(q)}},[e]),g.jsx(XA,{isPresent:e,childRef:d,sizeRef:m,pop:c,children:c===!1?n:D.cloneElement(n,{ref:v})})}const QA=({children:n,initial:e,isPresent:i,onExitComplete:a,custom:l,presenceAffectsLayout:c,mode:u,anchorX:d,anchorY:m,root:p})=>{const y=Af(ZA),v=D.useId(),x=D.useRef(i),_=D.useRef(a);jf(()=>{x.current=i,_.current=a});let S=!0,T=D.useMemo(()=>(S=!1,{id:v,initial:e,isPresent:i,custom:l,onExitComplete:k=>{y.set(k,!0);for(const R of y.values())if(!R)return;a&&a()},register:k=>(y.set(k,!1),()=>{var R;y.delete(k),!x.current&&!y.size&&((R=_.current)==null||R.call(_))})}),[i,y,a]);return c&&S&&(T={...T}),D.useMemo(()=>{y.forEach((k,R)=>y.set(R,!1))},[i]),D.useEffect(()=>{!i&&!y.size&&a&&a()},[i]),n=g.jsx(JA,{pop:u==="popLayout",isPresent:i,anchorX:d,anchorY:m,root:p,children:n}),g.jsx(Pc.Provider,{value:T,children:n})};function ZA(){return new Map}function Jx(n=!0){const e=D.useContext(Pc);if(e===null)return[!0,null];const{isPresent:i,onExitComplete:a,register:l}=e,c=D.useId();D.useEffect(()=>{if(n)return l(c)},[n]);const u=D.useCallback(()=>n&&a&&a(c),[c,a,n]);return!i&&a?[!1,u]:[!0]}const Jl=n=>n.key||"";function ab(n){const e=[];return D.Children.forEach(n,i=>{D.isValidElement(i)&&e.push(i)}),e}const si=({children:n,custom:e,initial:i=!0,onExitComplete:a,presenceAffectsLayout:l=!0,mode:c="sync",propagate:u=!1,anchorX:d="left",anchorY:m="top",root:p})=>{const[y,v]=Jx(u),x=D.useMemo(()=>ab(n),[n]),_=u&&!y?[]:x.map(Jl),S=D.useRef(!0),T=D.useRef(x),k=Af(()=>new Map),R=D.useRef(new Set),[M,N]=D.useState(x),[P,G]=D.useState(x);jf(()=>{S.current=!1,T.current=x;for(let Y=0;Y{const se=Jl(Y),J=u&&!y?!1:x===P||_.includes(se),he=()=>{if(R.current.has(se))return;if(k.has(se))R.current.add(se),k.set(se,!0);else return;let fe=!0;k.forEach(Ae=>{Ae||(fe=!1)}),fe&&(q==null||q(),G(T.current),u&&(v==null||v()),a&&a())};return g.jsx(QA,{isPresent:J,initial:!S.current||i?void 0:!1,custom:e,presenceAffectsLayout:l,mode:c,root:p,onExitComplete:J?void 0:he,anchorX:d,anchorY:m,children:Y},se)})})},Qx=D.createContext({strict:!1}),rb={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]};let ob=!1;function WA(){if(ob)return;const n={};for(const e in rb)n[e]={isEnabled:i=>rb[e].some(a=>!!i[a])};jx(n),ob=!0}function Zx(){return WA(),LE()}function e2(n){const e=Zx();for(const i in n)e[i]={...e[i],...n[i]};jx(e)}const t2=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","propagate","ignoreStrict","viewport"]);function Nc(n){return n.startsWith("while")||n.startsWith("drag")&&n!=="draggable"||n.startsWith("layout")||n.startsWith("onTap")||n.startsWith("onPan")||n.startsWith("onLayout")||t2.has(n)}let Wx=n=>!Nc(n);function n2(n){typeof n=="function"&&(Wx=e=>e.startsWith("on")?!Nc(e):n(e))}try{n2(require("@emotion/is-prop-valid").default)}catch{}function s2(n,e,i){const a={};for(const l in n)l==="values"&&typeof n.values=="object"||Ut(n[l])||(Wx(l)||i===!0&&Nc(l)||!e&&!Nc(l)||n.draggable&&l.startsWith("onDrag"))&&(a[l]=n[l]);return a}const qc=D.createContext({});function i2(n,e){if($c(n)){const{initial:i,animate:a}=n;return{initial:i===!1||uo(i)?i:void 0,animate:uo(a)?a:void 0}}return n.inherit!==!1?e:{}}function a2(n){const{initial:e,animate:i}=i2(n,D.useContext(qc));return D.useMemo(()=>({initial:e,animate:i}),[lb(e),lb(i)])}function lb(n){return Array.isArray(n)?n.join(" "):n}const Wf=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function ew(n,e,i){for(const a in e)!Ut(e[a])&&!Mx(a,i)&&(n[a]=e[a])}function r2({transformTemplate:n},e){return D.useMemo(()=>{const i=Wf();return Jf(i,e,n),Object.assign({},i.vars,i.style)},[e])}function o2(n,e){const i=n.style||{},a={};return ew(a,i,n),Object.assign(a,r2(n,e)),a}function l2(n,e){const i={},a=o2(n,e);return n.drag&&n.dragListener!==!1&&(i.draggable=!1,a.userSelect=a.WebkitUserSelect=a.WebkitTouchCallout="none",a.touchAction=n.drag===!0?"none":`pan-${n.drag==="x"?"y":"x"}`),n.tabIndex===void 0&&(n.onTap||n.onTapStart||n.whileTap)&&(i.tabIndex=0),i.style=a,i}const tw=()=>({...Wf(),attrs:{}});function c2(n,e,i,a){const l=D.useMemo(()=>{const c=tw();return Bx(c,e,Lx(a),n.transformTemplate,n.style),{...c.attrs,style:{...c.style}}},[e]);if(n.style){const c={};ew(c,n.style,n),l.style={...c,...l.style}}return l}const u2=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function em(n){return typeof n!="string"||n.includes("-")?!1:!!(u2.indexOf(n)>-1||/[A-Z]/u.test(n))}function h2(n,e,i,{latestValues:a},l,c=!1,u){const m=(u??em(n)?c2:l2)(e,a,l,n),p=s2(e,typeof n=="string",c),y=n!==D.Fragment?{...p,...m,ref:i}:{},{children:v}=e,x=D.useMemo(()=>Ut(v)?v.get():v,[v]);return D.createElement(n,{...y,children:x})}function d2({scrapeMotionValuesFromProps:n,createRenderState:e},i,a,l){return{latestValues:f2(i,a,l,n),renderState:e()}}function f2(n,e,i,a){const l={},c=a(n,{});for(const x in c)l[x]=gc(c[x]);let{initial:u,animate:d}=n;const m=$c(n),p=Ex(n);e&&p&&!m&&n.inherit!==!1&&(u===void 0&&(u=e.initial),d===void 0&&(d=e.animate));let y=i?i.initial===!1:!1;y=y||u===!1;const v=y?d:u;if(v&&typeof v!="boolean"&&!Hc(v)){const x=Array.isArray(v)?v:[v];for(let _=0;_(e,i)=>{const a=D.useContext(qc),l=D.useContext(Pc),c=()=>d2(n,e,a,l);return i?c():Af(c)},m2=nw({scrapeMotionValuesFromProps:Qf,createRenderState:Wf}),p2=nw({scrapeMotionValuesFromProps:zx,createRenderState:tw}),g2=Symbol.for("motionComponentSymbol");function y2(n,e,i){const a=D.useRef(i);D.useInsertionEffect(()=>{a.current=i});const l=D.useRef(null);return D.useCallback(c=>{var d;c&&((d=n.onMount)==null||d.call(n,c)),e&&(c?e.mount(c):e.unmount());const u=a.current;if(typeof u=="function")if(c){const m=u(c);typeof m=="function"&&(l.current=m)}else l.current?(l.current(),l.current=null):u(c);else u&&(u.current=c)},[e])}const sw=D.createContext({});function Ca(n){return n&&typeof n=="object"&&Object.prototype.hasOwnProperty.call(n,"current")}function b2(n,e,i,a,l,c){var N,P;const{visualElement:u}=D.useContext(qc),d=D.useContext(Qx),m=D.useContext(Pc),p=D.useContext(Zf),y=p.reducedMotion,v=p.skipAnimations,x=D.useRef(null),_=D.useRef(!1);a=a||d.renderer,!x.current&&a&&(x.current=a(n,{visualState:e,parent:u,props:i,presenceContext:m,blockInitialAnimation:m?m.initial===!1:!1,reducedMotionConfig:y,skipAnimations:v,isSVG:c}),_.current&&x.current&&(x.current.manuallyAnimateOnMount=!0));const S=x.current,T=D.useContext(sw);S&&!S.projection&&l&&(S.type==="html"||S.type==="svg")&&v2(x.current,i,l,T);const k=D.useRef(!1);D.useInsertionEffect(()=>{S&&k.current&&S.update(i,m)});const R=i[fx],M=D.useRef(!!R&&typeof window<"u"&&!((N=window.MotionHandoffIsComplete)!=null&&N.call(window,R))&&((P=window.MotionHasOptimisedAnimation)==null?void 0:P.call(window,R)));return jf(()=>{_.current=!0,S&&(k.current=!0,window.MotionIsMounted=!0,S.updateFeatures(),S.scheduleRenderMicrotask(),M.current&&S.animationState&&S.animationState.animateChanges())}),D.useEffect(()=>{S&&(!M.current&&S.animationState&&S.animationState.animateChanges(),M.current&&(queueMicrotask(()=>{var G;(G=window.MotionHandoffMarkAsComplete)==null||G.call(window,R)}),M.current=!1),S.enteringChildren=void 0)}),S}function v2(n,e,i,a){const{layoutId:l,layout:c,drag:u,dragConstraints:d,layoutScroll:m,layoutRoot:p,layoutAnchor:y,layoutCrossfade:v}=e;n.projection=new i(n.latestValues,e["data-framer-portal-id"]?void 0:iw(n.parent)),n.projection.setOptions({layoutId:l,layout:c,alwaysMeasureLayout:!!u||d&&Ca(d),visualElement:n,animationType:typeof c=="string"?c:"both",initialPromotionConfig:a,crossfade:v,layoutScroll:m,layoutRoot:p,layoutAnchor:y})}function iw(n){if(n)return n.options.allowProjection!==!1?n.projection:iw(n.parent)}function xd(n,{forwardMotionProps:e=!1,type:i}={},a,l){a&&e2(a);const c=i?i==="svg":em(n),u=c?p2:m2;function d(p,y){let v;const x={...D.useContext(Zf),...p,layoutId:x2(p)},{isStatic:_}=x,S=a2(p),T=u(p,_);if(!_&&typeof window<"u"){w2();const k=_2(x);v=k.MeasureLayout,S.visualElement=b2(n,T,x,l,k.ProjectionNode,c)}return g.jsxs(qc.Provider,{value:S,children:[v&&S.visualElement?g.jsx(v,{visualElement:S.visualElement,...x}):null,h2(n,p,y2(T,S.visualElement,y),T,_,e,c)]})}d.displayName=`motion.${typeof n=="string"?n:`create(${n.displayName??n.name??""})`}`;const m=D.forwardRef(d);return m[g2]=n,m}function x2({layoutId:n}){const e=D.useContext(Ef).id;return e&&n!==void 0?e+"-"+n:n}function w2(n,e){D.useContext(Qx).strict}function _2(n){const e=Zx(),{drag:i,layout:a}=e;if(!i&&!a)return{};const l={...i,...a};return{MeasureLayout:i!=null&&i.isEnabled(n)||a!=null&&a.isEnabled(n)?l.MeasureLayout:void 0,ProjectionNode:l.ProjectionNode}}function S2(n,e){if(typeof Proxy>"u")return xd;const i=new Map,a=(c,u)=>xd(c,u,n,e),l=(c,u)=>a(c,u);return new Proxy(l,{get:(c,u)=>u==="create"?a:(i.has(u)||i.set(u,xd(u,void 0,n,e)),i.get(u))})}const T2=(n,e)=>e.isSVG??em(n)?new eA(e):new YE(e,{allowProjection:n!==D.Fragment});class E2 extends ci{constructor(e){super(e),e.animationState||(e.animationState=aA(e))}updateAnimationControlsSubscription(){const{animate:e}=this.node.getProps();Hc(e)&&(this.unmountControls=e.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:e}=this.node.getProps(),{animate:i}=this.node.prevProps||{};e!==i&&this.updateAnimationControlsSubscription()}unmount(){var e;this.node.animationState.reset(),(e=this.unmountControls)==null||e.call(this)}}let A2=0;class j2 extends ci{constructor(){super(...arguments),this.id=A2++,this.isExitComplete=!1}update(){var c;if(!this.node.presenceContext)return;const{isPresent:e,onExitComplete:i}=this.node.presenceContext,{isPresent:a}=this.node.prevPresenceContext||{};if(!this.node.animationState||e===a)return;if(e&&a===!1){if(this.isExitComplete){const{initial:u,custom:d}=this.node.getProps();if(typeof u=="string"||typeof u=="object"&&u!==null&&!Array.isArray(u)){const m=Vi(this.node,u,d);if(m){const{transition:p,transitionEnd:y,...v}=m;for(const x in v)(c=this.node.getValue(x))==null||c.jump(v[x])}}this.node.animationState.reset(),this.node.animationState.animateChanges()}else this.node.animationState.setActive("exit",!1);this.isExitComplete=!1;return}const l=this.node.animationState.setActive("exit",!e);i&&!e&&l.then(()=>{this.isExitComplete=!0,i(this.id)})}mount(){const{register:e,onExitComplete:i}=this.node.presenceContext||{};i&&i(this.id),e&&(this.unmount=e(this.id))}unmount(){}}const N2={animation:{Feature:E2},exit:{Feature:j2}};function jo(n){return{point:{x:n.pageX,y:n.pageY}}}const k2=n=>e=>Gf(e)&&n(e,jo(e));function so(n,e,i,a){return ho(n,e,k2(i),a)}const aw=({current:n})=>n?n.ownerDocument.defaultView:null,cb=(n,e)=>Math.abs(n-e);function R2(n,e){const i=cb(n.x,e.x),a=cb(n.y,e.y);return Math.sqrt(i**2+a**2)}const ub=new Set(["auto","scroll"]);class rw{constructor(e,i,{transformPagePoint:a,contextWindow:l=window,dragSnapToOrigin:c=!1,distanceThreshold:u=3,element:d}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.lastRawMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.scrollPositions=new Map,this.removeScrollListeners=null,this.onElementScroll=S=>{this.handleScroll(S.target)},this.onWindowScroll=()=>{this.handleScroll(window)},this.updatePoint=()=>{if(!(this.lastMoveEvent&&this.lastMoveEventInfo))return;this.lastRawMoveEventInfo&&(this.lastMoveEventInfo=Ql(this.lastRawMoveEventInfo,this.transformPagePoint));const S=wd(this.lastMoveEventInfo,this.history),T=this.startEvent!==null,k=R2(S.offset,{x:0,y:0})>=this.distanceThreshold;if(!T&&!k)return;const{point:R}=S,{timestamp:M}=Bt;this.history.push({...R,timestamp:M});const{onStart:N,onMove:P}=this.handlers;T||(N&&N(this.lastMoveEvent,S),this.startEvent=this.lastMoveEvent),P&&P(this.lastMoveEvent,S)},this.handlePointerMove=(S,T)=>{this.lastMoveEvent=S,this.lastRawMoveEventInfo=T,this.lastMoveEventInfo=Ql(T,this.transformPagePoint),Xe.update(this.updatePoint,!0)},this.handlePointerUp=(S,T)=>{this.end();const{onEnd:k,onSessionEnd:R,resumeAnimation:M}=this.handlers;if((this.dragSnapToOrigin||!this.startEvent)&&M&&M(),!(this.lastMoveEvent&&this.lastMoveEventInfo))return;const N=wd(S.type==="pointercancel"?this.lastMoveEventInfo:Ql(T,this.transformPagePoint),this.history);this.startEvent&&k&&k(S,N),R&&R(S,N)},!Gf(e))return;this.dragSnapToOrigin=c,this.handlers=i,this.transformPagePoint=a,this.distanceThreshold=u,this.contextWindow=l||window;const m=jo(e),p=Ql(m,this.transformPagePoint),{point:y}=p,{timestamp:v}=Bt;this.history=[{...y,timestamp:v}];const{onSessionStart:x}=i;x&&x(e,wd(p,this.history));const _={passive:!0,capture:!0};this.removeListeners=To(so(this.contextWindow,"pointermove",this.handlePointerMove,_),so(this.contextWindow,"pointerup",this.handlePointerUp,_),so(this.contextWindow,"pointercancel",this.handlePointerUp,_)),d&&this.startScrollTracking(d)}startScrollTracking(e){let i=e.parentElement;for(;i;){const a=getComputedStyle(i);(ub.has(a.overflowX)||ub.has(a.overflowY))&&this.scrollPositions.set(i,{x:i.scrollLeft,y:i.scrollTop}),i=i.parentElement}this.scrollPositions.set(window,{x:window.scrollX,y:window.scrollY}),window.addEventListener("scroll",this.onElementScroll,{capture:!0}),window.addEventListener("scroll",this.onWindowScroll),this.removeScrollListeners=()=>{window.removeEventListener("scroll",this.onElementScroll,{capture:!0}),window.removeEventListener("scroll",this.onWindowScroll)}}handleScroll(e){const i=this.scrollPositions.get(e);if(!i)return;const a=e===window,l=a?{x:window.scrollX,y:window.scrollY}:{x:e.scrollLeft,y:e.scrollTop},c={x:l.x-i.x,y:l.y-i.y};c.x===0&&c.y===0||(a?this.lastMoveEventInfo&&(this.lastMoveEventInfo.point.x+=c.x,this.lastMoveEventInfo.point.y+=c.y):this.history.length>0&&(this.history[0].x-=c.x,this.history[0].y-=c.y),this.scrollPositions.set(e,l),Xe.update(this.updatePoint,!0))}updateHandlers(e){this.handlers=e}end(){this.removeListeners&&this.removeListeners(),this.removeScrollListeners&&this.removeScrollListeners(),this.scrollPositions.clear(),li(this.updatePoint)}}function Ql(n,e){return e?{point:e(n.point)}:n}function hb(n,e){return{x:n.x-e.x,y:n.y-e.y}}function wd({point:n},e){return{point:n,delta:hb(n,ow(e)),offset:hb(n,C2(e)),velocity:O2(e,.1)}}function C2(n){return n[0]}function ow(n){return n[n.length-1]}function O2(n,e){if(n.length<2)return{x:0,y:0};let i=n.length-1,a=null;const l=ow(n);for(;i>=0&&(a=n[i],!(l.timestamp-a.timestamp>dn(e)));)i--;if(!a)return{x:0,y:0};a===n[0]&&n.length>2&&l.timestamp-a.timestamp>dn(e)*2&&(a=n[1]);const c=En(l.timestamp-a.timestamp);if(c===0)return{x:0,y:0};const u={x:(l.x-a.x)/c,y:(l.y-a.y)/c};return u.x===1/0&&(u.x=0),u.y===1/0&&(u.y=0),u}function D2(n,{min:e,max:i},a){return e!==void 0&&ni&&(n=a?Ye(i,n,a.max):Math.min(n,i)),n}function db(n,e,i){return{min:e!==void 0?n.min+e:void 0,max:i!==void 0?n.max+i-(n.max-n.min):void 0}}function M2(n,{top:e,left:i,bottom:a,right:l}){return{x:db(n.x,i,l),y:db(n.y,e,a)}}function fb(n,e){let i=e.min-n.min,a=e.max-n.max;return e.max-e.mina?i=lo(e.min,e.max-a,n.min):a>l&&(i=lo(n.min,n.max-l,e.min)),ts(0,1,i)}function L2(n,e){const i={};return e.min!==void 0&&(i.min=e.min-n.min),e.max!==void 0&&(i.max=e.max-n.min),i}const of=.35;function z2(n=of){return n===!1?n=0:n===!0&&(n=of),{x:mb(n,"left","right"),y:mb(n,"top","bottom")}}function mb(n,e,i){return{min:pb(n,e),max:pb(n,i)}}function pb(n,e){return typeof n=="number"?n:n[e]||0}const P2=new WeakMap;class V2{constructor(e){this.openDragLock=null,this.isDragging=!1,this.currentDirection=null,this.originPoint={x:0,y:0},this.constraints=!1,this.hasMutatedConstraints=!1,this.elastic=St(),this.latestPointerEvent=null,this.latestPanInfo=null,this.visualElement=e}start(e,{snapToCursor:i=!1,distanceThreshold:a}={}){const{presenceContext:l}=this.visualElement;if(l&&l.isPresent===!1)return;const c=v=>{i&&this.snapToCursor(jo(v).point),this.stopAnimation()},u=(v,x)=>{const{drag:_,dragPropagation:S,onDragStart:T}=this.getProps();if(_&&!S&&(this.openDragLock&&this.openDragLock(),this.openDragLock=fE(_),!this.openDragLock))return;this.latestPointerEvent=v,this.latestPanInfo=x,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),Jn(R=>{let M=this.getAxisMotionValue(R).get()||0;if(es.test(M)){const{projection:N}=this.visualElement;if(N&&N.layout){const P=N.layout.layoutBox[R];P&&(M=It(P)*(parseFloat(M)/100))}}this.originPoint[R]=M}),T&&Xe.update(()=>T(v,x),!1,!0),Jd(this.visualElement,"transform");const{animationState:k}=this.visualElement;k&&k.setActive("whileDrag",!0)},d=(v,x)=>{this.latestPointerEvent=v,this.latestPanInfo=x;const{dragPropagation:_,dragDirectionLock:S,onDirectionLock:T,onDrag:k}=this.getProps();if(!_&&!this.openDragLock)return;const{offset:R}=x;if(S&&this.currentDirection===null){this.currentDirection=$2(R),this.currentDirection!==null&&T&&T(this.currentDirection);return}this.updateAxis("x",x.point,R),this.updateAxis("y",x.point,R),this.visualElement.render(),k&&Xe.update(()=>k(v,x),!1,!0)},m=(v,x)=>{this.latestPointerEvent=v,this.latestPanInfo=x,this.stop(v,x),this.latestPointerEvent=null,this.latestPanInfo=null},p=()=>{const{dragSnapToOrigin:v}=this.getProps();(v||this.constraints)&&this.startAnimation({x:0,y:0})},{dragSnapToOrigin:y}=this.getProps();this.panSession=new rw(e,{onSessionStart:c,onStart:u,onMove:d,onSessionEnd:m,resumeAnimation:p},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:y,distanceThreshold:a,contextWindow:aw(this.visualElement),element:this.visualElement.current})}stop(e,i){const a=e||this.latestPointerEvent,l=i||this.latestPanInfo,c=this.isDragging;if(this.cancel(),!c||!l||!a)return;const{velocity:u}=l;this.startAnimation(u);const{onDragEnd:d}=this.getProps();d&&Xe.postRender(()=>d(a,l))}cancel(){this.isDragging=!1;const{projection:e,animationState:i}=this.visualElement;e&&(e.isAnimationBlocked=!1),this.endPanSession();const{dragPropagation:a}=this.getProps();!a&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),i&&i.setActive("whileDrag",!1)}endPanSession(){this.panSession&&this.panSession.end(),this.panSession=void 0}updateAxis(e,i,a){const{drag:l}=this.getProps();if(!a||!Zl(e,l,this.currentDirection))return;const c=this.getAxisMotionValue(e);let u=this.originPoint[e]+a[e];this.constraints&&this.constraints[e]&&(u=D2(u,this.constraints[e],this.elastic[e])),c.set(u)}resolveConstraints(){var c;const{dragConstraints:e,dragElastic:i}=this.getProps(),a=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):(c=this.visualElement.projection)==null?void 0:c.layout,l=this.constraints;e&&Ca(e)?this.constraints||(this.constraints=this.resolveRefConstraints()):e&&a?this.constraints=M2(a.layoutBox,e):this.constraints=!1,this.elastic=z2(i),l!==this.constraints&&!Ca(e)&&a&&this.constraints&&!this.hasMutatedConstraints&&Jn(u=>{this.constraints!==!1&&this.getAxisMotionValue(u)&&(this.constraints[u]=L2(a.layoutBox[u],this.constraints[u]))})}resolveRefConstraints(){const{dragConstraints:e,onMeasureDragConstraints:i}=this.getProps();if(!e||!Ca(e))return!1;const a=e.current,{projection:l}=this.visualElement;if(!l||!l.layout)return!1;l.root&&(l.root.scroll=void 0,l.root.updateScroll());const c=$E(a,l.root,this.visualElement.getTransformPagePoint());let u=B2(l.layout.layoutBox,c);if(i){const d=i(PE(u));this.hasMutatedConstraints=!!d,d&&(u=kx(d))}return u}startAnimation(e){const{drag:i,dragMomentum:a,dragElastic:l,dragTransition:c,dragSnapToOrigin:u,onDragTransitionEnd:d}=this.getProps(),m=this.constraints||{},p=Jn(y=>{if(!Zl(y,i,this.currentDirection))return;let v=m&&m[y]||{};(u===!0||u===y)&&(v={min:0,max:0});const x=l?200:1e6,_=l?40:1e7,S={type:"inertia",velocity:a?e[y]:0,bounceStiffness:x,bounceDamping:_,timeConstant:750,restDelta:1,restSpeed:10,...c,...v};return this.startAxisValueAnimation(y,S)});return Promise.all(p).then(d)}startAxisValueAnimation(e,i){const a=this.getAxisMotionValue(e);return Jd(this.visualElement,e),a.start(Hf(e,a,0,i,this.visualElement,!1))}stopAnimation(){Jn(e=>this.getAxisMotionValue(e).stop())}getAxisMotionValue(e){const i=`_drag${e.toUpperCase()}`,l=this.visualElement.getProps()[i];return l||this.visualElement.getValue(e,this.visualElement.latestValues[e]??0)}snapToCursor(e){Jn(i=>{const{drag:a}=this.getProps();if(!Zl(i,a,this.currentDirection))return;const{projection:l}=this.visualElement,c=this.getAxisMotionValue(i);if(l&&l.layout){const{min:u,max:d}=l.layout.layoutBox[i],m=c.get()||0;c.set(e[i]-Ye(u,d,.5)+m)}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:e,dragConstraints:i}=this.getProps(),{projection:a}=this.visualElement;if(!Ca(i)||!a||!this.constraints)return;this.stopAnimation();const l={x:0,y:0};Jn(u=>{const d=this.getAxisMotionValue(u);if(d&&this.constraints!==!1){const m=d.get();l[u]=U2({min:m,max:m},this.constraints[u])}});const{transformTemplate:c}=this.visualElement.getProps();this.visualElement.current.style.transform=c?c({},""):"none",a.root&&a.root.updateScroll(),a.updateLayout(),this.constraints=!1,this.resolveConstraints(),Jn(u=>{if(!Zl(u,e,null))return;const d=this.getAxisMotionValue(u),{min:m,max:p}=this.constraints[u];d.set(Ye(m,p,l[u]))}),this.visualElement.render()}addListeners(){if(!this.visualElement.current)return;P2.set(this.visualElement,this);const e=this.visualElement.current,i=so(e,"pointerdown",p=>{const{drag:y,dragListener:v=!0}=this.getProps(),x=p.target,_=x!==e&&vE(x);y&&v&&!_&&this.start(p)});let a;const l=()=>{const{dragConstraints:p}=this.getProps();Ca(p)&&p.current&&(this.constraints=this.resolveRefConstraints(),a||(a=H2(e,p.current,()=>this.scalePositionWithinConstraints())))},{projection:c}=this.visualElement,u=c.addEventListener("measure",l);c&&!c.layout&&(c.root&&c.root.updateScroll(),c.updateLayout()),Xe.read(l);const d=ho(window,"resize",()=>this.scalePositionWithinConstraints()),m=c.addEventListener("didUpdate",(({delta:p,hasLayoutChanged:y})=>{this.isDragging&&y&&(Jn(v=>{const x=this.getAxisMotionValue(v);x&&(this.originPoint[v]+=p[v].translate,x.set(x.get()+p[v].translate))}),this.visualElement.render())}));return()=>{d(),i(),u(),m&&m(),a&&a()}}getProps(){const e=this.visualElement.getProps(),{drag:i=!1,dragDirectionLock:a=!1,dragPropagation:l=!1,dragConstraints:c=!1,dragElastic:u=of,dragMomentum:d=!0}=e;return{...e,drag:i,dragDirectionLock:a,dragPropagation:l,dragConstraints:c,dragElastic:u,dragMomentum:d}}}function gb(n){let e=!0;return()=>{if(e){e=!1;return}n()}}function H2(n,e,i){const a=S0(n,gb(i)),l=S0(e,gb(i));return()=>{a(),l()}}function Zl(n,e,i){return(e===!0||e===n)&&(i===null||i===n)}function $2(n,e=10){let i=null;return Math.abs(n.y)>e?i="y":Math.abs(n.x)>e&&(i="x"),i}class q2 extends ci{constructor(e){super(e),this.removeGroupControls=An,this.removeListeners=An,this.controls=new V2(e)}mount(){const{dragControls:e}=this.node.getProps();e&&(this.removeGroupControls=e.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||An}update(){const{dragControls:e}=this.node.getProps(),{dragControls:i}=this.node.prevProps||{};e!==i&&(this.removeGroupControls(),e&&(this.removeGroupControls=e.subscribe(this.controls)))}unmount(){this.removeGroupControls(),this.removeListeners(),this.controls.isDragging||this.controls.endPanSession()}}const _d=n=>(e,i)=>{n&&Xe.update(()=>n(e,i),!1,!0)};class F2 extends ci{constructor(){super(...arguments),this.removePointerDownListener=An}onPointerDown(e){this.session=new rw(e,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:aw(this.node)})}createPanHandlers(){const{onPanSessionStart:e,onPanStart:i,onPan:a,onPanEnd:l}=this.node.getProps();return{onSessionStart:_d(e),onStart:_d(i),onMove:_d(a),onEnd:(c,u)=>{delete this.session,l&&Xe.postRender(()=>l(c,u))}}}mount(){this.removePointerDownListener=so(this.node.current,"pointerdown",e=>this.onPointerDown(e))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}let Sd=!1;class I2 extends D.Component{componentDidMount(){const{visualElement:e,layoutGroup:i,switchLayoutGroup:a,layoutId:l}=this.props,{projection:c}=e;c&&(i.group&&i.group.add(c),a&&a.register&&l&&a.register(c),Sd&&c.root.didUpdate(),c.addEventListener("animationComplete",()=>{this.safeToRemove()}),c.setOptions({...c.options,layoutDependency:this.props.layoutDependency,onExitComplete:()=>this.safeToRemove()})),yc.hasEverUpdated=!0}getSnapshotBeforeUpdate(e){const{layoutDependency:i,visualElement:a,drag:l,isPresent:c}=this.props,{projection:u}=a;return u&&(u.isPresent=c,e.layoutDependency!==i&&u.setOptions({...u.options,layoutDependency:i}),Sd=!0,l||e.layoutDependency!==i||i===void 0||e.isPresent!==c?u.willUpdate():this.safeToRemove(),e.isPresent!==c&&(c?u.promote():u.relegate()||Xe.postRender(()=>{const d=u.getStack();(!d||!d.members.length)&&this.safeToRemove()}))),null}componentDidUpdate(){const{visualElement:e,layoutAnchor:i}=this.props,{projection:a}=e;a&&(a.options.layoutAnchor=i,a.root.didUpdate(),If.postRender(()=>{!a.currentAnimation&&a.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:e,layoutGroup:i,switchLayoutGroup:a}=this.props,{projection:l}=e;Sd=!0,l&&(l.scheduleCheckAfterUnmount(),i&&i.group&&i.group.remove(l),a&&a.deregister&&a.deregister(l))}safeToRemove(){const{safeToRemove:e}=this.props;e&&e()}render(){return null}}function lw(n){const[e,i]=Jx(),a=D.useContext(Ef);return g.jsx(I2,{...n,layoutGroup:a,switchLayoutGroup:D.useContext(sw),isPresent:e,safeToRemove:i})}const G2={pan:{Feature:F2},drag:{Feature:q2,ProjectionNode:Xx,MeasureLayout:lw}};function yb(n,e,i){const{props:a}=n;n.animationState&&a.whileHover&&n.animationState.setActive("whileHover",i==="Start");const l="onHover"+i,c=a[l];c&&Xe.postRender(()=>c(e,jo(e)))}class K2 extends ci{mount(){const{current:e}=this.node;e&&(this.unmount=pE(e,(i,a)=>(yb(this.node,a,"Start"),l=>yb(this.node,l,"End"))))}unmount(){}}class Y2 extends ci{constructor(){super(...arguments),this.isActive=!1}onFocus(){let e=!1;try{e=this.node.current.matches(":focus-visible")}catch{e=!0}!e||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){!this.isActive||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=To(ho(this.node.current,"focus",()=>this.onFocus()),ho(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}function bb(n,e,i){const{props:a}=n;if(n.current instanceof HTMLButtonElement&&n.current.disabled)return;n.animationState&&a.whileTap&&n.animationState.setActive("whileTap",i==="Start");const l="onTap"+(i==="End"?"":i),c=a[l];c&&Xe.postRender(()=>c(e,jo(e)))}class X2 extends ci{mount(){const{current:e}=this.node;if(!e)return;const{globalTapTarget:i,propagate:a}=this.node.props;this.unmount=wE(e,(l,c)=>(bb(this.node,c,"Start"),(u,{success:d})=>bb(this.node,u,d?"End":"Cancel")),{useGlobalTarget:i,stopPropagation:(a==null?void 0:a.tap)===!1})}unmount(){}}const lf=new WeakMap,Td=new WeakMap,J2=n=>{const e=lf.get(n.target);e&&e(n)},Q2=n=>{n.forEach(J2)};function Z2({root:n,...e}){const i=n||document;Td.has(i)||Td.set(i,{});const a=Td.get(i),l=JSON.stringify(e);return a[l]||(a[l]=new IntersectionObserver(Q2,{root:n,...e})),a[l]}function W2(n,e,i){const a=Z2(e);return lf.set(n,i),a.observe(n),()=>{lf.delete(n),a.unobserve(n)}}const ej={some:0,all:1};class tj extends ci{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){var m;(m=this.stopObserver)==null||m.call(this);const{viewport:e={}}=this.node.getProps(),{root:i,margin:a,amount:l="some",once:c}=e,u={root:i?i.current:void 0,rootMargin:a,threshold:typeof l=="number"?l:ej[l]},d=p=>{const{isIntersecting:y}=p;if(this.isInView===y||(this.isInView=y,c&&!y&&this.hasEnteredView))return;y&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",y);const{onViewportEnter:v,onViewportLeave:x}=this.node.getProps(),_=y?v:x;_&&_(p)};this.stopObserver=W2(this.node.current,u,d)}mount(){this.startObserver()}update(){if(typeof IntersectionObserver>"u")return;const{props:e,prevProps:i}=this.node;["amount","margin","root"].some(nj(e,i))&&this.startObserver()}unmount(){var e;(e=this.stopObserver)==null||e.call(this),this.hasEnteredView=!1,this.isInView=!1}}function nj({viewport:n={}},{viewport:e={}}={}){return i=>n[i]!==e[i]}const sj={inView:{Feature:tj},tap:{Feature:X2},focus:{Feature:Y2},hover:{Feature:K2}},ij={layout:{ProjectionNode:Xx,MeasureLayout:lw}},aj={...N2,...sj,...G2,...ij},rj=S2(aj,T2),un=rj;/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const oj=n=>n.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),lj=n=>n.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,i,a)=>a?a.toUpperCase():i.toLowerCase()),vb=n=>{const e=lj(n);return e.charAt(0).toUpperCase()+e.slice(1)},cw=(...n)=>n.filter((e,i,a)=>!!e&&e.trim()!==""&&a.indexOf(e)===i).join(" ").trim(),cj=n=>{for(const e in n)if(e.startsWith("aria-")||e==="role"||e==="title")return!0};/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */var uj={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const hj=D.forwardRef(({color:n="currentColor",size:e=24,strokeWidth:i=2,absoluteStrokeWidth:a,className:l="",children:c,iconNode:u,...d},m)=>D.createElement("svg",{ref:m,...uj,width:e,height:e,stroke:n,strokeWidth:a?Number(i)*24/Number(e):i,className:cw("lucide",l),...!c&&!cj(d)&&{"aria-hidden":"true"},...d},[...u.map(([p,y])=>D.createElement(p,y)),...Array.isArray(c)?c:[c]]));/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Me=(n,e)=>{const i=D.forwardRef(({className:a,...l},c)=>D.createElement(hj,{ref:c,iconNode:e,className:cw(`lucide-${oj(vb(n))}`,`lucide-${n}`,a),...l}));return i.displayName=vb(n),i};/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const dj=[["path",{d:"M8 3 4 7l4 4",key:"9rb6wj"}],["path",{d:"M4 7h16",key:"6tx8e3"}],["path",{d:"m16 21 4-4-4-4",key:"siv7j2"}],["path",{d:"M20 17H4",key:"h6l3hr"}]],fj=Me("arrow-left-right",dj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const mj=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]],pj=Me("arrow-right",mj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const gj=[["rect",{width:"20",height:"12",x:"2",y:"6",rx:"2",key:"9lu3g6"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}],["path",{d:"M6 12h.01M18 12h.01",key:"113zkx"}]],xb=Me("banknote",gj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const yj=[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}],["path",{d:"M8 14h.01",key:"6423bh"}],["path",{d:"M12 14h.01",key:"1etili"}],["path",{d:"M16 14h.01",key:"1gbofw"}],["path",{d:"M8 18h.01",key:"lrp35t"}],["path",{d:"M12 18h.01",key:"mhygvu"}],["path",{d:"M16 18h.01",key:"kzsmim"}]],bj=Me("calendar-days",yj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const vj=[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}]],xj=Me("calendar",vj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const wj=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],_j=Me("chevron-down",wj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Sj=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]],Tj=Me("circle-alert",Sj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ej=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],kc=Me("circle-check",Ej);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Aj=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}],["path",{d:"M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662",key:"154egf"}]],jj=Me("circle-user",Aj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Nj=[["path",{d:"M21.54 15H17a2 2 0 0 0-2 2v4.54",key:"1djwo0"}],["path",{d:"M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",key:"1tzkfa"}],["path",{d:"M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05",key:"14pb5j"}],["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],uw=Me("earth",Nj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const kj=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],Rj=Me("external-link",kj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Cj=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],Oj=Me("eye",Cj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Dj=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M8 13h2",key:"yr2amv"}],["path",{d:"M14 13h2",key:"un5t4a"}],["path",{d:"M8 17h2",key:"2yhykz"}],["path",{d:"M14 17h2",key:"10kma7"}]],Oa=Me("file-spreadsheet",Dj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Mj=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]],Wl=Me("file-text",Mj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Bj=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z",key:"sc7q7i"}]],Uj=Me("funnel",Bj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Lj=[["rect",{x:"3",y:"8",width:"18",height:"4",rx:"1",key:"bkv52"}],["path",{d:"M12 8v13",key:"1c76mn"}],["path",{d:"M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7",key:"6wjy6b"}],["path",{d:"M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5",key:"1ihvrl"}]],wb=Me("gift",Lj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const zj=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]],Pj=Me("history",zj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Vj=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]],hw=Me("info",Vj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Hj=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]],$a=Me("loader-circle",Hj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const $j=[["circle",{cx:"12",cy:"16",r:"1",key:"1au0dj"}],["rect",{x:"3",y:"10",width:"18",height:"12",rx:"2",key:"6s8ecr"}],["path",{d:"M7 10V7a5 5 0 0 1 10 0v3",key:"1pqi11"}]],qj=Me("lock-keyhole",$j);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Fj=[["path",{d:"m16 17 5-5-5-5",key:"1bji2h"}],["path",{d:"M21 12H9",key:"dn1m92"}],["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}]],Ij=Me("log-out",Fj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Gj=[["path",{d:"M4 5h16",key:"1tepv9"}],["path",{d:"M4 12h16",key:"1lakjw"}],["path",{d:"M4 19h16",key:"1djgab"}]],dw=Me("menu",Gj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Kj=[["path",{d:"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z",key:"18887p"}],["path",{d:"M12 15h.01",key:"q59x07"}],["path",{d:"M12 7v4",key:"xawao1"}]],Yj=Me("message-square-warning",Kj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Xj=[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]],Jj=Me("play",Xj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Qj=[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]],Zj=Me("plus",Qj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Wj=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],eN=Me("refresh-cw",Wj);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const tN=[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]],Jr=Me("search",tN);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const nN=[["path",{d:"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z",key:"1ffxy3"}],["path",{d:"m21.854 2.147-10.94 10.939",key:"12cjpa"}]],fw=Me("send",nN);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const sN=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],Ea=Me("trash-2",sN);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const iN=[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]],fo=Me("triangle-alert",iN);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const aN=[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]],Gr=Me("upload",aN);/** + * @license lucide-react v0.546.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const rN=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],Ka=Me("x",rN),mo=[{code:"GT",name:"Guatemala",flag:"/cruce-cuentas/gt-flag.png",currency:"GTQ",locale:"es-GT",enabled:!0},{code:"TT",name:"Trinidad y Tobago",flag:"/cruce-cuentas/tt-flag.png",currency:"TTD",locale:"en-TT",enabled:!0},{code:"PA",name:"Panamá",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Panama.svg",currency:"USD",locale:"es-PA",enabled:!0},{code:"SV",name:"El Salvador",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_El_Salvador.svg.webp",currency:"USD",locale:"es-SV",enabled:!0},{code:"HN",name:"Honduras",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp",currency:"HNL",locale:"es-HN",enabled:!0},{code:"PR",name:"Puerto Rico",flag:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/bandera-nacional-tela-puerto-rico_113767-1759.avif",currency:"USD",locale:"es-PR",enabled:!0}];function _b(n){return mo.find(e=>e.code===n)??mo[0]}const oN="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png";function lN({currentView:n,onViewChange:e,onNewCruce:i,isSidebarOpen:a=!1,onCloseSidebar:l,user:c,isAuthEnabled:u=!1,onLogout:d,country:m,onCountryChange:p,onChooseAnotherCountry:y}){const[v,x]=D.useState(!1),_=D.useRef(null);return D.useEffect(()=>{function S(T){_.current&&!_.current.contains(T.target)&&x(!1)}return document.addEventListener("mousedown",S),()=>document.removeEventListener("mousedown",S)},[]),g.jsxs("aside",{className:`bg-neutral-100 h-screen w-64 fixed left-0 top-0 flex flex-col z-50 pb-6 min-h-screen font-sans transition-transform duration-300 md:translate-x-0 ${a?"translate-x-0":"-translate-x-full"}`,children:[g.jsx("div",{className:"absolute right-0 top-0 h-[80px] w-px bg-[#D0D0D0] pointer-events-none z-10 hidden md:block"}),g.jsx("div",{className:"absolute right-0 top-[84px] bottom-0 w-px bg-[#D0D0D0] pointer-events-none z-10 hidden md:block"}),g.jsxs("div",{className:"h-[84px] px-4 flex justify-between md:justify-center items-center bg-white border-b-4 border-[#6CC24A] relative",children:[g.jsx("img",{alt:"GomezLee Marketing logo",className:"h-10 object-contain selection:bg-transparent",src:oN}),g.jsx("button",{onClick:l,className:"md:hidden p-2 text-neutral-500 hover:text-neutral-800 transition-colors","aria-label":"Cerrar menú",children:g.jsx(Ka,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-4 pt-5",children:[g.jsxs("label",{className:"block",children:[g.jsxs("span",{className:"text-[9px] uppercase tracking-wider font-black text-neutral-400 flex items-center gap-1.5 mb-1.5",children:[g.jsx(uw,{className:"w-3.5 h-3.5"}),"País"]}),g.jsxs("div",{className:"relative",ref:_,children:[g.jsxs("button",{type:"button",onClick:()=>x(!v),className:"w-full border border-neutral-300 bg-white px-3 py-2 text-xs font-bold text-[#4F758B] focus:outline-none focus:border-[#4F758B] flex items-center justify-between transition-colors hover:bg-neutral-50",children:[g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("img",{src:m.flag,alt:"",className:"w-5 h-3.5 object-cover shadow-sm border border-neutral-200"}),g.jsx("span",{children:m.name})]}),g.jsx(_j,{className:`w-4 h-4 text-[#4F758B] transition-transform ${v?"rotate-180":""}`})]}),v&&g.jsx("ul",{className:"absolute left-0 right-0 top-full mt-1 bg-white border border-neutral-300 shadow-lg z-50 py-1",children:mo.map(S=>g.jsx("li",{children:g.jsxs("button",{type:"button",disabled:!S.enabled,onClick:()=>{p(S.code),x(!1)},className:`w-full text-left px-3 py-2.5 text-xs font-bold flex items-center gap-2.5 transition-colors ${S.enabled?"hover:bg-neutral-100 text-[#4F758B]":"opacity-60 cursor-not-allowed text-neutral-500 bg-neutral-50"}`,children:[g.jsx("img",{src:S.flag,alt:"",className:"w-6 h-4 object-cover shadow-sm border border-neutral-200 bg-white flex-shrink-0"}),g.jsxs("span",{className:"min-w-0 text-left leading-tight",children:[g.jsx("span",{className:"block",children:S.name}),!S.enabled&&g.jsx("span",{className:"block mt-1 text-[10px] text-neutral-400 font-medium italic",children:"Próximamente"})]})]})},S.code))})]})]}),g.jsx("button",{type:"button",onClick:y,className:"mt-2 text-[9px] font-bold uppercase tracking-wider text-neutral-400 hover:text-[#4F758B]",children:"Ver países disponibles"})]}),g.jsx("nav",{className:"flex-1 flex flex-col mt-5",children:g.jsxs("ul",{className:"space-y-1",children:[g.jsx("li",{children:g.jsxs("button",{onClick:()=>e("cruce"),className:`w-full flex items-center gap-3 px-6 py-3.5 transition-all duration-150 text-left font-bold text-sm tracking-wide ${n==="cruce"?"bg-[#4F758B] text-white":"text-neutral-600 hover:bg-neutral-200 hover:text-neutral-800"}`,children:[g.jsx(fj,{className:`w-4 h-4 ${n==="cruce"?"text-[#6CC24A]":""}`}),g.jsx("span",{children:"Cruce de Cuentas"})]})}),g.jsx("li",{children:g.jsxs("button",{onClick:()=>e("historial"),className:`w-full flex items-center gap-3 px-6 py-3.5 transition-all duration-150 text-left font-bold text-sm tracking-wide ${n==="historial"?"bg-[#4F758B] text-white":"text-neutral-600 hover:bg-neutral-200 hover:text-neutral-800"}`,children:[g.jsx(Pj,{className:`w-4 h-4 ${n==="historial"?"text-[#6CC24A]":""}`}),g.jsx("span",{children:"Reportes Históricos"})]})})]})}),g.jsxs("div",{className:"px-4 space-y-3 pt-4 border-t border-[#D0D0D0] bg-neutral-100",children:[c&&g.jsxs("div",{className:"border border-neutral-200 bg-white px-3 py-3 flex items-center gap-2",children:[g.jsx(jj,{className:"w-4 h-4 text-[#4F758B] flex-shrink-0"}),g.jsxs("div",{className:"min-w-0",children:[g.jsx("p",{className:"text-[11px] font-black text-neutral-700 truncate",title:c.name,children:c.name}),g.jsx("p",{className:"text-[9px] text-neutral-400 truncate",title:c.email,children:c.email})]})]}),g.jsxs("button",{onClick:i,className:"w-full bg-[#6CC24A] hover:bg-[#5bb03c] text-white py-3 px-4 font-bold text-xs uppercase tracking-wider flex items-center justify-center gap-2 transition-all duration-150 shadow-sm active:scale-98",children:[g.jsx(Zj,{className:"w-4 h-4"}),g.jsx("span",{children:"Nuevo Cruce"})]}),u&&d&&g.jsxs("button",{onClick:d,className:"w-full border border-neutral-300 bg-white hover:bg-neutral-50 text-neutral-600 py-2.5 px-4 font-bold text-[11px] uppercase tracking-wider flex items-center justify-center gap-2 transition-all duration-150",children:[g.jsx(Ij,{className:"w-4 h-4"}),g.jsx("span",{children:"Cerrar sesión"})]})]})]})}function Fc(n,e){var i={};for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&e.indexOf(a)<0&&(i[a]=n[a]);if(n!=null&&typeof Object.getOwnPropertySymbols=="function")for(var l=0,a=Object.getOwnPropertySymbols(n);ln?(...e)=>n(...e):(...e)=>fetch(...e);class tm extends Error{constructor(e,i="FunctionsError",a){super(e),this.name=i,this.context=a}toJSON(){return{name:this.name,message:this.message,context:this.context}}}class hN extends tm{constructor(e){super("Failed to send a request to the Edge Function","FunctionsFetchError",e)}}class Sb extends tm{constructor(e){super("Relay Error invoking the Edge Function","FunctionsRelayError",e)}}class Tb extends tm{constructor(e){super("Edge Function returned a non-2xx status code","FunctionsHttpError",e)}}var cf;(function(n){n.Any="any",n.ApNortheast1="ap-northeast-1",n.ApNortheast2="ap-northeast-2",n.ApSouth1="ap-south-1",n.ApSoutheast1="ap-southeast-1",n.ApSoutheast2="ap-southeast-2",n.CaCentral1="ca-central-1",n.EuCentral1="eu-central-1",n.EuWest1="eu-west-1",n.EuWest2="eu-west-2",n.EuWest3="eu-west-3",n.SaEast1="sa-east-1",n.UsEast1="us-east-1",n.UsWest1="us-west-1",n.UsWest2="us-west-2"})(cf||(cf={}));class dN{constructor(e,{headers:i={},customFetch:a,region:l=cf.Any}={}){this.url=e,this.headers=i,this.region=l,this.fetch=uN(a)}setAuth(e){this.headers.Authorization=`Bearer ${e}`}invoke(e){return cN(this,arguments,void 0,function*(i,a={}){var l;let c,u;try{const{headers:d,method:m,body:p,signal:y,timeout:v}=a;let x={},{region:_}=a;_||(_=this.region);const S=new URL(`${this.url}/${i}`);_&&_!=="any"&&(x["x-region"]=_,S.searchParams.set("forceFunctionRegion",_));let T;const k=!!d&&Object.keys(d).some(X=>X.toLowerCase()==="content-type");p&&!k?typeof Blob<"u"&&p instanceof Blob||p instanceof ArrayBuffer?(x["Content-Type"]="application/octet-stream",T=p):typeof p=="string"?(x["Content-Type"]="text/plain",T=p):typeof FormData<"u"&&p instanceof FormData?T=p:(x["Content-Type"]="application/json",T=JSON.stringify(p)):p&&typeof p!="string"&&!(typeof Blob<"u"&&p instanceof Blob)&&!(p instanceof ArrayBuffer)&&!(typeof FormData<"u"&&p instanceof FormData)?T=JSON.stringify(p):T=p;let R=y;v&&(u=new AbortController,c=setTimeout(()=>u.abort(),v),y?(R=u.signal,y.addEventListener("abort",()=>u.abort())):R=u.signal);const M=yield this.fetch(S.toString(),{method:m||"POST",headers:Object.assign(Object.assign(Object.assign({},x),this.headers),d),body:T,signal:R}).catch(X=>{throw new hN(X)}),N=M.headers.get("x-relay-error");if(N&&N==="true")throw new Sb(M);if(!M.ok)throw new Tb(M);let P=((l=M.headers.get("Content-Type"))!==null&&l!==void 0?l:"text/plain").split(";")[0].trim(),G;return P==="application/json"?G=yield M.json():P==="application/octet-stream"||P==="application/pdf"?G=yield M.blob():P==="text/event-stream"?G=M:P==="multipart/form-data"?G=yield M.formData():G=yield M.text(),{data:G,error:null,response:M}}catch(d){return{data:null,error:d,response:d instanceof Tb||d instanceof Sb?d.context:void 0}}finally{c&&clearTimeout(c)}})}}const mw=3,Eb=n=>Math.min(1e3*2**n,3e4),fN=[520,503],pw=["GET","HEAD","OPTIONS"];var Ab=class extends Error{constructor(n){super(n.message),this.name="PostgrestError",this.details=n.details,this.hint=n.hint,this.code=n.code}toJSON(){return{name:this.name,message:this.message,details:this.details,hint:this.hint,code:this.code}}};function jb(n,e){return new Promise(i=>{if(e!=null&&e.aborted){i();return}const a=setTimeout(()=>{e==null||e.removeEventListener("abort",l),i()},n);function l(){clearTimeout(a),i()}e==null||e.addEventListener("abort",l)})}function mN(n,e,i,a){return!(!a||i>=mw||!pw.includes(n)||!fN.includes(e))}var pN=class{constructor(n){var e,i,a,l,c;this.shouldThrowOnError=!1,this.retryEnabled=!0,this.method=n.method,this.url=n.url,this.headers=new Headers(n.headers),this.schema=n.schema,this.body=n.body,this.shouldThrowOnError=(e=n.shouldThrowOnError)!==null&&e!==void 0?e:!1,this.signal=n.signal,this.isMaybeSingle=(i=n.isMaybeSingle)!==null&&i!==void 0?i:!1,this.shouldStripNulls=(a=n.shouldStripNulls)!==null&&a!==void 0?a:!1,this.urlLengthLimit=(l=n.urlLengthLimit)!==null&&l!==void 0?l:8e3,this.retryEnabled=(c=n.retry)!==null&&c!==void 0?c:!0,n.fetch?this.fetch=n.fetch:this.fetch=fetch}throwOnError(){return this.shouldThrowOnError=!0,this}stripNulls(){if(this.headers.get("Accept")==="text/csv")throw new Error("stripNulls() cannot be used with csv()");return this.shouldStripNulls=!0,this}setHeader(n,e){return this.headers=new Headers(this.headers),this.headers.set(n,e),this}retry(n){return this.retryEnabled=n,this}then(n,e){var i=this;if(this.schema===void 0||(["GET","HEAD"].includes(this.method)?this.headers.set("Accept-Profile",this.schema):this.headers.set("Content-Profile",this.schema)),this.method!=="GET"&&this.method!=="HEAD"&&this.headers.set("Content-Type","application/json"),this.shouldStripNulls){const u=this.headers.get("Accept");u==="application/vnd.pgrst.object+json"?this.headers.set("Accept","application/vnd.pgrst.object+json;nulls=stripped"):(!u||u==="application/json")&&this.headers.set("Accept","application/vnd.pgrst.array+json;nulls=stripped")}const a=this.fetch;let c=(async()=>{let u=0;for(;;){const p={};i.headers.forEach((v,x)=>{p[x]=v}),u>0&&(p["X-Retry-Count"]=String(u));let y;try{y=await a(i.url.toString(),{method:i.method,headers:p,body:JSON.stringify(i.body,(v,x)=>typeof x=="bigint"?x.toString():x),signal:i.signal})}catch(v){if((v==null?void 0:v.name)==="AbortError"||(v==null?void 0:v.code)==="ABORT_ERR"||!pw.includes(i.method))throw v;if(i.retryEnabled&&u{var d;let m="",p="",y="";const v=u==null?void 0:u.cause;if(v){var x,_,S,T;const M=(x=v==null?void 0:v.message)!==null&&x!==void 0?x:"",N=(_=v==null?void 0:v.code)!==null&&_!==void 0?_:"";m=`${(S=u==null?void 0:u.name)!==null&&S!==void 0?S:"FetchError"}: ${u==null?void 0:u.message}`,m+=` + +Caused by: ${(T=v==null?void 0:v.name)!==null&&T!==void 0?T:"Error"}: ${M}`,N&&(m+=` (${N})`),v!=null&&v.stack&&(m+=` +${v.stack}`)}else{var k;m=(k=u==null?void 0:u.stack)!==null&&k!==void 0?k:""}const R=this.url.toString().length;return(u==null?void 0:u.name)==="AbortError"||(u==null?void 0:u.code)==="ABORT_ERR"?(y="",p="Request was aborted (timeout or manual cancellation)",R>this.urlLengthLimit&&(p+=`. Note: Your request URL is ${R} characters, which may exceed server limits. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [many IDs])), consider using an RPC function to pass values server-side.`)):((v==null?void 0:v.name)==="HeadersOverflowError"||(v==null?void 0:v.code)==="UND_ERR_HEADERS_OVERFLOW")&&(y="",p="HTTP headers exceeded server limits (typically 16KB)",R>this.urlLengthLimit&&(p+=`. Your request URL is ${R} characters. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [200+ IDs])), consider using an RPC function instead.`)),{success:!1,error:{message:`${(d=u==null?void 0:u.name)!==null&&d!==void 0?d:"FetchError"}: ${u==null?void 0:u.message}`,details:m,hint:p,code:y},data:null,count:null,status:0,statusText:""}})),c.then(n,e)}async processResponse(n){var e=this;let i=null,a=null,l=null,c=n.status,u=n.statusText;if(n.ok){var d,m;if(e.method!=="HEAD"){var p;const x=await n.text();if(x!=="")if(e.headers.get("Accept")==="text/csv")a=x;else if(e.headers.get("Accept")&&(!((p=e.headers.get("Accept"))===null||p===void 0)&&p.includes("application/vnd.pgrst.plan+text")))a=x;else try{a=JSON.parse(x)}catch{if(i={message:x},a=null,e.shouldThrowOnError)throw new Ab({message:x,details:"",hint:"",code:""})}}const y=(d=e.headers.get("Prefer"))===null||d===void 0?void 0:d.match(/count=(exact|planned|estimated)/),v=(m=n.headers.get("content-range"))===null||m===void 0?void 0:m.split("/");y&&v&&v.length>1&&(l=parseInt(v[1])),e.isMaybeSingle&&Array.isArray(a)&&(a.length>1?(i={code:"PGRST116",details:`Results contain ${a.length} rows, application/vnd.pgrst.object+json requires 1 row`,hint:null,message:"JSON object requested, multiple (or no) rows returned"},a=null,l=null,c=406,u="Not Acceptable"):a.length===1?a=a[0]:a=null)}else{const y=await n.text();try{i=JSON.parse(y),Array.isArray(i)&&n.status===404&&(a=[],i=null,c=200,u="OK")}catch{n.status===404&&y===""?(c=204,u="No Content"):i={message:y}}if(i&&e.shouldThrowOnError)throw new Ab(i)}return{success:i===null,error:i,data:a,count:l,status:c,statusText:u}}returns(){return this}overrideTypes(){return this}},gN=class extends pN{throwOnError(){return super.throwOnError()}select(n){let e=!1;const i=(n??"*").split("").map(a=>/\s/.test(a)&&!e?"":(a==='"'&&(e=!e),a)).join("");return this.url.searchParams.set("select",i),this.headers.append("Prefer","return=representation"),this}order(n,{ascending:e=!0,nullsFirst:i,foreignTable:a,referencedTable:l=a}={}){const c=l?`${l}.order`:"order",u=this.url.searchParams.get(c);return this.url.searchParams.set(c,`${u?`${u},`:""}${n}.${e?"asc":"desc"}${i===void 0?"":i?".nullsfirst":".nullslast"}`),this}limit(n,{foreignTable:e,referencedTable:i=e}={}){const a=typeof i>"u"?"limit":`${i}.limit`;return this.url.searchParams.set(a,`${n}`),this}range(n,e,{foreignTable:i,referencedTable:a=i}={}){const l=typeof a>"u"?"offset":`${a}.offset`,c=typeof a>"u"?"limit":`${a}.limit`;return this.url.searchParams.set(l,`${n}`),this.url.searchParams.set(c,`${e-n+1}`),this}abortSignal(n){return this.signal=n,this}single(){return this.headers.set("Accept","application/vnd.pgrst.object+json"),this}maybeSingle(){return this.isMaybeSingle=!0,this}csv(){return this.headers.set("Accept","text/csv"),this}geojson(){return this.headers.set("Accept","application/geo+json"),this}explain({analyze:n=!1,verbose:e=!1,settings:i=!1,buffers:a=!1,wal:l=!1,format:c="text"}={}){var u;const d=[n?"analyze":null,e?"verbose":null,i?"settings":null,a?"buffers":null,l?"wal":null].filter(Boolean).join("|"),m=(u=this.headers.get("Accept"))!==null&&u!==void 0?u:"application/json";return this.headers.set("Accept",`application/vnd.pgrst.plan+${c}; for="${m}"; options=${d};`),c==="json"?this:this}rollback(){return this.headers.append("Prefer","tx=rollback"),this}returns(){return this}maxAffected(n){return this.headers.append("Prefer","handling=strict"),this.headers.append("Prefer",`max-affected=${n}`),this}};const Nb=new RegExp("[,()]");var Da=class extends gN{throwOnError(){return super.throwOnError()}eq(n,e){return this.url.searchParams.append(n,`eq.${e}`),this}neq(n,e){return this.url.searchParams.append(n,`neq.${e}`),this}gt(n,e){return this.url.searchParams.append(n,`gt.${e}`),this}gte(n,e){return this.url.searchParams.append(n,`gte.${e}`),this}lt(n,e){return this.url.searchParams.append(n,`lt.${e}`),this}lte(n,e){return this.url.searchParams.append(n,`lte.${e}`),this}like(n,e){return this.url.searchParams.append(n,`like.${e}`),this}likeAllOf(n,e){return this.url.searchParams.append(n,`like(all).{${e.join(",")}}`),this}likeAnyOf(n,e){return this.url.searchParams.append(n,`like(any).{${e.join(",")}}`),this}ilike(n,e){return this.url.searchParams.append(n,`ilike.${e}`),this}ilikeAllOf(n,e){return this.url.searchParams.append(n,`ilike(all).{${e.join(",")}}`),this}ilikeAnyOf(n,e){return this.url.searchParams.append(n,`ilike(any).{${e.join(",")}}`),this}regexMatch(n,e){return this.url.searchParams.append(n,`match.${e}`),this}regexIMatch(n,e){return this.url.searchParams.append(n,`imatch.${e}`),this}is(n,e){return this.url.searchParams.append(n,`is.${e}`),this}isDistinct(n,e){return this.url.searchParams.append(n,`isdistinct.${e}`),this}in(n,e){const i=Array.from(new Set(e)).map(a=>typeof a=="string"&&Nb.test(a)?`"${a}"`:`${a}`).join(",");return this.url.searchParams.append(n,`in.(${i})`),this}notIn(n,e){const i=Array.from(new Set(e)).map(a=>typeof a=="string"&&Nb.test(a)?`"${a}"`:`${a}`).join(",");return this.url.searchParams.append(n,`not.in.(${i})`),this}contains(n,e){return typeof e=="string"?this.url.searchParams.append(n,`cs.${e}`):Array.isArray(e)?this.url.searchParams.append(n,`cs.{${e.join(",")}}`):this.url.searchParams.append(n,`cs.${JSON.stringify(e)}`),this}containedBy(n,e){return typeof e=="string"?this.url.searchParams.append(n,`cd.${e}`):Array.isArray(e)?this.url.searchParams.append(n,`cd.{${e.join(",")}}`):this.url.searchParams.append(n,`cd.${JSON.stringify(e)}`),this}rangeGt(n,e){return this.url.searchParams.append(n,`sr.${e}`),this}rangeGte(n,e){return this.url.searchParams.append(n,`nxl.${e}`),this}rangeLt(n,e){return this.url.searchParams.append(n,`sl.${e}`),this}rangeLte(n,e){return this.url.searchParams.append(n,`nxr.${e}`),this}rangeAdjacent(n,e){return this.url.searchParams.append(n,`adj.${e}`),this}overlaps(n,e){return typeof e=="string"?this.url.searchParams.append(n,`ov.${e}`):this.url.searchParams.append(n,`ov.{${e.join(",")}}`),this}textSearch(n,e,{config:i,type:a}={}){let l="";a==="plain"?l="pl":a==="phrase"?l="ph":a==="websearch"&&(l="w");const c=i===void 0?"":`(${i})`;return this.url.searchParams.append(n,`${l}fts${c}.${e}`),this}match(n){return Object.entries(n).filter(([e,i])=>i!==void 0).forEach(([e,i])=>{this.url.searchParams.append(e,`eq.${i}`)}),this}not(n,e,i){return this.url.searchParams.append(n,`not.${e}.${i}`),this}or(n,{foreignTable:e,referencedTable:i=e}={}){const a=i?`${i}.or`:"or";return this.url.searchParams.append(a,`(${n})`),this}filter(n,e,i){return this.url.searchParams.append(n,`${e}.${i}`),this}},yN=class{constructor(n,{headers:e={},schema:i,fetch:a,urlLengthLimit:l=8e3,retry:c}){this.url=n,this.headers=new Headers(e),this.schema=i,this.fetch=a,this.urlLengthLimit=l,this.retry=c}cloneRequestState(){return{url:new URL(this.url.toString()),headers:new Headers(this.headers)}}select(n,e){const{head:i=!1,count:a}=e??{},l=i?"HEAD":"GET";let c=!1;const u=(n??"*").split("").map(p=>/\s/.test(p)&&!c?"":(p==='"'&&(c=!c),p)).join(""),{url:d,headers:m}=this.cloneRequestState();return d.searchParams.set("select",u),a&&m.append("Prefer",`count=${a}`),new Da({method:l,url:d,headers:m,schema:this.schema,fetch:this.fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}insert(n,{count:e,defaultToNull:i=!0}={}){var a;const l="POST",{url:c,headers:u}=this.cloneRequestState();if(e&&u.append("Prefer",`count=${e}`),i||u.append("Prefer","missing=default"),Array.isArray(n)){const d=n.reduce((m,p)=>m.concat(Object.keys(p)),[]);if(d.length>0){const m=[...new Set(d)].map(p=>`"${p}"`);c.searchParams.set("columns",m.join(","))}}return new Da({method:l,url:c,headers:u,schema:this.schema,body:n,fetch:(a=this.fetch)!==null&&a!==void 0?a:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}upsert(n,{onConflict:e,ignoreDuplicates:i=!1,count:a,defaultToNull:l=!0}={}){var c;const u="POST",{url:d,headers:m}=this.cloneRequestState();if(m.append("Prefer",`resolution=${i?"ignore":"merge"}-duplicates`),e!==void 0&&d.searchParams.set("on_conflict",e),a&&m.append("Prefer",`count=${a}`),l||m.append("Prefer","missing=default"),Array.isArray(n)){const p=n.reduce((y,v)=>y.concat(Object.keys(v)),[]);if(p.length>0){const y=[...new Set(p)].map(v=>`"${v}"`);d.searchParams.set("columns",y.join(","))}}return new Da({method:u,url:d,headers:m,schema:this.schema,body:n,fetch:(c=this.fetch)!==null&&c!==void 0?c:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}update(n,{count:e}={}){var i;const a="PATCH",{url:l,headers:c}=this.cloneRequestState();return e&&c.append("Prefer",`count=${e}`),new Da({method:a,url:l,headers:c,schema:this.schema,body:n,fetch:(i=this.fetch)!==null&&i!==void 0?i:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}delete({count:n}={}){var e;const i="DELETE",{url:a,headers:l}=this.cloneRequestState();return n&&l.append("Prefer",`count=${n}`),new Da({method:i,url:a,headers:l,schema:this.schema,fetch:(e=this.fetch)!==null&&e!==void 0?e:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}};function po(n){"@babel/helpers - typeof";return po=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},po(n)}function bN(n,e){if(po(n)!="object"||!n)return n;var i=n[Symbol.toPrimitive];if(i!==void 0){var a=i.call(n,e);if(po(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(n)}function vN(n){var e=bN(n,"string");return po(e)=="symbol"?e:e+""}function xN(n,e,i){return(e=vN(e))in n?Object.defineProperty(n,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[e]=i,n}function kb(n,e){var i=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(l){return Object.getOwnPropertyDescriptor(n,l).enumerable})),i.push.apply(i,a)}return i}function ec(n){for(var e=1;e0?this.fetch=(p,y)=>{const v=new AbortController,x=setTimeout(()=>v.abort(),c),_=y==null?void 0:y.signal;if(_){if(_.aborted)return clearTimeout(x),m(p,y);const S=()=>{clearTimeout(x),v.abort()};return _.addEventListener("abort",S,{once:!0}),m(p,ec(ec({},y),{},{signal:v.signal})).finally(()=>{clearTimeout(x),_.removeEventListener("abort",S)})}return m(p,ec(ec({},y),{},{signal:v.signal})).finally(()=>clearTimeout(x))}:this.fetch=m,this.retry=d}from(e){if(!e||typeof e!="string"||e.trim()==="")throw new Error("Invalid relation name: relation must be a non-empty string.");return new yN(new URL(`${this.url}/${e}`),{headers:new Headers(this.headers),schema:this.schemaName,fetch:this.fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}schema(e){return new gw(this.url,{headers:this.headers,schema:e,fetch:this.fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}rpc(e,i={},{head:a=!1,get:l=!1,count:c}={}){var u;let d;const m=new URL(`${this.url}/rpc/${e}`);let p;const y=_=>_!==null&&typeof _=="object"&&(!Array.isArray(_)||_.some(y)),v=a&&Object.values(i).some(y);v?(d="POST",p=i):a||l?(d=a?"HEAD":"GET",Object.entries(i).filter(([_,S])=>S!==void 0).map(([_,S])=>[_,Array.isArray(S)?`{${S.join(",")}}`:`${S}`]).forEach(([_,S])=>{m.searchParams.append(_,S)})):(d="POST",p=i);const x=new Headers(this.headers);return v?x.set("Prefer",c?`count=${c},return=minimal`:"return=minimal"):c&&x.set("Prefer",`count=${c}`),new Da({method:d,url:m,headers:x,schema:this.schemaName,body:p,fetch:(u=this.fetch)!==null&&u!==void 0?u:fetch,urlLengthLimit:this.urlLengthLimit,retry:this.retry})}};class _N{constructor(){}static detectEnvironment(){var e;if(typeof WebSocket<"u")return{type:"native",wsConstructor:WebSocket};const i=globalThis;if(typeof globalThis<"u"&&typeof i.WebSocket<"u")return{type:"native",wsConstructor:i.WebSocket};const a=typeof global<"u"?global:void 0;if(a&&typeof a.WebSocket<"u")return{type:"native",wsConstructor:a.WebSocket};if(typeof globalThis<"u"&&typeof i.WebSocketPair<"u"&&typeof globalThis.WebSocket>"u")return{type:"cloudflare",error:"Cloudflare Workers detected. WebSocket clients are not supported in Cloudflare Workers.",workaround:"Use Cloudflare Workers WebSocket API for server-side WebSocket handling, or deploy to a different runtime."};if(typeof globalThis<"u"&&i.EdgeRuntime||typeof navigator<"u"&&(!((e=navigator.userAgent)===null||e===void 0)&&e.includes("Vercel-Edge")))return{type:"unsupported",error:"Edge runtime detected (Vercel Edge/Netlify Edge). WebSockets are not supported in edge functions.",workaround:"Use serverless functions or a different deployment target for WebSocket functionality."};const l=globalThis.process;if(l){const c=l.versions;if(c&&c.node)return{type:"unsupported",error:"Node.js detected but native WebSocket not found.",workaround:"Ensure you are running Node.js 22+ or provide a WebSocket implementation via the transport option."}}return{type:"unsupported",error:"Unknown JavaScript runtime without WebSocket support.",workaround:"Ensure you're running in a supported environment (browser, Node.js, Deno) or provide a custom WebSocket implementation."}}static getWebSocketConstructor(){const e=this.detectEnvironment();if(e.wsConstructor)return e.wsConstructor;let i=e.error||"WebSocket not supported in this environment.";throw e.workaround&&(i+=` + +Suggested solution: ${e.workaround}`),new Error(i)}static isWebSocketSupported(){try{return this.detectEnvironment().type==="native"}catch{return!1}}}const SN="2.110.2",TN=`realtime-js/${SN}`,EN="1.0.0",yw="2.0.0",AN=yw,jN=1e4,NN=100,ii={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},bw={close:"phx_close",error:"phx_error",join:"phx_join",leave:"phx_leave",access_token:"access_token"},uf={connecting:"connecting",closing:"closing",closed:"closed"};class kN{constructor(e){this.HEADER_LENGTH=1,this.USER_BROADCAST_PUSH_META_LENGTH=6,this.KINDS={userBroadcastPush:3,userBroadcast:4},this.BINARY_ENCODING=0,this.JSON_ENCODING=1,this.BROADCAST_EVENT="broadcast",this.allowedMetadataKeys=[],this.allowedMetadataKeys=e??[]}encode(e,i){if(e.event===this.BROADCAST_EVENT&&!(e.payload instanceof ArrayBuffer)&&typeof e.payload.event=="string")return i(this._binaryEncodeUserBroadcastPush(e));let a=[e.join_ref,e.ref,e.topic,e.event,e.payload];return i(JSON.stringify(a))}_binaryEncodeUserBroadcastPush(e){var i;return this._isArrayBuffer((i=e.payload)===null||i===void 0?void 0:i.payload)?this._encodeBinaryUserBroadcastPush(e):this._encodeJsonUserBroadcastPush(e)}_encodeBinaryUserBroadcastPush(e){var i,a;const l=(a=(i=e.payload)===null||i===void 0?void 0:i.payload)!==null&&a!==void 0?a:new ArrayBuffer(0);return this._encodeUserBroadcastPush(e,this.BINARY_ENCODING,l)}_encodeJsonUserBroadcastPush(e){var i,a;const l=(a=(i=e.payload)===null||i===void 0?void 0:i.payload)!==null&&a!==void 0?a:{},u=new TextEncoder().encode(JSON.stringify(l)).buffer;return this._encodeUserBroadcastPush(e,this.JSON_ENCODING,u)}_encodeUserBroadcastPush(e,i,a){var l,c;const u=e.topic,d=(l=e.ref)!==null&&l!==void 0?l:"",m=(c=e.join_ref)!==null&&c!==void 0?c:"",p=e.payload.event,y=this.allowedMetadataKeys?this._pick(e.payload,this.allowedMetadataKeys):{},v=Object.keys(y).length===0?"":JSON.stringify(y);if(m.length>255)throw new Error(`joinRef length ${m.length} exceeds maximum of 255`);if(d.length>255)throw new Error(`ref length ${d.length} exceeds maximum of 255`);if(u.length>255)throw new Error(`topic length ${u.length} exceeds maximum of 255`);if(p.length>255)throw new Error(`userEvent length ${p.length} exceeds maximum of 255`);if(v.length>255)throw new Error(`metadata length ${v.length} exceeds maximum of 255`);const x=this.USER_BROADCAST_PUSH_META_LENGTH+m.length+d.length+u.length+p.length+v.length,_=new ArrayBuffer(this.HEADER_LENGTH+x);let S=new DataView(_),T=0;S.setUint8(T++,this.KINDS.userBroadcastPush),S.setUint8(T++,m.length),S.setUint8(T++,d.length),S.setUint8(T++,u.length),S.setUint8(T++,p.length),S.setUint8(T++,v.length),S.setUint8(T++,i),Array.from(m,R=>S.setUint8(T++,R.charCodeAt(0))),Array.from(d,R=>S.setUint8(T++,R.charCodeAt(0))),Array.from(u,R=>S.setUint8(T++,R.charCodeAt(0))),Array.from(p,R=>S.setUint8(T++,R.charCodeAt(0))),Array.from(v,R=>S.setUint8(T++,R.charCodeAt(0)));var k=new Uint8Array(_.byteLength+a.byteLength);return k.set(new Uint8Array(_),0),k.set(new Uint8Array(a),_.byteLength),k.buffer}decode(e,i){if(this._isArrayBuffer(e)){let a=this._binaryDecode(e);return i(a)}if(typeof e=="string"){const a=JSON.parse(e),[l,c,u,d,m]=a;return i({join_ref:l,ref:c,topic:u,event:d,payload:m})}return i({})}_binaryDecode(e){const i=new DataView(e),a=i.getUint8(0),l=new TextDecoder;switch(a){case this.KINDS.userBroadcast:return this._decodeUserBroadcast(e,i,l)}}_decodeUserBroadcast(e,i,a){const l=i.getUint8(1),c=i.getUint8(2),u=i.getUint8(3),d=i.getUint8(4);let m=this.HEADER_LENGTH+4;const p=a.decode(e.slice(m,m+l));m=m+l;const y=a.decode(e.slice(m,m+c));m=m+c;const v=a.decode(e.slice(m,m+u));m=m+u;const x=e.slice(m,e.byteLength),_=d===this.JSON_ENCODING?JSON.parse(a.decode(x)):x,S={type:this.BROADCAST_EVENT,event:y,payload:_};return u>0&&(S.meta=JSON.parse(v)),{join_ref:null,ref:null,topic:p,event:this.BROADCAST_EVENT,payload:S}}_isArrayBuffer(e){var i;return e instanceof ArrayBuffer||((i=e==null?void 0:e.constructor)===null||i===void 0?void 0:i.name)==="ArrayBuffer"}_pick(e,i){return!e||typeof e!="object"?{}:Object.fromEntries(Object.entries(e).filter(([a])=>i.includes(a)))}}var Je;(function(n){n.abstime="abstime",n.bool="bool",n.date="date",n.daterange="daterange",n.float4="float4",n.float8="float8",n.int2="int2",n.int4="int4",n.int4range="int4range",n.int8="int8",n.int8range="int8range",n.json="json",n.jsonb="jsonb",n.money="money",n.numeric="numeric",n.oid="oid",n.reltime="reltime",n.text="text",n.time="time",n.timestamp="timestamp",n.timestamptz="timestamptz",n.timetz="timetz",n.tsrange="tsrange",n.tstzrange="tstzrange"})(Je||(Je={}));const Rb=(n,e,i={})=>{var a;const l=(a=i.skipTypes)!==null&&a!==void 0?a:[];return e?Object.keys(e).reduce((c,u)=>(c[u]=RN(u,n,e,l),c),{}):{}},RN=(n,e,i,a)=>{const l=e.find(d=>d.name===n),c=l==null?void 0:l.type,u=i[n];return c&&!a.includes(c)?vw(c,u):hf(u)},vw=(n,e)=>{if(n.charAt(0)==="_"){const i=n.slice(1,n.length);return MN(e,i)}switch(n){case Je.bool:return CN(e);case Je.float4:case Je.float8:case Je.int2:case Je.int4:case Je.int8:case Je.numeric:case Je.oid:return ON(e);case Je.json:case Je.jsonb:return DN(e);case Je.timestamp:return BN(e);case Je.abstime:case Je.date:case Je.daterange:case Je.int4range:case Je.int8range:case Je.money:case Je.reltime:case Je.text:case Je.time:case Je.timestamptz:case Je.timetz:case Je.tsrange:case Je.tstzrange:return hf(e);default:return hf(e)}},hf=n=>n,CN=n=>{switch(n){case"t":return!0;case"f":return!1;default:return n}},ON=n=>{if(typeof n=="string"){const e=parseFloat(n);if(!Number.isNaN(e))return e}return n},DN=n=>{if(typeof n=="string")try{return JSON.parse(n)}catch{return n}return n},MN=(n,e)=>{if(typeof n!="string")return n;const i=n.length-1,a=n[i];if(n[0]==="{"&&a==="}"){let c;const u=n.slice(1,i);try{c=JSON.parse("["+u+"]")}catch{c=u?u.split(","):[]}return c.map(d=>vw(e,d))}return n},BN=n=>typeof n=="string"?n.replace(" ","T"):n,xw=n=>{const e=new URL(n);return e.protocol=e.protocol.replace(/^ws/i,"http"),e.pathname=e.pathname.replace(/\/+$/,"").replace(/\/socket\/websocket$/i,"").replace(/\/socket$/i,"").replace(/\/websocket$/i,""),e.pathname===""||e.pathname==="/"?e.pathname="/api/broadcast":e.pathname=e.pathname+"/api/broadcast",e.href};var io=n=>typeof n=="function"?n:function(){return n},UN=typeof self<"u"?self:null,Ma=typeof window<"u"?window:null,Qn=UN||Ma||globalThis,LN="2.0.0",zN=1e4,PN=1e3,Wn={connecting:0,open:1,closing:2,closed:3},Wt={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},js={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},df={longpoll:"longpoll",websocket:"websocket"},VN={complete:4},ff="base64url.bearer.phx.",tc=class{constructor(n,e,i,a){this.channel=n,this.event=e,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=a,this.timeoutTimer=null,this.recHooks=[],this.sent=!1,this.ref=void 0}resend(n){this.timeout=n,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()}))}receive(n,e){return this.hasReceived(n)&&e(this.receivedResp.response),this.recHooks.push({status:n,callback:e}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}destroy(){this.cancelRefEvent(),this.cancelTimeout()}matchReceive({status:n,response:e,_ref:i}){this.recHooks.filter(a=>a.status===n).forEach(a=>a.callback(e))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,n=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=n,this.matchReceive(n)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(n){return this.receivedResp&&this.receivedResp.status===n}trigger(n,e){this.channel.trigger(this.refEvent,{status:n,response:e})}},ww=class{constructor(n,e){this.callback=n,this.timerCalc=e,this.timer=void 0,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}},HN=class{constructor(n,e,i){this.state=Wt.closed,this.topic=n,this.params=io(e||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new tc(this,js.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new ww(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=Wt.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(a=>a.send()),this.pushBuffer=[]}),this.joinPush.receive("error",a=>{this.state=Wt.errored,this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,a),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic}`),this.state=Wt.closed,this.socket.remove(this)}),this.onError(a=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,a),this.isJoining()&&this.joinPush.reset(),this.state=Wt.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic}`,this.joinPush.timeout),new tc(this,js.leave,io({}),this.timeout).send(),this.state=Wt.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(js.reply,(a,l)=>{this.trigger(this.replyEventName(l),a)})}join(n=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=n,this.joinedOnce=!0,this.rejoin(),this.joinPush}teardown(){this.pushBuffer.forEach(n=>n.destroy()),this.pushBuffer=[],this.rejoinTimer.reset(),this.joinPush.destroy(),this.state=Wt.closed,this.bindings=[]}onClose(n){this.on(js.close,n)}onError(n){return this.on(js.error,e=>n(e))}on(n,e){let i=this.bindingRef++;return this.bindings.push({event:n,ref:i,callback:e}),i}off(n,e){this.bindings=this.bindings.filter(i=>!(i.event===n&&(typeof e>"u"||e===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(n,e,i=this.timeout){if(e=e||{},!this.joinedOnce)throw new Error(`tried to push '${n}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let a=new tc(this,n,function(){return e},i);return this.canPush()?a.send():(a.startTimeout(),this.pushBuffer.push(a)),a}leave(n=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=Wt.leaving;let e=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(js.close,"leave")},i=new tc(this,js.leave,io({}),n);return i.receive("ok",()=>e()).receive("timeout",()=>e()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(n,e,i){return e}filterBindings(n,e,i){return!0}isMember(n,e,i,a){return this.topic!==n?!1:a&&a!==this.joinRef()?(this.socket.hasLogger()&&this.socket.log("channel","dropping outdated message",{topic:n,event:e,payload:i,joinRef:a}),!1):!0}joinRef(){return this.joinPush.ref}rejoin(n=this.timeout){this.isLeaving()||(this.socket.leaveOpenTopic(this.topic),this.state=Wt.joining,this.joinPush.resend(n))}trigger(n,e,i,a){let l=this.onMessage(n,e,i,a);if(e&&!l)throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");let c=this.bindings.filter(u=>u.event===n&&this.filterBindings(u,e,i));for(let u=0;um.abort(),l),d.signal=m.signal),Qn.fetch(e,d).then(p=>p.text()).then(p=>this.parseJSON(p)).then(p=>u&&u(p)).catch(p=>{p.name==="AbortError"&&c?c():u&&u(null)}),m}static xdomainRequest(n,e,i,a,l,c,u){return n.timeout=l,n.open(e,i),n.onload=()=>{let d=this.parseJSON(n.responseText);u&&u(d)},c&&(n.ontimeout=c),n.onprogress=()=>{},n.send(a),n}static xhrRequest(n,e,i,a,l,c,u,d){n.open(e,i,!0),n.timeout=c;for(let[m,p]of Object.entries(a))n.setRequestHeader(m,p);return n.onerror=()=>d&&d(null),n.onreadystatechange=()=>{if(n.readyState===VN.complete&&d){let m=this.parseJSON(n.responseText);d(m)}},u&&(n.ontimeout=u),n.send(l),n}static parseJSON(n){if(!n||n==="")return null;try{return JSON.parse(n)}catch{return console&&console.log("failed to parse JSON response",n),null}}static serialize(n,e){let i=[];for(var a in n){if(!Object.prototype.hasOwnProperty.call(n,a))continue;let l=e?`${e}[${a}]`:a,c=n[a];typeof c=="object"?i.push(this.serialize(c,l)):i.push(encodeURIComponent(l)+"="+encodeURIComponent(c))}return i.join("&")}static appendParams(n,e){if(Object.keys(e).length===0)return n;let i=n.match(/\?/)?"&":"?";return`${n}${i}${this.serialize(e)}`}},$N=n=>{let e="",i=new Uint8Array(n),a=i.byteLength;for(let l=0;lthis.poll(),0)}normalizeEndpoint(n){return n.replace("ws://","http://").replace("wss://","https://").replace(new RegExp("(.*)/"+df.websocket),"$1/"+df.longpoll)}endpointURL(){return Rc.appendParams(this.pollEndpoint,{token:this.token})}closeAndRetry(n,e,i){this.close(n,e,i),this.readyState=Wn.connecting}ontimeout(){this.onerror("timeout"),this.closeAndRetry(1005,"timeout",!1)}isActive(){return this.readyState===Wn.open||this.readyState===Wn.connecting}poll(){const n={Accept:"application/json"};this.authToken&&(n["X-Phoenix-AuthToken"]=this.authToken),this.ajax("GET",n,null,()=>this.ontimeout(),e=>{if(e){var{status:i,token:a,messages:l}=e;if(i===410&&this.token!==null){this.onerror(410),this.closeAndRetry(3410,"session_gone",!1);return}this.token=a}else i=0;switch(i){case 200:l.forEach(c=>{setTimeout(()=>this.onmessage({data:c}),0)}),this.poll();break;case 204:this.poll();break;case 410:this.readyState=Wn.open,this.onopen({}),this.poll();break;case 403:this.onerror(403),this.close(1008,"forbidden",!1);break;case 0:case 500:this.onerror(500),this.closeAndRetry(1011,"internal server error",500);break;default:throw new Error(`unhandled poll status ${i}`)}})}send(n){typeof n!="string"&&(n=$N(n)),this.currentBatch?this.currentBatch.push(n):this.awaitingBatchAck?this.batchBuffer.push(n):(this.currentBatch=[n],this.currentBatchTimer=setTimeout(()=>{this.batchSend(this.currentBatch),this.currentBatch=null},0))}batchSend(n){this.awaitingBatchAck=!0,this.ajax("POST",{"Content-Type":"application/x-ndjson"},n.join(` +`),()=>this.onerror("timeout"),e=>{this.awaitingBatchAck=!1,!e||e.status!==200?(this.onerror(e&&e.status),this.closeAndRetry(1011,"internal server error",!1)):this.batchBuffer.length>0&&(this.batchSend(this.batchBuffer),this.batchBuffer=[])})}close(n,e,i){for(let l of this.reqs)l.abort();this.readyState=Wn.closed;let a=Object.assign({code:1e3,reason:void 0,wasClean:!0},{code:n,reason:e,wasClean:i});this.batchBuffer=[],clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,typeof CloseEvent<"u"?this.onclose(new CloseEvent("close",a)):this.onclose(a)}ajax(n,e,i,a,l){let c,u=()=>{this.reqs.delete(c),a()};c=Rc.request(n,this.endpointURL(),e,i,this.timeout,u,d=>{this.reqs.delete(c),this.isActive()&&l(d)}),this.reqs.add(c)}},qN=class Qr{constructor(e,i={}){let a=i.events||{state:"presence_state",diff:"presence_diff"};this.state={},this.pendingDiffs=[],this.channel=e,this.joinRef=null,this.caller={onJoin:function(){},onLeave:function(){},onSync:function(){}},this.channel.on(a.state,l=>{let{onJoin:c,onLeave:u,onSync:d}=this.caller;this.joinRef=this.channel.joinRef(),this.state=Qr.syncState(this.state,l,c,u),this.pendingDiffs.forEach(m=>{this.state=Qr.syncDiff(this.state,m,c,u)}),this.pendingDiffs=[],d()}),this.channel.on(a.diff,l=>{let{onJoin:c,onLeave:u,onSync:d}=this.caller;this.inPendingSyncState()?this.pendingDiffs.push(l):(this.state=Qr.syncDiff(this.state,l,c,u),d())})}onJoin(e){this.caller.onJoin=e}onLeave(e){this.caller.onLeave=e}onSync(e){this.caller.onSync=e}list(e){return Qr.list(this.state,e)}inPendingSyncState(){return!this.joinRef||this.joinRef!==this.channel.joinRef()}static syncState(e,i,a,l){let c=this.clone(e),u={},d={};return this.map(c,(m,p)=>{i[m]||(d[m]=p)}),this.map(i,(m,p)=>{let y=c[m];if(y){let v=p.metas.map(T=>T.phx_ref),x=y.metas.map(T=>T.phx_ref),_=p.metas.filter(T=>x.indexOf(T.phx_ref)<0),S=y.metas.filter(T=>v.indexOf(T.phx_ref)<0);_.length>0&&(u[m]=p,u[m].metas=_),S.length>0&&(d[m]=this.clone(y),d[m].metas=S)}else u[m]=p}),this.syncDiff(c,{joins:u,leaves:d},a,l)}static syncDiff(e,i,a,l){let{joins:c,leaves:u}=this.clone(i);return a||(a=function(){}),l||(l=function(){}),this.map(c,(d,m)=>{let p=e[d];if(e[d]=this.clone(m),p){let y=e[d].metas.map(x=>x.phx_ref),v=p.metas.filter(x=>y.indexOf(x.phx_ref)<0);e[d].metas.unshift(...v)}a(d,p,m)}),this.map(u,(d,m)=>{let p=e[d];if(!p)return;let y=m.metas.map(v=>v.phx_ref);p.metas=p.metas.filter(v=>y.indexOf(v.phx_ref)<0),l(d,p,m),p.metas.length===0&&delete e[d]}),e}static list(e,i){return i||(i=function(a,l){return l}),this.map(e,(a,l)=>i(a,l))}static map(e,i){return Object.getOwnPropertyNames(e).map(a=>i(a,e[a]))}static clone(e){return JSON.parse(JSON.stringify(e))}},nc={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(n,e){if(n.payload.constructor===ArrayBuffer)return e(this.binaryEncode(n));{let i=[n.join_ref,n.ref,n.topic,n.event,n.payload];return e(JSON.stringify(i))}},decode(n,e){if(n.constructor===ArrayBuffer)return e(this.binaryDecode(n));{let[i,a,l,c,u]=JSON.parse(n);return e({join_ref:i,ref:a,topic:l,event:c,payload:u})}},binaryEncode(n){let{join_ref:e,ref:i,event:a,topic:l,payload:c}=n,u=this.META_LENGTH+e.length+i.length+l.length+a.length,d=new ArrayBuffer(this.HEADER_LENGTH+u),m=new DataView(d),p=0;m.setUint8(p++,this.KINDS.push),m.setUint8(p++,e.length),m.setUint8(p++,i.length),m.setUint8(p++,l.length),m.setUint8(p++,a.length),Array.from(e,v=>m.setUint8(p++,v.charCodeAt(0))),Array.from(i,v=>m.setUint8(p++,v.charCodeAt(0))),Array.from(l,v=>m.setUint8(p++,v.charCodeAt(0))),Array.from(a,v=>m.setUint8(p++,v.charCodeAt(0)));var y=new Uint8Array(d.byteLength+c.byteLength);return y.set(new Uint8Array(d),0),y.set(new Uint8Array(c),d.byteLength),y.buffer},binaryDecode(n){let e=new DataView(n),i=e.getUint8(0),a=new TextDecoder;switch(i){case this.KINDS.push:return this.decodePush(n,e,a);case this.KINDS.reply:return this.decodeReply(n,e,a);case this.KINDS.broadcast:return this.decodeBroadcast(n,e,a)}},decodePush(n,e,i){let a=e.getUint8(1),l=e.getUint8(2),c=e.getUint8(3),u=this.HEADER_LENGTH+this.META_LENGTH-1,d=i.decode(n.slice(u,u+a));u=u+a;let m=i.decode(n.slice(u,u+l));u=u+l;let p=i.decode(n.slice(u,u+c));u=u+c;let y=n.slice(u,n.byteLength);return{join_ref:d,ref:null,topic:m,event:p,payload:y}},decodeReply(n,e,i){let a=e.getUint8(1),l=e.getUint8(2),c=e.getUint8(3),u=e.getUint8(4),d=this.HEADER_LENGTH+this.META_LENGTH,m=i.decode(n.slice(d,d+a));d=d+a;let p=i.decode(n.slice(d,d+l));d=d+l;let y=i.decode(n.slice(d,d+c));d=d+c;let v=i.decode(n.slice(d,d+u));d=d+u;let x=n.slice(d,n.byteLength),_={status:v,response:x};return{join_ref:m,ref:p,topic:y,event:js.reply,payload:_}},decodeBroadcast(n,e,i){let a=e.getUint8(1),l=e.getUint8(2),c=this.HEADER_LENGTH+2,u=i.decode(n.slice(c,c+a));c=c+a;let d=i.decode(n.slice(c,c+l));c=c+l;let m=n.slice(c,n.byteLength);return{join_ref:null,ref:null,topic:u,event:d,payload:m}}},FN=class{constructor(n,e={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.timeout=e.timeout||zN,this.transport=e.transport||Qn.WebSocket||Aa,this.conn=void 0,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=e.longPollFallbackMs,this.fallbackTimer=null;let i=null;try{i=Qn&&Qn.sessionStorage}catch{}this.sessionStore=e.sessionStorage||i,this.establishedConnections=0,this.defaultEncoder=nc.encode.bind(nc),this.defaultDecoder=nc.decode.bind(nc),this.closeWasClean=!0,this.disconnecting=!1,this.binaryType=e.binaryType||"arraybuffer",this.connectClock=1,this.pageHidden=!1,this.encode=void 0,this.decode=void 0,this.transport!==Aa?(this.encode=e.encode||this.defaultEncoder,this.decode=e.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let a=null;Ma&&Ma.addEventListener&&(Ma.addEventListener("pagehide",l=>{this.conn&&(this.disconnect(),a=this.connectClock)}),Ma.addEventListener("pageshow",l=>{a===this.connectClock&&(a=null,this.connect())}),Ma.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?this.pageHidden=!0:(this.pageHidden=!1,!this.isConnected()&&!this.closeWasClean&&this.teardown(()=>this.connect()))})),this.heartbeatIntervalMs=e.heartbeatIntervalMs||3e4,this.autoSendHeartbeat=e.autoSendHeartbeat??!0,this.heartbeatCallback=e.heartbeatCallback??(()=>{}),this.rejoinAfterMs=l=>e.rejoinAfterMs?e.rejoinAfterMs(l):[1e3,2e3,5e3][l-1]||1e4,this.reconnectAfterMs=l=>e.reconnectAfterMs?e.reconnectAfterMs(l):[10,50,100,150,200,250,500,1e3,2e3][l-1]||5e3,this.logger=e.logger||null,!this.logger&&e.debug&&(this.logger=(l,c,u)=>{console.log(`${l}: ${c}`,u)}),this.longpollerTimeout=e.longpollerTimeout||2e4,this.params=io(e.params||{}),this.endPoint=`${n}/${df.websocket}`,this.vsn=e.vsn||LN,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.heartbeatSentAt=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new ww(()=>{if(this.pageHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardown(async()=>{e.beforeReconnect&&await e.beforeReconnect(),this.connect()})},this.reconnectAfterMs),this.authToken=e.authToken}getLongPollTransport(){return Aa}replaceTransport(n){this.connectClock++,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.conn&&(this.conn.close(),this.conn=null),this.transport=n}protocol(){return location.protocol.match(/^https/)?"wss":"ws"}endPointURL(){let n=Rc.appendParams(Rc.appendParams(this.endPoint,this.params()),{vsn:this.vsn});return n.charAt(0)!=="/"?n:n.charAt(1)==="/"?`${this.protocol()}:${n}`:`${this.protocol()}://${location.host}${n}`}disconnect(n,e,i){this.connectClock++,this.disconnecting=!0,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.teardown(()=>{this.disconnecting=!1,n&&n()},e,i)}connect(n){n&&(console&&console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"),this.params=io(n)),!(this.conn&&!this.disconnecting)&&(this.longPollFallbackMs&&this.transport!==Aa?this.connectWithFallback(Aa,this.longPollFallbackMs):this.transportConnect())}log(n,e,i){this.logger&&this.logger(n,e,i)}hasLogger(){return this.logger!==null}onOpen(n){let e=this.makeRef();return this.stateChangeCallbacks.open.push([e,n]),e}onClose(n){let e=this.makeRef();return this.stateChangeCallbacks.close.push([e,n]),e}onError(n){let e=this.makeRef();return this.stateChangeCallbacks.error.push([e,n]),e}onMessage(n){let e=this.makeRef();return this.stateChangeCallbacks.message.push([e,n]),e}onHeartbeat(n){this.heartbeatCallback=n}ping(n){if(!this.isConnected())return!1;let e=this.makeRef(),i=Date.now();this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:e});let a=this.onMessage(l=>{l.ref===e&&(this.off([a]),n(Date.now()-i))});return!0}transportName(n){switch(n){case Aa:return"LongPoll";default:return n.name}}transportConnect(){this.connectClock++,this.closeWasClean=!1;let n;this.authToken&&(n=["phoenix",`${ff}${btoa(this.authToken).replace(/=/g,"")}`]),this.conn=new this.transport(this.endPointURL(),n),this.conn.binaryType=this.binaryType,this.conn.timeout=this.longpollerTimeout,this.conn.onopen=()=>this.onConnOpen(),this.conn.onerror=e=>this.onConnError(e),this.conn.onmessage=e=>this.onConnMessage(e),this.conn.onclose=e=>this.onConnClose(e)}getSession(n){return this.sessionStore&&this.sessionStore.getItem(n)}storeSession(n,e){this.sessionStore&&this.sessionStore.setItem(n,e)}connectWithFallback(n,e=2500){clearTimeout(this.fallbackTimer);let i=!1,a=!0,l,c,u=this.transportName(n),d=m=>{this.log("transport",`falling back to ${u}...`,m),this.off([l,c]),a=!1,this.replaceTransport(n),this.transportConnect()};if(this.getSession(`phx:fallback:${u}`))return d("memorized");this.fallbackTimer=setTimeout(d,e),c=this.onError(m=>{this.log("transport","error",m),a&&!i&&(clearTimeout(this.fallbackTimer),d(m))}),this.fallbackRef&&this.off([this.fallbackRef]),this.fallbackRef=this.onOpen(()=>{if(i=!0,!a){let m=this.transportName(n);return this.primaryPassedHealthCheck||this.storeSession(`phx:fallback:${m}`,"true"),this.log("transport",`established ${m} fallback`)}clearTimeout(this.fallbackTimer),this.fallbackTimer=setTimeout(d,e),this.ping(m=>{this.log("transport","connected to primary after",m),this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer)})}),this.transportConnect()}clearHeartbeats(){clearTimeout(this.heartbeatTimer),clearTimeout(this.heartbeatTimeoutTimer)}onConnOpen(){this.hasLogger()&&this.log("transport",`connected to ${this.endPointURL()}`),this.closeWasClean=!1,this.disconnecting=!1,this.establishedConnections++,this.flushSendBuffer(),this.reconnectTimer.reset(),this.autoSendHeartbeat&&this.resetHeartbeat(),this.triggerStateCallbacks("open")}heartbeatTimeout(){if(this.pendingHeartbeatRef){this.pendingHeartbeatRef=null,this.heartbeatSentAt=null,this.hasLogger()&&this.log("transport","heartbeat timeout. Attempting to re-establish connection");try{this.heartbeatCallback("timeout")}catch(n){this.log("error","error in heartbeat callback",n)}this.triggerChanError(new Error("heartbeat timeout")),this.closeWasClean=!1,this.teardown(()=>this.reconnectTimer.scheduleTimeout(),PN,"heartbeat timeout")}}resetHeartbeat(){this.conn&&this.conn.skipHeartbeat||(this.pendingHeartbeatRef=null,this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}teardown(n,e,i){if(!this.conn)return n&&n();const a=this.conn;this.waitForBufferDone(a,()=>{e?a.close(e,i||""):a.close(),this.waitForSocketClosed(a,()=>{this.conn===a&&(this.conn.onopen=function(){},this.conn.onerror=function(){},this.conn.onmessage=function(){},this.conn.onclose=function(){},this.conn=null),n&&n()})})}waitForBufferDone(n,e,i=1){if(i===5||!n.bufferedAmount){e();return}setTimeout(()=>{this.waitForBufferDone(n,e,i+1)},150*i)}waitForSocketClosed(n,e,i=1){if(i===5||n.readyState===Wn.closed){e();return}setTimeout(()=>{this.waitForSocketClosed(n,e,i+1)},150*i)}onConnClose(n){this.conn&&(this.conn.onclose=()=>{}),this.hasLogger()&&this.log("transport","close",n),this.triggerChanError(n),this.clearHeartbeats(),this.closeWasClean||this.reconnectTimer.scheduleTimeout(),this.triggerStateCallbacks("close",n)}onConnError(n){this.hasLogger()&&this.log("transport","error",n);let e=this.transport,i=this.establishedConnections;this.triggerStateCallbacks("error",n,e,i),(e===this.transport||i>0)&&this.triggerChanError(n)}triggerChanError(n){this.channels.forEach(e=>{e.isErrored()||e.isLeaving()||e.isClosed()||e.trigger(js.error,n)})}connectionState(){switch(this.conn&&this.conn.readyState){case Wn.connecting:return"connecting";case Wn.open:return"open";case Wn.closing:return"closing";default:return"closed"}}isConnected(){return this.connectionState()==="open"}remove(n){this.off(n.stateChangeRefs),this.channels=this.channels.filter(e=>e!==n)}off(n){for(let e in this.stateChangeCallbacks)this.stateChangeCallbacks[e]=this.stateChangeCallbacks[e].filter(([i])=>n.indexOf(i)===-1)}channel(n,e={}){let i=new HN(n,e,this);return this.channels.push(i),i}push(n){if(this.hasLogger()){let{topic:e,event:i,payload:a,ref:l,join_ref:c}=n;this.log("push",`${e} ${i} (${c}, ${l})`,a)}this.isConnected()?this.encode(n,e=>this.conn.send(e)):this.sendBuffer.push(()=>this.encode(n,e=>this.conn.send(e)))}makeRef(){let n=this.ref+1;return n===this.ref?this.ref=0:this.ref=n,this.ref.toString()}sendHeartbeat(){if(!this.isConnected()){try{this.heartbeatCallback("disconnected")}catch(n){this.log("error","error in heartbeat callback",n)}return}if(this.pendingHeartbeatRef){this.heartbeatTimeout();return}this.pendingHeartbeatRef=this.makeRef(),this.heartbeatSentAt=Date.now(),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:this.pendingHeartbeatRef});try{this.heartbeatCallback("sent")}catch(n){this.log("error","error in heartbeat callback",n)}this.heartbeatTimeoutTimer=setTimeout(()=>this.heartbeatTimeout(),this.heartbeatIntervalMs)}flushSendBuffer(){this.isConnected()&&this.sendBuffer.length>0&&(this.sendBuffer.forEach(n=>n()),this.sendBuffer=[])}onConnMessage(n){this.decode(n.data,e=>{let{topic:i,event:a,payload:l,ref:c,join_ref:u}=e;if(c&&c===this.pendingHeartbeatRef){const d=this.heartbeatSentAt?Date.now()-this.heartbeatSentAt:void 0;this.clearHeartbeats();try{this.heartbeatCallback(l.status==="ok"?"ok":"error",d)}catch(m){this.log("error","error in heartbeat callback",m)}this.pendingHeartbeatRef=null,this.heartbeatSentAt=null,this.autoSendHeartbeat&&(this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}this.hasLogger()&&this.log("receive",`${l.status||""} ${i} ${a} ${c&&"("+c+")"||""}`.trim(),l);for(let d=0;d{try{a(...e)}catch(l){this.log("error",`error in ${n} callback`,l)}})}catch(i){this.log("error",`error triggering ${n} callbacks`,i)}}leaveOpenTopic(n){let e=this.channels.find(i=>i.topic===n&&(i.isJoined()||i.isJoining()));e&&(this.hasLogger()&&this.log("transport",`leaving duplicate topic "${n}"`),e.leave())}};class ao{constructor(e,i){const a=GN(i);this.presence=new qN(e.getChannel(),a),this.presence.onJoin((l,c,u)=>{const d=ao.onJoinPayload(l,c,u);e.getChannel().trigger("presence",d)}),this.presence.onLeave((l,c,u)=>{const d=ao.onLeavePayload(l,c,u);e.getChannel().trigger("presence",d)}),this.presence.onSync(()=>{e.getChannel().trigger("presence",{event:"sync"})})}get state(){return ao.transformState(this.presence.state)}static transformState(e){return e=IN(e),Object.getOwnPropertyNames(e).reduce((i,a)=>{const l=e[a];return i[a]=bc(l),i},{})}static onJoinPayload(e,i,a){const l=Cb(i),c=bc(a);return{event:"join",key:e,currentPresences:l,newPresences:c}}static onLeavePayload(e,i,a){const l=Cb(i),c=bc(a);return{event:"leave",key:e,currentPresences:l,leftPresences:c}}}function bc(n){return n.metas.map(e=>(e.presence_ref=e.phx_ref,delete e.phx_ref,delete e.phx_ref_prev,e))}function IN(n){return JSON.parse(JSON.stringify(n))}function GN(n){return(n==null?void 0:n.events)&&{events:n.events}}function Cb(n){return n!=null&&n.metas?bc(n):[]}var Ob;(function(n){n.SYNC="sync",n.JOIN="join",n.LEAVE="leave"})(Ob||(Ob={}));class KN{get state(){return this.presenceAdapter.state}constructor(e,i){this.channel=e,this.presenceAdapter=new ao(this.channel.channelAdapter,i)}}function YN(n){if(n instanceof Error)return n;if(typeof n=="string")return new Error(n);if(n&&typeof n=="object"){const e=n;if(typeof e.code=="number"){const i=typeof e.reason=="string"&&e.reason?` (${e.reason})`:"";return new Error(`socket closed: ${e.code}${i}`,{cause:n})}return new Error("channel error: transport failure",{cause:n})}return new Error("channel error: connection lost")}class XN{constructor(e,i,a){const l=JN(a);this.channel=e.getSocket().channel(i,l),this.socket=e}get state(){return this.channel.state}set state(e){this.channel.state=e}get joinedOnce(){return this.channel.joinedOnce}get joinPush(){return this.channel.joinPush}get rejoinTimer(){return this.channel.rejoinTimer}on(e,i){return this.channel.on(e,i)}off(e,i){this.channel.off(e,i)}subscribe(e){return this.channel.join(e)}unsubscribe(e){return this.channel.leave(e)}teardown(){this.channel.teardown()}onClose(e){this.channel.onClose(e)}onError(e){return this.channel.onError(e)}push(e,i,a){let l;try{l=this.channel.push(e,i,a)}catch{throw new Error(`tried to push '${e}' to '${this.channel.topic}' before joining. Use channel.subscribe() before pushing events`)}if(this.channel.pushBuffer.length>NN){const c=this.channel.pushBuffer.shift();c.cancelTimeout(),this.socket.log("channel",`discarded push due to buffer overflow: ${c.event}`,c.payload())}return l}updateJoinPayload(e){const i=this.channel.joinPush.payload();this.channel.joinPush.payload=()=>Object.assign(Object.assign({},i),e)}canPush(){return this.socket.isConnected()&&this.state===ii.joined}isJoined(){return this.state===ii.joined}isJoining(){return this.state===ii.joining}isClosed(){return this.state===ii.closed}isLeaving(){return this.state===ii.leaving}updateFilterBindings(e){this.channel.filterBindings=e}updatePayloadTransform(e){this.channel.onMessage=e}getChannel(){return this.channel}}function JN(n){return{config:Object.assign({broadcast:{ack:!1,self:!1},presence:{key:"",enabled:!1},private:!1},n.config)}}const QN=/[,()"\\]/,ZN=n=>QN.test(n)||n!==n.trim(),WN=n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`,Db=n=>{const e=n===null?"null":String(n);return ZN(e)?WN(e):e},ek=n=>n===null?"null":String(n),tk=(n,e)=>{if(n==="in"){const i=Array.isArray(e)?e:[e];if(i.length===0)throw new Error("Realtime `in` filter requires at least one value.");return`in.(${Array.from(new Set(i)).map(l=>Db(l)).join(",")})`}return n==="is"?`is.${ek(e)}`:`${n}.${Db(e)}`};class nk{constructor(){this.filters=[]}add(e,i,a,l=!1){const c=l?"not.":"";return this.filters.push(`${e}=${c}${tk(i,a)}`),this}eq(e,i){return this.add(e,"eq",i)}neq(e,i){return this.add(e,"neq",i)}gt(e,i){return this.add(e,"gt",i)}gte(e,i){return this.add(e,"gte",i)}lt(e,i){return this.add(e,"lt",i)}lte(e,i){return this.add(e,"lte",i)}in(e,i){return this.add(e,"in",i)}like(e,i){return this.add(e,"like",i)}ilike(e,i){return this.add(e,"ilike",i)}match(e,i){return this.add(e,"match",i)}imatch(e,i){return this.add(e,"imatch",i)}is(e,i){return this.add(e,"is",i)}isDistinct(e,i){return this.add(e,"isdistinct",i)}not(e,i,a){return this.add(e,i,a,!0)}build(){return this.filters.join(",")}toString(){return this.build()}}var Mb;(function(n){n.ALL="*",n.INSERT="INSERT",n.UPDATE="UPDATE",n.DELETE="DELETE"})(Mb||(Mb={}));var Va;(function(n){n.BROADCAST="broadcast",n.PRESENCE="presence",n.POSTGRES_CHANGES="postgres_changes",n.SYSTEM="system"})(Va||(Va={}));var Ns;(function(n){n.SUBSCRIBED="SUBSCRIBED",n.TIMED_OUT="TIMED_OUT",n.CLOSED="CLOSED",n.CHANNEL_ERROR="CHANNEL_ERROR"})(Ns||(Ns={}));class ro{get state(){return this.channelAdapter.state}set state(e){this.channelAdapter.state=e}get joinedOnce(){return this.channelAdapter.joinedOnce}get timeout(){return this.socket.timeout}get joinPush(){return this.channelAdapter.joinPush}get rejoinTimer(){return this.channelAdapter.rejoinTimer}constructor(e,i={config:{}},a){var l,c;if(this.topic=e,this.params=i,this.socket=a,this.bindings={},this.subTopic=e.replace(/^realtime:/i,""),this.params.config=Object.assign({broadcast:{ack:!1,self:!1},presence:{key:"",enabled:!1},private:!1},i.config),this.channelAdapter=new XN(this.socket.socketAdapter,e,this.params),this.presence=new KN(this),this._onClose(()=>{this.socket._remove(this)}),this._updateFilterTransform(),this.broadcastEndpointURL=xw(this.socket.socketAdapter.endPointURL()),this.private=this.params.config.private||!1,!this.private&&(!((c=(l=this.params.config)===null||l===void 0?void 0:l.broadcast)===null||c===void 0)&&c.replay))throw new Error(`tried to use replay on public channel '${this.topic}'. It must be a private channel.`)}subscribe(e,i=this.timeout){var a,l,c;if(this.socket.isConnected()||this.socket.connect(),this.channelAdapter.isClosed()){const{config:{broadcast:u,presence:d,private:m}}=this.params,p=(l=(a=this.bindings.postgres_changes)===null||a===void 0?void 0:a.map(_=>_.filter))!==null&&l!==void 0?l:[],y=!!this.bindings[Va.PRESENCE]&&this.bindings[Va.PRESENCE].length>0||((c=this.params.config.presence)===null||c===void 0?void 0:c.enabled)===!0,v={},x={broadcast:u,presence:Object.assign(Object.assign({},d),{enabled:y}),postgres_changes:p,private:m};this.socket.accessTokenValue&&(v.access_token=this.socket.accessTokenValue),this._onError(_=>{e==null||e(Ns.CHANNEL_ERROR,YN(_))}),this._onClose(()=>e==null?void 0:e(Ns.CLOSED)),this.updateJoinPayload(Object.assign({config:x},v)),this._updateFilterMessage(),this.channelAdapter.subscribe(i).receive("ok",async({postgres_changes:_})=>{if(this.socket._isManualToken()||this.socket.setAuth(),_===void 0){e==null||e(Ns.SUBSCRIBED);return}this._updatePostgresBindings(_,e)}).receive("error",_=>{this.state=ii.errored;const S=Object.values(_).join(", ")||"error";e==null||e(Ns.CHANNEL_ERROR,new Error(S,{cause:_}))}).receive("timeout",()=>{e==null||e(Ns.TIMED_OUT)})}return this}_updatePostgresBindings(e,i){var a;const l=this.bindings.postgres_changes,c=(a=l==null?void 0:l.length)!==null&&a!==void 0?a:0,u=[];for(let d=0;d{var u,d,m;const p=this.channelAdapter.push(e.type,e,i.timeout||this.timeout);e.type==="broadcast"&&!(!((m=(d=(u=this.params)===null||u===void 0?void 0:u.config)===null||d===void 0?void 0:d.broadcast)===null||m===void 0)&&m.ack)&&c("ok"),p.receive("ok",()=>c("ok")),p.receive("error",()=>c("error")),p.receive("timeout",()=>c("timed out"))})}updateJoinPayload(e){this.channelAdapter.updateJoinPayload(e)}async unsubscribe(e=this.timeout){return new Promise(i=>{this.channelAdapter.unsubscribe(e).receive("ok",()=>i("ok")).receive("timeout",()=>i("timed out")).receive("error",()=>i("error"))})}teardown(){this.channelAdapter.teardown()}async _fetchWithTimeout(e,i,a){const l=new AbortController,c=setTimeout(()=>l.abort(),a),u=await this.socket.fetch(e,Object.assign(Object.assign({},i),{signal:l.signal}));return clearTimeout(c),u}_on(e,i,a){const l=e.toLocaleLowerCase(),c=i==null?void 0:i.filter;(c instanceof nk||typeof c=="object"&&c!==null&&typeof c.build=="function")&&(i=Object.assign(Object.assign({},i),{filter:c.build()}));const u=this.channelAdapter.on(e,a),d={type:l,filter:i,callback:a,ref:u};return this.bindings[l]?this.bindings[l].push(d):this.bindings[l]=[d],this._updateFilterMessage(),this}_onClose(e){this.channelAdapter.onClose(e)}_onError(e){this.channelAdapter.onError(e)}_updateFilterMessage(){this.channelAdapter.updateFilterBindings((e,i,a)=>{var l,c,u,d,m,p,y;const v=e.event.toLocaleLowerCase();if(this._notThisChannelEvent(v,a))return!1;const x=(l=this.bindings[v])===null||l===void 0?void 0:l.find(_=>_.ref===e.ref);if(!x)return!0;if(["broadcast","presence","postgres_changes"].includes(v))if("id"in x){const _=x.id,S=(c=x.filter)===null||c===void 0?void 0:c.event;return _&&((u=i.ids)===null||u===void 0?void 0:u.includes(_))&&(S==="*"||(S==null?void 0:S.toLocaleLowerCase())===((d=i.data)===null||d===void 0?void 0:d.type.toLocaleLowerCase()))}else{const _=(p=(m=x==null?void 0:x.filter)===null||m===void 0?void 0:m.event)===null||p===void 0?void 0:p.toLocaleLowerCase();return _==="*"||_===((y=i==null?void 0:i.event)===null||y===void 0?void 0:y.toLocaleLowerCase())}else return x.type.toLocaleLowerCase()===v})}_notThisChannelEvent(e,i){const{close:a,error:l,leave:c,join:u}=bw;return i&&[a,l,c,u].includes(e)&&i!==this.joinPush.ref}_updateFilterTransform(){this.channelAdapter.updatePayloadTransform((e,i,a)=>{if(typeof i=="object"&&"ids"in i){const l=i.data,{schema:c,table:u,commit_timestamp:d,type:m,errors:p}=l;return Object.assign(Object.assign({},{schema:c,table:u,commit_timestamp:d,eventType:m,new:{},old:{},errors:p}),this._getPayloadRecords(l))}return i})}copyBindings(e){if(this.joinedOnce)throw new Error("cannot copy bindings into joined channel");for(const i in e.bindings)for(const a of e.bindings[i])this._on(a.type,a.filter,a.callback)}static isFilterValueEqual(e,i){return(e??void 0)===(i??void 0)}_getPayloadRecords(e){const i={new:{},old:{}};return(e.type==="INSERT"||e.type==="UPDATE")&&(i.new=Rb(e.columns,e.record)),(e.type==="UPDATE"||e.type==="DELETE")&&(i.old=Rb(e.columns,e.old_record)),i}}class sk{constructor(e,i){this.socket=new FN(e,i)}get timeout(){return this.socket.timeout}get endPoint(){return this.socket.endPoint}get transport(){return this.socket.transport}get heartbeatIntervalMs(){return this.socket.heartbeatIntervalMs}get heartbeatCallback(){return this.socket.heartbeatCallback}set heartbeatCallback(e){this.socket.heartbeatCallback=e}get heartbeatTimer(){return this.socket.heartbeatTimer}get pendingHeartbeatRef(){return this.socket.pendingHeartbeatRef}get reconnectTimer(){return this.socket.reconnectTimer}get vsn(){return this.socket.vsn}get encode(){return this.socket.encode}get decode(){return this.socket.decode}get reconnectAfterMs(){return this.socket.reconnectAfterMs}get sendBuffer(){return this.socket.sendBuffer}get stateChangeCallbacks(){return this.socket.stateChangeCallbacks}connect(){this.socket.connect()}disconnect(e,i,a,l=1e4){return new Promise(c=>{setTimeout(()=>c("timeout"),l),this.socket.disconnect(()=>{e(),c("ok")},i,a)})}push(e){this.socket.push(e)}log(e,i,a){this.socket.log(e,i,a)}makeRef(){return this.socket.makeRef()}onOpen(e){this.socket.onOpen(e)}onClose(e){this.socket.onClose(e)}onError(e){this.socket.onError(e)}onMessage(e){this.socket.onMessage(e)}isConnected(){return this.socket.isConnected()}isConnecting(){return this.socket.connectionState()==uf.connecting}isDisconnecting(){return this.socket.connectionState()==uf.closing}connectionState(){return this.socket.connectionState()}endPointURL(){return this.socket.endPointURL()}sendHeartbeat(){this.socket.sendHeartbeat()}getSocket(){return this.socket}}const Bb={HEARTBEAT_INTERVAL:25e3},ik=[1e3,2e3,5e3,1e4],ak=1e4;function rk(){const n=new Map;return{get length(){return n.size},clear(){n.clear()},getItem(e){return n.has(e)?n.get(e):null},key(e){var i;return(i=Array.from(n.keys())[e])!==null&&i!==void 0?i:null},removeItem(e){n.delete(e)},setItem(e,i){n.set(e,String(i))}}}function ok(){try{if(typeof globalThis<"u"&&globalThis.sessionStorage)return globalThis.sessionStorage}catch{}return rk()}const lk=` + addEventListener("message", (e) => { + if (e.data.event === "start") { + setInterval(() => postMessage({ event: "keepAlive" }), e.data.interval); + } + });`;class ck{get endPoint(){return this.socketAdapter.endPoint}get timeout(){return this.socketAdapter.timeout}get transport(){return this.socketAdapter.transport}get heartbeatCallback(){return this.socketAdapter.heartbeatCallback}get heartbeatIntervalMs(){return this.socketAdapter.heartbeatIntervalMs}get heartbeatTimer(){return this.worker?this._workerHeartbeatTimer:this.socketAdapter.heartbeatTimer}get pendingHeartbeatRef(){return this.worker?this._pendingWorkerHeartbeatRef:this.socketAdapter.pendingHeartbeatRef}get reconnectTimer(){return this.socketAdapter.reconnectTimer}get vsn(){return this.socketAdapter.vsn}get encode(){return this.socketAdapter.encode}get decode(){return this.socketAdapter.decode}get reconnectAfterMs(){return this.socketAdapter.reconnectAfterMs}get sendBuffer(){return this.socketAdapter.sendBuffer}get stateChangeCallbacks(){return this.socketAdapter.stateChangeCallbacks}constructor(e,i){var a;if(this.channels=new Array,this.accessTokenValue=null,this.accessToken=null,this.apiKey=null,this.httpEndpoint="",this.headers={},this.params={},this.ref=0,this.serializer=new kN,this._manuallySetToken=!1,this._authPromise=null,this._workerHeartbeatTimer=void 0,this._pendingWorkerHeartbeatRef=null,this._pendingDisconnectTimer=null,this._disconnectOnEmptyChannelsAfterMs=0,this._resolveFetch=c=>c?(...u)=>c(...u):(...u)=>fetch(...u),!(!((a=i==null?void 0:i.params)===null||a===void 0)&&a.apikey))throw new Error("API key is required to connect to Realtime");this.apiKey=i.params.apikey;const l=this._initializeOptions(i);this.socketAdapter=new sk(e,l),this.httpEndpoint=xw(e),this.fetch=this._resolveFetch(i==null?void 0:i.fetch)}connect(){if(!(this.isConnecting()||this.isDisconnecting()||this.isConnected())){this.accessToken&&!this._authPromise&&this._setAuthSafely("connect"),this._setupConnectionHandlers();try{this.socketAdapter.connect()}catch(e){const i=e.message;throw new Error(`WebSocket not available: ${i}`)}this._handleNodeJsRaceCondition()}}endpointURL(){return this.socketAdapter.endPointURL()}async disconnect(e,i){return this._cancelPendingDisconnect(),this.isDisconnecting()?"ok":await this.socketAdapter.disconnect(()=>{clearInterval(this._workerHeartbeatTimer),this._terminateWorker()},e,i)}getChannels(){return this.channels}async removeChannel(e){const i=await e.unsubscribe();return i==="ok"&&e.teardown(),i}async removeAllChannels(){const e=this.channels.map(async a=>{const l=await a.unsubscribe();return a.teardown(),l}),i=await Promise.all(e);return await this.disconnect(),i}log(e,i,a){this.socketAdapter.log(e,i,a)}connectionState(){return this.socketAdapter.connectionState()||uf.closed}isConnected(){return this.socketAdapter.isConnected()}isConnecting(){return this.socketAdapter.isConnecting()}isDisconnecting(){return this.socketAdapter.isDisconnecting()}channel(e,i={config:{}}){const a=`realtime:${e}`,l=this.getChannels().find(c=>c.topic===a);if(l)return l;{const c=new ro(`realtime:${e}`,i,this);return this._cancelPendingDisconnect(),this.channels.push(c),c}}push(e){this.socketAdapter.push(e)}async setAuth(e=null){this._authPromise=this._performAuth(e);try{await this._authPromise}finally{this._authPromise=null}}_isManualToken(){return this._manuallySetToken}async sendHeartbeat(){this.socketAdapter.sendHeartbeat()}onHeartbeat(e){this.socketAdapter.heartbeatCallback=this._wrapHeartbeatCallback(e)}_makeRef(){return this.socketAdapter.makeRef()}_remove(e){this.channels=this.channels.filter(i=>i.topic!==e.topic),this.channels.length===0&&(this.log("transport","no channels remaining, scheduling disconnect"),this._schedulePendingDisconnect())}_schedulePendingDisconnect(){if(this._cancelPendingDisconnect(),this._disconnectOnEmptyChannelsAfterMs===0){this.log("transport","disconnecting immediately - no channels"),this.disconnect();return}this._pendingDisconnectTimer=setTimeout(()=>{this._pendingDisconnectTimer=null,this.channels.length===0&&(this.log("transport","deferred disconnect fired - no channels, disconnecting"),this.disconnect())},this._disconnectOnEmptyChannelsAfterMs),this.log("transport",`deferred disconnect scheduled in ${this._disconnectOnEmptyChannelsAfterMs}ms`)}_cancelPendingDisconnect(){this._pendingDisconnectTimer!==null&&(this.log("transport","pending disconnect cancelled - channel activity detected"),clearTimeout(this._pendingDisconnectTimer),this._pendingDisconnectTimer=null)}async _performAuth(e=null){let i,a=!1;if(e)i=e,a=!0;else if(this.accessToken)try{i=await this.accessToken()}catch(l){this.log("error","Error fetching access token from callback",l),i=this.accessTokenValue}else i=this.accessTokenValue;a?this._manuallySetToken=!0:this.accessToken&&(this._manuallySetToken=!1),this.accessTokenValue!=i&&(this.accessTokenValue=i,this.channels.forEach(l=>{const c={access_token:i,version:TN};i&&l.updateJoinPayload(c),l.joinedOnce&&l.channelAdapter.isJoined()&&l.channelAdapter.push(bw.access_token,{access_token:i})}))}async _waitForAuthIfNeeded(){this._authPromise&&await this._authPromise}_setAuthSafely(e="general"){this._isManualToken()||this.setAuth().catch(i=>{this.log("error",`Error setting auth in ${e}`,i)})}_setupConnectionHandlers(){this.socketAdapter.onOpen(()=>{(this._authPromise||(this.accessToken&&!this.accessTokenValue?this.setAuth():Promise.resolve())).catch(i=>{this.log("error","error waiting for auth on connect",i)}),this.worker&&!this.workerRef&&this._startWorkerHeartbeat()}),this.socketAdapter.onClose(()=>{this.worker&&this.workerRef&&this._terminateWorker()}),this.socketAdapter.onMessage(e=>{e.ref&&e.ref===this._pendingWorkerHeartbeatRef&&(this._pendingWorkerHeartbeatRef=null)})}_handleNodeJsRaceCondition(){this.socketAdapter.isConnected()&&this.socketAdapter.getSocket().onConnOpen()}_wrapHeartbeatCallback(e){return(i,a)=>{i!=="disconnected"&&(i=="sent"&&this._setAuthSafely(),e&&e(i,a))}}_startWorkerHeartbeat(){this.workerUrl?this.log("worker",`starting worker for from ${this.workerUrl}`):this.log("worker","starting default worker");const e=this._workerObjectUrl(this.workerUrl);this.workerRef=new Worker(e),this.workerRef.onerror=i=>{this.log("worker","worker error",i.message),this._terminateWorker(),this.disconnect()},this.workerRef.onmessage=i=>{i.data.event==="keepAlive"&&this.sendHeartbeat()},this.workerRef.postMessage({event:"start",interval:this.heartbeatIntervalMs})}_terminateWorker(){this.workerRef&&(this.log("worker","terminating worker"),this.workerRef.terminate(),this.workerRef=void 0)}_workerObjectUrl(e){let i;if(e)i=e;else{const a=new Blob([lk],{type:"application/javascript"});i=URL.createObjectURL(a)}return i}_initializeOptions(e){var i,a,l,c,u,d,m,p,y,v,x,_;this.worker=(i=e==null?void 0:e.worker)!==null&&i!==void 0?i:!1,this.accessToken=(a=e==null?void 0:e.accessToken)!==null&&a!==void 0?a:null;const S={};S.timeout=(l=e==null?void 0:e.timeout)!==null&&l!==void 0?l:jN,S.heartbeatIntervalMs=(c=e==null?void 0:e.heartbeatIntervalMs)!==null&&c!==void 0?c:Bb.HEARTBEAT_INTERVAL,this._disconnectOnEmptyChannelsAfterMs=(u=e==null?void 0:e.disconnectOnEmptyChannelsAfterMs)!==null&&u!==void 0?u:2*((d=e==null?void 0:e.heartbeatIntervalMs)!==null&&d!==void 0?d:Bb.HEARTBEAT_INTERVAL),S.transport=(m=e==null?void 0:e.transport)!==null&&m!==void 0?m:_N.getWebSocketConstructor(),S.params=e==null?void 0:e.params,S.logger=e==null?void 0:e.logger,S.heartbeatCallback=this._wrapHeartbeatCallback(e==null?void 0:e.heartbeatCallback),S.sessionStorage=(p=e==null?void 0:e.sessionStorage)!==null&&p!==void 0?p:ok(),S.reconnectAfterMs=(y=e==null?void 0:e.reconnectAfterMs)!==null&&y!==void 0?y:(M=>ik[M-1]||ak);let T,k;const R=(v=e==null?void 0:e.vsn)!==null&&v!==void 0?v:AN;switch(R){case EN:T=(M,N)=>N(JSON.stringify(M)),k=(M,N)=>N(JSON.parse(M));break;case yw:T=this.serializer.encode.bind(this.serializer),k=this.serializer.decode.bind(this.serializer);break;default:throw new Error(`Unsupported serializer version: ${S.vsn}`)}if(S.vsn=R,S.encode=(x=e==null?void 0:e.encode)!==null&&x!==void 0?x:T,S.decode=(_=e==null?void 0:e.decode)!==null&&_!==void 0?_:k,S.beforeReconnect=this._reconnectAuth.bind(this),(e!=null&&e.logLevel||e!=null&&e.log_level)&&(this.logLevel=e.logLevel||e.log_level,S.params=Object.assign(Object.assign({},S.params),{log_level:this.logLevel})),this.worker){if(typeof window<"u"&&!window.Worker)throw new Error("Web Worker is not supported");this.workerUrl=e==null?void 0:e.workerUrl,S.autoSendHeartbeat=!this.worker}return S}async _reconnectAuth(){await this._waitForAuthIfNeeded(),this.isConnected()||this.connect()}}var go=class extends Error{constructor(n,e){var i;super(n),this.name="IcebergError",this.status=e.status,this.icebergType=e.icebergType,this.icebergCode=e.icebergCode,this.details=e.details,this.isCommitStateUnknown=e.icebergType==="CommitStateUnknownException"||[500,502,504].includes(e.status)&&((i=e.icebergType)==null?void 0:i.includes("CommitState"))===!0}isNotFound(){return this.status===404}isConflict(){return this.status===409}isAuthenticationTimeout(){return this.status===419}};function uk(n,e,i){const a=new URL(e,n);if(i)for(const[l,c]of Object.entries(i))c!==void 0&&a.searchParams.set(l,c);return a.toString()}async function hk(n){return!n||n.type==="none"?{}:n.type==="bearer"?{Authorization:`Bearer ${n.token}`}:n.type==="header"?{[n.name]:n.value}:n.type==="custom"?await n.getHeaders():{}}function dk(n){const e=n.fetchImpl??globalThis.fetch;return{async request({method:i,path:a,query:l,body:c,headers:u}){const d=uk(n.baseUrl,a,l),m=await hk(n.auth),p=await e(d,{method:i,headers:{...c?{"Content-Type":"application/json"}:{},...m,...u},body:c?JSON.stringify(c):void 0}),y=await p.text(),v=(p.headers.get("content-type")||"").includes("application/json"),x=v&&y?JSON.parse(y):y;if(!p.ok){const _=v?x:void 0,S=_==null?void 0:_.error;throw new go((S==null?void 0:S.message)??`Request failed with status ${p.status}`,{status:p.status,icebergType:S==null?void 0:S.type,icebergCode:S==null?void 0:S.code,details:_})}return{status:p.status,headers:p.headers,data:x}}}}function sc(n){return n.join("")}var fk=class{constructor(n,e=""){this.client=n,this.prefix=e}async listNamespaces(n){const e=n?{parent:sc(n.namespace)}:void 0;return(await this.client.request({method:"GET",path:`${this.prefix}/namespaces`,query:e})).data.namespaces.map(a=>({namespace:a}))}async createNamespace(n,e){const i={namespace:n.namespace,properties:e==null?void 0:e.properties};return(await this.client.request({method:"POST",path:`${this.prefix}/namespaces`,body:i})).data}async dropNamespace(n){await this.client.request({method:"DELETE",path:`${this.prefix}/namespaces/${sc(n.namespace)}`})}async loadNamespaceMetadata(n){return{properties:(await this.client.request({method:"GET",path:`${this.prefix}/namespaces/${sc(n.namespace)}`})).data.properties}}async namespaceExists(n){try{return await this.client.request({method:"HEAD",path:`${this.prefix}/namespaces/${sc(n.namespace)}`}),!0}catch(e){if(e instanceof go&&e.status===404)return!1;throw e}}async createNamespaceIfNotExists(n,e){try{return await this.createNamespace(n,e)}catch(i){if(i instanceof go&&i.status===409)return;throw i}}};function ja(n){return n.join("")}var mk=class{constructor(n,e="",i){this.client=n,this.prefix=e,this.accessDelegation=i}async listTables(n){return(await this.client.request({method:"GET",path:`${this.prefix}/namespaces/${ja(n.namespace)}/tables`})).data.identifiers}async createTable(n,e){const i={};return this.accessDelegation&&(i["X-Iceberg-Access-Delegation"]=this.accessDelegation),(await this.client.request({method:"POST",path:`${this.prefix}/namespaces/${ja(n.namespace)}/tables`,body:e,headers:i})).data.metadata}async updateTable(n,e){const i=await this.client.request({method:"POST",path:`${this.prefix}/namespaces/${ja(n.namespace)}/tables/${n.name}`,body:e});return{"metadata-location":i.data["metadata-location"],metadata:i.data.metadata}}async dropTable(n,e){await this.client.request({method:"DELETE",path:`${this.prefix}/namespaces/${ja(n.namespace)}/tables/${n.name}`,query:{purgeRequested:String((e==null?void 0:e.purge)??!1)}})}async loadTable(n){const e={};return this.accessDelegation&&(e["X-Iceberg-Access-Delegation"]=this.accessDelegation),(await this.client.request({method:"GET",path:`${this.prefix}/namespaces/${ja(n.namespace)}/tables/${n.name}`,headers:e})).data.metadata}async tableExists(n){const e={};this.accessDelegation&&(e["X-Iceberg-Access-Delegation"]=this.accessDelegation);try{return await this.client.request({method:"HEAD",path:`${this.prefix}/namespaces/${ja(n.namespace)}/tables/${n.name}`,headers:e}),!0}catch(i){if(i instanceof go&&i.status===404)return!1;throw i}}async createTableIfNotExists(n,e){try{return await this.createTable(n,e)}catch(i){if(i instanceof go&&i.status===409)return await this.loadTable({namespace:n.namespace,name:e.name});throw i}}},pk=class{constructor(n){var a;let e="v1";n.catalogName&&(e+=`/${n.catalogName}`);const i=n.baseUrl.endsWith("/")?n.baseUrl:`${n.baseUrl}/`;this.client=dk({baseUrl:i,auth:n.auth,fetchImpl:n.fetch}),this.accessDelegation=(a=n.accessDelegation)==null?void 0:a.join(","),this.namespaceOps=new fk(this.client,e),this.tableOps=new mk(this.client,e,this.accessDelegation)}async listNamespaces(n){return this.namespaceOps.listNamespaces(n)}async createNamespace(n,e){return this.namespaceOps.createNamespace(n,e)}async dropNamespace(n){await this.namespaceOps.dropNamespace(n)}async loadNamespaceMetadata(n){return this.namespaceOps.loadNamespaceMetadata(n)}async listTables(n){return this.tableOps.listTables(n)}async createTable(n,e){return this.tableOps.createTable(n,e)}async updateTable(n,e){return this.tableOps.updateTable(n,e)}async dropTable(n,e){await this.tableOps.dropTable(n,e)}async loadTable(n){return this.tableOps.loadTable(n)}async namespaceExists(n){return this.namespaceOps.namespaceExists(n)}async tableExists(n){return this.tableOps.tableExists(n)}async createNamespaceIfNotExists(n,e){return this.namespaceOps.createNamespaceIfNotExists(n,e)}async createTableIfNotExists(n,e){return this.tableOps.createTableIfNotExists(n,e)}};function yo(n){"@babel/helpers - typeof";return yo=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},yo(n)}function gk(n,e){if(yo(n)!="object"||!n)return n;var i=n[Symbol.toPrimitive];if(i!==void 0){var a=i.call(n,e);if(yo(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(n)}function yk(n){var e=gk(n,"string");return yo(e)=="symbol"?e:e+""}function bk(n,e,i){return(e=yk(e))in n?Object.defineProperty(n,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[e]=i,n}function Ub(n,e){var i=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(l){return Object.getOwnPropertyDescriptor(n,l).enumerable})),i.push.apply(i,a)}return i}function ge(n){for(var e=1;en?(...e)=>n(...e):(...e)=>fetch(...e),wk=n=>{if(typeof n!="object"||n===null)return!1;const e=Object.getPrototypeOf(n);return(e===null||e===Object.prototype||Object.getPrototypeOf(e)===null)&&!(Symbol.toStringTag in n)&&!(Symbol.iterator in n)},pf=n=>{if(Array.isArray(n))return n.map(i=>pf(i));if(typeof n=="function"||n!==Object(n))return n;const e={};return Object.entries(n).forEach(([i,a])=>{const l=i.replace(/([-_][a-z])/gi,c=>c.toUpperCase().replace(/[-_]/g,""));e[l]=pf(a)}),e},_k=n=>!n||typeof n!="string"||n.length===0||n.length>100||n.trim()!==n||n.includes("/")||n.includes("\\")?!1:/^[\w!.\*'() &$@=;:+,?-]+$/.test(n),Lb=n=>{if(typeof n=="object"&&n!==null){const e=n;if(typeof e.msg=="string")return e.msg;if(typeof e.message=="string")return e.message;if(typeof e.error_description=="string")return e.error_description;if(typeof e.error=="string")return e.error;if(typeof e.error=="object"&&e.error!==null){const i=e.error;if(typeof i.message=="string")return i.message}}return JSON.stringify(n)},Sk=async(n,e,i,a)=>{if(n!==null&&typeof n=="object"&&"json"in n&&typeof n.json=="function"){const l=n;let c=parseInt(String(l.status),10);Number.isFinite(c)||(c=500),l.json().then(u=>{const d=(u==null?void 0:u.statusCode)||(u==null?void 0:u.code)||c+"";e(new mf(Lb(u),c,d,a))}).catch(()=>{const u=c+"";e(new mf(l.statusText||`HTTP ${c} error`,c,u,a))})}else e(new _w(Lb(n),n,a))},Tk=(n,e,i,a)=>{const l={method:n,headers:(e==null?void 0:e.headers)||{}};if(n==="GET"||n==="HEAD"||!a)return ge(ge({},l),i);if(wk(a)){var c;const u=(e==null?void 0:e.headers)||{};let d;for(const[m,p]of Object.entries(u))m.toLowerCase()==="content-type"&&(d=p);l.headers=Cc(u,"Content-Type",(c=d)!==null&&c!==void 0?c:"application/json"),l.body=JSON.stringify(a)}else l.body=a;return e!=null&&e.duplex&&(l.duplex=e.duplex),ge(ge({},l),i)};async function Kr(n,e,i,a,l,c,u){return new Promise((d,m)=>{n(i,Tk(e,a,l,c)).then(p=>{if(!p.ok)throw p;if(a!=null&&a.noResolveJson)return p;if(u==="vectors"){const y=p.headers.get("content-type");if(p.headers.get("content-length")==="0"||p.status===204)return{};if(!y||!y.includes("application/json"))return{}}return p.json()}).then(p=>d(p)).catch(p=>Sk(p,m,a,u))})}function Sw(n="storage"){return{get:async(e,i,a,l)=>Kr(e,"GET",i,a,l,void 0,n),post:async(e,i,a,l,c)=>Kr(e,"POST",i,l,c,a,n),put:async(e,i,a,l,c)=>Kr(e,"PUT",i,l,c,a,n),head:async(e,i,a,l)=>Kr(e,"HEAD",i,ge(ge({},a),{},{noResolveJson:!0}),l,void 0,n),remove:async(e,i,a,l,c)=>Kr(e,"DELETE",i,l,c,a,n)}}const Ek=Sw("storage"),{get:bo,post:Vn,put:gf,head:Ak,remove:vo}=Ek,hn=Sw("vectors");var Qa=class{constructor(n,e={},i,a="storage"){this.shouldThrowOnError=!1,this.url=n,this.headers=vk(e),this.fetch=xk(i),this.namespace=a}throwOnError(){return this.shouldThrowOnError=!0,this}setHeader(n,e){return this.headers=Cc(this.headers,n,e),this}async handleOperation(n){var e=this;try{return{data:await n(),error:null}}catch(i){if(e.shouldThrowOnError)throw i;if(Gc(i))return{data:null,error:i};throw i}}};let Tw;Tw=Symbol.toStringTag;var jk=class{constructor(n,e){this.downloadFn=n,this.shouldThrowOnError=e,this[Tw]="StreamDownloadBuilder",this.promise=null}then(n,e){return this.getPromise().then(n,e)}catch(n){return this.getPromise().catch(n)}finally(n){return this.getPromise().finally(n)}getPromise(){return this.promise||(this.promise=this.execute()),this.promise}async execute(){var n=this;try{return{data:(await n.downloadFn()).body,error:null}}catch(e){if(n.shouldThrowOnError)throw e;if(Gc(e))return{data:null,error:e};throw e}}};let Ew;Ew=Symbol.toStringTag;var Nk=class{constructor(n,e){this.downloadFn=n,this.shouldThrowOnError=e,this[Ew]="BlobDownloadBuilder",this.promise=null}asStream(){return new jk(this.downloadFn,this.shouldThrowOnError)}then(n,e){return this.getPromise().then(n,e)}catch(n){return this.getPromise().catch(n)}finally(n){return this.getPromise().finally(n)}getPromise(){return this.promise||(this.promise=this.execute()),this.promise}async execute(){var n=this;try{return{data:await(await n.downloadFn()).blob(),error:null}}catch(e){if(n.shouldThrowOnError)throw e;if(Gc(e))return{data:null,error:e};throw e}}};const Ed={limit:100,offset:0,sortBy:{column:"name",order:"asc"}},zb={cacheControl:"3600",contentType:"text/plain;charset=UTF-8",upsert:!1};var kk=class extends Qa{constructor(n,e={},i,a){super(n,e,a,"storage"),this.bucketId=i}async uploadOrUpdate(n,e,i,a){var l=this;return l.handleOperation(async()=>{let c;const u=ge(ge({},zb),a);let d=ge(ge({},l.headers),n==="POST"&&{"x-upsert":String(u.upsert)});const m=u.metadata;if(typeof Blob<"u"&&i instanceof Blob?(c=new FormData,c.append("cacheControl",u.cacheControl),m&&c.append("metadata",l.encodeMetadata(m)),c.append("",i)):typeof FormData<"u"&&i instanceof FormData?(c=i,c.has("cacheControl")||c.append("cacheControl",u.cacheControl),m&&!c.has("metadata")&&c.append("metadata",l.encodeMetadata(m))):(c=i,d["cache-control"]=`max-age=${u.cacheControl}`,d["content-type"]=u.contentType,m&&(d["x-metadata"]=l.toBase64(l.encodeMetadata(m))),(typeof ReadableStream<"u"&&c instanceof ReadableStream||c&&typeof c=="object"&&"pipe"in c&&typeof c.pipe=="function")&&!u.duplex&&(u.duplex="half")),a!=null&&a.headers)for(const[x,_]of Object.entries(a.headers))d=Cc(d,x,_);const p=l._removeEmptyFolders(e),y=l._getFinalPath(p),v=await(n=="PUT"?gf:Vn)(l.fetch,`${l.url}/object/${y}`,c,ge({headers:d},u!=null&&u.duplex?{duplex:u.duplex}:{}));return{path:p,id:v.Id,fullPath:v.Key}})}async upload(n,e,i){return this.uploadOrUpdate("POST",n,e,i)}async uploadToSignedUrl(n,e,i,a){var l=this;const c=l._removeEmptyFolders(n),u=l._getFinalPath(c),d=new URL(l.url+`/object/upload/sign/${u}`);return d.searchParams.set("token",e),l.handleOperation(async()=>{let m;const p=ge(ge({},zb),a);let y=ge(ge({},l.headers),{"x-upsert":String(p.upsert)});const v=p.metadata;if(typeof Blob<"u"&&i instanceof Blob?(m=new FormData,m.append("cacheControl",p.cacheControl),v&&m.append("metadata",l.encodeMetadata(v)),m.append("",i)):typeof FormData<"u"&&i instanceof FormData?(m=i,m.has("cacheControl")||m.append("cacheControl",p.cacheControl),v&&!m.has("metadata")&&m.append("metadata",l.encodeMetadata(v))):(m=i,y["cache-control"]=`max-age=${p.cacheControl}`,y["content-type"]=p.contentType,v&&(y["x-metadata"]=l.toBase64(l.encodeMetadata(v))),(typeof ReadableStream<"u"&&m instanceof ReadableStream||m&&typeof m=="object"&&"pipe"in m&&typeof m.pipe=="function")&&!p.duplex&&(p.duplex="half")),a!=null&&a.headers)for(const[x,_]of Object.entries(a.headers))y=Cc(y,x,_);return{path:c,fullPath:(await gf(l.fetch,d.toString(),m,ge({headers:y},p!=null&&p.duplex?{duplex:p.duplex}:{}))).Key}})}async createSignedUploadUrl(n,e){var i=this;return i.handleOperation(async()=>{let a=i._getFinalPath(n);const l=ge({},i.headers);e!=null&&e.upsert&&(l["x-upsert"]="true");const c=await Vn(i.fetch,`${i.url}/object/upload/sign/${a}`,{},{headers:l}),u=new URL(i.url+c.url),d=u.searchParams.get("token");if(!d)throw new Ic("No token returned by API");return{signedUrl:u.toString(),path:n,token:d}})}async update(n,e,i){return this.uploadOrUpdate("PUT",n,e,i)}async move(n,e,i){var a=this;return a.handleOperation(async()=>await Vn(a.fetch,`${a.url}/object/move`,{bucketId:a.bucketId,sourceKey:n,destinationKey:e,destinationBucket:i==null?void 0:i.destinationBucket},{headers:a.headers}))}async copy(n,e,i){var a=this;return a.handleOperation(async()=>({path:(await Vn(a.fetch,`${a.url}/object/copy`,{bucketId:a.bucketId,sourceKey:n,destinationKey:e,destinationBucket:i==null?void 0:i.destinationBucket},{headers:a.headers})).Key}))}async createSignedUrl(n,e,i){var a=this;return a.handleOperation(async()=>{let l=a._getFinalPath(n);const c=typeof(i==null?void 0:i.transform)=="object"&&i.transform!==null&&Object.keys(i.transform).length>0;let u=await Vn(a.fetch,`${a.url}/object/sign/${l}`,ge({expiresIn:e},c?{transform:i.transform}:{}),{headers:a.headers});const d=new URLSearchParams;i!=null&&i.download&&d.set("download",i.download===!0?"":i.download),(i==null?void 0:i.cacheNonce)!=null&&d.set("cacheNonce",String(i.cacheNonce));const m=d.toString();return{signedUrl:encodeURI(`${a.url}${u.signedURL}${m?`&${m}`:""}`)}})}async createSignedUrls(n,e,i){var a=this;return a.handleOperation(async()=>{const l=await Vn(a.fetch,`${a.url}/object/sign/${a.bucketId}`,{expiresIn:e,paths:n},{headers:a.headers}),c=new URLSearchParams;i!=null&&i.download&&c.set("download",i.download===!0?"":i.download),(i==null?void 0:i.cacheNonce)!=null&&c.set("cacheNonce",String(i.cacheNonce));const u=c.toString();return l.map(d=>ge(ge({},d),{},{signedUrl:d.signedURL?encodeURI(`${a.url}${d.signedURL}${u?`&${u}`:""}`):null}))})}download(n,e,i){const a=typeof(e==null?void 0:e.transform)=="object"&&e.transform!==null&&Object.keys(e.transform).length>0?"render/image/authenticated":"object",l=new URLSearchParams;e!=null&&e.transform&&this.applyTransformOptsToQuery(l,e.transform),(e==null?void 0:e.cacheNonce)!=null&&l.set("cacheNonce",String(e.cacheNonce));const c=l.toString(),u=this._getFinalPath(n),d=()=>bo(this.fetch,`${this.url}/${a}/${u}${c?`?${c}`:""}`,{headers:this.headers,noResolveJson:!0},i);return new Nk(d,this.shouldThrowOnError)}async info(n){var e=this;const i=e._getFinalPath(n);return e.handleOperation(async()=>pf(await bo(e.fetch,`${e.url}/object/info/${i}`,{headers:e.headers})))}async exists(n){var e=this;const i=e._getFinalPath(n);try{return await Ak(e.fetch,`${e.url}/object/${i}`,{headers:e.headers}),{data:!0,error:null}}catch(l){if(e.shouldThrowOnError)throw l;if(Gc(l)){var a;const c=l instanceof mf?l.status:l instanceof _w?(a=l.originalError)===null||a===void 0?void 0:a.status:void 0;if(c!==void 0&&[400,404].includes(c))return{data:!1,error:l}}throw l}}getPublicUrl(n,e){const i=this._getFinalPath(n),a=new URLSearchParams;e!=null&&e.download&&a.set("download",e.download===!0?"":e.download),e!=null&&e.transform&&this.applyTransformOptsToQuery(a,e.transform),(e==null?void 0:e.cacheNonce)!=null&&a.set("cacheNonce",String(e.cacheNonce));const l=a.toString(),c=typeof(e==null?void 0:e.transform)=="object"&&e.transform!==null&&Object.keys(e.transform).length>0?"render/image":"object";return{data:{publicUrl:encodeURI(`${this.url}/${c}/public/${i}`)+(l?`?${l}`:"")}}}async remove(n){var e=this;return e.handleOperation(async()=>await vo(e.fetch,`${e.url}/object/${e.bucketId}`,{prefixes:n},{headers:e.headers}))}async purgeCache(n,e,i){var a=this;return a.handleOperation(async()=>{const l=a._getFinalPath(n),c=new URLSearchParams;e!=null&&e.transformations&&c.set("transformations","true");const u=c.toString();return await vo(a.fetch,`${a.url}/cdn/${l}${u?`?${u}`:""}`,{},{headers:a.headers},i)})}async list(n,e,i){var a=this;return a.handleOperation(async()=>{const l=e!=null&&e.sortBy?ge(ge({},Ed.sortBy),e.sortBy):Ed.sortBy,c=ge(ge(ge({},Ed),e),{},{sortBy:l,prefix:n||""});return await Vn(a.fetch,`${a.url}/object/list/${a.bucketId}`,c,{headers:a.headers},i)})}async listV2(n,e){var i=this;return i.handleOperation(async()=>{const a=ge({},n);return await Vn(i.fetch,`${i.url}/object/list-v2/${i.bucketId}`,a,{headers:i.headers},e)})}encodeMetadata(n){return JSON.stringify(n)}toBase64(n){return typeof Buffer<"u"?Buffer.from(n).toString("base64"):btoa(n)}_getFinalPath(n){return`${this.bucketId}/${n.replace(/^\/+/,"")}`}_removeEmptyFolders(n){return n.replace(/^\/|\/$/g,"").replace(/\/+/g,"/")}applyTransformOptsToQuery(n,e){return e.width&&n.set("width",e.width.toString()),e.height&&n.set("height",e.height.toString()),e.resize&&n.set("resize",e.resize),e.format&&n.set("format",e.format),e.quality&&n.set("quality",e.quality.toString()),n}};const Rk="2.110.2",No={"X-Client-Info":`storage-js/${Rk}`};var Ck=class extends Qa{constructor(n,e={},i,a){const l=new URL(n);a!=null&&a.useNewHostname&&/supabase\.(co|in|red)$/.test(l.hostname)&&!l.hostname.includes("storage.supabase.")&&(l.hostname=l.hostname.replace("supabase.","storage.supabase."));const c=l.href.replace(/\/$/,""),u=ge(ge({},No),e);super(c,u,i,"storage")}async listBuckets(n){var e=this;return e.handleOperation(async()=>{const i=e.listBucketOptionsToQueryString(n);return await bo(e.fetch,`${e.url}/bucket${i}`,{headers:e.headers})})}async getBucket(n){var e=this;return e.handleOperation(async()=>await bo(e.fetch,`${e.url}/bucket/${n}`,{headers:e.headers}))}async createBucket(n,e={public:!1}){var i=this;return i.handleOperation(async()=>await Vn(i.fetch,`${i.url}/bucket`,{id:n,name:n,type:e.type,public:e.public,file_size_limit:e.fileSizeLimit,allowed_mime_types:e.allowedMimeTypes},{headers:i.headers}))}async updateBucket(n,e){var i=this;return i.handleOperation(async()=>await gf(i.fetch,`${i.url}/bucket/${n}`,{id:n,name:n,public:e.public,file_size_limit:e.fileSizeLimit,allowed_mime_types:e.allowedMimeTypes},{headers:i.headers}))}async emptyBucket(n){var e=this;return e.handleOperation(async()=>await Vn(e.fetch,`${e.url}/bucket/${n}/empty`,{},{headers:e.headers}))}async deleteBucket(n){var e=this;return e.handleOperation(async()=>await vo(e.fetch,`${e.url}/bucket/${n}`,{},{headers:e.headers}))}async purgeBucketCache(n,e,i){var a=this;return a.handleOperation(async()=>{const l=new URLSearchParams;e!=null&&e.transformations&&l.set("transformations","true");const c=l.toString();return await vo(a.fetch,`${a.url}/cdn/${n}${c?`?${c}`:""}`,{},{headers:a.headers},i)})}listBucketOptionsToQueryString(n){const e={};return n&&("limit"in n&&(e.limit=String(n.limit)),"offset"in n&&(e.offset=String(n.offset)),n.search&&(e.search=n.search),n.sortColumn&&(e.sortColumn=n.sortColumn),n.sortOrder&&(e.sortOrder=n.sortOrder)),Object.keys(e).length>0?"?"+new URLSearchParams(e).toString():""}},Ok=class extends Qa{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),e);super(a,l,i,"storage")}async createBucket(n){var e=this;return e.handleOperation(async()=>await Vn(e.fetch,`${e.url}/bucket`,{name:n},{headers:e.headers}))}async listBuckets(n){var e=this;return e.handleOperation(async()=>{const i=new URLSearchParams;(n==null?void 0:n.limit)!==void 0&&i.set("limit",n.limit.toString()),(n==null?void 0:n.offset)!==void 0&&i.set("offset",n.offset.toString()),n!=null&&n.sortColumn&&i.set("sortColumn",n.sortColumn),n!=null&&n.sortOrder&&i.set("sortOrder",n.sortOrder),n!=null&&n.search&&i.set("search",n.search);const a=i.toString(),l=a?`${e.url}/bucket?${a}`:`${e.url}/bucket`;return await bo(e.fetch,l,{headers:e.headers})})}async deleteBucket(n){var e=this;return e.handleOperation(async()=>await vo(e.fetch,`${e.url}/bucket/${n}`,{},{headers:e.headers}))}from(n){var e=this;if(!_k(n))throw new Ic("Invalid bucket name: File, folder, and bucket names must follow AWS object key naming guidelines and should avoid the use of any other characters.");const i=new pk({baseUrl:this.url,catalogName:n,auth:{type:"custom",getHeaders:async()=>e.headers},fetch:this.fetch}),a=this.shouldThrowOnError;return new Proxy(i,{get(l,c){const u=l[c];return typeof u!="function"?u:async(...d)=>{try{return{data:await u.apply(l,d),error:null}}catch(m){if(a)throw m;return{data:null,error:m}}}}})}},Dk=class extends Qa{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),{},{"Content-Type":"application/json"},e);super(a,l,i,"vectors")}async createIndex(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/CreateIndex`,n,{headers:e.headers})||{})}async getIndex(n,e){var i=this;return i.handleOperation(async()=>await hn.post(i.fetch,`${i.url}/GetIndex`,{vectorBucketName:n,indexName:e},{headers:i.headers}))}async listIndexes(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/ListIndexes`,n,{headers:e.headers}))}async deleteIndex(n,e){var i=this;return i.handleOperation(async()=>await hn.post(i.fetch,`${i.url}/DeleteIndex`,{vectorBucketName:n,indexName:e},{headers:i.headers})||{})}},Mk=class extends Qa{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),{},{"Content-Type":"application/json"},e);super(a,l,i,"vectors")}async putVectors(n){var e=this;if(n.vectors.length<1||n.vectors.length>500)throw new Error("Vector batch size must be between 1 and 500 items");return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/PutVectors`,n,{headers:e.headers})||{})}async getVectors(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/GetVectors`,n,{headers:e.headers}))}async listVectors(n){var e=this;if(n.segmentCount!==void 0){if(n.segmentCount<1||n.segmentCount>16)throw new Error("segmentCount must be between 1 and 16");if(n.segmentIndex!==void 0&&(n.segmentIndex<0||n.segmentIndex>=n.segmentCount))throw new Error(`segmentIndex must be between 0 and ${n.segmentCount-1}`)}return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/ListVectors`,n,{headers:e.headers}))}async queryVectors(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/QueryVectors`,n,{headers:e.headers}))}async deleteVectors(n){var e=this;if(n.keys.length<1||n.keys.length>500)throw new Error("Keys batch size must be between 1 and 500 items");return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/DeleteVectors`,n,{headers:e.headers})||{})}},Bk=class extends Qa{constructor(n,e={},i){const a=n.replace(/\/$/,""),l=ge(ge({},No),{},{"Content-Type":"application/json"},e);super(a,l,i,"vectors")}async createBucket(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/CreateVectorBucket`,{vectorBucketName:n},{headers:e.headers})||{})}async getBucket(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/GetVectorBucket`,{vectorBucketName:n},{headers:e.headers}))}async listBuckets(n={}){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/ListVectorBuckets`,n,{headers:e.headers}))}async deleteBucket(n){var e=this;return e.handleOperation(async()=>await hn.post(e.fetch,`${e.url}/DeleteVectorBucket`,{vectorBucketName:n},{headers:e.headers})||{})}},Uk=class extends Bk{constructor(n,e={}){super(n,e.headers||{},e.fetch)}from(n){return new Lk(this.url,this.headers,n,this.fetch)}async createBucket(n){var e=()=>super.createBucket,i=this;return e().call(i,n)}async getBucket(n){var e=()=>super.getBucket,i=this;return e().call(i,n)}async listBuckets(n={}){var e=()=>super.listBuckets,i=this;return e().call(i,n)}async deleteBucket(n){var e=()=>super.deleteBucket,i=this;return e().call(i,n)}},Lk=class extends Dk{constructor(n,e,i,a){super(n,e,a),this.vectorBucketName=i}async createIndex(n){var e=()=>super.createIndex,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName}))}async listIndexes(n={}){var e=()=>super.listIndexes,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName}))}async getIndex(n){var e=()=>super.getIndex,i=this;return e().call(i,i.vectorBucketName,n)}async deleteIndex(n){var e=()=>super.deleteIndex,i=this;return e().call(i,i.vectorBucketName,n)}index(n){return new zk(this.url,this.headers,this.vectorBucketName,n,this.fetch)}},zk=class extends Mk{constructor(n,e,i,a,l){super(n,e,l),this.vectorBucketName=i,this.indexName=a}async putVectors(n){var e=()=>super.putVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async getVectors(n){var e=()=>super.getVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async listVectors(n={}){var e=()=>super.listVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async queryVectors(n){var e=()=>super.queryVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}async deleteVectors(n){var e=()=>super.deleteVectors,i=this;return e().call(i,ge(ge({},n),{},{vectorBucketName:i.vectorBucketName,indexName:i.indexName}))}},Pk=class extends Ck{constructor(n,e={},i,a){super(n,e,i,a)}from(n){return new kk(this.url,this.headers,n,this.fetch)}get vectors(){return new Uk(this.url+"/vector",{headers:this.headers,fetch:this.fetch})}get analytics(){return new Ok(this.url+"/iceberg",this.headers,this.fetch)}};const Aw="2.110.2",ks=30*1e3,Zr=3,Ad=Zr*ks,Vk=2*ks,Hk="http://localhost:9999",$k="supabase.auth.token",qk={"X-Client-Info":`gotrue-js/${Aw}`},yf="X-Supabase-Api-Version",jw={"2024-01-01":{timestamp:Date.parse("2024-01-01T00:00:00.0Z"),name:"2024-01-01"}},Fk=/^([a-z0-9_-]{4})*($|[a-z0-9_-]{3}$|[a-z0-9_-]{2}$)$/i,Ik=600*1e3;class xo extends Error{constructor(e,i,a){super(e),this.__isAuthError=!0,this.name="AuthError",this.status=i,this.code=a}toJSON(){return{name:this.name,message:this.message,status:this.status,code:this.code}}}function ne(n){return typeof n=="object"&&n!==null&&"__isAuthError"in n}class Gk extends xo{constructor(e,i,a){super(e,i,a),this.name="AuthApiError",this.status=i,this.code=a}}function Kk(n){return ne(n)&&n.name==="AuthApiError"}class Hn extends xo{constructor(e,i){super(e),this.name="AuthUnknownError",this.originalError=i}}class ss extends xo{constructor(e,i,a,l){super(e,a,l),this.name=i,this.status=a}}class Nt extends ss{constructor(){super("Auth session missing!","AuthSessionMissingError",400,void 0)}}function ic(n){return ne(n)&&n.name==="AuthSessionMissingError"}class Na extends ss{constructor(){super("Auth session or user missing","AuthInvalidTokenResponseError",500,void 0)}}class ac extends ss{constructor(e){super(e,"AuthInvalidCredentialsError",400,void 0)}}class rc extends ss{constructor(e,i=null){super(e,"AuthImplicitGrantRedirectError",500,void 0),this.details=null,this.details=i}toJSON(){return Object.assign(Object.assign({},super.toJSON()),{details:this.details})}}function Yk(n){return ne(n)&&n.name==="AuthImplicitGrantRedirectError"}class Pb extends ss{constructor(e,i=null){super(e,"AuthPKCEGrantCodeExchangeError",500,void 0),this.details=null,this.details=i}toJSON(){return Object.assign(Object.assign({},super.toJSON()),{details:this.details})}}class Xk extends ss{constructor(){super("PKCE code verifier not found in storage. This can happen if the auth flow was initiated in a different browser or device, or if the storage was cleared. For SSR frameworks (Next.js, SvelteKit, etc.), use @supabase/ssr on both the server and client to store the code verifier in cookies.","AuthPKCECodeVerifierMissingError",400,"pkce_code_verifier_not_found")}}class bf extends ss{constructor(e,i){super(e,"AuthRetryableFetchError",i,void 0)}}function Vb(n){return ne(n)&&n.name==="AuthRetryableFetchError"}class Hb extends ss{constructor(e="Refresh result discarded: session state changed mid-flight (e.g., concurrent signOut)"){super(e,"AuthRefreshDiscardedError",409,void 0)}}function Jk(n){return ne(n)&&n.name==="AuthRefreshDiscardedError"}class $b extends ss{constructor(e,i,a){super(e,"AuthWeakPasswordError",i,"weak_password"),this.reasons=a}toJSON(){return Object.assign(Object.assign({},super.toJSON()),{reasons:this.reasons})}}class Oc extends ss{constructor(e){super(e,"AuthInvalidJwtError",400,"invalid_jwt")}}const Dc="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".split(""),qb=` +\r=`.split(""),Qk=(()=>{const n=new Array(128);for(let e=0;e=6;){const a=e.queue>>e.queuedBits-6&63;i(Dc[a]),e.queuedBits-=6}else if(e.queuedBits>0)for(e.queue=e.queue<<6-e.queuedBits,e.queuedBits=6;e.queuedBits>=6;){const a=e.queue>>e.queuedBits-6&63;i(Dc[a]),e.queuedBits-=6}}function Nw(n,e,i){const a=Qk[n];if(a>-1)for(e.queue=e.queue<<6|a,e.queuedBits+=6;e.queuedBits>=8;)i(e.queue>>e.queuedBits-8&255),e.queuedBits-=8;else{if(a===-2)return;throw new Error(`Invalid Base64-URL character "${String.fromCharCode(n)}"`)}}function Ib(n){const e=[],i=u=>{e.push(String.fromCodePoint(u))},a={utf8seq:0,codepoint:0},l={queue:0,queuedBits:0},c=u=>{eR(u,a,i)};for(let u=0;u>6),e(128|n&63);return}else if(n<=65535){e(224|n>>12),e(128|n>>6&63),e(128|n&63);return}else if(n<=1114111){e(240|n>>18),e(128|n>>12&63),e(128|n>>6&63),e(128|n&63);return}throw new Error(`Unrecognized Unicode codepoint: ${n.toString(16)}`)}function Wk(n,e){for(let i=0;i55295&&a<=56319){const l=(a-55296)*1024&65535;a=(n.charCodeAt(i+1)-56320&65535|l)+65536,i+=1}Zk(a,e)}}function eR(n,e,i){if(e.utf8seq===0){if(n<=127){i(n);return}for(let a=1;a<6;a+=1)if((n>>7-a&1)===0){e.utf8seq=a;break}if(e.utf8seq===2)e.codepoint=n&31;else if(e.utf8seq===3)e.codepoint=n&15;else if(e.utf8seq===4)e.codepoint=n&7;else throw new Error("Invalid UTF-8 sequence");e.utf8seq-=1}else if(e.utf8seq>0){if(n<=127)throw new Error("Invalid UTF-8 sequence");e.codepoint=e.codepoint<<6|n&63,e.utf8seq-=1,e.utf8seq===0&&i(e.codepoint)}}function qa(n){const e=[],i={queue:0,queuedBits:0},a=l=>{e.push(l)};for(let l=0;le.push(i)),new Uint8Array(e)}function zi(n){const e=[],i={queue:0,queuedBits:0},a=l=>{e.push(l)};return n.forEach(l=>Fb(l,i,a)),Fb(null,i,a),e.join("")}function nR(n){return Math.round(Date.now()/1e3)+n}function sR(){return Symbol("auth-callback")}const Vt=()=>typeof window<"u"&&typeof document<"u",Oi={tested:!1,writable:!1},kw=()=>{if(!Vt())return!1;try{if(typeof globalThis.localStorage!="object")return!1}catch{return!1}if(Oi.tested)return Oi.writable;const n=`lswt-${Math.random()}${Math.random()}`;try{globalThis.localStorage.setItem(n,n),globalThis.localStorage.removeItem(n),Oi.tested=!0,Oi.writable=!0}catch{Oi.tested=!0,Oi.writable=!1}return Oi.writable};function iR(n){const e={},i=new URL(n);if(i.hash&&i.hash[0]==="#")try{new URLSearchParams(i.hash.substring(1)).forEach((l,c)=>{e[c]=l})}catch{}return i.searchParams.forEach((a,l)=>{e[l]=a}),e}const Rw=n=>n?(...e)=>n(...e):(...e)=>fetch(...e),aR=n=>typeof n=="object"&&n!==null&&"status"in n&&"ok"in n&&"json"in n&&typeof n.json=="function",Ba=async(n,e,i)=>{await n.setItem(e,JSON.stringify(i))},Un=async(n,e)=>{const i=await n.getItem(e);if(!i)return null;try{return JSON.parse(i)}catch{return null}},ft=async(n,e)=>{await n.removeItem(e)};class Kc{constructor(){this.promise=new Kc.promiseConstructor((e,i)=>{this.resolve=e,this.reject=i})}}Kc.promiseConstructor=Promise;function oc(n){const e=n.split(".");if(e.length!==3)throw new Oc("Invalid JWT structure");for(let a=0;a{setTimeout(()=>e(null),n)})}function oR(n,e){return new Promise((a,l)=>{(async()=>{for(let c=0;c<1/0;c++)try{const u=await n(c);if(!e(c,null,u)){a(u);return}}catch(u){if(!e(c,u)){l(u);return}}})()})}function lR(n){return("0"+n.toString(16)).substr(-2)}function cR(){const e=new Uint32Array(56);if(typeof crypto>"u"){const i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",a=i.length;let l="";for(let c=0;c<56;c++)l+=i.charAt(Math.floor(Math.random()*a));return l}return crypto.getRandomValues(e),Array.from(e,lR).join("")}async function uR(n){const i=new TextEncoder().encode(n),a=await crypto.subtle.digest("SHA-256",i),l=new Uint8Array(a);return Array.from(l).map(c=>String.fromCharCode(c)).join("")}async function hR(n){if(!(typeof crypto<"u"&&typeof crypto.subtle<"u"&&typeof TextEncoder<"u"))return console.warn("WebCrypto API is not supported. Code challenge method will default to use plain instead of sha256."),n;const i=await uR(n);return btoa(i).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async function Di(n,e,i=!1){const a=cR();let l=a;i&&(l+="/recovery"),await Ba(n,`${e}-code-verifier`,l);const c=await hR(a);return[c,a===c?"plain":"s256"]}const dR=/^2[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$/i;function fR(n){const e=n.headers.get(yf);if(!e||!e.match(dR))return null;try{return new Date(`${e}T00:00:00.0Z`)}catch{return null}}function mR(n){if(!n)throw new Error("Missing exp claim");const e=Math.floor(Date.now()/1e3);if(n<=e)throw new Error("JWT has expired")}function pR(n){switch(n){case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}};default:throw new Error("Invalid alg claim")}}const gR=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;function Es(n){if(!gR.test(n))throw new Error("@supabase/auth-js: Expected parameter to be UUID but is not")}function zn(n){if(!n.passkey)throw new Error("@supabase/auth-js: the passkey API is experimental and disabled by default. Enable it by passing `auth: { experimental: { passkey: true } }` to createClient (or to the GoTrueClient constructor).")}function jd(){const n={};return new Proxy(n,{get:(e,i)=>{if(i==="__isUserNotAvailableProxy")return!0;if(typeof i=="symbol"){const a=i.toString();if(a==="Symbol(Symbol.toPrimitive)"||a==="Symbol(Symbol.toStringTag)"||a==="Symbol(util.inspect.custom)")return}throw new Error(`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Accessing the "${i}" property of the session object is not supported. Please use getUser() instead.`)},set:(e,i)=>{throw new Error(`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Setting the "${i}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`)},deleteProperty:(e,i)=>{throw new Error(`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Deleting the "${i}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`)}})}function yR(n,e){return new Proxy(n,{get:(i,a,l)=>{if(a==="__isInsecureUserWarningProxy")return!0;if(typeof a=="symbol"){const c=a.toString();if(c==="Symbol(Symbol.toPrimitive)"||c==="Symbol(Symbol.toStringTag)"||c==="Symbol(util.inspect.custom)"||c==="Symbol(nodejs.util.inspect.custom)")return Reflect.get(i,a,l)}return!e.value&&typeof a=="string"&&(console.warn("Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server."),e.value=!0),Reflect.get(i,a,l)}})}function Gb(n){return JSON.parse(JSON.stringify(n))}const Bi=n=>{if(typeof n=="object"&&n!==null){const e=n;if(typeof e.msg=="string")return e.msg;if(typeof e.message=="string")return e.message;if(typeof e.error_description=="string")return e.error_description;if(typeof e.error=="string")return e.error}return JSON.stringify(n)},bR=[500,501,502,503,504,520,521,522,523,524,525,526,527,528,529,530];async function Kb(n){var e;if(!aR(n))throw new bf(Bi(n),0);if(bR.includes(n.status))throw new bf(Bi(n),n.status);let i;try{i=await n.json()}catch(c){throw new Hn(Bi(c),c)}let a;const l=fR(n);if(l&&l.getTime()>=jw["2024-01-01"].timestamp&&typeof i=="object"&&i&&typeof i.code=="string"?a=i.code:typeof i=="object"&&i&&typeof i.error_code=="string"&&(a=i.error_code),a){if(a==="weak_password")throw new $b(Bi(i),n.status,((e=i.weak_password)===null||e===void 0?void 0:e.reasons)||[]);if(a==="session_not_found")throw new Nt}else if(typeof i=="object"&&i&&typeof i.weak_password=="object"&&i.weak_password&&Array.isArray(i.weak_password.reasons)&&i.weak_password.reasons.length&&i.weak_password.reasons.reduce((c,u)=>c&&typeof u=="string",!0))throw new $b(Bi(i),n.status,i.weak_password.reasons);throw new Gk(Bi(i),n.status||500,a)}const vR=(n,e,i,a)=>{const l={method:n,headers:(e==null?void 0:e.headers)||{}};return n==="GET"?l:(l.headers=Object.assign({"Content-Type":"application/json;charset=UTF-8"},e==null?void 0:e.headers),l.body=JSON.stringify(a),Object.assign(Object.assign({},l),i))};async function ue(n,e,i,a){var l;const c=Object.assign({},a==null?void 0:a.headers);c[yf]||(c[yf]=jw["2024-01-01"].name),a!=null&&a.jwt&&(c.Authorization=`Bearer ${a.jwt}`);const u=(l=a==null?void 0:a.query)!==null&&l!==void 0?l:{};a!=null&&a.redirectTo&&(u.redirect_to=a.redirectTo);const d=Object.keys(u).length?"?"+new URLSearchParams(u).toString():"",m=await xR(n,e,i+d,{headers:c,noResolveJson:a==null?void 0:a.noResolveJson},{},a==null?void 0:a.body);return a!=null&&a.xform?a==null?void 0:a.xform(m):{data:Object.assign({},m),error:null}}async function xR(n,e,i,a,l,c){const u=vR(e,a,l,c);let d;try{d=await n(i,Object.assign({},u))}catch(m){throw console.error(m),new bf(Bi(m),0)}if(d.ok||await Kb(d),a!=null&&a.noResolveJson)return d;try{return await d.json()}catch(m){await Kb(m)}}function Tn(n){var e;let i=null;SR(n)&&(i=Object.assign({},n),n.expires_at||(i.expires_at=nR(n.expires_in)));const a=(e=n.user)!==null&&e!==void 0?e:typeof(n==null?void 0:n.id)=="string"?n:null;return{data:{session:i,user:a},error:null}}function Yb(n){const e=Tn(n);return!e.error&&n.weak_password&&typeof n.weak_password=="object"&&Array.isArray(n.weak_password.reasons)&&n.weak_password.reasons.length&&n.weak_password.message&&typeof n.weak_password.message=="string"&&n.weak_password.reasons.reduce((i,a)=>i&&typeof a=="string",!0)&&(e.data.weak_password=n.weak_password),e}function ai(n){var e;return{data:{user:(e=n.user)!==null&&e!==void 0?e:n},error:null}}function wR(n){return{data:n,error:null}}function _R(n){const{action_link:e,email_otp:i,hashed_token:a,redirect_to:l,verification_type:c}=n,u=Fc(n,["action_link","email_otp","hashed_token","redirect_to","verification_type"]),d={action_link:e,email_otp:i,hashed_token:a,redirect_to:l,verification_type:c},m=Object.assign({},u);return{data:{properties:d,user:m},error:null}}function Xb(n){return n}function SR(n){return!!n.access_token&&!!n.refresh_token&&!!n.expires_in}const Nd=["global","local","others"];class TR{constructor({url:e="",headers:i={},fetch:a,experimental:l}){this.url=e,this.headers=i,this.fetch=Rw(a),this.experimental=l??{},this.mfa={listFactors:this._listFactors.bind(this),deleteFactor:this._deleteFactor.bind(this)},this.oauth={listClients:this._listOAuthClients.bind(this),createClient:this._createOAuthClient.bind(this),getClient:this._getOAuthClient.bind(this),updateClient:this._updateOAuthClient.bind(this),deleteClient:this._deleteOAuthClient.bind(this),regenerateClientSecret:this._regenerateOAuthClientSecret.bind(this)},this.customProviders={listProviders:this._listCustomProviders.bind(this),createProvider:this._createCustomProvider.bind(this),getProvider:this._getCustomProvider.bind(this),updateProvider:this._updateCustomProvider.bind(this),deleteProvider:this._deleteCustomProvider.bind(this)},this.passkey={listPasskeys:this._adminListPasskeys.bind(this),deletePasskey:this._adminDeletePasskey.bind(this)}}async signOut(e,i=Nd[0]){if(Nd.indexOf(i)<0)throw new Error(`@supabase/auth-js: Parameter scope must be one of ${Nd.join(", ")}`);try{return await ue(this.fetch,"POST",`${this.url}/logout?scope=${i}`,{headers:this.headers,jwt:e,noResolveJson:!0}),{data:null,error:null}}catch(a){if(ne(a))return{data:null,error:a};throw a}}async inviteUserByEmail(e,i={}){try{return await ue(this.fetch,"POST",`${this.url}/invite`,{body:{email:e,data:i.data},headers:this.headers,redirectTo:i.redirectTo,xform:ai})}catch(a){if(ne(a))return{data:{user:null},error:a};throw a}}async generateLink(e){try{const{options:i}=e,a=Fc(e,["options"]),l=Object.assign(Object.assign({},a),i);return"newEmail"in a&&(l.new_email=a==null?void 0:a.newEmail,delete l.newEmail),await ue(this.fetch,"POST",`${this.url}/admin/generate_link`,{body:l,headers:this.headers,xform:_R,redirectTo:i==null?void 0:i.redirectTo})}catch(i){if(ne(i))return{data:{properties:null,user:null},error:i};throw i}}async createUser(e){try{return await ue(this.fetch,"POST",`${this.url}/admin/users`,{body:e,headers:this.headers,xform:ai})}catch(i){if(ne(i))return{data:{user:null},error:i};throw i}}async listUsers(e){var i,a,l,c,u,d,m;try{const p={nextPage:null,lastPage:0,total:0},y=await ue(this.fetch,"GET",`${this.url}/admin/users`,{headers:this.headers,noResolveJson:!0,query:{page:(a=(i=e==null?void 0:e.page)===null||i===void 0?void 0:i.toString())!==null&&a!==void 0?a:"",per_page:(c=(l=e==null?void 0:e.perPage)===null||l===void 0?void 0:l.toString())!==null&&c!==void 0?c:""},xform:Xb});if(y.error)throw y.error;const v=await y.json(),x=(u=y.headers.get("x-total-count"))!==null&&u!==void 0?u:0,_=(m=(d=y.headers.get("link"))===null||d===void 0?void 0:d.split(","))!==null&&m!==void 0?m:[];return _.length>0&&(_.forEach(S=>{const T=parseInt(S.split(";")[0].split("=")[1].substring(0,1)),k=JSON.parse(S.split(";")[1].split("=")[1]);p[`${k}Page`]=T}),p.total=parseInt(x)),{data:Object.assign(Object.assign({},v),p),error:null}}catch(p){if(ne(p))return{data:{users:[]},error:p};throw p}}async getUserById(e){Es(e);try{return await ue(this.fetch,"GET",`${this.url}/admin/users/${e}`,{headers:this.headers,xform:ai})}catch(i){if(ne(i))return{data:{user:null},error:i};throw i}}async updateUserById(e,i){Es(e);try{return await ue(this.fetch,"PUT",`${this.url}/admin/users/${e}`,{body:i,headers:this.headers,xform:ai})}catch(a){if(ne(a))return{data:{user:null},error:a};throw a}}async deleteUser(e,i=!1){Es(e);try{return await ue(this.fetch,"DELETE",`${this.url}/admin/users/${e}`,{headers:this.headers,body:{should_soft_delete:i},xform:ai})}catch(a){if(ne(a))return{data:{user:null},error:a};throw a}}async _listFactors(e){Es(e.userId);try{const{data:i,error:a}=await ue(this.fetch,"GET",`${this.url}/admin/users/${e.userId}/factors`,{headers:this.headers,xform:l=>({data:{factors:l},error:null})});return{data:i,error:a}}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _deleteFactor(e){Es(e.userId),Es(e.id);try{return{data:await ue(this.fetch,"DELETE",`${this.url}/admin/users/${e.userId}/factors/${e.id}`,{headers:this.headers}),error:null}}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _listOAuthClients(e){var i,a,l,c,u,d,m;try{const p={nextPage:null,lastPage:0,total:0},y=await ue(this.fetch,"GET",`${this.url}/admin/oauth/clients`,{headers:this.headers,noResolveJson:!0,query:{page:(a=(i=e==null?void 0:e.page)===null||i===void 0?void 0:i.toString())!==null&&a!==void 0?a:"",per_page:(c=(l=e==null?void 0:e.perPage)===null||l===void 0?void 0:l.toString())!==null&&c!==void 0?c:""},xform:Xb});if(y.error)throw y.error;const v=await y.json(),x=(u=y.headers.get("x-total-count"))!==null&&u!==void 0?u:0,_=(m=(d=y.headers.get("link"))===null||d===void 0?void 0:d.split(","))!==null&&m!==void 0?m:[];return _.length>0&&(_.forEach(S=>{const T=parseInt(S.split(";")[0].split("=")[1].substring(0,1)),k=JSON.parse(S.split(";")[1].split("=")[1]);p[`${k}Page`]=T}),p.total=parseInt(x)),{data:Object.assign(Object.assign({},v),p),error:null}}catch(p){if(ne(p))return{data:{clients:[]},error:p};throw p}}async _createOAuthClient(e){try{return await ue(this.fetch,"POST",`${this.url}/admin/oauth/clients`,{body:e,headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _getOAuthClient(e){try{return await ue(this.fetch,"GET",`${this.url}/admin/oauth/clients/${e}`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _updateOAuthClient(e,i){try{return await ue(this.fetch,"PUT",`${this.url}/admin/oauth/clients/${e}`,{body:i,headers:this.headers,xform:a=>({data:a,error:null})})}catch(a){if(ne(a))return{data:null,error:a};throw a}}async _deleteOAuthClient(e){try{return await ue(this.fetch,"DELETE",`${this.url}/admin/oauth/clients/${e}`,{headers:this.headers,noResolveJson:!0}),{data:null,error:null}}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _regenerateOAuthClientSecret(e){try{return await ue(this.fetch,"POST",`${this.url}/admin/oauth/clients/${e}/regenerate_secret`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _listCustomProviders(e){try{const i={};return e!=null&&e.type&&(i.type=e.type),await ue(this.fetch,"GET",`${this.url}/admin/custom-providers`,{headers:this.headers,query:i,xform:a=>{var l;return{data:{providers:(l=a==null?void 0:a.providers)!==null&&l!==void 0?l:[]},error:null}}})}catch(i){if(ne(i))return{data:{providers:[]},error:i};throw i}}async _createCustomProvider(e){try{return await ue(this.fetch,"POST",`${this.url}/admin/custom-providers`,{body:e,headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _getCustomProvider(e){try{return await ue(this.fetch,"GET",`${this.url}/admin/custom-providers/${e}`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _updateCustomProvider(e,i){try{return await ue(this.fetch,"PUT",`${this.url}/admin/custom-providers/${e}`,{body:i,headers:this.headers,xform:a=>({data:a,error:null})})}catch(a){if(ne(a))return{data:null,error:a};throw a}}async _deleteCustomProvider(e){try{return await ue(this.fetch,"DELETE",`${this.url}/admin/custom-providers/${e}`,{headers:this.headers,noResolveJson:!0}),{data:null,error:null}}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _adminListPasskeys(e){zn(this.experimental),Es(e.userId);try{return await ue(this.fetch,"GET",`${this.url}/admin/users/${e.userId}/passkeys`,{headers:this.headers,xform:i=>({data:i,error:null})})}catch(i){if(ne(i))return{data:null,error:i};throw i}}async _adminDeletePasskey(e){zn(this.experimental),Es(e.userId),Es(e.passkeyId);try{return await ue(this.fetch,"DELETE",`${this.url}/admin/users/${e.userId}/passkeys/${e.passkeyId}`,{headers:this.headers,noResolveJson:!0}),{data:null,error:null}}catch(i){if(ne(i))return{data:null,error:i};throw i}}}function Jb(n={}){return{getItem:e=>n[e]||null,setItem:(e,i)=>{n[e]=i},removeItem:e=>{delete n[e]}}}globalThis&&kw()&&globalThis.localStorage&&globalThis.localStorage.getItem("supabase.gotrue-js.locks.debug");class ER extends Error{constructor(e){super(e),this.isAcquireTimeout=!0}}function AR(){if(typeof globalThis!="object")try{Object.defineProperty(Object.prototype,"__magic__",{get:function(){return this},configurable:!0}),__magic__.globalThis=__magic__,delete Object.prototype.__magic__}catch{typeof self<"u"&&(self.globalThis=self)}}function Cw(n){if(!/^0x[a-fA-F0-9]{40}$/.test(n))throw new Error(`@supabase/auth-js: Address "${n}" is invalid.`);return n.toLowerCase()}function jR(n){return parseInt(n,16)}function NR(n){const e=new TextEncoder().encode(n);return"0x"+Array.from(e,a=>a.toString(16).padStart(2,"0")).join("")}function kR(n){var e;const{chainId:i,domain:a,expirationTime:l,issuedAt:c=new Date,nonce:u,notBefore:d,requestId:m,resources:p,scheme:y,uri:v,version:x}=n;{if(!Number.isInteger(i))throw new Error(`@supabase/auth-js: Invalid SIWE message field "chainId". Chain ID must be a EIP-155 chain ID. Provided value: ${i}`);if(!a)throw new Error('@supabase/auth-js: Invalid SIWE message field "domain". Domain must be provided.');if(u&&u.length<8)throw new Error(`@supabase/auth-js: Invalid SIWE message field "nonce". Nonce must be at least 8 characters. Provided value: ${u}`);if(!v)throw new Error('@supabase/auth-js: Invalid SIWE message field "uri". URI must be provided.');if(x!=="1")throw new Error(`@supabase/auth-js: Invalid SIWE message field "version". Version must be '1'. Provided value: ${x}`);if(!((e=n.statement)===null||e===void 0)&&e.includes(` +`))throw new Error(`@supabase/auth-js: Invalid SIWE message field "statement". Statement must not include '\\n'. Provided value: ${n.statement}`)}const _=Cw(n.address),S=y?`${y}://${a}`:a,T=n.statement?`${n.statement} +`:"",k=`${S} wants you to sign in with your Ethereum account: +${_} + +${T}`;let R=`URI: ${v} +Version: ${x} +Chain ID: ${i}${u?` +Nonce: ${u}`:""} +Issued At: ${c.toISOString()}`;if(l&&(R+=` +Expiration Time: ${l.toISOString()}`),d&&(R+=` +Not Before: ${d.toISOString()}`),m&&(R+=` +Request ID: ${m}`),p){let M=` +Resources:`;for(const N of p){if(!N||typeof N!="string")throw new Error(`@supabase/auth-js: Invalid SIWE message field "resources". Every resource must be a valid string. Provided value: ${N}`);M+=` +- ${N}`}R+=M}return`${k} +${R}`}class bt extends Error{constructor({message:e,code:i,cause:a,name:l}){var c;super(e,{cause:a}),this.__isWebAuthnError=!0,this.name=(c=l??(a instanceof Error?a.name:void 0))!==null&&c!==void 0?c:"Unknown Error",this.code=i}toJSON(){return{name:this.name,message:this.message,code:this.code}}}class Mc extends bt{constructor(e,i){super({code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:i,message:e}),this.name="WebAuthnUnknownError",this.originalError=i}}function RR({error:n,options:e}){var i,a,l;const{publicKey:c}=e;if(!c)throw Error("options was missing required publicKey property");if(n.name==="AbortError"){if(e.signal instanceof AbortSignal)return new bt({message:"Registration ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:n})}else if(n.name==="ConstraintError"){if(((i=c.authenticatorSelection)===null||i===void 0?void 0:i.requireResidentKey)===!0)return new bt({message:"Discoverable credentials were required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",cause:n});if(e.mediation==="conditional"&&((a=c.authenticatorSelection)===null||a===void 0?void 0:a.userVerification)==="required")return new bt({message:"User verification was required during automatic registration but it could not be performed",code:"ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",cause:n});if(((l=c.authenticatorSelection)===null||l===void 0?void 0:l.userVerification)==="required")return new bt({message:"User verification was required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",cause:n})}else{if(n.name==="InvalidStateError")return new bt({message:"The authenticator was previously registered",code:"ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",cause:n});if(n.name==="NotAllowedError")return new bt({message:n.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n});if(n.name==="NotSupportedError")return c.pubKeyCredParams.filter(d=>d.type==="public-key").length===0?new bt({message:'No entry in pubKeyCredParams was of type "public-key"',code:"ERROR_MALFORMED_PUBKEYCREDPARAMS",cause:n}):new bt({message:"No available authenticator supported any of the specified pubKeyCredParams algorithms",code:"ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",cause:n});if(n.name==="SecurityError"){const u=window.location.hostname;if(Ow(u)){if(c.rp.id!==u)return new bt({message:`The RP ID "${c.rp.id}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:n})}else return new bt({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:n})}else if(n.name==="TypeError"){if(c.user.id.byteLength<1||c.user.id.byteLength>64)return new bt({message:"User ID was not between 1 and 64 characters",code:"ERROR_INVALID_USER_ID_LENGTH",cause:n})}else if(n.name==="UnknownError")return new bt({message:"The authenticator was unable to process the specified options, or could not create a new credential",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:n})}return new bt({message:"a Non-Webauthn related error has occurred",code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n})}function CR({error:n,options:e}){const{publicKey:i}=e;if(!i)throw Error("options was missing required publicKey property");if(n.name==="AbortError"){if(e.signal instanceof AbortSignal)return new bt({message:"Authentication ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:n})}else{if(n.name==="NotAllowedError")return new bt({message:n.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n});if(n.name==="SecurityError"){const a=window.location.hostname;if(Ow(a)){if(i.rpId!==a)return new bt({message:`The RP ID "${i.rpId}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:n})}else return new bt({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:n})}else if(n.name==="UnknownError")return new bt({message:"The authenticator was unable to process the specified options, or could not create a new assertion signature",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:n})}return new bt({message:"a Non-Webauthn related error has occurred",code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:n})}class OR{createNewAbortSignal(){if(this.controller){const i=new Error("Cancelling existing WebAuthn API call for new one");i.name="AbortError",this.controller.abort(i)}const e=new AbortController;return this.controller=e,e.signal}cancelCeremony(){if(this.controller){const e=new Error("Manually cancelling existing WebAuthn API call");e.name="AbortError",this.controller.abort(e),this.controller=void 0}}}const vf=new OR;function Qb(n){if(!n)throw new Error("Credential creation options are required");if(typeof PublicKeyCredential<"u"&&"parseCreationOptionsFromJSON"in PublicKeyCredential&&typeof PublicKeyCredential.parseCreationOptionsFromJSON=="function")return PublicKeyCredential.parseCreationOptionsFromJSON(n);const{challenge:e,user:i,excludeCredentials:a}=n,l=Fc(n,["challenge","user","excludeCredentials"]),c=qa(e).buffer,u=Object.assign(Object.assign({},i),{id:qa(i.id).buffer}),d=Object.assign(Object.assign({},l),{challenge:c,user:u});if(a&&a.length>0){d.excludeCredentials=new Array(a.length);for(let m=0;m0){c.allowCredentials=new Array(i.length);for(let u=0;ul!==null&&typeof l=="object"&&!Array.isArray(l),i=l=>l instanceof ArrayBuffer||ArrayBuffer.isView(l),a={};for(const l of n)if(l)for(const c in l){const u=l[c];if(u!==void 0)if(Array.isArray(u))a[c]=u;else if(i(u))a[c]=u;else if(e(u)){const d=a[c];e(d)?a[c]=Uc(d,u):a[c]=Uc(u)}else a[c]=u}return a}function BR(n,e){return Uc(DR,n,e||{})}function UR(n,e){return Uc(MR,n,e||{})}class LR{constructor(e){this.client=e,this.enroll=this._enroll.bind(this),this.challenge=this._challenge.bind(this),this.verify=this._verify.bind(this),this.authenticate=this._authenticate.bind(this),this.register=this._register.bind(this)}async _enroll(e){return this.client.mfa.enroll(Object.assign(Object.assign({},e),{factorType:"webauthn"}))}async _challenge({factorId:e,webauthn:i,friendlyName:a,signal:l},c){var u;try{const{data:d,error:m}=await this.client.mfa.challenge({factorId:e,webauthn:i});if(!d)return{data:null,error:m};const p=l??vf.createNewAbortSignal();if(d.webauthn.type==="create"){const{user:y}=d.webauthn.credential_options.publicKey;if(!y.name){const v=a;if(v)y.name=`${y.id}:${v}`;else{const _=(await this.client.getUser()).data.user,S=((u=_==null?void 0:_.user_metadata)===null||u===void 0?void 0:u.name)||(_==null?void 0:_.email)||(_==null?void 0:_.id)||"User";y.name=`${y.id}:${S}`}}y.displayName||(y.displayName=y.name)}switch(d.webauthn.type){case"create":{const y=BR(d.webauthn.credential_options.publicKey,c==null?void 0:c.create),{data:v,error:x}=await Dw({publicKey:y,signal:p});return v?{data:{factorId:e,challengeId:d.id,webauthn:{type:d.webauthn.type,credential_response:v}},error:null}:{data:null,error:x}}case"request":{const y=UR(d.webauthn.credential_options.publicKey,c==null?void 0:c.request),{data:v,error:x}=await Mw(Object.assign(Object.assign({},d.webauthn.credential_options),{publicKey:y,signal:p}));return v?{data:{factorId:e,challengeId:d.id,webauthn:{type:d.webauthn.type,credential_response:v}},error:null}:{data:null,error:x}}}}catch(d){return ne(d)?{data:null,error:d}:{data:null,error:new Hn("Unexpected error in challenge",d)}}}async _verify({challengeId:e,factorId:i,webauthn:a}){return this.client.mfa.verify({factorId:i,challengeId:e,webauthn:a})}async _authenticate({factorId:e,webauthn:{rpId:i=typeof window<"u"?window.location.hostname:void 0,rpOrigins:a=typeof window<"u"?[window.location.origin]:void 0,signal:l}={}},c){if(!i)return{data:null,error:new xo("rpId is required for WebAuthn authentication")};try{if(!Bc())return{data:null,error:new Hn("Browser does not support WebAuthn",null)};const{data:u,error:d}=await this.challenge({factorId:e,webauthn:{rpId:i,rpOrigins:a},signal:l},{request:c});if(!u)return{data:null,error:d};const{webauthn:m}=u;return this._verify({factorId:e,challengeId:u.challengeId,webauthn:{type:m.type,rpId:i,rpOrigins:a,credential_response:m.credential_response}})}catch(u){return ne(u)?{data:null,error:u}:{data:null,error:new Hn("Unexpected error in authenticate",u)}}}async _register({friendlyName:e,webauthn:{rpId:i=typeof window<"u"?window.location.hostname:void 0,rpOrigins:a=typeof window<"u"?[window.location.origin]:void 0,signal:l}={}},c){if(!i)return{data:null,error:new xo("rpId is required for WebAuthn registration")};try{if(!Bc())return{data:null,error:new Hn("Browser does not support WebAuthn",null)};const{data:u,error:d}=await this._enroll({friendlyName:e});if(!u)return await this.client.mfa.listFactors().then(y=>{var v;return(v=y.data)===null||v===void 0?void 0:v.all.find(x=>x.factor_type==="webauthn"&&x.friendly_name===e&&x.status!=="unverified")}).then(y=>y?this.client.mfa.unenroll({factorId:y==null?void 0:y.id}):void 0),{data:null,error:d};const{data:m,error:p}=await this._challenge({factorId:u.id,friendlyName:u.friendly_name,webauthn:{rpId:i,rpOrigins:a},signal:l},{create:c});return m?this._verify({factorId:u.id,challengeId:m.challengeId,webauthn:{rpId:i,rpOrigins:a,type:m.webauthn.type,credential_response:m.webauthn.credential_response}}):{data:null,error:p}}catch(u){return ne(u)?{data:null,error:u}:{data:null,error:new Hn("Unexpected error in register",u)}}}}AR();const zR={url:Hk,storageKey:$k,autoRefreshToken:!0,persistSession:!0,detectSessionInUrl:!0,headers:qk,flowType:"implicit",debug:!1,hasCustomAuthorizationHeader:!1,throwOnError:!1,lockAcquireTimeout:5e3,skipAutoInitialize:!1,experimental:{}},ka={};class wo{get jwks(){var e,i;return(i=(e=ka[this.storageKey])===null||e===void 0?void 0:e.jwks)!==null&&i!==void 0?i:{keys:[]}}set jwks(e){ka[this.storageKey]=Object.assign(Object.assign({},ka[this.storageKey]),{jwks:e})}get jwks_cached_at(){var e,i;return(i=(e=ka[this.storageKey])===null||e===void 0?void 0:e.cachedAt)!==null&&i!==void 0?i:Number.MIN_SAFE_INTEGER}set jwks_cached_at(e){ka[this.storageKey]=Object.assign(Object.assign({},ka[this.storageKey]),{cachedAt:e})}constructor(e){var i,a,l;this.userStorage=null,this.memoryStorage=null,this.stateChangeEmitters=new Map,this.autoRefreshTicker=null,this.autoRefreshTickTimeout=null,this.visibilityChangedCallback=null,this.refreshingDeferred=null,this.lastRefreshFailure=null,this._sessionRemovalEpoch=0,this.initializePromise=null,this._pendingInitNotifications=null,this.detectSessionInUrl=!0,this.hasCustomAuthorizationHeader=!1,this.suppressGetSessionWarning=!1,this.lock=null,this.lockAcquired=!1,this.pendingInLock=[],this.broadcastChannel=null,this.logger=console.log;const c=Object.assign(Object.assign({},zR),e);if(this.storageKey=c.storageKey,this.instanceID=(i=wo.nextInstanceID[this.storageKey])!==null&&i!==void 0?i:0,wo.nextInstanceID[this.storageKey]=this.instanceID+1,this.logDebugMessages=!!c.debug,typeof c.debug=="function"&&(this.logger=c.debug),this.instanceID>0&&Vt()){const u=`${this._logPrefix()} Multiple GoTrueClient instances detected in the same browser context. It is not an error, but this should be avoided as it may produce undefined behavior when used concurrently under the same storage key.`;console.warn(u),this.logDebugMessages&&console.trace(u)}if(this.persistSession=c.persistSession,this.autoRefreshToken=c.autoRefreshToken,this.experimental=(a=c.experimental)!==null&&a!==void 0?a:{},this.admin=new TR({url:c.url,headers:c.headers,fetch:c.fetch,experimental:this.experimental}),this.url=c.url,this.headers=c.headers,this.fetch=Rw(c.fetch),this.detectSessionInUrl=c.detectSessionInUrl,this.flowType=c.flowType,this.hasCustomAuthorizationHeader=c.hasCustomAuthorizationHeader,this.throwOnError=c.throwOnError,this.lockAcquireTimeout=c.lockAcquireTimeout,c.lock!=null&&(this.lock=c.lock),this.jwks||(this.jwks={keys:[]},this.jwks_cached_at=Number.MIN_SAFE_INTEGER),this.mfa={verify:this._verify.bind(this),enroll:this._enroll.bind(this),unenroll:this._unenroll.bind(this),challenge:this._challenge.bind(this),listFactors:this._listFactors.bind(this),challengeAndVerify:this._challengeAndVerify.bind(this),getAuthenticatorAssuranceLevel:this._getAuthenticatorAssuranceLevel.bind(this),webauthn:new LR(this)},this.oauth={getAuthorizationDetails:this._getAuthorizationDetails.bind(this),approveAuthorization:this._approveAuthorization.bind(this),denyAuthorization:this._denyAuthorization.bind(this),listGrants:this._listOAuthGrants.bind(this),revokeGrant:this._revokeOAuthGrant.bind(this)},this.passkey={startRegistration:this._startPasskeyRegistration.bind(this),verifyRegistration:this._verifyPasskeyRegistration.bind(this),startAuthentication:this._startPasskeyAuthentication.bind(this),verifyAuthentication:this._verifyPasskeyAuthentication.bind(this),list:this._listPasskeys.bind(this),update:this._updatePasskey.bind(this),delete:this._deletePasskey.bind(this)},this.persistSession?(c.storage?this.storage=c.storage:kw()?this.storage=globalThis.localStorage:(this.memoryStorage={},this.storage=Jb(this.memoryStorage)),c.userStorage&&(this.userStorage=c.userStorage)):(this.memoryStorage={},this.storage=Jb(this.memoryStorage)),Vt()&&globalThis.BroadcastChannel&&this.persistSession&&this.storageKey){try{this.broadcastChannel=new globalThis.BroadcastChannel(this.storageKey)}catch(u){console.error("Failed to create a new BroadcastChannel, multi-tab state changes will not be available",u)}(l=this.broadcastChannel)===null||l===void 0||l.addEventListener("message",async u=>{this._debug("received broadcast notification from other tab or client",u),(u.data.event==="TOKEN_REFRESHED"||u.data.event==="SIGNED_IN")&&(this.lastRefreshFailure=null);try{await this._notifyAllSubscribers(u.data.event,u.data.session,!1)}catch(d){this._debug("#broadcastChannel","error",d)}})}c.skipAutoInitialize||this.initialize().catch(u=>{this._debug("#initialize()","error",u)})}isThrowOnErrorEnabled(){return this.throwOnError}_returnResult(e){if(this.throwOnError&&e&&e.error)throw e.error;return e}_logPrefix(){return`GoTrueClient@${this.storageKey}:${this.instanceID} (${Aw}) ${new Date().toISOString()}`}_debug(...e){return this.logDebugMessages&&this.logger(this._logPrefix(),...e),this}async initialize(){var e;if(this.initializePromise)return await this.initializePromise;this._pendingInitNotifications=[],this.initializePromise=(async()=>this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._initialize()):await this._initialize())();const i=await this.initializePromise,a=(e=this._pendingInitNotifications)!==null&&e!==void 0?e:[];this._pendingInitNotifications=null;for(const l of a)await this._notifyAllSubscribers(l.event,l.session,l.broadcast);return i}async _initialize(){var e;try{let i={},a="none";if(Vt()&&(i=iR(window.location.href),this._isImplicitGrantCallback(i)?a="implicit":await this._isPKCECallback(i)&&(a="pkce")),Vt()&&this.detectSessionInUrl&&a!=="none"){const{data:l,error:c}=await this._getSessionFromURL(i,a);if(c){if(this._debug("#_initialize()","error detecting session from URL",c),Yk(c)){const m=(e=c.details)===null||e===void 0?void 0:e.code;if(m==="identity_already_exists"||m==="identity_not_found"||m==="single_identity_not_deletable")return{error:c}}return{error:c}}const{session:u,redirectType:d}=l;return this._debug("#_initialize()","detected session in URL",u,"redirect type",d),await this._saveSession(u),setTimeout(async()=>{d==="recovery"?await this._notifyAllSubscribers("PASSWORD_RECOVERY",u):await this._notifyAllSubscribers("SIGNED_IN",u)},0),{error:null}}return await this._recoverAndRefresh(),{error:null}}catch(i){return ne(i)?this._returnResult({error:i}):this._returnResult({error:new Hn("Unexpected error during initialization",i)})}finally{await this._handleVisibilityChange(),this._debug("#_initialize()","end")}}async signInAnonymously(e){var i,a,l;try{const c=await ue(this.fetch,"POST",`${this.url}/signup`,{headers:this.headers,body:{data:(a=(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.data)!==null&&a!==void 0?a:{},gotrue_meta_security:{captcha_token:(l=e==null?void 0:e.options)===null||l===void 0?void 0:l.captchaToken}},xform:Tn}),{data:u,error:d}=c;if(d||!u)return this._returnResult({data:{user:null,session:null},error:d});const m=u.session,p=u.user;return u.session&&(await this._saveSession(u.session),await this._notifyAllSubscribers("SIGNED_IN",m)),this._returnResult({data:{user:p,session:m},error:null})}catch(c){if(ne(c))return this._returnResult({data:{user:null,session:null},error:c});throw c}}async signUp(e){var i,a,l;try{let c;if("email"in e){const{email:y,password:v,options:x}=e;let _=null,S=null;this.flowType==="pkce"&&([_,S]=await Di(this.storage,this.storageKey)),c=await ue(this.fetch,"POST",`${this.url}/signup`,{headers:this.headers,redirectTo:x==null?void 0:x.emailRedirectTo,body:{email:y,password:v,data:(i=x==null?void 0:x.data)!==null&&i!==void 0?i:{},gotrue_meta_security:{captcha_token:x==null?void 0:x.captchaToken},code_challenge:_,code_challenge_method:S},xform:Tn})}else if("phone"in e){const{phone:y,password:v,options:x}=e;c=await ue(this.fetch,"POST",`${this.url}/signup`,{headers:this.headers,body:{phone:y,password:v,data:(a=x==null?void 0:x.data)!==null&&a!==void 0?a:{},channel:(l=x==null?void 0:x.channel)!==null&&l!==void 0?l:"sms",gotrue_meta_security:{captcha_token:x==null?void 0:x.captchaToken}},xform:Tn})}else throw new ac("You must provide either an email or phone number and a password");const{data:u,error:d}=c;if(d||!u)return await ft(this.storage,`${this.storageKey}-code-verifier`),this._returnResult({data:{user:null,session:null},error:d});const m=u.session,p=u.user;return u.session&&(await this._saveSession(u.session),await this._notifyAllSubscribers("SIGNED_IN",m)),this._returnResult({data:{user:p,session:m},error:null})}catch(c){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(c))return this._returnResult({data:{user:null,session:null},error:c});throw c}}async signInWithPassword(e){try{let i;if("email"in e){const{email:c,password:u,options:d}=e;i=await ue(this.fetch,"POST",`${this.url}/token?grant_type=password`,{headers:this.headers,body:{email:c,password:u,gotrue_meta_security:{captcha_token:d==null?void 0:d.captchaToken}},xform:Yb})}else if("phone"in e){const{phone:c,password:u,options:d}=e;i=await ue(this.fetch,"POST",`${this.url}/token?grant_type=password`,{headers:this.headers,body:{phone:c,password:u,gotrue_meta_security:{captcha_token:d==null?void 0:d.captchaToken}},xform:Yb})}else throw new ac("You must provide either an email or phone number and a password");const{data:a,error:l}=i;if(l)return this._returnResult({data:{user:null,session:null},error:l});if(!a||!a.session||!a.user){const c=new Na;return this._returnResult({data:{user:null,session:null},error:c})}return a.session&&(await this._saveSession(a.session),await this._notifyAllSubscribers("SIGNED_IN",a.session)),this._returnResult({data:Object.assign({user:a.user,session:a.session},a.weak_password?{weakPassword:a.weak_password}:null),error:l})}catch(i){if(ne(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async signInWithOAuth(e){var i,a,l,c;return await this._handleProviderSignIn(e.provider,{redirectTo:(i=e.options)===null||i===void 0?void 0:i.redirectTo,scopes:(a=e.options)===null||a===void 0?void 0:a.scopes,queryParams:(l=e.options)===null||l===void 0?void 0:l.queryParams,skipBrowserRedirect:(c=e.options)===null||c===void 0?void 0:c.skipBrowserRedirect})}async exchangeCodeForSession(e){return await this.initializePromise,this.lock!=null?this._acquireLock(this.lockAcquireTimeout,async()=>this._exchangeCodeForSession(e)):this._exchangeCodeForSession(e)}async signInWithWeb3(e){const{chain:i}=e;switch(i){case"ethereum":return await this.signInWithEthereum(e);case"solana":return await this.signInWithSolana(e);default:throw new Error(`@supabase/auth-js: Unsupported chain "${i}"`)}}async signInWithEthereum(e){var i,a,l,c,u,d,m,p,y,v,x;let _,S;if("message"in e)_=e.message,S=e.signature;else{const{chain:T,wallet:k,statement:R,options:M}=e;let N;if(Vt())if(typeof k=="object")N=k;else{const se=window;if("ethereum"in se&&typeof se.ethereum=="object"&&"request"in se.ethereum&&typeof se.ethereum.request=="function")N=se.ethereum;else throw new Error("@supabase/auth-js: No compatible Ethereum wallet interface on the window object (window.ethereum) detected. Make sure the user already has a wallet installed and connected for this app. Prefer passing the wallet interface object directly to signInWithWeb3({ chain: 'ethereum', wallet: resolvedUserWallet }) instead.")}else{if(typeof k!="object"||!(M!=null&&M.url))throw new Error("@supabase/auth-js: Both wallet and url must be specified in non-browser environments.");N=k}const P=new URL((i=M==null?void 0:M.url)!==null&&i!==void 0?i:window.location.href),G=await N.request({method:"eth_requestAccounts"}).then(se=>se).catch(()=>{throw new Error("@supabase/auth-js: Wallet method eth_requestAccounts is missing or invalid")});if(!G||G.length===0)throw new Error("@supabase/auth-js: No accounts available. Please ensure the wallet is connected.");const X=Cw(G[0]);let q=(a=M==null?void 0:M.signInWithEthereum)===null||a===void 0?void 0:a.chainId;if(!q){const se=await N.request({method:"eth_chainId"});q=jR(se)}const Y={domain:P.host,address:X,statement:R,uri:P.href,version:"1",chainId:q,nonce:(l=M==null?void 0:M.signInWithEthereum)===null||l===void 0?void 0:l.nonce,issuedAt:(u=(c=M==null?void 0:M.signInWithEthereum)===null||c===void 0?void 0:c.issuedAt)!==null&&u!==void 0?u:new Date,expirationTime:(d=M==null?void 0:M.signInWithEthereum)===null||d===void 0?void 0:d.expirationTime,notBefore:(m=M==null?void 0:M.signInWithEthereum)===null||m===void 0?void 0:m.notBefore,requestId:(p=M==null?void 0:M.signInWithEthereum)===null||p===void 0?void 0:p.requestId,resources:(y=M==null?void 0:M.signInWithEthereum)===null||y===void 0?void 0:y.resources};_=kR(Y),S=await N.request({method:"personal_sign",params:[NR(_),X]})}try{const{data:T,error:k}=await ue(this.fetch,"POST",`${this.url}/token?grant_type=web3`,{headers:this.headers,body:Object.assign({chain:"ethereum",message:_,signature:S},!((v=e.options)===null||v===void 0)&&v.captchaToken?{gotrue_meta_security:{captcha_token:(x=e.options)===null||x===void 0?void 0:x.captchaToken}}:null),xform:Tn});if(k)throw k;if(!T||!T.session||!T.user){const R=new Na;return this._returnResult({data:{user:null,session:null},error:R})}return T.session&&(await this._saveSession(T.session),await this._notifyAllSubscribers("SIGNED_IN",T.session)),this._returnResult({data:Object.assign({},T),error:k})}catch(T){if(ne(T))return this._returnResult({data:{user:null,session:null},error:T});throw T}}async signInWithSolana(e){var i,a,l,c,u,d,m,p,y,v,x,_;let S,T;if("message"in e)S=e.message,T=e.signature;else{const{chain:k,wallet:R,statement:M,options:N}=e;let P;if(Vt())if(typeof R=="object")P=R;else{const X=window;if("solana"in X&&typeof X.solana=="object"&&("signIn"in X.solana&&typeof X.solana.signIn=="function"||"signMessage"in X.solana&&typeof X.solana.signMessage=="function"))P=X.solana;else throw new Error("@supabase/auth-js: No compatible Solana wallet interface on the window object (window.solana) detected. Make sure the user already has a wallet installed and connected for this app. Prefer passing the wallet interface object directly to signInWithWeb3({ chain: 'solana', wallet: resolvedUserWallet }) instead.")}else{if(typeof R!="object"||!(N!=null&&N.url))throw new Error("@supabase/auth-js: Both wallet and url must be specified in non-browser environments.");P=R}const G=new URL((i=N==null?void 0:N.url)!==null&&i!==void 0?i:window.location.href);if("signIn"in P&&P.signIn){const X=await P.signIn(Object.assign(Object.assign(Object.assign({issuedAt:new Date().toISOString()},N==null?void 0:N.signInWithSolana),{version:"1",domain:G.host,uri:G.href}),M?{statement:M}:null));let q;if(Array.isArray(X)&&X[0]&&typeof X[0]=="object")q=X[0];else if(X&&typeof X=="object"&&"signedMessage"in X&&"signature"in X)q=X;else throw new Error("@supabase/auth-js: Wallet method signIn() returned unrecognized value");if("signedMessage"in q&&"signature"in q&&(typeof q.signedMessage=="string"||q.signedMessage instanceof Uint8Array)&&q.signature instanceof Uint8Array)S=typeof q.signedMessage=="string"?q.signedMessage:new TextDecoder().decode(q.signedMessage),T=q.signature;else throw new Error("@supabase/auth-js: Wallet method signIn() API returned object without signedMessage and signature fields")}else{if(!("signMessage"in P)||typeof P.signMessage!="function"||!("publicKey"in P)||typeof P!="object"||!P.publicKey||!("toBase58"in P.publicKey)||typeof P.publicKey.toBase58!="function")throw new Error("@supabase/auth-js: Wallet does not have a compatible signMessage() and publicKey.toBase58() API");S=[`${G.host} wants you to sign in with your Solana account:`,P.publicKey.toBase58(),...M?["",M,""]:[""],"Version: 1",`URI: ${G.href}`,`Issued At: ${(l=(a=N==null?void 0:N.signInWithSolana)===null||a===void 0?void 0:a.issuedAt)!==null&&l!==void 0?l:new Date().toISOString()}`,...!((c=N==null?void 0:N.signInWithSolana)===null||c===void 0)&&c.notBefore?[`Not Before: ${N.signInWithSolana.notBefore}`]:[],...!((u=N==null?void 0:N.signInWithSolana)===null||u===void 0)&&u.expirationTime?[`Expiration Time: ${N.signInWithSolana.expirationTime}`]:[],...!((d=N==null?void 0:N.signInWithSolana)===null||d===void 0)&&d.chainId?[`Chain ID: ${N.signInWithSolana.chainId}`]:[],...!((m=N==null?void 0:N.signInWithSolana)===null||m===void 0)&&m.nonce?[`Nonce: ${N.signInWithSolana.nonce}`]:[],...!((p=N==null?void 0:N.signInWithSolana)===null||p===void 0)&&p.requestId?[`Request ID: ${N.signInWithSolana.requestId}`]:[],...!((v=(y=N==null?void 0:N.signInWithSolana)===null||y===void 0?void 0:y.resources)===null||v===void 0)&&v.length?["Resources",...N.signInWithSolana.resources.map(q=>`- ${q}`)]:[]].join(` +`);const X=await P.signMessage(new TextEncoder().encode(S),"utf8");if(!X||!(X instanceof Uint8Array))throw new Error("@supabase/auth-js: Wallet signMessage() API returned an recognized value");T=X}}try{const{data:k,error:R}=await ue(this.fetch,"POST",`${this.url}/token?grant_type=web3`,{headers:this.headers,body:Object.assign({chain:"solana",message:S,signature:zi(T)},!((x=e.options)===null||x===void 0)&&x.captchaToken?{gotrue_meta_security:{captcha_token:(_=e.options)===null||_===void 0?void 0:_.captchaToken}}:null),xform:Tn});if(R)throw R;if(!k||!k.session||!k.user){const M=new Na;return this._returnResult({data:{user:null,session:null},error:M})}return k.session&&(await this._saveSession(k.session),await this._notifyAllSubscribers("SIGNED_IN",k.session)),this._returnResult({data:Object.assign({},k),error:R})}catch(k){if(ne(k))return this._returnResult({data:{user:null,session:null},error:k});throw k}}async _exchangeCodeForSession(e){const i=await Un(this.storage,`${this.storageKey}-code-verifier`),[a,l]=(i??"").split("/");try{if(!a&&this.flowType==="pkce")throw new Xk;const{data:c,error:u}=await ue(this.fetch,"POST",`${this.url}/token?grant_type=pkce`,{headers:this.headers,body:{auth_code:e,code_verifier:a},xform:Tn});if(await ft(this.storage,`${this.storageKey}-code-verifier`),u)throw u;if(!c||!c.session||!c.user){const d=new Na;return this._returnResult({data:{user:null,session:null,redirectType:null},error:d})}return c.session&&(await this._saveSession(c.session),await this._notifyAllSubscribers(l==="recovery"?"PASSWORD_RECOVERY":"SIGNED_IN",c.session)),this._returnResult({data:Object.assign(Object.assign({},c),{redirectType:l??null}),error:u})}catch(c){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(c))return this._returnResult({data:{user:null,session:null,redirectType:null},error:c});throw c}}async signInWithIdToken(e){try{const{options:i,provider:a,token:l,access_token:c,nonce:u}=e,d=await ue(this.fetch,"POST",`${this.url}/token?grant_type=id_token`,{headers:this.headers,body:{provider:a,id_token:l,access_token:c,nonce:u,gotrue_meta_security:{captcha_token:i==null?void 0:i.captchaToken}},xform:Tn}),{data:m,error:p}=d;if(p)return this._returnResult({data:{user:null,session:null},error:p});if(!m||!m.session||!m.user){const y=new Na;return this._returnResult({data:{user:null,session:null},error:y})}return m.session&&(await this._saveSession(m.session),await this._notifyAllSubscribers("SIGNED_IN",m.session)),this._returnResult({data:m,error:p})}catch(i){if(ne(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async signInWithOtp(e){var i,a,l,c,u;try{if("email"in e){const{email:d,options:m}=e;let p=null,y=null;this.flowType==="pkce"&&([p,y]=await Di(this.storage,this.storageKey));const{error:v}=await ue(this.fetch,"POST",`${this.url}/otp`,{headers:this.headers,body:{email:d,data:(i=m==null?void 0:m.data)!==null&&i!==void 0?i:{},create_user:(a=m==null?void 0:m.shouldCreateUser)!==null&&a!==void 0?a:!0,gotrue_meta_security:{captcha_token:m==null?void 0:m.captchaToken},code_challenge:p,code_challenge_method:y},redirectTo:m==null?void 0:m.emailRedirectTo});return this._returnResult({data:{user:null,session:null},error:v})}if("phone"in e){const{phone:d,options:m}=e,{data:p,error:y}=await ue(this.fetch,"POST",`${this.url}/otp`,{headers:this.headers,body:{phone:d,data:(l=m==null?void 0:m.data)!==null&&l!==void 0?l:{},create_user:(c=m==null?void 0:m.shouldCreateUser)!==null&&c!==void 0?c:!0,gotrue_meta_security:{captcha_token:m==null?void 0:m.captchaToken},channel:(u=m==null?void 0:m.channel)!==null&&u!==void 0?u:"sms"}});return this._returnResult({data:{user:null,session:null,messageId:p==null?void 0:p.message_id},error:y})}throw new ac("You must provide either an email or phone number.")}catch(d){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(d))return this._returnResult({data:{user:null,session:null},error:d});throw d}}async verifyOtp(e){var i,a;try{let l,c;"options"in e&&(l=(i=e.options)===null||i===void 0?void 0:i.redirectTo,c=(a=e.options)===null||a===void 0?void 0:a.captchaToken);const{data:u,error:d}=await ue(this.fetch,"POST",`${this.url}/verify`,{headers:this.headers,body:Object.assign(Object.assign({},e),{gotrue_meta_security:{captcha_token:c}}),redirectTo:l,xform:Tn});if(d)throw d;if(!u)throw new Error("An error occurred on token verification.");const m=u.session,p=u.user;return m!=null&&m.access_token&&(await this._saveSession(m),await this._notifyAllSubscribers(e.type=="recovery"?"PASSWORD_RECOVERY":"SIGNED_IN",m)),this._returnResult({data:{user:p,session:m},error:null})}catch(l){if(ne(l))return this._returnResult({data:{user:null,session:null},error:l});throw l}}async signInWithSSO(e){var i,a,l,c,u;try{let d=null,m=null;this.flowType==="pkce"&&([d,m]=await Di(this.storage,this.storageKey));const p=await ue(this.fetch,"POST",`${this.url}/sso`,{body:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},"providerId"in e?{provider_id:e.providerId}:null),"domain"in e?{domain:e.domain}:null),{redirect_to:(a=(i=e.options)===null||i===void 0?void 0:i.redirectTo)!==null&&a!==void 0?a:void 0}),!((l=e==null?void 0:e.options)===null||l===void 0)&&l.captchaToken?{gotrue_meta_security:{captcha_token:e.options.captchaToken}}:null),{skip_http_redirect:!0,code_challenge:d,code_challenge_method:m}),headers:this.headers,xform:wR});return!((c=p.data)===null||c===void 0)&&c.url&&Vt()&&!(!((u=e.options)===null||u===void 0)&&u.skipBrowserRedirect)&&window.location.assign(p.data.url),this._returnResult(p)}catch(d){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(d))return this._returnResult({data:null,error:d});throw d}}async reauthenticate(){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._reauthenticate()):await this._reauthenticate()}async _reauthenticate(){try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;if(a)throw a;if(!i)throw new Nt;const{error:l}=await ue(this.fetch,"GET",`${this.url}/reauthenticate`,{headers:this.headers,jwt:i.access_token});return this._returnResult({data:{user:null,session:null},error:l})})}catch(e){if(ne(e))return this._returnResult({data:{user:null,session:null},error:e});throw e}}async resend(e){try{const i=`${this.url}/resend`;if("email"in e){const{email:a,type:l,options:c}=e;let u=null,d=null;this.flowType==="pkce"&&([u,d]=await Di(this.storage,this.storageKey));const{error:m}=await ue(this.fetch,"POST",i,{headers:this.headers,body:{email:a,type:l,gotrue_meta_security:{captcha_token:c==null?void 0:c.captchaToken},code_challenge:u,code_challenge_method:d},redirectTo:c==null?void 0:c.emailRedirectTo});return m&&await ft(this.storage,`${this.storageKey}-code-verifier`),this._returnResult({data:{user:null,session:null},error:m})}else if("phone"in e){const{phone:a,type:l,options:c}=e,{data:u,error:d}=await ue(this.fetch,"POST",i,{headers:this.headers,body:{phone:a,type:l,gotrue_meta_security:{captcha_token:c==null?void 0:c.captchaToken}}});return this._returnResult({data:{user:null,session:null,messageId:u==null?void 0:u.message_id},error:d})}throw new ac("You must provide either an email or phone number and a type")}catch(i){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async getSession(){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>this._useSession(async e=>e)):await this._useSession(async e=>e)}async _acquireLock(e,i){this._debug("#_acquireLock","begin",e);try{if(this.lockAcquired){const a=this.pendingInLock.length?this.pendingInLock[this.pendingInLock.length-1]:Promise.resolve(),l=(async()=>(await a,await i()))();return this.pendingInLock.push((async()=>{try{await l}catch{}})()),l}return await this.lock(`lock:${this.storageKey}`,e,async()=>{this._debug("#_acquireLock","lock acquired for storage key",this.storageKey);try{this.lockAcquired=!0;const a=i();for(this.pendingInLock.push((async()=>{try{await a}catch{}})()),await a;this.pendingInLock.length;){const l=[...this.pendingInLock];await Promise.all(l),this.pendingInLock.splice(0,l.length)}return await a}finally{this._debug("#_acquireLock","lock released for storage key",this.storageKey),this.lockAcquired=!1}})}finally{this._debug("#_acquireLock","end")}}async _useSession(e){this._debug("#_useSession","begin");try{const i=await this.__loadSession();return await e(i)}finally{this._debug("#_useSession","end")}}async __loadSession(){this._debug("#__loadSession()","begin"),this.lock!=null&&!this.lockAcquired&&this._debug("#__loadSession()","used outside of an acquired lock!",new Error().stack);try{let e=null;const i=await Un(this.storage,this.storageKey);if(this._debug("#getSession()","session from storage",i),i!==null&&(this._isValidSession(i)?e=i:(this._debug("#getSession()","session from storage is not valid"),await this._removeSession())),!e)return{data:{session:null},error:null};const a=e.expires_at?e.expires_at*1e3-Date.now()Date.now())){const d=await Un(this.storage,this.storageKey);if(d&&d.refresh_token===e.refresh_token)return this._returnResult({data:{session:e},error:null})}return this._returnResult({data:{session:null},error:c})}return this._returnResult({data:{session:l},error:null})}finally{this._debug("#__loadSession()","end")}}async getUser(e){if(e)return await this._getUser(e);await this.initializePromise;let i;return this.lock!=null?i=await this._acquireLock(this.lockAcquireTimeout,async()=>await this._getUser()):i=await this._getUser(),i.data.user&&(this.suppressGetSessionWarning=!0),i}async _getUser(e){try{return e?await ue(this.fetch,"GET",`${this.url}/user`,{headers:this.headers,jwt:e,xform:ai}):await this._useSession(async i=>{var a,l,c;const{data:u,error:d}=i;if(d)throw d;return!(!((a=u.session)===null||a===void 0)&&a.access_token)&&!this.hasCustomAuthorizationHeader?{data:{user:null},error:new Nt}:await ue(this.fetch,"GET",`${this.url}/user`,{headers:this.headers,jwt:(c=(l=u.session)===null||l===void 0?void 0:l.access_token)!==null&&c!==void 0?c:void 0,xform:ai})})}catch(i){if(ne(i))return ic(i)&&(await this._removeSession(),await ft(this.storage,`${this.storageKey}-code-verifier`)),this._returnResult({data:{user:null},error:i});throw i}}async updateUser(e,i={}){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._updateUser(e,i)):await this._updateUser(e,i)}async _updateUser(e,i={}){try{return await this._useSession(async a=>{const{data:l,error:c}=a;if(c)throw c;if(!l.session)throw new Nt;const u=l.session;let d=null,m=null;this.flowType==="pkce"&&e.email!=null&&([d,m]=await Di(this.storage,this.storageKey));const{data:p,error:y}=await ue(this.fetch,"PUT",`${this.url}/user`,{headers:this.headers,redirectTo:i==null?void 0:i.emailRedirectTo,body:Object.assign(Object.assign({},e),{code_challenge:d,code_challenge_method:m}),jwt:u.access_token,xform:ai});if(y)throw y;return u.user=p.user,await this._saveSession(u),await this._notifyAllSubscribers("USER_UPDATED",u),this._returnResult({data:{user:u.user},error:null})})}catch(a){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(a))return this._returnResult({data:{user:null},error:a});throw a}}async setSession(e){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._setSession(e)):await this._setSession(e)}async _setSession(e){try{if(!e.access_token||!e.refresh_token)throw new Nt;const i=Date.now()/1e3;let a=i,l=!0,c=null;const{payload:u}=oc(e.access_token);if(u.exp&&(a=u.exp,l=a<=i),l){const{data:d,error:m}=await this._callRefreshToken(e.refresh_token);if(m)return this._returnResult({data:{user:null,session:null},error:m});if(!d)return{data:{user:null,session:null},error:null};c=d}else{const{data:d,error:m}=await this._getUser(e.access_token);if(m)return this._returnResult({data:{user:null,session:null},error:m});c={access_token:e.access_token,refresh_token:e.refresh_token,user:d.user,token_type:"bearer",expires_in:a-i,expires_at:a},await this._saveSession(c),await this._notifyAllSubscribers("SIGNED_IN",c)}return this._returnResult({data:{user:c.user,session:c},error:null})}catch(i){if(ne(i))return this._returnResult({data:{session:null,user:null},error:i});throw i}}async refreshSession(e){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._refreshSession(e)):await this._refreshSession(e)}async _refreshSession(e){try{return await this._useSession(async i=>{var a;if(!e){const{data:u,error:d}=i;if(d)throw d;e=(a=u.session)!==null&&a!==void 0?a:void 0}if(!(e!=null&&e.refresh_token))throw new Nt;const{data:l,error:c}=await this._callRefreshToken(e.refresh_token);return c?this._returnResult({data:{user:null,session:null},error:c}):l?this._returnResult({data:{user:l.user,session:l},error:null}):this._returnResult({data:{user:null,session:null},error:null})})}catch(i){if(ne(i))return this._returnResult({data:{user:null,session:null},error:i});throw i}}async _getSessionFromURL(e,i){var a;try{if(!Vt())throw new rc("No browser detected.");if(e.error||e.error_description||e.error_code)throw new rc(e.error_description||"Error in URL with unspecified error_description",{error:e.error||"unspecified_error",code:e.error_code||"unspecified_code"});switch(i){case"implicit":if(this.flowType==="pkce")throw new Pb("Not a valid PKCE flow url.");break;case"pkce":if(this.flowType==="implicit")throw new rc("Not a valid implicit grant flow url.");break;default:}if(i==="pkce"){if(this._debug("#_initialize()","begin","is PKCE flow",!0),!e.code)throw new Pb("No code detected.");const{data:N,error:P}=await this._exchangeCodeForSession(e.code);if(P)throw P;const G=new URL(window.location.href);return G.searchParams.delete("code"),window.history.replaceState(window.history.state,"",G.toString()),{data:{session:N.session,redirectType:(a=N.redirectType)!==null&&a!==void 0?a:null},error:null}}const{provider_token:l,provider_refresh_token:c,access_token:u,refresh_token:d,expires_in:m,expires_at:p,token_type:y}=e;if(!u||!m||!d||!y)throw new rc("No session defined in URL");const v=Math.round(Date.now()/1e3),x=parseInt(m);let _=v+x;p&&(_=parseInt(p));const S=_-v;S*1e3<=ks&&console.warn(`@supabase/gotrue-js: Session as retrieved from URL expires in ${S}s, should have been closer to ${x}s`);const T=_-x;v-T>=120?console.warn("@supabase/gotrue-js: Session as retrieved from URL was issued over 120s ago, URL could be stale",T,_,v):v-T<0&&console.warn("@supabase/gotrue-js: Session as retrieved from URL was issued in the future? Check the device clock for skew",T,_,v);const{data:k,error:R}=await this._getUser(u);if(R)throw R;const M={provider_token:l,provider_refresh_token:c,access_token:u,expires_in:x,expires_at:_,refresh_token:d,token_type:y,user:k.user};return window.location.hash="",this._debug("#_getSessionFromURL()","clearing window.location.hash"),this._returnResult({data:{session:M,redirectType:e.type},error:null})}catch(l){if(ne(l))return this._returnResult({data:{session:null,redirectType:null},error:l});throw l}}_isImplicitGrantCallback(e){return typeof this.detectSessionInUrl=="function"?this.detectSessionInUrl(new URL(window.location.href),e):!!(e.access_token||e.error||e.error_description||e.error_code)}async _isPKCECallback(e){const i=await Un(this.storage,`${this.storageKey}-code-verifier`);return!!(e.code&&i)}async signOut(e={scope:"global"}){return await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>await this._signOut(e)):await this._signOut(e)}async _signOut({scope:e}={scope:"global"}){return await this._useSession(async i=>{var a;const l=async()=>{await this._removeSession(),await ft(this.storage,`${this.storageKey}-code-verifier`)},{data:c,error:u}=i;if(u&&!ic(u))return this._returnResult({error:u});const d=(a=c.session)===null||a===void 0?void 0:a.access_token;if(d){const{error:m}=await this.admin.signOut(d,e);if(m&&!(Kk(m)&&(m.status===404||m.status===401||m.status===403)||ic(m)))return e!=="others"&&await l(),this._returnResult({error:m})}return e!=="others"&&await l(),this._returnResult({error:null})})}onAuthStateChange(e){const i=sR(),a={id:i,callback:e,unsubscribe:()=>{this._debug("#unsubscribe()","state change callback with id removed",i),this.stateChangeEmitters.delete(i)}};return this._debug("#onAuthStateChange()","registered callback with id",i),this.stateChangeEmitters.set(i,a),(async()=>(await this.initializePromise,this.lock!=null?await this._acquireLock(this.lockAcquireTimeout,async()=>{this._emitInitialSession(i)}):await this._emitInitialSession(i)))(),{data:{subscription:a}}}async _emitInitialSession(e){return await this._useSession(async i=>{var a,l;try{const{data:{session:c},error:u}=i;if(u)throw u;await((a=this.stateChangeEmitters.get(e))===null||a===void 0?void 0:a.callback("INITIAL_SESSION",c)),this._debug("INITIAL_SESSION","callback id",e,"session",c)}catch(c){await((l=this.stateChangeEmitters.get(e))===null||l===void 0?void 0:l.callback("INITIAL_SESSION",null)),this._debug("INITIAL_SESSION","callback id",e,"error",c),ic(c)?console.warn(c):console.error(c)}})}async resetPasswordForEmail(e,i={}){let a=null,l=null;this.flowType==="pkce"&&([a,l]=await Di(this.storage,this.storageKey,!0));try{return await ue(this.fetch,"POST",`${this.url}/recover`,{body:{email:e,code_challenge:a,code_challenge_method:l,gotrue_meta_security:{captcha_token:i.captchaToken}},headers:this.headers,redirectTo:i.redirectTo})}catch(c){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(c))return this._returnResult({data:null,error:c});throw c}}async getUserIdentities(){var e;try{const{data:i,error:a}=await this.getUser();if(a)throw a;return this._returnResult({data:{identities:(e=i.user.identities)!==null&&e!==void 0?e:[]},error:null})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async linkIdentity(e){return"token"in e?this.linkIdentityIdToken(e):this.linkIdentityOAuth(e)}async linkIdentityOAuth(e){var i;try{const{data:a,error:l}=await this._useSession(async c=>{var u,d,m,p,y;const{data:v,error:x}=c;if(x)throw x;const _=await this._getUrlForProvider(`${this.url}/user/identities/authorize`,e.provider,{redirectTo:(u=e.options)===null||u===void 0?void 0:u.redirectTo,scopes:(d=e.options)===null||d===void 0?void 0:d.scopes,queryParams:(m=e.options)===null||m===void 0?void 0:m.queryParams,skipBrowserRedirect:!0});return await ue(this.fetch,"GET",_,{headers:this.headers,jwt:(y=(p=v.session)===null||p===void 0?void 0:p.access_token)!==null&&y!==void 0?y:void 0})});if(l)throw l;return Vt()&&!(!((i=e.options)===null||i===void 0)&&i.skipBrowserRedirect)&&window.location.assign(a==null?void 0:a.url),this._returnResult({data:{provider:e.provider,url:a==null?void 0:a.url},error:null})}catch(a){if(ne(a))return this._returnResult({data:{provider:e.provider,url:null},error:a});throw a}}async linkIdentityIdToken(e){return await this._useSession(async i=>{var a;try{const{error:l,data:{session:c}}=i;if(l)throw l;const{options:u,provider:d,token:m,access_token:p,nonce:y}=e,v=await ue(this.fetch,"POST",`${this.url}/token?grant_type=id_token`,{headers:this.headers,jwt:(a=c==null?void 0:c.access_token)!==null&&a!==void 0?a:void 0,body:{provider:d,id_token:m,access_token:p,nonce:y,link_identity:!0,gotrue_meta_security:{captcha_token:u==null?void 0:u.captchaToken}},xform:Tn}),{data:x,error:_}=v;return _?this._returnResult({data:{user:null,session:null},error:_}):!x||!x.session||!x.user?this._returnResult({data:{user:null,session:null},error:new Na}):(x.session&&(await this._saveSession(x.session),await this._notifyAllSubscribers("USER_UPDATED",x.session)),this._returnResult({data:x,error:_}))}catch(l){if(await ft(this.storage,`${this.storageKey}-code-verifier`),ne(l))return this._returnResult({data:{user:null,session:null},error:l});throw l}})}async unlinkIdentity(e){try{return await this._useSession(async i=>{var a,l;const{data:c,error:u}=i;if(u)throw u;return await ue(this.fetch,"DELETE",`${this.url}/user/identities/${e.identity_id}`,{headers:this.headers,jwt:(l=(a=c.session)===null||a===void 0?void 0:a.access_token)!==null&&l!==void 0?l:void 0})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _refreshAccessToken(e){const i="#_refreshAccessToken()";this._debug(i,"begin");try{const a=Date.now();return await oR(async l=>(l>0&&await rR(200*Math.pow(2,l-1)),this._debug(i,"refreshing attempt",l),await ue(this.fetch,"POST",`${this.url}/token?grant_type=refresh_token`,{body:{refresh_token:e},headers:this.headers,xform:Tn})),(l,c)=>{const u=200*Math.pow(2,l);return c&&Vb(c)&&Date.now()+u-aDate.now())?this._debug(l,"proactive refresh failed, access token still valid — preserving session"):await this._removeSession()}return this.lastRefreshFailure={refreshToken:e,result:u,expiresAt:Date.now()+Vk},(i=this.refreshingDeferred)===null||i===void 0||i.resolve(u),u}throw(a=this.refreshingDeferred)===null||a===void 0||a.reject(c),c}finally{this.refreshingDeferred=null,this._debug(l,"end")}}async _notifyAllSubscribers(e,i,a=!0){if(this._pendingInitNotifications!==null&&a){this._pendingInitNotifications.push({event:e,session:i,broadcast:a});return}const l=`#_notifyAllSubscribers(${e})`;this._debug(l,"begin",i,`broadcast = ${a}`);try{this.broadcastChannel&&a&&this.broadcastChannel.postMessage({event:e,session:i});const c=[],u=Array.from(this.stateChangeEmitters.values()).map(async d=>{try{await d.callback(e,i)}catch(m){c.push(m)}});if(await Promise.all(u),c.length>0){for(let d=0;dthis._autoRefreshTokenTick(),ks);this.autoRefreshTicker=e,e&&typeof e=="object"&&typeof e.unref=="function"?e.unref():typeof Deno<"u"&&typeof Deno.unrefTimer=="function"&&Deno.unrefTimer(e);const i=setTimeout(async()=>{await this.initializePromise,await this._autoRefreshTokenTick()},0);this.autoRefreshTickTimeout=i,i&&typeof i=="object"&&typeof i.unref=="function"?i.unref():typeof Deno<"u"&&typeof Deno.unrefTimer=="function"&&Deno.unrefTimer(i)}async _stopAutoRefresh(){this._debug("#_stopAutoRefresh()");const e=this.autoRefreshTicker;this.autoRefreshTicker=null,e&&clearInterval(e);const i=this.autoRefreshTickTimeout;this.autoRefreshTickTimeout=null,i&&clearTimeout(i)}async startAutoRefresh(){this._removeVisibilityChangedCallback(),await this._startAutoRefresh()}async stopAutoRefresh(){this._removeVisibilityChangedCallback(),await this._stopAutoRefresh()}async dispose(){var e;this._removeVisibilityChangedCallback(),await this._stopAutoRefresh(),(e=this.broadcastChannel)===null||e===void 0||e.close(),this.broadcastChannel=null,this.stateChangeEmitters.clear()}async _autoRefreshTokenTick(){if(this._debug("#_autoRefreshTokenTick()","begin"),this.lock!=null){try{await this._acquireLock(0,async()=>{try{const e=Date.now();try{return await this._useSession(async i=>{const{data:{session:a}}=i;if(!a||!a.refresh_token||!a.expires_at){this._debug("#_autoRefreshTokenTick()","no session");return}const l=Math.floor((a.expires_at*1e3-e)/ks);this._debug("#_autoRefreshTokenTick()",`access token expires in ${l} ticks, a tick lasts ${ks}ms, refresh threshold is ${Zr} ticks`),l<=Zr&&await this._callRefreshToken(a.refresh_token)})}catch(i){console.error("Auto refresh tick failed with error. This is likely a transient error.",i)}}finally{this._debug("#_autoRefreshTokenTick()","end")}})}catch(e){if(e instanceof ER)this._debug("auto refresh token tick lock not available");else throw e}return}if(this.refreshingDeferred!==null){this._debug("#_autoRefreshTokenTick()","refresh already in flight, skipping");return}try{const e=Date.now();try{await this._useSession(async i=>{const{data:{session:a}}=i;if(!a||!a.refresh_token||!a.expires_at){this._debug("#_autoRefreshTokenTick()","no session");return}const l=Math.floor((a.expires_at*1e3-e)/ks);this._debug("#_autoRefreshTokenTick()",`access token expires in ${l} ticks, a tick lasts ${ks}ms, refresh threshold is ${Zr} ticks`),l<=Zr&&await this._callRefreshToken(a.refresh_token)})}catch(i){console.error("Auto refresh tick failed with error. This is likely a transient error.",i)}}finally{this._debug("#_autoRefreshTokenTick()","end")}}async _handleVisibilityChange(){if(this._debug("#_handleVisibilityChange()"),!Vt()||!(window!=null&&window.addEventListener))return this.autoRefreshToken&&this.startAutoRefresh(),!1;try{this.visibilityChangedCallback=async()=>{try{await this._onVisibilityChanged(!1)}catch(e){this._debug("#visibilityChangedCallback","error",e)}},window==null||window.addEventListener("visibilitychange",this.visibilityChangedCallback),await this._onVisibilityChanged(!0)}catch(e){console.error("_handleVisibilityChange",e)}}async _onVisibilityChanged(e){const i=`#_onVisibilityChanged(${e})`;if(this._debug(i,"visibilityState",document.visibilityState),document.visibilityState==="visible"){if(this.autoRefreshToken&&this._startAutoRefresh(),!e)if(await this.initializePromise,this.lock!=null)await this._acquireLock(this.lockAcquireTimeout,async()=>{if(document.visibilityState!=="visible"){this._debug(i,"acquired the lock to recover the session, but the browser visibilityState is no longer visible, aborting");return}await this._recoverAndRefresh()});else{if(document.visibilityState!=="visible"){this._debug(i,"visibilityState is no longer visible, skipping recovery");return}await this._recoverAndRefresh()}}else document.visibilityState==="hidden"&&this.autoRefreshToken&&this._stopAutoRefresh()}async _getUrlForProvider(e,i,a){const l=[`provider=${encodeURIComponent(i)}`];if(a!=null&&a.redirectTo&&l.push(`redirect_to=${encodeURIComponent(a.redirectTo)}`),a!=null&&a.scopes&&l.push(`scopes=${encodeURIComponent(a.scopes)}`),this.flowType==="pkce"){const[c,u]=await Di(this.storage,this.storageKey),d=new URLSearchParams({code_challenge:`${encodeURIComponent(c)}`,code_challenge_method:`${encodeURIComponent(u)}`});l.push(d.toString())}if(a!=null&&a.queryParams){const c=new URLSearchParams(a.queryParams);l.push(c.toString())}return a!=null&&a.skipBrowserRedirect&&l.push(`skip_http_redirect=${a.skipBrowserRedirect}`),`${e}?${l.join("&")}`}async _unenroll(e){try{return await this._useSession(async i=>{var a;const{data:l,error:c}=i;return c?this._returnResult({data:null,error:c}):await ue(this.fetch,"DELETE",`${this.url}/factors/${e.factorId}`,{headers:this.headers,jwt:(a=l==null?void 0:l.session)===null||a===void 0?void 0:a.access_token})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _enroll(e){try{return await this._useSession(async i=>{var a,l;const{data:c,error:u}=i;if(u)return this._returnResult({data:null,error:u});const d=Object.assign({friendly_name:e.friendlyName,factor_type:e.factorType},e.factorType==="phone"?{phone:e.phone}:e.factorType==="totp"?{issuer:e.issuer}:{}),{data:m,error:p}=await ue(this.fetch,"POST",`${this.url}/factors`,{body:d,headers:this.headers,jwt:(a=c==null?void 0:c.session)===null||a===void 0?void 0:a.access_token});return p?this._returnResult({data:null,error:p}):(e.factorType==="totp"&&m.type==="totp"&&(!((l=m==null?void 0:m.totp)===null||l===void 0)&&l.qr_code)&&(m.totp.qr_code=`data:image/svg+xml;utf-8,${m.totp.qr_code}`),this._returnResult({data:m,error:null}))})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _verify(e){const i=async()=>{try{return await this._useSession(async a=>{var l;const{data:c,error:u}=a;if(u)return this._returnResult({data:null,error:u});const d=Object.assign({challenge_id:e.challengeId},"webauthn"in e?{webauthn:Object.assign(Object.assign({},e.webauthn),{credential_response:e.webauthn.type==="create"?Wb(e.webauthn.credential_response):ev(e.webauthn.credential_response)})}:{code:e.code}),{data:m,error:p}=await ue(this.fetch,"POST",`${this.url}/factors/${e.factorId}/verify`,{body:d,headers:this.headers,jwt:(l=c==null?void 0:c.session)===null||l===void 0?void 0:l.access_token});return p?this._returnResult({data:null,error:p}):(await this._saveSession(Object.assign({expires_at:Math.round(Date.now()/1e3)+m.expires_in},m)),await this._notifyAllSubscribers("MFA_CHALLENGE_VERIFIED",m),this._returnResult({data:m,error:p}))})}catch(a){if(ne(a))return this._returnResult({data:null,error:a});throw a}};return this.lock!=null?this._acquireLock(this.lockAcquireTimeout,i):i()}async _challenge(e){const i=async()=>{try{return await this._useSession(async a=>{var l;const{data:c,error:u}=a;if(u)return this._returnResult({data:null,error:u});const d=await ue(this.fetch,"POST",`${this.url}/factors/${e.factorId}/challenge`,{body:e,headers:this.headers,jwt:(l=c==null?void 0:c.session)===null||l===void 0?void 0:l.access_token});if(d.error)return d;const{data:m}=d;if(m.type!=="webauthn")return{data:m,error:null};switch(m.webauthn.type){case"create":return{data:Object.assign(Object.assign({},m),{webauthn:Object.assign(Object.assign({},m.webauthn),{credential_options:Object.assign(Object.assign({},m.webauthn.credential_options),{publicKey:Qb(m.webauthn.credential_options.publicKey)})})}),error:null};case"request":return{data:Object.assign(Object.assign({},m),{webauthn:Object.assign(Object.assign({},m.webauthn),{credential_options:Object.assign(Object.assign({},m.webauthn.credential_options),{publicKey:Zb(m.webauthn.credential_options.publicKey)})})}),error:null}}})}catch(a){if(ne(a))return this._returnResult({data:null,error:a});throw a}};return this.lock!=null?this._acquireLock(this.lockAcquireTimeout,i):i()}async _challengeAndVerify(e){const{data:i,error:a}=await this._challenge({factorId:e.factorId});return a?this._returnResult({data:null,error:a}):await this._verify({factorId:e.factorId,challengeId:i.id,code:e.code})}async _listFactors(){var e;const{data:{user:i},error:a}=await this.getUser();if(a)return{data:null,error:a};const l={all:[],phone:[],totp:[],webauthn:[]};for(const c of(e=i==null?void 0:i.factors)!==null&&e!==void 0?e:[])l.all.push(c),c.status==="verified"&&l[c.factor_type].push(c);return{data:l,error:null}}async _getAuthenticatorAssuranceLevel(e){var i,a,l,c;if(e)try{const{payload:_}=oc(e);let S=null;_.aal&&(S=_.aal);let T=S;const{data:{user:k},error:R}=await this.getUser(e);if(R)return this._returnResult({data:null,error:R});((a=(i=k==null?void 0:k.factors)===null||i===void 0?void 0:i.filter(P=>P.status==="verified"))!==null&&a!==void 0?a:[]).length>0&&(T="aal2");const N=_.amr||[];return{data:{currentLevel:S,nextLevel:T,currentAuthenticationMethods:N},error:null}}catch(_){if(ne(_))return this._returnResult({data:null,error:_});throw _}const{data:{session:u},error:d}=await this.getSession();if(d)return this._returnResult({data:null,error:d});if(!u)return{data:{currentLevel:null,nextLevel:null,currentAuthenticationMethods:[]},error:null};const{payload:m}=oc(u.access_token);let p=null;m.aal&&(p=m.aal);let y=p;((c=(l=u.user.factors)===null||l===void 0?void 0:l.filter(_=>_.status==="verified"))!==null&&c!==void 0?c:[]).length>0&&(y="aal2");const x=m.amr||[];return{data:{currentLevel:p,nextLevel:y,currentAuthenticationMethods:x},error:null}}async _getAuthorizationDetails(e){try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;return l?this._returnResult({data:null,error:l}):a?await ue(this.fetch,"GET",`${this.url}/oauth/authorizations/${e}`,{headers:this.headers,jwt:a.access_token,xform:c=>({data:c,error:null})}):this._returnResult({data:null,error:new Nt})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _approveAuthorization(e,i){try{return await this._useSession(async a=>{const{data:{session:l},error:c}=a;if(c)return this._returnResult({data:null,error:c});if(!l)return this._returnResult({data:null,error:new Nt});const u=await ue(this.fetch,"POST",`${this.url}/oauth/authorizations/${e}/consent`,{headers:this.headers,jwt:l.access_token,body:{action:"approve"},xform:d=>({data:d,error:null})});return u.data&&u.data.redirect_url&&Vt()&&!(i!=null&&i.skipBrowserRedirect)&&window.location.assign(u.data.redirect_url),u})}catch(a){if(ne(a))return this._returnResult({data:null,error:a});throw a}}async _denyAuthorization(e,i){try{return await this._useSession(async a=>{const{data:{session:l},error:c}=a;if(c)return this._returnResult({data:null,error:c});if(!l)return this._returnResult({data:null,error:new Nt});const u=await ue(this.fetch,"POST",`${this.url}/oauth/authorizations/${e}/consent`,{headers:this.headers,jwt:l.access_token,body:{action:"deny"},xform:d=>({data:d,error:null})});return u.data&&u.data.redirect_url&&Vt()&&!(i!=null&&i.skipBrowserRedirect)&&window.location.assign(u.data.redirect_url),u})}catch(a){if(ne(a))return this._returnResult({data:null,error:a});throw a}}async _listOAuthGrants(){try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;return a?this._returnResult({data:null,error:a}):i?await ue(this.fetch,"GET",`${this.url}/user/oauth/grants`,{headers:this.headers,jwt:i.access_token,xform:l=>({data:l,error:null})}):this._returnResult({data:null,error:new Nt})})}catch(e){if(ne(e))return this._returnResult({data:null,error:e});throw e}}async _revokeOAuthGrant(e){try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;return l?this._returnResult({data:null,error:l}):a?(await ue(this.fetch,"DELETE",`${this.url}/user/oauth/grants`,{headers:this.headers,jwt:a.access_token,query:{client_id:e.clientId},noResolveJson:!0}),{data:{},error:null}):this._returnResult({data:null,error:new Nt})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async fetchJwk(e,i={keys:[]}){let a=i.keys.find(d=>d.kid===e);if(a)return a;const l=Date.now();if(a=this.jwks.keys.find(d=>d.kid===e),a&&this.jwks_cached_at+Ik>l)return a;const{data:c,error:u}=await ue(this.fetch,"GET",`${this.url}/.well-known/jwks.json`,{headers:this.headers});if(u)throw u;return!c.keys||c.keys.length===0||(this.jwks=c,this.jwks_cached_at=l,a=c.keys.find(d=>d.kid===e),!a)?null:a}async getClaims(e,i={}){try{let a=e;if(!a){const{data:_,error:S}=await this.getSession();if(S||!_.session)return this._returnResult({data:null,error:S});a=_.session.access_token}const{header:l,payload:c,signature:u,raw:{header:d,payload:m}}=oc(a);if(!(i!=null&&i.allowExpired))try{mR(c.exp)}catch(_){throw new Oc(_ instanceof Error?_.message:"JWT validation failed")}const p=!l.alg||l.alg.startsWith("HS")||!l.kid||!("crypto"in globalThis&&"subtle"in globalThis.crypto)?null:await this.fetchJwk(l.kid,i!=null&&i.keys?{keys:i.keys}:i==null?void 0:i.jwks);if(!p){const{error:_}=await this.getUser(a);if(_)throw _;return{data:{claims:c,header:l,signature:u},error:null}}const y=pR(l.alg),v=await crypto.subtle.importKey("jwk",p,y,!0,["verify"]);if(!await crypto.subtle.verify(y,v,u,tR(`${d}.${m}`)))throw new Oc("Invalid JWT signature");return{data:{claims:c,header:l,signature:u},error:null}}catch(a){if(ne(a))return this._returnResult({data:null,error:a});throw a}}async signInWithPasskey(e){var i,a,l;zn(this.experimental);try{if(!Bc())return this._returnResult({data:null,error:new Hn("Browser does not support WebAuthn",null)});const{data:c,error:u}=await this._startPasskeyAuthentication({options:{captchaToken:(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.captchaToken}});if(u||!c)return this._returnResult({data:null,error:u});const d=Zb(c.options),m=(l=(a=e==null?void 0:e.options)===null||a===void 0?void 0:a.signal)!==null&&l!==void 0?l:vf.createNewAbortSignal(),{data:p,error:y}=await Mw({publicKey:d,signal:m});if(y||!p)return this._returnResult({data:null,error:y??new Hn("WebAuthn ceremony failed",null)});const v=ev(p);return this._verifyPasskeyAuthentication({challengeId:c.challenge_id,credential:v})}catch(c){if(ne(c))return this._returnResult({data:null,error:c});throw c}}async registerPasskey(e){var i,a;zn(this.experimental);try{if(!Bc())return this._returnResult({data:null,error:new Hn("Browser does not support WebAuthn",null)});const{data:l,error:c}=await this._startPasskeyRegistration();if(c||!l)return this._returnResult({data:null,error:c});const u=Qb(l.options),d=(a=(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.signal)!==null&&a!==void 0?a:vf.createNewAbortSignal(),{data:m,error:p}=await Dw({publicKey:u,signal:d});if(p||!m)return this._returnResult({data:null,error:p??new Hn("WebAuthn ceremony failed",null)});const y=Wb(m);return this._verifyPasskeyRegistration({challengeId:l.challenge_id,credential:y})}catch(l){if(ne(l))return this._returnResult({data:null,error:l});throw l}}async _startPasskeyRegistration(){zn(this.experimental);try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;if(a)return this._returnResult({data:null,error:a});if(!i)return this._returnResult({data:null,error:new Nt});const{data:l,error:c}=await ue(this.fetch,"POST",`${this.url}/passkeys/registration/options`,{headers:this.headers,jwt:i.access_token,body:{}});return c?this._returnResult({data:null,error:c}):this._returnResult({data:l,error:null})})}catch(e){if(ne(e))return this._returnResult({data:null,error:e});throw e}}async _verifyPasskeyRegistration(e){zn(this.experimental);try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;if(l)return this._returnResult({data:null,error:l});if(!a)return this._returnResult({data:null,error:new Nt});const{data:c,error:u}=await ue(this.fetch,"POST",`${this.url}/passkeys/registration/verify`,{headers:this.headers,jwt:a.access_token,body:{challenge_id:e.challengeId,credential:e.credential}});return u?this._returnResult({data:null,error:u}):this._returnResult({data:c,error:null})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _startPasskeyAuthentication(e){var i;zn(this.experimental);try{const{data:a,error:l}=await ue(this.fetch,"POST",`${this.url}/passkeys/authentication/options`,{headers:this.headers,body:{gotrue_meta_security:{captcha_token:(i=e==null?void 0:e.options)===null||i===void 0?void 0:i.captchaToken}}});return l?this._returnResult({data:null,error:l}):this._returnResult({data:a,error:null})}catch(a){if(ne(a))return this._returnResult({data:null,error:a});throw a}}async _verifyPasskeyAuthentication(e){zn(this.experimental);try{const{data:i,error:a}=await ue(this.fetch,"POST",`${this.url}/passkeys/authentication/verify`,{headers:this.headers,body:{challenge_id:e.challengeId,credential:e.credential},xform:Tn});return a?this._returnResult({data:null,error:a}):(i.session&&(await this._saveSession(i.session),await this._notifyAllSubscribers("SIGNED_IN",i.session)),this._returnResult({data:i,error:null}))}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _listPasskeys(){zn(this.experimental);try{return await this._useSession(async e=>{const{data:{session:i},error:a}=e;if(a)return this._returnResult({data:null,error:a});if(!i)return this._returnResult({data:null,error:new Nt});const{data:l,error:c}=await ue(this.fetch,"GET",`${this.url}/passkeys`,{headers:this.headers,jwt:i.access_token,xform:u=>({data:u,error:null})});return c?this._returnResult({data:null,error:c}):this._returnResult({data:l,error:null})})}catch(e){if(ne(e))return this._returnResult({data:null,error:e});throw e}}async _updatePasskey(e){zn(this.experimental);try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;if(l)return this._returnResult({data:null,error:l});if(!a)return this._returnResult({data:null,error:new Nt});const{data:c,error:u}=await ue(this.fetch,"PATCH",`${this.url}/passkeys/${e.passkeyId}`,{headers:this.headers,jwt:a.access_token,body:{friendly_name:e.friendlyName}});return u?this._returnResult({data:null,error:u}):this._returnResult({data:c,error:null})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}async _deletePasskey(e){zn(this.experimental);try{return await this._useSession(async i=>{const{data:{session:a},error:l}=i;if(l)return this._returnResult({data:null,error:l});if(!a)return this._returnResult({data:null,error:new Nt});const{error:c}=await ue(this.fetch,"DELETE",`${this.url}/passkeys/${e.passkeyId}`,{headers:this.headers,jwt:a.access_token,noResolveJson:!0});return c?this._returnResult({data:null,error:c}):this._returnResult({data:null,error:null})})}catch(i){if(ne(i))return this._returnResult({data:null,error:i});throw i}}}wo.nextInstanceID={};const PR=wo,VR="2.110.2";let Wr="",Lc;if(typeof Deno<"u"){var kd;Wr="deno",Lc=(kd=Deno.version)===null||kd===void 0?void 0:kd.deno}else if(typeof document<"u")Wr="web";else if(typeof navigator<"u"&&navigator.product==="ReactNative")Wr="react-native";else{var Rd;Wr="node",Lc=typeof process<"u"?(Rd=process.version)===null||Rd===void 0?void 0:Rd.replace(/^v/,""):void 0}const Bw=[`runtime=${Wr}`];Lc&&Bw.push(`runtime-version=${Lc}`);const HR={"X-Client-Info":`supabase-js/${VR}; ${Bw.join("; ")}`},$R={headers:HR},qR={schema:"public"},FR={autoRefreshToken:!0,persistSession:!0,detectSessionInUrl:!0,flowType:"implicit"},IR={},GR={enabled:!1,respectSamplingDecision:!0};function KR(n,e,i,a){function l(c){return c instanceof i?c:new i(function(u){u(c)})}return new(i||(i=Promise))(function(c,u){function d(y){try{p(a.next(y))}catch(v){u(v)}}function m(y){try{p(a.throw(y))}catch(v){u(v)}}function p(y){y.done?c(y.value):l(y.value).then(d,m)}p((a=a.apply(n,[])).next())})}let Cd=null;const YR="@opentelemetry/api";function XR(){return Cd===null&&(Cd=import(YR).catch(()=>null)),Cd}function JR(){return KR(this,void 0,void 0,function*(){try{const n=yield XR();if(!n||!n.propagation||!n.context)return null;const e={};n.propagation.inject(n.context.active(),e);const i=e.traceparent;return i?{traceparent:i,tracestate:e.tracestate,baggage:e.baggage}:null}catch{return null}})}function QR(n){if(!n||typeof n!="string")return null;const e=n.split("-");if(e.length!==4)return null;const[i,a,l,c]=e;if(i.length!==2||a.length!==32||l.length!==16||c.length!==2)return null;const u=/^[0-9a-f]+$/i;return!u.test(i)||!u.test(a)||!u.test(l)||!u.test(c)||a==="00000000000000000000000000000000"||l==="0000000000000000"?null:{version:i,traceId:a,parentId:l,traceFlags:c,isSampled:(parseInt(c,16)&1)===1}}function ZR(n,e){if(!n||!e||e.length===0)return!1;let i;if(n instanceof URL)i=n;else try{i=new URL(n)}catch{return!1}for(const a of e)try{if(typeof a=="string"){if(WR(i.hostname,a))return!0}else if(a instanceof RegExp){if(a.test(i.hostname))return!0}else if(typeof a=="function"&&a(i))return!0}catch{continue}return!1}function WR(n,e){if(e===n)return!0;if(e.startsWith("*.")){const i=e.slice(2);if(n.endsWith(i)&&(n===i||n.endsWith("."+i)))return!0}return!1}function eC(n){const e=[];try{const i=new URL(n);e.push(i.hostname)}catch{}return e.push("*.supabase.co","*.supabase.in"),e.push("localhost","127.0.0.1","[::1]"),e}function _o(n){"@babel/helpers - typeof";return _o=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_o(n)}function tC(n,e){if(_o(n)!="object"||!n)return n;var i=n[Symbol.toPrimitive];if(i!==void 0){var a=i.call(n,e);if(_o(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(e==="string"?String:Number)(n)}function nC(n){var e=tC(n,"string");return _o(e)=="symbol"?e:e+""}function sC(n,e,i){return(e=nC(e))in n?Object.defineProperty(n,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[e]=i,n}function tv(n,e){var i=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(l){return Object.getOwnPropertyDescriptor(n,l).enumerable})),i.push.apply(i,a)}return i}function lt(n){for(var e=1;en?(...e)=>n(...e):(...e)=>fetch(...e),aC=()=>Headers,rC=(n,e,i,a,l)=>{const c=iC(a),u=aC(),d=(l==null?void 0:l.enabled)===!0,m=(l==null?void 0:l.respectSamplingDecision)!==!1,p=d?eC(e):null;return async(y,v)=>{var x;const _=(x=await i())!==null&&x!==void 0?x:n;let S=new u(v==null?void 0:v.headers);if(S.has("apikey")||S.set("apikey",n),S.has("Authorization")||S.set("Authorization",`Bearer ${_}`),p){const T=await oC(y,p,m);T&&(T.traceparent&&!S.has("traceparent")&&S.set("traceparent",T.traceparent),T.tracestate&&!S.has("tracestate")&&S.set("tracestate",T.tracestate),T.baggage&&!S.has("baggage")&&S.set("baggage",T.baggage))}return c(y,lt(lt({},v),{},{headers:S}))}};async function oC(n,e,i){if(!ZR(typeof n=="string"||n instanceof URL?n:n.url,e))return null;const a=await JR();if(!a||!a.traceparent)return null;if(i){const l=QR(a.traceparent);if(l&&!l.isSampled)return null}return a}function nv(n){return typeof n=="boolean"?{enabled:n}:n}function lC(n){return n.endsWith("/")?n:n+"/"}function cC(n,e){var i,a,l,c,u,d;const{db:m,auth:p,realtime:y,global:v}=n,{db:x,auth:_,realtime:S,global:T}=e,k=nv(n.tracePropagation),R=nv(e.tracePropagation),M={db:lt(lt({},x),m),auth:lt(lt({},_),p),realtime:lt(lt({},S),y),storage:{},global:lt(lt(lt({},T),v),{},{headers:lt(lt({},(i=T==null?void 0:T.headers)!==null&&i!==void 0?i:{}),(a=v==null?void 0:v.headers)!==null&&a!==void 0?a:{})}),tracePropagation:{enabled:(l=(c=k==null?void 0:k.enabled)!==null&&c!==void 0?c:R==null?void 0:R.enabled)!==null&&l!==void 0?l:!1,respectSamplingDecision:(u=(d=k==null?void 0:k.respectSamplingDecision)!==null&&d!==void 0?d:R==null?void 0:R.respectSamplingDecision)!==null&&u!==void 0?u:!0},accessToken:async()=>""};return n.accessToken?M.accessToken=n.accessToken:delete M.accessToken,M}function uC(n){const e=n==null?void 0:n.trim();if(!e)throw new Error("supabaseUrl is required.");if(!e.match(/^https?:\/\//i))throw new Error("Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.");try{return new URL(lC(e))}catch{throw Error("Invalid supabaseUrl: Provided URL is malformed.")}}var hC=class extends PR{constructor(n){super(n)}},dC=class{constructor(n,e,i){var a,l;this.supabaseUrl=n,this.supabaseKey=e;const c=uC(n);if(!e)throw new Error("supabaseKey is required.");this.realtimeUrl=new URL("realtime/v1",c),this.realtimeUrl.protocol=this.realtimeUrl.protocol.replace("http","ws"),this.authUrl=new URL("auth/v1",c),this.storageUrl=new URL("storage/v1",c),this.functionsUrl=new URL("functions/v1",c);const u=`sb-${c.hostname.split(".")[0]}-auth-token`,d={db:qR,realtime:IR,auth:lt(lt({},FR),{},{storageKey:u}),global:$R,tracePropagation:GR},m=cC(i??{},d);if(this.settings=m,this.storageKey=(a=m.auth.storageKey)!==null&&a!==void 0?a:"",this.headers=(l=m.global.headers)!==null&&l!==void 0?l:{},m.accessToken)this.accessToken=m.accessToken,this.auth=new Proxy({},{get:(y,v)=>{throw new Error(`@supabase/supabase-js: Supabase Client is configured with the accessToken option, accessing supabase.auth.${String(v)} is not possible`)}});else{var p;this.auth=this._initSupabaseAuthClient((p=m.auth)!==null&&p!==void 0?p:{},this.headers,m.global.fetch)}this.fetch=rC(e,n,this._getAccessToken.bind(this),m.global.fetch,m.tracePropagation),this.realtime=this._initRealtimeClient(lt({headers:this.headers,accessToken:this._getAccessToken.bind(this),fetch:this.fetch},m.realtime)),this.accessToken&&Promise.resolve(this.accessToken()).then(y=>this.realtime.setAuth(y)).catch(y=>console.warn("Failed to set initial Realtime auth token:",y)),this.rest=new wN(new URL("rest/v1",c).href,{headers:this.headers,schema:m.db.schema,fetch:this.fetch,timeout:m.db.timeout,urlLengthLimit:m.db.urlLengthLimit}),this.storage=new Pk(this.storageUrl.href,this.headers,this.fetch,i==null?void 0:i.storage),m.accessToken||this._listenForAuthEvents()}get functions(){return new dN(this.functionsUrl.href,{headers:this.headers,customFetch:this.fetch})}from(n){return this.rest.from(n)}schema(n){return this.rest.schema(n)}rpc(n,e={},i={head:!1,get:!1,count:void 0}){return this.rest.rpc(n,e,i)}channel(n,e={config:{}}){return this.realtime.channel(n,e)}getChannels(){return this.realtime.getChannels()}removeChannel(n){return this.realtime.removeChannel(n)}removeAllChannels(){return this.realtime.removeAllChannels()}async _getAccessToken(){var n=this,e,i;if(n.accessToken)return await n.accessToken();const{data:a}=await n.auth.getSession();return(e=(i=a.session)===null||i===void 0?void 0:i.access_token)!==null&&e!==void 0?e:n.supabaseKey}_initSupabaseAuthClient({autoRefreshToken:n,persistSession:e,detectSessionInUrl:i,storage:a,userStorage:l,storageKey:c,flowType:u,lock:d,debug:m,throwOnError:p,experimental:y,lockAcquireTimeout:v,skipAutoInitialize:x},_,S){const T={Authorization:`Bearer ${this.supabaseKey}`,apikey:`${this.supabaseKey}`};return new hC({url:this.authUrl.href,headers:lt(lt({},T),_),storageKey:c,autoRefreshToken:n,persistSession:e,detectSessionInUrl:i,storage:a,userStorage:l,flowType:u,lock:d,debug:m,throwOnError:p,experimental:y,fetch:S,lockAcquireTimeout:v,skipAutoInitialize:x,hasCustomAuthorizationHeader:Object.keys(this.headers).some(k=>k.toLowerCase()==="authorization")})}_initRealtimeClient(n){return new ck(this.realtimeUrl.href,lt(lt({},n),{},{params:lt(lt({},{apikey:this.supabaseKey}),n==null?void 0:n.params)}))}_listenForAuthEvents(){return this.auth.onAuthStateChange((n,e)=>{this._handleTokenChanged(n,"CLIENT",e==null?void 0:e.access_token)})}_handleTokenChanged(n,e,i){(n==="TOKEN_REFRESHED"||n==="SIGNED_IN")&&this.changedAccessToken!==i?(this.changedAccessToken=i,this.realtime.setAuth(i)):n==="SIGNED_OUT"&&(this.realtime.setAuth(),e=="STORAGE"&&this.auth.signOut(),this.changedAccessToken=void 0)}};const fC=(n,e,i)=>new dC(n,e,i);function mC(){if(typeof window<"u")return!1;const n=globalThis.process;if(!n)return!1;const e=n.version;if(e==null)return!1;const i=e.match(/^v(\d+)\./);return i?parseInt(i[1],10)<=20:!1}mC()&&console.warn("⚠️ Node.js 20 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 22 or later. For more information, visit: https://github.com/orgs/supabase/discussions/45715");const pC="https://dbit.digitalcompass.agency",Uw="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE",oo=!!Uw,ut=oo?fC(pC,Uw,{auth:{persistSession:!0,autoRefreshToken:!0,detectSessionInUrl:!0}}):null;function Lw(){return new URL("/cruce-cuentas/",window.location.origin).toString()}async function gC(){if(typeof window>"u"||typeof fetch>"u")return;const n=Lw(),e=new AbortController,i=window.setTimeout(()=>e.abort(),8e3);try{await(await fetch(n,{method:"GET",cache:"reload",credentials:"same-origin",redirect:"follow",headers:{Accept:"text/html,application/xhtml+xml"},signal:e.signal})).text()}catch(a){console.warn("No se pudo refrescar preventivamente la entrada de la app:",a)}finally{window.clearTimeout(i)}}const yC=180,bC=8e3;async function So(n=yC){if(!ut)return null;const{data:e,error:i}=await ut.auth.getSession();if(i)throw i;let a=e.session;if(!a)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de continuar.");const l=Number(a.expires_at||0)*1e3;if(!l||l-Date.now()<=n*1e3){const u=await ut.auth.refreshSession();if(u.error)throw u.error;if(a=u.data.session,!a)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de continuar.")}return a}async function sv(n){if(typeof window>"u"||typeof fetch>"u")return;if(typeof navigator<"u"&&navigator.onLine===!1)throw new Error("No hay conexión a Internet. Verifica tu conexión e intenta nuevamente.");let e;try{e=new URL(n,window.location.origin)}catch{return}if(e.origin===window.location.origin)return;const i=new URL("/favicon.ico",e.origin);i.searchParams.set("_glm_resume",String(Date.now()));const a=new AbortController,l=window.setTimeout(()=>a.abort(),bC);try{await fetch(i.toString(),{method:"GET",mode:"no-cors",cache:"no-store",credentials:"omit",signal:a.signal})}catch(c){if(typeof navigator<"u"&&navigator.onLine===!1)throw new Error("No hay conexión a Internet. Verifica tu conexión e intenta nuevamente.");console.warn("No se pudo precalentar la conexión con n8n:",c)}finally{window.clearTimeout(l)}}function iv(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`glm-${Date.now()}-${Math.random().toString(36).slice(2,12)}`}function av(n,e){try{const i=new URL(n,window.location.origin);return i.searchParams.set("client_request_id",e),i.searchParams.set("_ts",String(Date.now())),i.toString()}catch{return n}}const vC="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-bamboo-correccion",Od=["Nombre mal escrito en BambooHR","Apodo o nombre de uso","Nombre incompleto / nombres adicionales","Otro"];function xC(n){try{return n?JSON.parse(n):null}catch{return null}}function zw({row:n,country:e,periodRange:i,operatorName:a,operatorEmail:l,executionId:c,reportUrl:u,onClose:d,onSuccess:m,onShowToast:p}){const y=n.bestBambooCandidate,[v,x]=D.useState((y==null?void 0:y.employeeName)||""),[_,S]=D.useState((y==null?void 0:y.employeeNumber)||""),[T,k]=D.useState(Od[0]),[R,M]=D.useState(""),[N,P]=D.useState(!1),[G,X]=D.useState(!1);D.useEffect(()=>{x((y==null?void 0:y.employeeName)||""),S((y==null?void 0:y.employeeNumber)||""),k(Od[0]),M(""),P(!1)},[n.id,y==null?void 0:y.employeeName,y==null?void 0:y.employeeNumber]);const q=D.useMemo(()=>e.code==="TT"?"Laurele Nicome-Jack":e.code==="PA"?"Delarys Peñalba":e.code==="SV"?"Carlos Antonio Malta Méndez":e.code==="HN"?"Melody Chang Blandin":e.code==="PR"?"Noralmy Laureano":"haguirre@gomezleemarketing.com",[e.code]),Y=D.useMemo(()=>Array.from(new Set([n.employee,n.bankNameFile,n.bankAccountHolder].map(J=>String(J||"").trim()).filter(Boolean))),[n.employee,n.bankNameFile,n.bankAccountHolder]),se=async()=>{var J;if(!v.trim()){p("Escribe el nombre correcto del empleado en BambooHR.","error");return}if(!N){p("Confirma que se trata de la misma persona antes de enviar el caso.","error");return}if(!ut){p("Supabase no está configurado en esta aplicación.","error");return}X(!0);try{const he=await So(300),fe=he==null?void 0:he.access_token;if(!fe)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de reportar el caso.");const Ae={country:e.code,country_name:e.name,source_name:n.employee,source_names:Y,bank_name_file:n.bankNameFile||"",bank_account_holder:n.bankAccountHolder||"",bank_account:n.account||"",bank_amount:n.amountBank,currency:e.currency,bamboo_name:v.trim(),bamboo_employee_number:_.trim(),reason:T,comment:R.trim(),confirmed_same_person:!0,execution_id:c||"",report_url:u||"",period_label:i.label,period_start:i.start,period_end:i.end,requested_by_name:a,requested_by_email:l},ve=new FormData;ve.append("payload",JSON.stringify(Ae)),ve.append("access_token",fe);const Ce=await fetch(vC,{method:"POST",body:ve}),L=await Ce.text(),K=xC(L);if(!Ce.ok||(K==null?void 0:K.ok)===!1){const Z=(K==null?void 0:K.message)||(K==null?void 0:K.error)||((J=K==null?void 0:K.errors)==null?void 0:J[0])||L||"No se pudo enviar el caso a RRHH.";throw new Error(String(Z))}m(n.id),p(`Caso enviado a RRHH de ${e.name}. La equivalencia quedó registrada para futuros cruces.`,"success"),d()}catch(he){const fe=he instanceof Error?he.message:"No se pudo enviar el caso a RRHH.";p(fe,"error")}finally{X(!1)}};return g.jsxs("div",{className:"fixed inset-0 z-[90] flex items-center justify-center px-4 py-6",children:[g.jsx("button",{type:"button",className:"absolute inset-0 bg-black/45",onClick:G?void 0:d,"aria-label":"Cerrar"}),g.jsxs("div",{className:"relative w-full max-w-2xl bg-white border border-[#D0D0D0] shadow-2xl max-h-[92vh] overflow-y-auto",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A]"}),g.jsxs("div",{className:"px-6 py-5 border-b border-neutral-200 flex items-start justify-between gap-4",children:[g.jsxs("div",{children:[g.jsx("p",{className:"text-[10px] uppercase tracking-[0.18em] font-black text-[#6B8FA3]",children:"Banco sin BambooHR"}),g.jsx("h2",{className:"text-xl font-black text-[#4F758B] mt-1",children:"Reportar corrección de identidad a RRHH"}),g.jsx("p",{className:"text-xs text-neutral-500 mt-2 leading-relaxed",children:"Esta validación crea una equivalencia permanente para que el mismo falso positivo no vuelva a aparecer mientras RRHH corrige BambooHR."})]}),g.jsx("button",{type:"button",onClick:d,disabled:G,className:"p-2 text-neutral-400 hover:text-neutral-700 hover:bg-neutral-100 disabled:opacity-40","aria-label":"Cerrar modal",children:g.jsx(Ka,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-6 py-5 space-y-5",children:[g.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-3 bg-[#F5F5F5] border border-neutral-200 p-4",children:[g.jsxs("div",{children:[g.jsx("p",{className:"text-[9px] uppercase tracking-wider font-black text-[#6B8FA3]",children:"Nombre detectado"}),g.jsx("p",{className:"text-sm font-black text-neutral-800 mt-1",children:n.employee})]}),g.jsxs("div",{children:[g.jsx("p",{className:"text-[9px] uppercase tracking-wider font-black text-[#6B8FA3]",children:"Cuenta bancaria"}),g.jsx("p",{className:"text-sm font-mono font-bold text-neutral-700 mt-1",children:n.account||"—"})]}),g.jsxs("div",{className:"sm:col-span-2",children:[g.jsx("p",{className:"text-[9px] uppercase tracking-wider font-black text-[#6B8FA3]",children:"Se notificará a"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700 mt-1",children:q})]})]}),(y==null?void 0:y.employeeName)&&g.jsxs("div",{className:"border border-[#D6E8F4] bg-[#F7FBFD] p-4 flex items-start gap-3",children:[g.jsx(fo,{className:"w-4 h-4 text-[#4F758B] mt-0.5 flex-shrink-0"}),g.jsxs("div",{className:"text-xs text-neutral-600 leading-relaxed",children:[g.jsx("strong",{className:"text-[#4F758B]",children:"Candidato sugerido por el cruce:"})," ",y.employeeName,y.employeeNumber?` · Employee Number ${y.employeeNumber}`:"",". Verifícalo manualmente antes de confirmar."]})]}),g.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[g.jsxs("label",{className:"sm:col-span-2",children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Nombre correcto en BambooHR *"}),g.jsx("input",{type:"text",value:v,onChange:J=>x(J.target.value),placeholder:"Ej.: Jonathan Alexander Pérez",className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B]"})]}),g.jsxs("label",{children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Employee Number"}),g.jsx("input",{type:"text",value:_,onChange:J=>S(J.target.value),placeholder:"Recomendado para mayor seguridad",className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B]"})]}),g.jsxs("label",{children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Motivo *"}),g.jsx("select",{value:T,onChange:J=>k(J.target.value),className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B] bg-white",children:Od.map(J=>g.jsx("option",{value:J,children:J},J))})]}),g.jsxs("label",{className:"sm:col-span-2",children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Comentario para RRHH"}),g.jsx("textarea",{value:R,onChange:J=>M(J.target.value),rows:3,placeholder:"Detalle adicional que ayude a RRHH a corregir el registro.",className:"w-full border border-[#D0D0D0] px-3 py-2.5 text-sm font-medium focus:outline-none focus:border-[#4F758B] resize-y"})]})]}),g.jsxs("label",{className:"flex items-start gap-3 border border-[#D0D0D0] p-4 cursor-pointer",children:[g.jsx("input",{type:"checkbox",checked:N,onChange:J=>P(J.target.checked),className:"mt-0.5 accent-[#6CC24A]"}),g.jsxs("span",{className:"text-xs text-neutral-600 leading-relaxed",children:["Confirmo que revisé el caso manualmente y que ",g.jsx("strong",{children:n.employee})," corresponde a la misma persona indicada arriba en BambooHR."]})]})]}),g.jsxs("div",{className:"px-6 py-4 border-t border-neutral-200 bg-neutral-50 flex flex-col-reverse sm:flex-row sm:justify-end gap-2",children:[g.jsx("button",{type:"button",onClick:d,disabled:G,className:"px-4 py-2.5 border border-neutral-300 bg-white text-neutral-600 text-xs font-black uppercase tracking-wider disabled:opacity-40",children:"Cancelar"}),g.jsx("button",{type:"button",onClick:se,disabled:G||!N||!v.trim(),className:"px-5 py-2.5 bg-[#4F758B] text-white text-xs font-black uppercase tracking-wider inline-flex items-center justify-center gap-2 border-l-4 border-[#6CC24A] disabled:bg-neutral-300 disabled:border-neutral-300 disabled:cursor-not-allowed",children:G?g.jsxs(g.Fragment,{children:[g.jsx("span",{className:"w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin"}),"Enviando..."]}):g.jsxs(g.Fragment,{children:[g.jsx(fw,{className:"w-4 h-4"}),"Enviar a RRHH"]})})]})]})]})}const wC="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-reportar";function _C(n){try{return n?JSON.parse(n):null}catch{return null}}function SC(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`report-${Date.now()}-${Math.random().toString(36).slice(2,10)}`}function Pw({country:n,operatorName:e,operatorEmail:i,section:a,onShowToast:l}){const[c,u]=D.useState(!1),[d,m]=D.useState(""),[p,y]=D.useState(!1),v=()=>{p||u(!1)},x=async()=>{const _=d.trim();if(!_){l("Escribe una descripción del reporte.","error");return}if(!ut){l("Supabase no está configurado en esta aplicación.","error");return}y(!0);try{const S=await So(300),T=S==null?void 0:S.access_token;if(!T)throw new Error("Tu sesión expiró. Inicia sesión nuevamente antes de enviar el reporte.");const k={message:_.slice(0,5e3),userName:e,userEmail:i,country:n.code,countryName:n.name,section:a,submittedAt:new Date().toISOString(),pageUrl:window.location.href,appSessionId:SC()},R=new FormData;R.append("payload",JSON.stringify(k)),R.append("access_token",T);const M=await fetch(wC,{method:"POST",body:R}),N=await M.text(),P=_C(N);if(!M.ok||(P==null?void 0:P.ok)===!1){const G=(P==null?void 0:P.message)||(P==null?void 0:P.error)||N||"No se pudo enviar el reporte.";throw new Error(String(G))}m(""),u(!1),l("Reporte enviado correctamente.","success")}catch(S){l(S instanceof Error?S.message:"No se pudo enviar el reporte.","error")}finally{y(!1)}};return g.jsxs(g.Fragment,{children:[g.jsxs("button",{type:"button",onClick:()=>u(!0),className:"inline-flex items-center justify-center gap-2 border border-[#4F758B]/25 bg-white text-[#4F758B] h-9 px-2.5 sm:px-3 text-xs font-black hover:border-[#4F758B]/55 hover:bg-[#4F758B]/5 transition-colors shadow-sm",title:"Reportar un error o incidencia","aria-label":"Reportar",children:[g.jsx(Yj,{className:"w-4 h-4"}),g.jsx("span",{className:"hidden sm:inline",children:"Reportar"})]}),c&&g.jsxs("div",{className:"fixed inset-0 z-[90] flex items-center justify-center px-4 py-6",children:[g.jsx("button",{type:"button",className:"absolute inset-0 bg-black/45",onClick:v,"aria-label":"Cerrar reporte"}),g.jsxs("div",{className:"relative w-full max-w-2xl bg-white border border-[#D0D0D0] shadow-2xl",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A]"}),g.jsxs("div",{className:"px-6 py-5 border-b border-neutral-200 flex items-center justify-between gap-4",children:[g.jsx("h2",{className:"text-xl font-black text-[#4F758B]",children:"Reporte"}),g.jsx("button",{type:"button",onClick:v,disabled:p,className:"p-2 text-neutral-400 hover:text-neutral-700 hover:bg-neutral-100 disabled:opacity-40","aria-label":"Cerrar modal",children:g.jsx(Ka,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-6 py-5",children:[g.jsxs("label",{className:"block",children:[g.jsx("span",{className:"block text-[10px] uppercase tracking-wider font-black text-[#4F758B] mb-1.5",children:"Descripción del Reporte *"}),g.jsx("textarea",{value:d,onChange:_=>m(_.target.value.slice(0,5e3)),rows:7,autoFocus:!0,disabled:p,placeholder:"Escribe aquí el error, incidencia o mejora que deseas reportar...",className:"w-full resize-y min-h-[150px] border border-[#D0D0D0] bg-white px-3.5 py-3 text-sm text-neutral-700 placeholder-neutral-400 focus:outline-none focus:border-[#4F758B] disabled:bg-neutral-50"})]}),g.jsxs("div",{className:"mt-1.5 text-right text-[10px] text-neutral-400",children:[d.length,"/5000"]})]}),g.jsxs("div",{className:"px-6 py-4 border-t border-neutral-200 bg-neutral-50 flex items-center justify-end gap-3",children:[g.jsx("button",{type:"button",onClick:v,disabled:p,className:"border border-neutral-300 bg-white text-neutral-600 px-4 py-2.5 text-xs font-bold hover:bg-neutral-100 disabled:opacity-50",children:"Cancelar"}),g.jsxs("button",{type:"button",onClick:()=>void x(),disabled:p||!d.trim(),className:"bg-[#4F758B] hover:bg-[#3F6174] text-white px-4 py-2.5 text-xs font-black inline-flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",children:[p?g.jsx($a,{className:"w-4 h-4 animate-spin"}):g.jsx(fw,{className:"w-4 h-4"}),g.jsx("span",{children:p?"Enviando...":"Enviar reporte"})]})]})]})]})]})}const Vw=[{value:1,label:"Enero"},{value:2,label:"Febrero"},{value:3,label:"Marzo"},{value:4,label:"Abril"},{value:5,label:"Mayo"},{value:6,label:"Junio"},{value:7,label:"Julio"},{value:8,label:"Agosto"},{value:9,label:"Septiembre"},{value:10,label:"Octubre"},{value:11,label:"Noviembre"},{value:12,label:"Diciembre"}],rv=[{value:"quincena_15",label:"Quincena 15",description:"Del día 1 al 15 del mes."},{value:"quincena_30",label:"Quincena 30 / fin de mes",description:"Del día 16 al último día del mes."}],TC="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-analizar-nomina";function EC(n){return n==="TT"?"https://agenteit.digitalcompass.agency/webhook/nominatt-bamboo-test":n==="PA"?"https://agenteit.digitalcompass.agency/webhook/nominapa-bamboo-test":n==="SV"?"https://agenteit.digitalcompass.agency/webhook/nominasv-bamboo-test":n==="HN"?"https://agenteit.digitalcompass.agency/webhook/nominahn-bamboo-test":n==="PR"?"https://agenteit.digitalcompass.agency/webhook/nominapr-bamboo-test":"https://agenteit.digitalcompass.agency/webhook/nominagt-bamboo-test"}function AC(){return"https://agenteit.digitalcompass.agency/webhook/bono14gt-bamboo-test"}function jC(){const n=new Date().getFullYear(),e=n-5,i=n+10;return Array.from({length:i-e+1},(a,l)=>e+l)}function NC(){const n=new Date,e=n.getDate();let i=n.getFullYear(),a=n.getMonth()+1,l="quincena_15";return e<=15&&(a-=1,l="quincena_30",a===0&&(a=12,i-=1)),{year:i,month:a,periodType:l}}function zc(n){return n<1024*1024?`${(n/1024).toFixed(1)} KB`:`${(n/(1024*1024)).toFixed(2)} MB`}function Yr(n){const e=n.name.toLowerCase();return e.endsWith(".xlsx")||e.endsWith(".xls")}function Dd(n){return n.name.toLowerCase().endsWith(".csv")}function ov(n){return n.name.toLowerCase().endsWith(".pdf")}function kC(n){return Array.from(n).map(e=>({name:e.name,size:zc(e.size),type:"payroll",rawFile:e}))}function Md(n){return Array.from(n).map(e=>({name:e.name,size:zc(e.size),type:"bank",rawFile:e}))}function Ha(n,e,i){return`${n}-${String(e).padStart(2,"0")}-${String(i).padStart(2,"0")}`}function lv(n){const e=/^(\d{4})-(\d{2})-(\d{2})$/.exec(n);return e?`${e[3]}-${e[2]}-${e[1]}`:n}function cv(n,e,i){var c;const a=new Date(n,e,0).getDate(),l=((c=Vw.find(u=>u.value===e))==null?void 0:c.label)??"Mes";return i==="quincena_15"?{start:Ha(n,e,1),end:Ha(n,e,15),label:`${l} ${n} · Quincena 15`}:{start:Ha(n,e,16),end:Ha(n,e,a),label:`${l} ${n} · Quincena 30 / fin de mes`}}function RC(n){const e=n-1;return{start:Ha(e,7,1),end:Ha(n,6,30),label:`Bono 14 ${n} · Julio ${e} a junio ${n}`}}function Bd(n,e,i){if(n==null||Number.isNaN(n))return"—";const a=i==="USD"||i==="GTQ"||i==="TTD"?i:e.currency,l=a==="USD"?"en-US":e.locale;return new Intl.NumberFormat(l,{style:"currency",currency:a,minimumFractionDigits:2}).format(n)}function Xn(n,e=0){const i=Number(n);return Number.isFinite(i)?i:e}function Tt(n){return n.normalize("NFD").replace(/[\u0300-\u036f]/g,"").toLowerCase()}function CC(n,e){const i=Tt(String(n??e??""));return i.includes("coincid")?"Coincidencia":i.includes("resuelto")?"Resuelto":i.includes("no encontrado")?"No encontrado":i.includes("pendiente")?"Pendiente revisión":i.includes("riesgo")||i.includes("discrep")||i.includes("mal digitada")?"Riesgo":e==="coincidencia"?"Coincidencia":e==="posible_cuenta_mal_digitada"||e==="discrepancia"?"Riesgo":"Pendiente revisión"}function lc(n,e){for(const i of e){if(typeof n[i]=="number")return n[i];if(typeof n[i]=="string"&&n[i]!==""){const a=n[i].replace(/[Q,$\s]/g,"").replace(/,/g,""),l=Number(a);if(Number.isFinite(l))return l}}return null}function uv(n){return Array.isArray(n)?n.map((e,i)=>{const a=e,l=String(a.category??a.categoria??a.tipo??a.source??a.fuente??""),c=lc(a,["amountPayroll","monto_nomina","payroll_amount","amount_payroll","montoNomina"]),u=lc(a,["amountBank","monto_banco","bank_amount","amount_bank","montoBanco"]),d=lc(a,["difference","diferencia","diff"]);return{id:String(a.id??a.employeeNumber??a.employee_number??a.account??a.cuenta??`row-${i+1}`),employee:String(a.employee??a.empleado??a.name??a.nombre??a.employee_name??"Sin nombre"),employeeNumber:String(a.employeeNumber??a.employee_number??a.codigo??""),account:a.account||a.cuenta||a.bank_account?String(a.account??a.cuenta??a.bank_account):void 0,currency:String(a.currency??a.moneda??"").toUpperCase()||void 0,amountPayroll:c,amountBank:u,difference:d??(c!==null&&u!==null?c-u:null),status:CC(a.status??a.estado,l),category:l,observation:String(a.observation??a.observacion??a.note??a.nota??a.comentario??""),source:a.source??a.fuente??l,bankNameFile:String(a.bank_name_file??a.bankNameFile??""),bankAccountHolder:String(a.bank_account_holder??a.bankAccountHolder??""),bestBambooCandidate:a.best_bamboo_candidate&&typeof a.best_bamboo_candidate=="object"?{employeeNumber:String(a.best_bamboo_candidate.employee_number??""),employeeName:String(a.best_bamboo_candidate.employee_name??""),score:lc(a.best_bamboo_candidate,["score"])}:null,ambiguousBambooMatch:!!a.ambiguous_bamboo_match}}):[]}function hv(n){const i=(Array.isArray(n)?n[0]:n)??{},a=i.originalResponse??i.original_response??i.response??i,l=a.summary??a.resumen??i.summary??i.resumen??{},c=uv(a.rows??i.rows??a.resultados??i.resultados??[]),u=uv(a.bankWithoutBamboo??i.bankWithoutBamboo??a.banco_sin_bamboo_rows??i.banco_sin_bamboo_rows??[]).map(N=>({...N,category:N.category||"banco_sin_bamboo",source:"banco_sin_bamboo",status:N.status==="Coincidencia"?"Pendiente revisión":N.status})),d=new Map;[...c,...u].forEach((N,P)=>{const G=N.id||`${N.source??N.category??"row"}-${P}`;d.set(G,N)});const m=Array.from(d.values()),p=Xn(l.matches??l.coincidencias,c.filter(N=>N.status==="Coincidencia").length),y=Xn(l.discrepancies??l.discrepancias,c.filter(N=>{const P=Tt(String(N.category??N.source??""));return P.includes("discrepancia")||P.includes("posible_cuenta_mal_digitada")}).length),v=Xn(l.bankWithoutPayroll??l.bancoSinNomina??l.banco_sin_nomina,c.filter(N=>Ua(N,"banco_sin_nomina")).length),x=Xn(l.bankWithoutBamboo??l.bancoSinBamboo??l.banco_sin_bamboo,u.length),_=Xn(l.payrollWithoutAccount??l.nominaSinCuenta??l.nomina_sin_cuenta),S=Xn(l.nameDifferences??l.diferenciasNombreBanco??l.diferencias_nombre_banco,c.filter(N=>Tt(String(N.category??"")).includes("diferencia_nombre_banco")).length),T=Xn(l.possibleWrongAccounts??l.posiblesCuentasMalDigitadas??l.posibles_cuentas_mal_digitadas,c.filter(N=>Tt(String(N.category??"")).includes("posible_cuenta_mal_digitada")).length),k=y+v+x+_+S,R=a.metadata??i.metadata??{},M=String(R.period_type??R.periodType??"").toLowerCase();return{ok:!!(a.ok??i.ok??!0),reportKind:M==="bono14"?"bono14":"nomina",executionId:a.executionId||a.execution_id||i.executionId||i.execution_id?String(a.executionId??a.execution_id??i.executionId??i.execution_id):void 0,reportUrl:a.reportUrl||a.report_url||i.reportUrl||i.report_url||i.google_sheet_url?String(a.reportUrl??a.report_url??i.reportUrl??i.report_url??i.google_sheet_url):void 0,summary:{matches:p,discrepancies:y,bankWithoutPayroll:v,bankWithoutBamboo:x,payrollWithoutAccount:_,nameDifferences:S,possibleWrongAccounts:T,totalPending:k,totalRows:m.length,totalPayroll:Xn(l.totalNomina??l.total_nomina,void 0),totalBank:Xn(l.totalBanco??l.total_banco,void 0),totalDifference:Xn(l.diferenciaTotal??l.diferencia_total,void 0)},rows:m,message:a.message||i.message?String(a.message??i.message):void 0}}function OC(n){return n==="Coincidencia"?"bg-emerald-50 text-emerald-800 border-emerald-200":n==="Resuelto"?"bg-blue-50 text-blue-800 border-blue-200":n==="No encontrado"?"bg-amber-50 text-amber-800 border-amber-200":n==="Pendiente revisión"?"bg-neutral-100 text-neutral-700 border-neutral-300":"bg-red-50 text-[#ba1a1a] border-red-200"}function dv(n){const e=Tt(String(n.category??n.source??"")),i=Tt(n.observation??"");return n.status==="Coincidencia"||e.includes("coincidencia")?90:e.includes("posible_cuenta_mal_digitada")||i.includes("mal digitada")?1:e.includes("discrepancia")||n.status==="Riesgo"?2:e.includes("banco_sin_bamboo")?3:e.includes("banco_sin_nomina")?4:e.includes("diferencia_nombre_banco")?5:e.includes("nomina_sin_cuenta")?6:n.status==="Pendiente revisión"?7:n.status==="No encontrado"?10:20}function DC(n){return[...n].sort((e,i)=>{const a=dv(e)-dv(i);return a!==0?a:e.employee.localeCompare(i.employee,"es")})}function MC(n){const e=Tt(String(n.category??n.source??""));return e.includes("discrepancia")||e.includes("posible_cuenta_mal_digitada")}function Ua(n,e){return n.source===e||Tt(String(n.category??"")).includes(e)}function BC(n,e="nomina"){const i=Tt(String(n??""));return i?i.includes("posible_cuenta_mal_digitada")?"Posible cuenta mal digitada":i.includes("banco_sin_bamboo")?"Banco sin Bamboo":i.includes("banco_sin_nomina")?e==="bono14"?"Banco sin Bono 14":"Banco sin nómina":i.includes("diferencia_nombre_banco")?"Diferencia nombre banco":i.includes("nomina_sin_cuenta")?e==="bono14"?"Bono 14 sin cuenta":"Nómina sin cuenta":i.includes("discrepancia")?"Discrepancia":i.includes("coincidencia")?"Coincidencia":String(n).replace(/_/g," "):"—"}function fv(n){const e=n instanceof Error?n.message:"";return e.includes("planillas del banco repetidas")||e.toLowerCase().includes("sesión expiró")||e.toLowerCase().includes("no hay conexión a internet")?e:"No se pudo procesar el cruce. Verifica los archivos e intenta nuevamente."}function UC(n){try{return new TextDecoder("windows-1252").decode(n)}catch{return new TextDecoder("utf-8").decode(n)}}function mv(n,e){let i=0,a=!1;for(let l=0;lmv(n,a)>mv(n,i)?a:i)}function pv(n,e=Hw(n)){const i=[];let a="",l=!1;for(let c=0;cc.replace(/^\uFEFF/,"").trim())}function LC(n){const e=String(n??"").replace(/\D/g,"");return/^\d{1,20}$/.test(e)?e:null}function zC(n){const e=n.split(/\r?\n/).slice(0,120);for(const i of e.slice(0,25)){const l=Tt(i).match(/(?:consulta\s+(?:del\s+)?)?detalle\s+(?:del\s+)?envio\s+(\d{1,20})/);if(l!=null&&l[1])return l[1]}for(let i=0;iTt(m).trim()).findIndex(m=>m==="numero de envio"||m.includes("numero de envio"));if(!(d<0))for(let m=i+1;mi.length>1).map(([i,a])=>({shipmentNumber:i,fileNames:a}))}const VC={1:["enero","january","jan"],2:["febrero","february","feb"],3:["marzo","march","mar"],4:["abril","april","apr"],5:["mayo","may"],6:["junio","june","jun"],7:["julio","july","jul"],8:["agosto","august","aug"],9:["septiembre","setiembre","september","sep"],10:["octubre","october","oct"],11:["noviembre","november","nov"],12:["diciembre","december","dec"]};function HC(n){const e=Tt(n).replace(/[_\-.()]+/g," ").replace(/\s+/g," ").trim(),i=e.match(/\b(20\d{2})\b/),a=i?Number(i[1]):void 0;let l;for(const[d,m]of Object.entries(VC))if(m.some(p=>new RegExp(`\\b${p}\\b`,"i").test(e))){l=Number(d);break}const c=e.match(/\b(1[0-2]|[1-9])\s+(1|2)\s+(20\d{2})\b/);!l&&c&&(l=Number(c[1]));let u;return(c==null?void 0:c[2])==="1"?u="quincena_15":(c==null?void 0:c[2])==="2"?u="quincena_30":/\b(?:1q|q1|quincena\s*15|primera\s+quincena)\b/.test(e)?u="quincena_15":/\b(?:2q|q2|quincena\s*30|segunda\s+quincena|fin\s+de\s+mes)\b/.test(e)?u="quincena_30":/\b15\b/.test(e)?u="quincena_15":/\b(?:30|31)\b/.test(e)&&(u="quincena_30"),{year:a,month:l,periodType:u}}function yv(n){const e=NC();try{const i=window.localStorage.getItem(`glm_cruce_period_${n}`);if(!i)return e;const a=JSON.parse(i);if(Number.isInteger(a.year)&&Number.isInteger(a.month)&&a.month>=1&&a.month<=12&&(a.periodType==="quincena_15"||a.periodType==="quincena_30"))return a}catch{}return e}async function $C(n,e){const i=HC(n);try{const a=await fetch(TC,{method:"POST",headers:{"Content-Type":"application/json; charset=utf-8"},body:JSON.stringify({fileName:n,country:e.code,countryName:e.name})});if(!a.ok)return i;const l=await a.json().catch(()=>null),c=(l==null?void 0:l.detected)??(l==null?void 0:l.result)??(l==null?void 0:l.analysis)??l??{},u=String(c.periodType??c.period_type??""),d=Number(c.year)||i.year,m=Number(c.month)||i.month,p=u==="quincena_15"||u==="quincena_30"?u:i.periodType;return{year:d,month:m,periodType:p,confidence:Number.isFinite(Number(c.confidence))?Number(c.confidence):void 0,requiresManualReview:!!(c.requiresManualReview??c.requires_manual_review??!1),source:String(c.source??"gemini")}}catch{return{...i,source:"local_fallback",requiresManualReview:!(Number.isInteger(i.year)&&Number.isInteger(i.month)&&(i.periodType==="quincena_15"||i.periodType==="quincena_30"))}}}function qC({onShowToast:n,uploadedPayroll:e,setUploadedPayroll:i,uploadedPayrollFiles:a,setUploadedPayrollFiles:l,uploadedBankFiles:c,setUploadedBankFiles:u,isCruceExecuted:d,setIsCruceExecuted:m,response:p,setResponse:y,onOpenSidebar:v,operatorName:x,operatorEmail:_,resetToken:S=0,country:T}){const k=D.useMemo(()=>yv(T.code),[T.code]),R=T.code==="PA",M=T.code==="SV",N=T.code==="HN",P=T.code==="PR",G=R||M,X=P?"PDF":"CSV",q=P?".pdf":".csv",[Y,se]=D.useState(!1),[J,he]=D.useState(""),[fe,Ae]=D.useState("todos"),[ve,Ce]=D.useState(k.year),[L,K]=D.useState(k.month),[Z,pe]=D.useState(k.periodType),[ie,E]=D.useState(1),[H,W]=D.useState(""),[Q,oe]=D.useState(""),[re,_e]=D.useState(null),[ke,He]=D.useState([]),[en,jn]=D.useState(""),[Rs,is]=D.useState(null),[te,Be]=D.useState(()=>new Set),Ue=`glm_cruce_period_${T.code}`,xe=D.useRef(null),$e=D.useRef(null),kt=D.useRef(null),xt=D.useRef(null),Et=D.useMemo(()=>jC(),[]),Ht=D.useMemo(()=>cv(ve,L,Z),[ve,L,Z]),Nn=T.code==="GT"&&L===7&&Z==="quincena_15",We=D.useMemo(()=>RC(ve),[ve]),tn=(p==null?void 0:p.reportKind)==="bono14",Gt=tn?We.label:Ht.label,kn=D.useMemo(()=>DC((p==null?void 0:p.rows)??[]),[p==null?void 0:p.rows]),fn=D.useMemo(()=>{const C=Tt(Q.trim());return c.map((ee,ae)=>({file:ee,originalIndex:ae})).filter(({file:ee})=>!C||Tt(ee.name).includes(C))},[c,Q]),Rn=D.useMemo(()=>{const C=Tt(en.trim());return ke.map((ee,ae)=>({file:ee,originalIndex:ae})).filter(({file:ee})=>!C||Tt(ee.name).includes(C))},[ke,en]),Cs=kn.filter(C=>{const ee=Tt(H.trim());return Tt(`${C.employee} ${C.employeeNumber} ${C.account??""} ${C.observation} ${C.category??""} ${C.source??""}`).includes(ee)?fe==="discrepancias"?MC(C):fe==="coincidencias"?C.status==="Coincidencia":fe==="banco_sin_nomina"?Ua(C,"banco_sin_nomina"):fe==="banco_sin_bamboo"?Ua(C,"banco_sin_bamboo"):fe==="diferencia_nombre_banco"?Ua(C,"diferencia_nombre_banco"):fe==="nomina_sin_cuenta"?Ua(C,"nomina_sin_cuenta"):!0:!1}),Os=10,qn=Math.max(1,Math.ceil(Cs.length/Os)),as=Cs.slice((ie-1)*Os,ie*Os);D.useEffect(()=>{ie>qn&&E(qn)},[ie,qn]);const $t=D.useMemo(()=>{var C;return((C=rv.find(ee=>ee.value===Z))==null?void 0:C.description)??""},[Z]);D.useEffect(()=>{const C=yv(T.code);Ce(C.year),K(C.month),pe(C.periodType),E(1),W(""),oe(""),jn(""),_e(null),He([]),Ae("todos"),is(null),Be(new Set)},[T.code]),D.useEffect(()=>{window.localStorage.setItem(Ue,JSON.stringify({year:ve,month:L,periodType:Z}))},[Ue,ve,L,Z]),D.useEffect(()=>{W(""),oe(""),jn(""),_e(null),He([]),Ae("todos"),E(1),he(""),se(!1),is(null),Be(new Set),xe.current&&(xe.current.value=""),$e.current&&($e.current.value=""),kt.current&&(kt.current.value=""),xt.current&&(xt.current.value="")},[S]),D.useEffect(()=>{c.length===0&&Q&&oe("")},[c.length,Q]),D.useEffect(()=>{ke.length===0&&en&&jn("")},[ke.length,en]);const et=()=>{m(!1),y(null),E(1),W(""),Ae("todos")},Lt=async(C,ee=1)=>{const ae=await $C(C.name,T);if(Number.isInteger(ae.year)&&Number.isInteger(ae.month)&&(ae.periodType==="quincena_15"||ae.periodType==="quincena_30")){Ce(ae.year),K(ae.month),pe(ae.periodType);const Rt=cv(ae.year,ae.month,ae.periodType),st=ee>1?`${ee} archivos de nómina cargados.`:"Nómina cargada.";return ae.requiresManualReview?n(`${st} Período sugerido: ${Rt.label}. Confirma los datos antes de ejecutar.`,"info"):n(`${st} Período detectado: ${Rt.label}.`,"success"),!0}return!1},ko=async C=>{i({name:C.name,size:zc(C.size),type:"payroll",rawFile:C}),l([]),et(),await Lt(C)||n("Archivo de nómina cargado con éxito.","success")},Ro=async C=>{const ee=C.filter(Yr);if(ee.length===0){n(`Formato inválido. ${T.name} requiere uno o varios archivos Excel (.xlsx, .xls).`,"error");return}const ae=new Set(a.map(at=>{var it;return`${at.name}|${((it=at.rawFile)==null?void 0:it.size)??at.size}`})),nt=kC(ee).filter(at=>{var it;return!ae.has(`${at.name}|${((it=at.rawFile)==null?void 0:it.size)??at.size}`)}),Rt=[...a,...nt];l(Rt),i(null),et();const st=ee.find(at=>/quincenal/i.test(at.name))||ee[0];await Lt(st,Rt.length)||n(`${nt.length} archivo(s) de nómina de ${T.name} cargado(s) con éxito.`,"success")},ui=C=>{C.preventDefault()},hi=async C=>{C.preventDefault();const ee=Array.from(C.dataTransfer.files);if(ee.length===0)return;if(G){await Ro(ee);return}const ae=ee[0];Yr(ae)?await ko(ae):n("Formato inválido. Por favor carga un archivo Excel (.xlsx, .xls).","error")},Hi=C=>{C.preventDefault();const ae=Array.from(C.dataTransfer.files).filter(P?ov:Dd);if(ae.length===0){n(`Formato inválido. Por favor carga uno o varios archivos ${X} (${q}).`,"error");return}u([...c,...Md(ae)]),et(),n(`${ae.length} archivo(s) del banco cargado(s) con éxito.`,"success")},$i=async C=>{const ee=Array.from(C.target.files||[]);if(ee.length===0)return;if(G){await Ro(ee);return}const ae=ee[0];Yr(ae)?await ko(ae):n("Formato inválido. Por favor carga un archivo Excel (.xlsx, .xls).","error")},rs=C=>{l(a.filter((ee,ae)=>ae!==C)),xe.current&&(xe.current.value=""),et(),n("Archivo de nómina removido.","info")},qi=C=>{const ee=C.target.files;if(!ee||ee.length===0)return;const ae=Array.from(ee).filter(P?ov:Dd);if(ae.length===0){n(`Formato inválido. Por favor carga uno o varios archivos ${X} (${q}).`,"error");return}u([...c,...Md(ae)]),et(),n(`${ae.length} archivo(s) del banco cargado(s) con éxito.`,"success")},di=C=>{u(c.filter((ee,ae)=>ae!==C)),$e.current&&($e.current.value=""),et(),n("Planilla de banco removida.","info")},Xc=()=>{i(null),l([]),u([]),oe(""),xe.current&&(xe.current.value=""),$e.current&&($e.current.value=""),et(),n("Todos los archivos cargados fueron eliminados.","info")},Za=C=>{_e({name:C.name,size:zc(C.size),type:"payroll",rawFile:C}),et(),n("Archivo Excel de Bono 14 cargado con éxito.","success")},Wa=C=>{C.preventDefault();const ee=C.dataTransfer.files[0];if(ee){if(!Yr(ee)){n("Formato inválido. El Bono 14 requiere un archivo Excel (.xlsx, .xls).","error");return}Za(ee)}},fi=C=>{var ae;const ee=(ae=C.target.files)==null?void 0:ae[0];if(ee){if(!Yr(ee)){n("Formato inválido. El Bono 14 requiere un archivo Excel (.xlsx, .xls).","error");return}Za(ee)}},Co=C=>{const ee=C.filter(Dd);if(ee.length===0){n("Formato inválido. Carga uno o varios archivos CSV del Bono 14.","error");return}He([...ke,...Md(ee)]),et(),n(`${ee.length} archivo(s) bancario(s) de Bono 14 cargado(s).`,"success")},Oo=C=>{C.preventDefault(),Co(Array.from(C.dataTransfer.files))},Do=C=>{var ee;(ee=C.target.files)!=null&&ee.length&&Co(Array.from(C.target.files))},Mo=C=>{He(ke.filter((ee,ae)=>ae!==C)),xt.current&&(xt.current.value=""),et(),n("Archivo bancario de Bono 14 removido.","info")},er=async()=>{if(Y)return;if(!ve||!L||!Z){n("Debes seleccionar año, mes y tipo de cruce antes de ejecutar.","error");return}const C=G?a:e?[e]:[];if(C.length===0||C.some(ee=>!ee.rawFile)){n(G?`Debes cargar todos los archivos de nómina de ${T.name} correspondientes al período antes de ejecutar el cruce.`:"Debes cargar la nómina antes de ejecutar el cruce.","error");return}if(c.length===0||c.some(ee=>!ee.rawFile)){n("Debes cargar al menos una planilla del banco antes de ejecutar el cruce.","error");return}se(!0),he("Validando archivos..."),E(1);try{const ee=P?[]:await gv(c);if(ee.length>0){const Le=ee.map(zt=>`Número de envío ${zt.shipmentNumber}: ${zt.fileNames.join(", ")}`).join(` +`);throw new Error(`Hay planillas del banco repetidas. Revisa los archivos cargados antes de continuar. +${Le}`)}const ae=EC(T.code);if(!ae)throw new Error("No se pudo procesar el cruce. El servicio de conciliación no está disponible.");const nt=iv();he("Revalidando sesión..."),await So(300),he("Reactivando conexión segura..."),await sv(ae);const Rt=av(ae,nt),st=C.find(Le=>/quincenal/i.test(Le.name))||C[0],Cn={country:T.code,country_name:T.name,year:ve,month:L,period_type:Z,period_label:Ht.label,period_start:Ht.start,period_end:Ht.end,payroll_file_name:st.name,payroll_file_names:C.map(Le=>Le.name),bank_file_names:c.map(Le=>Le.name),requested_by_name:x,requested_by_email:_,auth_mode:"supabase_google",source_app:"portal-cruce-cuentas-glm",client_request_id:nt};he("Procesando cruce de cuentas...");const at=new FormData;at.append("metadata",JSON.stringify(Cn)),Object.entries(Cn).forEach(([Le,zt])=>{at.append(Le,Array.isArray(zt)?JSON.stringify(zt):String(zt))}),G&&C.forEach((Le,zt)=>{Le.rawFile&&(at.append("payroll_files",Le.rawFile,Le.name),at.append(`payroll_file_${zt+1}`,Le.rawFile,Le.name))}),st.rawFile&&at.append("payroll_file",st.rawFile,st.name),c.forEach((Le,zt)=>{Le.rawFile&&(at.append("bank_files",Le.rawFile,Le.name),at.append(`bank_file_${zt+1}`,Le.rawFile,Le.name))});const it=await fetch(Rt,{method:"POST",body:at,cache:"no-store",credentials:"omit"});he("Generando reporte...");const Ge=(it.headers.get("content-type")??"").includes("application/json")?await it.json():{ok:it.ok,message:await it.text()};if(!it.ok||(Ge==null?void 0:Ge.ok)===!1)throw new Error(typeof(Ge==null?void 0:Ge.message)=="string"?Ge.message:"No se pudo procesar el cruce. Verifica los archivos e intenta nuevamente.");const wt=hv(Ge);wt.reportKind="nomina",y(wt),m(!0),n(wt.message||"Cruce procesado correctamente.","success"),setTimeout(()=>{const Le=document.getElementById("resultados-seccion");Le&&Le.scrollIntoView({behavior:"smooth",block:"start"})},150)}catch(ee){const ae=fv(ee);m(!1),y(null),n(ae,"error")}finally{se(!1),he("")}},tr=async()=>{if(!Y){if(!Nn){n("El cruce de Bono 14 solo está disponible en Guatemala para julio · Quincena 15.","error");return}if(!(re!=null&&re.rawFile)){n("Debes cargar el Excel de Bono 14 antes de generar el reporte.","error");return}if(ke.length===0||ke.some(C=>!C.rawFile)){n("Debes cargar al menos un CSV bancario del Bono 14.","error");return}se(!0),he("Validando archivos de Bono 14..."),E(1);try{const C=await gv(ke);if(C.length>0){const Ge=C.map(wt=>`Número de envío ${wt.shipmentNumber}: ${wt.fileNames.join(", ")}`).join(` +`);throw new Error(`Hay planillas del banco repetidas. Revisa los archivos cargados antes de continuar. +${Ge}`)}const ee=AC(),ae=iv();he("Revalidando sesión..."),await So(300),he("Reactivando conexión segura..."),await sv(ee);const nt=av(ee,ae),Rt={country:"GT",country_name:"Guatemala",year:ve,month:7,period_type:"bono14",report_kind:"bono14",period_label:We.label,period_start:We.start,period_end:We.end,payroll_file_name:re.name,bank_file_names:ke.map(Ge=>Ge.name),requested_by_name:x,requested_by_email:_,auth_mode:"supabase_google",source_app:"portal-cruce-cuentas-glm-bono14",client_request_id:ae},st=new FormData;st.append("metadata",JSON.stringify(Rt)),Object.entries(Rt).forEach(([Ge,wt])=>{st.append(Ge,Array.isArray(wt)?JSON.stringify(wt):String(wt))}),st.append("payroll_file",re.rawFile,re.name),ke.forEach((Ge,wt)=>{Ge.rawFile&&(st.append("bank_files",Ge.rawFile,Ge.name),st.append(`bank_file_${wt+1}`,Ge.rawFile,Ge.name))}),he("Procesando Bono 14...");const Cn=await fetch(nt,{method:"POST",body:st,cache:"no-store",credentials:"omit"});he("Generando reporte independiente de Bono 14...");const it=(Cn.headers.get("content-type")??"").includes("application/json")?await Cn.json():{ok:Cn.ok,message:await Cn.text()};if(!Cn.ok||(it==null?void 0:it.ok)===!1)throw new Error(typeof(it==null?void 0:it.message)=="string"?it.message:"No se pudo procesar el Bono 14. Verifica los archivos e intenta nuevamente.");const Fn=hv(it);Fn.reportKind="bono14",y(Fn),m(!0),n(Fn.message||"Bono 14 procesado correctamente.","success"),setTimeout(()=>{const Ge=document.getElementById("resultados-seccion");Ge&&Ge.scrollIntoView({behavior:"smooth",block:"start"})},150)}catch(C){m(!1),y(null),n(fv(C),"error")}finally{se(!1),he("")}}},Bo=()=>{if(p!=null&&p.reportUrl){window.open(p.reportUrl,"_blank","noopener,noreferrer");return}n("El reporte aún no tiene un enlace disponible.","info")};return g.jsxs("div",{className:"flex-1 flex flex-col min-h-screen bg-neutral-50 text-neutral-800 font-sans",children:[g.jsxs("header",{className:"bg-white border-b-4 border-[#6CC24A] flex justify-between items-center w-full px-4 sm:px-8 h-[84px] sticky top-0 z-40 shadow-sm gap-x-4",children:[g.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 min-w-0",children:[g.jsx("button",{onClick:v,className:"md:hidden p-2 -ml-2 text-neutral-600 hover:text-neutral-800 hover:bg-neutral-100 transition-colors flex-shrink-0","aria-label":"Abrir menú",children:g.jsx(dw,{className:"w-6 h-6"})}),g.jsx("h1",{className:"text-xs sm:text-sm md:text-lg lg:text-2xl font-black text-[#4F758B] tracking-tight uppercase flex items-center gap-1.5 sm:gap-2 min-w-0",children:g.jsxs("span",{className:"truncate",children:["Cruce de Cuentas GLM - ",T.name]})})]}),g.jsxs("div",{className:"flex-shrink-0 flex items-center gap-2",children:[g.jsx(Pw,{country:T,operatorName:x,operatorEmail:_,section:"Cruce de Cuentas",onShowToast:n}),g.jsx("div",{className:"border border-neutral-200 bg-white px-2.5 py-2 shadow-sm","aria-label":`Bandera de ${T.name}`,children:g.jsx("img",{src:T.flag,alt:`Bandera de ${T.name}`,className:"h-5 sm:h-6 w-auto object-contain"})})]})]}),g.jsxs("main",{className:"flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 py-8 flex flex-col gap-8",children:[g.jsxs("section",{className:"bg-white border border-[#D0D0D0] p-5 shadow-sm",children:[g.jsx("div",{className:"flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-5",children:g.jsxs("div",{className:"flex items-center gap-3",children:[g.jsx("div",{className:"bg-[#4F758B]/10 p-2 rounded-full",children:g.jsx(hw,{className:"w-5 h-5 text-[#4F758B]"})}),g.jsx("div",{className:"pt-0.5",children:g.jsx("h2",{className:"text-sm font-black text-[#4F758B] uppercase tracking-wider leading-none",children:"Datos del cruce"})})]})}),g.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[g.jsxs("label",{className:"flex flex-col gap-1.5",children:[g.jsx("span",{className:"text-[10px] font-black uppercase tracking-wider text-neutral-500",children:"Año"}),g.jsx("select",{value:ve,onChange:C=>{Ce(Number(C.target.value)),et()},className:"border border-[#D0D0D0] bg-white px-3 py-2.5 text-sm font-bold text-neutral-700 focus:outline-none focus:border-[#4F758B]",children:Et.map(C=>g.jsx("option",{value:C,children:C},C))})]}),g.jsxs("label",{className:"flex flex-col gap-1.5",children:[g.jsx("span",{className:"text-[10px] font-black uppercase tracking-wider text-neutral-500",children:"Mes"}),g.jsx("select",{value:L,onChange:C=>{K(Number(C.target.value)),et()},className:"border border-[#D0D0D0] bg-white px-3 py-2.5 text-sm font-bold text-neutral-700 focus:outline-none focus:border-[#4F758B]",children:Vw.map(C=>g.jsx("option",{value:C.value,children:C.label},C.value))})]}),g.jsxs("label",{className:"flex flex-col gap-1.5",children:[g.jsx("span",{className:"text-[10px] font-black uppercase tracking-wider text-neutral-500",children:"Tipo de cruce"}),g.jsx("select",{value:Z,onChange:C=>{pe(C.target.value),et()},className:"border border-[#D0D0D0] bg-white px-3 py-2.5 text-sm font-bold text-neutral-700 focus:outline-none focus:border-[#4F758B]",children:rv.map(C=>g.jsx("option",{value:C.value,children:C.label},C.value))})]})]}),g.jsxs("div",{className:"mt-4 grid grid-cols-1 md:grid-cols-[1fr_auto] gap-3 items-center bg-[#4F758B]/5 border border-[#4F758B]/10 px-4 py-3",children:[g.jsxs("div",{className:"flex items-start gap-2 text-xs text-neutral-600",children:[g.jsx(bj,{className:"w-4 h-4 text-[#4F758B] mt-0.5 flex-shrink-0"}),g.jsxs("div",{children:[g.jsx("span",{className:"font-black text-[#4F758B]",children:"Período calculado:"})," ",lv(Ht.start)," al ",lv(Ht.end),g.jsx("p",{className:"text-[10px] text-neutral-500 mt-0.5",children:$t})]})]}),g.jsx("div",{className:"text-[10px] font-bold text-neutral-500 uppercase tracking-wider",children:Ht.label})]})]}),(G||N||P)&&(e!==null||a.length>0||c.length>0)&&g.jsx("div",{className:"flex justify-end",style:{marginBottom:"-20px"},children:g.jsxs("button",{type:"button",onClick:Xc,className:"inline-flex items-center gap-2 border border-red-100 bg-white px-3 py-2 text-[10px] font-black text-red-600 hover:bg-red-50 transition-colors uppercase tracking-wide shadow-sm","aria-label":"Borrar todos los archivos cargados",title:"Eliminar de una vez todas las nóminas y planillas del banco cargadas",children:[g.jsx(Ea,{className:"w-4 h-4"}),"Borrar todo"]})}),g.jsxs("section",{className:"grid grid-cols-1 lg:grid-cols-2 gap-6",children:[g.jsxs("div",{className:"bg-white border border-[#D0D0D0] p-6 flex flex-col justify-between relative shadow-sm hover:shadow-md transition-shadow",children:[g.jsx("div",{className:"absolute top-0 left-0 w-full h-1 bg-[#4F758B]"}),g.jsxs("div",{children:[g.jsxs("div",{className:"flex items-center gap-2.5 mb-4",children:[g.jsx(Oa,{className:"w-5 h-5 text-[#4F758B]"}),g.jsx("h2",{className:"text-sm font-black text-neutral-800 uppercase tracking-wider",children:"1. Carga de Nómina"})]}),g.jsx("input",{type:"file",ref:xe,onChange:$i,accept:".xlsx,.xls",multiple:G,className:"hidden"}),G?a.length>0?g.jsxs("div",{className:"space-y-3",children:[g.jsxs("div",{className:"border border-[#4F758B]/20 bg-[#4F758B]/5 px-4 py-3",children:[g.jsxs("p",{className:"text-[10px] font-black uppercase tracking-wider text-[#4F758B]",children:[a.length," archivo(s) de nómina cargado(s)"]}),g.jsx("p",{className:"text-[10px] text-neutral-500 mt-1",children:R?"Incluye la quincenal y todas las planillas extraordinarias, vacaciones o XIII Mes que correspondan al período.":"Incluye todos los archivos de nómina de El Salvador que correspondan a la quincena seleccionada (General, Karla Elias, Nestlé, Motorola u otros del mismo formato)."})]}),g.jsx("div",{className:"max-h-[260px] overflow-y-auto border border-neutral-200 divide-y divide-neutral-100",children:a.map((C,ee)=>g.jsxs("div",{className:"bg-white px-4 py-3 flex items-center justify-between gap-3",children:[g.jsxs("div",{className:"flex items-center gap-3 min-w-0",children:[g.jsx(Oa,{className:"w-6 h-6 text-[#6CC24A] flex-shrink-0"}),g.jsxs("div",{className:"overflow-hidden",children:[g.jsx("p",{className:"text-xs font-bold text-neutral-700 truncate max-w-[290px]",title:C.name,children:C.name}),g.jsxs("p",{className:"text-[10px] text-neutral-400 font-mono mt-0.5",children:[C.size," • Excel"]})]})]}),g.jsx("button",{onClick:()=>rs(ee),className:"p-1.5 text-neutral-400 hover:text-red-500 hover:bg-neutral-100 transition-all rounded","aria-label":`Remover ${C.name}`,children:g.jsx(Ea,{className:"w-4.5 h-4.5"})})]},`${C.name}-${ee}`))}),g.jsx("button",{type:"button",onClick:()=>{var C;return(C=xe.current)==null?void 0:C.click()},className:"w-full border border-dashed border-[#4F758B]/40 bg-white px-4 py-2.5 text-[10px] font-black text-[#4F758B] hover:bg-[#4F758B]/5 transition-colors uppercase tracking-wide",children:"Agregar más archivos de nómina"})]}):g.jsxs("div",{onDragOver:ui,onDrop:hi,onClick:()=>{var C;return(C=xe.current)==null?void 0:C.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-neutral-50 hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors flex flex-col items-center justify-center py-10 px-4 text-center cursor-pointer min-h-[190px]",children:[g.jsx(Gr,{className:"w-8 h-8 text-neutral-300 mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre la nómina y sus suplementos"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1 font-medium",children:"Puede seleccionar varios .xlsx / .xls a la vez"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivos"})]}):e?g.jsxs("div",{className:"border border-[#6CC24A]/30 bg-[#6CC24A]/5 p-5 flex items-center justify-between gap-3",children:[g.jsxs("div",{className:"flex items-center gap-3 min-w-0",children:[g.jsx(Oa,{className:"w-8 h-8 text-[#6CC24A] flex-shrink-0"}),g.jsxs("div",{className:"overflow-hidden",children:[g.jsx("p",{className:"text-xs font-bold text-neutral-700 truncate max-w-[300px]",title:e.name,children:e.name}),g.jsxs("p",{className:"text-[10px] text-neutral-400 font-mono mt-0.5",children:[e.size," • Excel"]})]})]}),g.jsx("button",{onClick:()=>{i(null),xe.current&&(xe.current.value=""),et(),n("Nómina removida.","info")},className:"p-1.5 text-neutral-400 hover:text-red-500 hover:bg-neutral-100 transition-all rounded","aria-label":"Remover nómina",children:g.jsx(Ea,{className:"w-4.5 h-4.5"})})]}):g.jsxs("div",{onDragOver:ui,onDrop:hi,onClick:()=>{var C;return(C=xe.current)==null?void 0:C.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-neutral-50 hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors flex flex-col items-center justify-center py-10 px-4 text-center cursor-pointer min-h-[190px]",children:[g.jsx(Gr,{className:"w-8 h-8 text-neutral-300 mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre y suelte archivo Excel"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1 font-medium",children:"Formato .xlsx, .xls"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivo"})]})]})]}),g.jsxs("div",{className:"bg-white border border-[#D0D0D0] p-6 flex flex-col justify-between relative shadow-sm hover:shadow-md transition-shadow",children:[g.jsx("div",{className:"absolute top-0 left-0 w-full h-1 bg-[#4F758B]"}),g.jsxs("div",{children:[g.jsxs("div",{className:"flex items-center gap-2.5 mb-2",children:[g.jsx(Wl,{className:"w-5 h-5 text-[#4F758B]"}),g.jsx("h2",{className:"text-sm font-black text-neutral-800 uppercase tracking-wider",children:"2. Carga de Planillas del Banco"})]}),g.jsx("p",{className:"text-[11px] text-neutral-500 mb-4 leading-relaxed",children:"Puede subir múltiples archivos separados o un archivo consolidado del período."}),g.jsx("input",{type:"file",ref:$e,onChange:qi,accept:P?".pdf":".csv",multiple:!0,className:"hidden"}),g.jsxs("div",{onDragOver:ui,onDrop:Hi,onClick:()=>{var C;return(C=$e.current)==null?void 0:C.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-neutral-50 hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors flex flex-col items-center justify-center py-8 px-4 text-center cursor-pointer min-h-[130px]",children:[g.jsx(Gr,{className:"w-7 h-7 text-neutral-300 mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:`Arrastre y suelte archivo ${X}`}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1 font-medium",children:`Formato ${q}`}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivos"})]}),c.length>0&&g.jsxs("div",{className:"mt-4 border border-neutral-200 bg-white",children:[g.jsxs("div",{className:"p-3 border-b border-neutral-200 bg-neutral-50",children:[g.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-2",children:[g.jsxs("div",{children:[g.jsxs("p",{className:"text-[11px] font-black text-neutral-700 uppercase tracking-wide",children:[c.length," ",c.length===1?"archivo cargado":"archivos cargados"]}),g.jsx("p",{className:"text-[9px] text-neutral-400 mt-0.5",children:"Busque por nombre para localizar y eliminar una planilla rápidamente."})]}),Q&&g.jsx("button",{type:"button",onClick:C=>{C.stopPropagation(),oe("")},className:"self-start sm:self-auto text-[9px] font-bold uppercase tracking-wide text-[#4F758B] hover:underline",children:"Limpiar búsqueda"})]}),g.jsxs("div",{className:"relative",children:[g.jsx(Jr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-neutral-400 pointer-events-none"}),g.jsx("input",{type:"search",value:Q,onChange:C=>oe(C.target.value),onClick:C=>C.stopPropagation(),placeholder:`Buscar archivo ${X} por nombre...`,className:"w-full h-9 pl-9 pr-3 border border-[#D0D0D0] bg-white text-[11px] text-neutral-700 outline-none focus:border-[#4F758B] focus:ring-2 focus:ring-[#4F758B]/10 transition-all","aria-label":`Buscar archivos ${X} cargados`})]}),Q&&g.jsxs("p",{className:"text-[9px] text-neutral-400 mt-2",children:["Mostrando ",fn.length," de ",c.length," archivos."]})]}),g.jsx("div",{className:"max-h-[330px] overflow-y-auto overscroll-contain p-2 space-y-2",children:fn.length>0?fn.map(({file:C,originalIndex:ee})=>g.jsxs("div",{className:"border border-neutral-200 bg-white px-3 py-3 flex items-center justify-between gap-3 hover:border-[#4F758B]/40 hover:bg-[#4F758B]/[0.025] transition-colors",children:[g.jsxs("div",{className:"flex items-start gap-3 min-w-0 flex-1",children:[g.jsx("div",{className:"w-7 h-7 mt-0.5 border border-[#4F758B]/20 bg-[#4F758B]/5 flex items-center justify-center flex-shrink-0",children:g.jsx(Wl,{className:"w-4 h-4 text-[#4F758B]"})}),g.jsxs("div",{className:"min-w-0 flex-1",children:[g.jsx("p",{className:"text-[11px] leading-4 font-bold text-neutral-700 whitespace-normal break-words",title:C.name,children:C.name}),g.jsxs("div",{className:"flex items-center gap-2 mt-1",children:[g.jsx("span",{className:"text-[9px] text-neutral-400 font-mono",children:C.size}),g.jsx("span",{className:"text-[8px] font-bold uppercase tracking-wide text-[#4F758B] bg-[#4F758B]/5 border border-[#4F758B]/10 px-1.5 py-0.5",children:X})]})]})]}),g.jsx("button",{type:"button",onClick:ae=>{ae.stopPropagation(),di(ee)},className:"w-8 h-8 flex items-center justify-center flex-shrink-0 text-neutral-400 hover:text-red-600 hover:bg-red-50 border border-transparent hover:border-red-100 rounded transition-colors","aria-label":`Remover ${C.name}`,title:`Eliminar ${C.name}`,children:g.jsx(Ea,{className:"w-4 h-4"})})]},`${C.name}-${ee}`)):g.jsxs("div",{className:"py-8 px-4 text-center",children:[g.jsx(Jr,{className:"w-5 h-5 text-neutral-300 mx-auto mb-2"}),g.jsx("p",{className:"text-[11px] font-bold text-neutral-600",children:"No encontramos archivos con ese nombre."}),g.jsx("p",{className:"text-[9px] text-neutral-400 mt-1",children:"Pruebe con otra parte del nombre del archivo."})]})})]})]})]})]}),Nn&&g.jsx("section",{className:"bg-white border border-[#D0D0D0] shadow-sm",children:g.jsxs("div",{className:"border-t-4 border-[#6CC24A] p-5 sm:p-6",children:[g.jsxs("div",{className:"flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4 mb-5",children:[g.jsxs("div",{className:"flex items-start gap-3",children:[g.jsx("div",{className:"bg-[#6CC24A]/10 p-2 rounded-full",children:g.jsx(wb,{className:"w-5 h-5 text-[#4F758B]"})}),g.jsxs("div",{children:[g.jsx("h2",{className:"text-sm font-black text-[#4F758B] uppercase tracking-wider",children:"Bono 14 · Reporte independiente"}),g.jsx("p",{className:"text-[11px] text-neutral-500 mt-1 max-w-3xl leading-relaxed",children:"Esta sección aparece únicamente para Guatemala · Julio · Quincena 15. El Bono 14 se procesa separado de la nómina quincenal y genera su propio Google Sheet e histórico."})]})]}),g.jsxs("div",{className:"text-left lg:text-right",children:[g.jsx("p",{className:"text-[9px] font-black uppercase tracking-wider text-neutral-400",children:"Período del Bono 14"}),g.jsxs("p",{className:"text-[11px] font-bold text-[#4F758B] mt-1",children:[We.start," al ",We.end]})]})]}),g.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-5",children:[g.jsxs("div",{className:"border border-neutral-200 p-4 bg-neutral-50/50",children:[g.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[g.jsx(Oa,{className:"w-4.5 h-4.5 text-[#4F758B]"}),g.jsx("h3",{className:"text-xs font-black uppercase tracking-wider text-neutral-700",children:"1. Excel de Bono 14"})]}),g.jsx("p",{className:"text-[10px] text-neutral-500 mb-3",children:"Archivo anual con los montos calculados de Bono 14."}),g.jsx("input",{type:"file",ref:kt,onChange:fi,accept:".xlsx,.xls",className:"hidden"}),re?g.jsxs("div",{className:"border border-neutral-200 bg-white px-3 py-3 flex items-center justify-between gap-3",children:[g.jsxs("div",{className:"min-w-0",children:[g.jsx("p",{className:"text-[11px] font-bold text-neutral-700 break-words",title:re.name,children:re.name}),g.jsxs("p",{className:"text-[9px] text-neutral-400 font-mono mt-1",children:[re.size," · Excel"]})]}),g.jsx("button",{type:"button",onClick:()=>{_e(null),kt.current&&(kt.current.value=""),et(),n("Excel de Bono 14 removido.","info")},className:"w-8 h-8 flex items-center justify-center text-neutral-400 hover:text-red-600 hover:bg-red-50 border border-transparent hover:border-red-100 rounded transition-colors","aria-label":"Remover Excel de Bono 14",children:g.jsx(Ea,{className:"w-4 h-4"})})]}):g.jsxs("div",{onDragOver:ui,onDrop:Wa,onClick:()=>{var C;return(C=kt.current)==null?void 0:C.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-white hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors py-8 px-4 text-center cursor-pointer",children:[g.jsx(Gr,{className:"w-7 h-7 text-neutral-300 mx-auto mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre el Excel de Bono 14"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1",children:"Formato .xlsx, .xls"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivo"})]})]}),g.jsxs("div",{className:"border border-neutral-200 p-4 bg-neutral-50/50",children:[g.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[g.jsx(Wl,{className:"w-4.5 h-4.5 text-[#4F758B]"}),g.jsx("h3",{className:"text-xs font-black uppercase tracking-wider text-neutral-700",children:"2. CSV bancarios del Bono 14"})]}),g.jsx("p",{className:"text-[10px] text-neutral-500 mb-3",children:"Puede cargar todos los envíos bancarios correspondientes al Bono 14."}),g.jsx("input",{type:"file",ref:xt,onChange:Do,accept:".csv",multiple:!0,className:"hidden"}),g.jsxs("div",{onDragOver:ui,onDrop:Oo,onClick:()=>{var C;return(C=xt.current)==null?void 0:C.click()},className:"border-2 border-dashed border-[#D0D0D0] bg-white hover:bg-[#4F758B]/5 hover:border-[#4F758B]/40 transition-colors py-6 px-4 text-center cursor-pointer",children:[g.jsx(Gr,{className:"w-6 h-6 text-neutral-300 mx-auto mb-2"}),g.jsx("p",{className:"text-xs font-bold text-neutral-700",children:"Arrastre uno o varios CSV"}),g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1",children:"Formato .csv"}),g.jsx("button",{type:"button",className:"mt-4 border border-[#D0D0D0] bg-white px-4 py-1.5 text-[10px] font-bold text-neutral-600 hover:border-[#4F758B] hover:text-[#4F758B] transition-colors shadow-sm uppercase tracking-wide",children:"Seleccionar Archivos"})]}),ke.length>0&&g.jsxs("div",{className:"mt-3 border border-neutral-200 bg-white",children:[g.jsxs("div",{className:"p-3 border-b border-neutral-200 bg-neutral-50",children:[g.jsxs("div",{className:"relative",children:[g.jsx(Jr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-neutral-400 pointer-events-none"}),g.jsx("input",{type:"search",value:en,onChange:C=>jn(C.target.value),onClick:C=>C.stopPropagation(),placeholder:"Buscar CSV de Bono 14...",className:"w-full h-9 pl-9 pr-3 border border-[#D0D0D0] bg-white text-[11px] text-neutral-700 outline-none focus:border-[#4F758B]"})]}),g.jsxs("p",{className:"text-[9px] text-neutral-400 mt-2",children:[ke.length," archivo(s) cargado(s)."]})]}),g.jsx("div",{className:"max-h-[250px] overflow-y-auto p-2 space-y-2",children:Rn.map(({file:C,originalIndex:ee})=>g.jsxs("div",{className:"border border-neutral-200 px-3 py-2.5 flex items-start justify-between gap-3",children:[g.jsxs("div",{className:"min-w-0",children:[g.jsx("p",{className:"text-[10px] leading-4 font-bold text-neutral-700 break-words",children:C.name}),g.jsx("p",{className:"text-[9px] text-neutral-400 font-mono mt-0.5",children:C.size})]}),g.jsx("button",{type:"button",onClick:ae=>{ae.stopPropagation(),Mo(ee)},className:"w-8 h-8 flex items-center justify-center flex-shrink-0 text-neutral-400 hover:text-red-600 hover:bg-red-50 rounded transition-colors","aria-label":`Remover ${C.name}`,children:g.jsx(Ea,{className:"w-4 h-4"})})]},`bonus14-${C.name}-${ee}`))})]})]})]}),g.jsxs("div",{className:"mt-5 flex flex-col items-center gap-2",children:[g.jsxs("button",{type:"button",onClick:tr,disabled:Y||!(re!=null&&re.rawFile)||ke.length===0,className:"inline-flex items-center justify-center gap-2 bg-[#4F758B] hover:bg-[#3d6378] text-white px-7 py-3 text-xs font-black uppercase tracking-wider transition-colors disabled:opacity-45 disabled:cursor-not-allowed min-w-[280px]",children:[Y?g.jsx($a,{className:"w-4 h-4 animate-spin"}):g.jsx(wb,{className:"w-4 h-4"}),"Generar reporte Bono 14"]}),g.jsxs("p",{className:"text-[9px] text-neutral-400 text-center",children:["Se guardará como un reporte separado: GT · ",ve," · Bono 14."]})]})]})}),g.jsx("section",{className:"flex flex-col items-center gap-3",children:g.jsx("button",{onClick:er,disabled:Y,className:"w-full sm:w-auto min-w-[280px] bg-[#4F758B] hover:bg-[#41677b] disabled:bg-neutral-300 disabled:cursor-not-allowed text-white px-8 py-4 font-black text-sm uppercase tracking-wider shadow-md flex items-center justify-center gap-3 transition-all duration-150 active:scale-[0.99] border-l-4 border-[#6CC24A]",children:Y?g.jsxs(g.Fragment,{children:[g.jsx($a,{className:"w-5 h-5 animate-spin"}),g.jsx("span",{children:J||"Procesando cruce de cuentas..."})]}):g.jsxs(g.Fragment,{children:[g.jsx(Jj,{className:"w-5 h-5"}),g.jsx("span",{children:"Ejecutar Cruce de Cuentas"})]})})}),g.jsx(si,{children:d&&p&&g.jsxs(un.section,{id:"resultados-seccion",initial:{opacity:0,y:20},animate:{opacity:1,y:0},exit:{opacity:0,y:10},className:"flex flex-col gap-5 scroll-mt-28",children:[g.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 gap-4",children:[g.jsxs("div",{className:"bg-white border border-emerald-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-emerald-700",children:[g.jsx(kc,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Coincidencias"})]}),g.jsx("p",{className:"text-3xl font-black text-emerald-800 mt-2",children:p.summary.matches})]}),g.jsxs("div",{className:"bg-white border border-red-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-[#ba1a1a]",children:[g.jsx(fo,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Discrepancias"})]}),g.jsx("p",{className:"text-3xl font-black text-[#ba1a1a] mt-2",children:p.summary.discrepancies})]}),g.jsxs("div",{className:"bg-white border border-amber-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-amber-800",children:[g.jsx(xb,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:tn?"Banco sin Bono 14":"Banco sin nómina"})]}),g.jsx("p",{className:"text-3xl font-black text-amber-800 mt-2",children:p.summary.bankWithoutPayroll})]}),g.jsxs("div",{className:"bg-white border border-orange-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-orange-800",children:[g.jsx(xb,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Banco sin Bamboo"})]}),g.jsx("p",{className:"text-3xl font-black text-orange-800 mt-2",children:p.summary.bankWithoutBamboo})]}),g.jsxs("div",{className:"bg-white border border-violet-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-violet-800",children:[g.jsx(Wl,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:"Diferencias nombre banco"})]}),g.jsx("p",{className:"text-3xl font-black text-violet-800 mt-2",children:p.summary.nameDifferences})]}),g.jsxs("div",{className:"bg-white border border-blue-200 p-4 shadow-sm",children:[g.jsxs("div",{className:"flex items-center gap-2 text-blue-800",children:[g.jsx(Oa,{className:"w-5 h-5"}),g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black",children:tn?"Bono 14 sin cuenta":"Nómina sin cuenta"})]}),g.jsx("p",{className:"text-3xl font-black text-blue-800 mt-2",children:p.summary.payrollWithoutAccount})]})]}),g.jsxs("div",{className:"bg-white border border-[#D0D0D0] shadow-sm overflow-hidden",children:[g.jsxs("div",{className:"p-5 border-b border-neutral-200 flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4",children:[g.jsxs("div",{children:[g.jsx("h2",{className:"text-lg font-black text-[#4F758B] uppercase tracking-tight",children:tn?"Resultados del Bono 14":"Resultados del Cruce"}),g.jsx("p",{className:"text-[11px] text-neutral-500 mt-1",children:Gt})]}),g.jsx("div",{className:"flex flex-col sm:flex-row gap-2",children:g.jsxs("button",{onClick:Bo,disabled:!p.reportUrl,className:"inline-flex items-center justify-center gap-2 border border-[#4F758B] text-[#4F758B] hover:bg-[#4F758B] hover:text-white px-4 py-2 text-xs font-bold transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[g.jsx(Rj,{className:"w-4 h-4"}),"Abrir Google Sheet"]})})]}),g.jsxs("div",{className:"p-4 border-b border-neutral-200 bg-neutral-50 flex flex-col lg:flex-row gap-3 lg:items-center lg:justify-between",children:[g.jsxs("div",{className:"relative w-full lg:max-w-sm",children:[g.jsx(Jr,{className:"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400"}),g.jsx("input",{type:"text",value:H,onChange:C=>{W(C.target.value),E(1)},placeholder:"Buscar empleado, Employee Number o cuenta...",className:"w-full pl-9 pr-3 py-2 border border-[#D0D0D0] text-xs font-medium focus:outline-none focus:border-[#4F758B]"})]}),g.jsx("div",{className:"flex flex-wrap gap-2",children:[["todos","Todos"],["discrepancias","Discrepancias"],["coincidencias","Coincidencias"],["banco_sin_nomina",tn?"Banco sin Bono 14":"Banco sin nómina"],["banco_sin_bamboo","Banco sin Bamboo"],["diferencia_nombre_banco","Diferencias nombre banco"],["nomina_sin_cuenta",tn?"Bono 14 sin cuenta":"Nómina sin cuenta"]].map(([C,ee])=>g.jsx("button",{onClick:()=>{Ae(C),E(1)},className:`px-3 py-1.5 text-[10px] uppercase tracking-wider font-black border transition-colors ${fe===C?"bg-[#4F758B] text-white border-[#4F758B]":"bg-white text-neutral-500 border-neutral-300 hover:border-[#4F758B] hover:text-[#4F758B]"}`,children:ee},C))})]}),g.jsx("div",{className:"overflow-x-auto",children:g.jsxs("table",{className:"w-full text-left border-collapse min-w-[960px] xl:min-w-full",children:[g.jsx("thead",{children:g.jsxs("tr",{className:"bg-[#4F758B] text-white text-xs font-bold uppercase tracking-wider",children:[g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Empleado"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Employee Number"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Cuenta"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-right",children:tn?"Monto Bono 14":"Monto Nómina"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-right",children:"Monto Banco"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-right",children:"Diferencia"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Estado"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Categoría"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Observación"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Acciones"})]})}),g.jsx("tbody",{className:"divide-y divide-neutral-200 text-xs",children:as.length>0?as.map(C=>g.jsxs("tr",{className:"hover:bg-neutral-50 transition-colors",children:[g.jsx("td",{className:"py-3 px-3 xl:px-4 font-bold text-neutral-800",children:C.employee}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-mono text-neutral-600",children:C.employeeNumber||"—"}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-mono text-neutral-500",children:C.account??"—"}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-right font-mono text-neutral-700",children:Bd(C.amountPayroll,T,C.currency)}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-right font-mono text-neutral-700",children:Bd(C.amountBank,T,C.currency)}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-right font-mono text-neutral-700",children:Bd(C.difference,T,C.currency)}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsx("span",{className:`inline-block px-2.5 py-1 text-[9px] font-black uppercase tracking-wider border ${OC(C.status)}`,children:C.status})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-neutral-600 max-w-[180px] capitalize",children:BC(C.category??C.source,(p==null?void 0:p.reportKind)??"nomina")}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-neutral-600 max-w-[300px]",children:C.observation}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:Ua(C,"banco_sin_bamboo")?te.has(C.id)?g.jsx("span",{className:"inline-flex items-center px-2.5 py-1 border border-emerald-200 bg-emerald-50 text-emerald-800 text-[9px] font-black uppercase tracking-wider",children:"Reportado a RRHH"}):g.jsx("button",{type:"button",onClick:()=>is(C),className:"inline-flex items-center justify-center px-3 py-2 border border-[#4F758B] text-[#4F758B] hover:bg-[#4F758B] hover:text-white text-[9px] font-black uppercase tracking-wider transition-colors whitespace-nowrap",children:"Reportar a RRHH"}):g.jsx("span",{className:"text-neutral-300",children:"—"})})]},C.id)):g.jsx("tr",{children:g.jsx("td",{colSpan:10,className:"py-12 text-center text-neutral-400 font-medium",children:"No hay resultados para el filtro seleccionado."})})})]})}),g.jsxs("div",{className:"p-4 border-t border-neutral-200 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 text-xs text-neutral-500",children:[g.jsxs("span",{children:["Mostrando ",as.length," de ",Cs.length," resultado(s)."]}),g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("button",{onClick:()=>E(C=>Math.max(1,C-1)),disabled:ie===1,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Anterior"}),g.jsxs("span",{className:"font-bold text-neutral-700",children:["Página ",ie," de ",qn]}),g.jsx("button",{onClick:()=>E(C=>Math.min(qn,C+1)),disabled:ie===qn,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Siguiente"})]})]})]})]})})]}),Rs&&p&&g.jsx(zw,{row:Rs,country:T,periodRange:tn?We:Ht,operatorName:x,operatorEmail:_,executionId:p.executionId,reportUrl:p.reportUrl,onClose:()=>is(null),onSuccess:C=>Be(ee=>{const ae=new Set(ee);return ae.add(C),ae}),onShowToast:n})]})}const cc=10;function FC(n){return n==="TT"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-tt-historicos":n==="PA"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pa-historicos":n==="SV"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-historicos":n==="HN"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-historicos":n==="PR"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pr-historicos":"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-historicos"}function IC(n){return n==="TT"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-tt-marcar-resuelto":n==="PA"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pa-marcar-resuelto":n==="SV"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-marcar-resuelto":n==="HN"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-marcar-resuelto":n==="PR"?"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pr-marcar-resuelto":"https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-marcar-resuelto"}function Pn(n){const e=Number(n);return Number.isFinite(e)?e:0}function GC(n,e){if(!n)return"";const i=new Date(String(n));return Number.isNaN(i.getTime())?String(n).slice(0,10):i.toLocaleDateString(e,{year:"numeric",month:"2-digit",day:"2-digit"})}function xf(n,e){if(!n)return"";const i=new Date(String(n));return Number.isNaN(i.getTime())?String(n):i.toLocaleString(e,{dateStyle:"short",timeStyle:"short"})}function KC(n){return String(n??"").toLowerCase().includes("resuelto")?"Resuelto":"Pendiente revisión"}function bv(n,e){const i=n,a=!Array.isArray(i)&&i?i:{};return(Array.isArray(i)?i:Array.isArray(a.reports)?a.reports:[]).map((c,u)=>{const d=c,m=Pn(d.discrepancias??d.discrepancies),p=Pn(d.bancoSinNomina??d.banco_sin_nomina??d.bankWithoutPayroll),y=Pn(d.bancoSinBamboo??d.banco_sin_bamboo??d.bankWithoutBamboo),v=Pn(d.nominaSinCuenta??d.nomina_sin_cuenta??d.payrollWithoutAccount),x=Pn(d.diferenciasNombreBanco??d.diferencias_nombre_banco??d.nameDifferences),_=m+p+y+v+x,S=String(d.country??d.pais??d.country_code??e.code).toUpperCase(),T=Array.isArray(d.archivosBanco)?d.archivosBanco.map(String):Array.isArray(d.bank_file_names)?d.bank_file_names.map(String):[],k=d.updated_at??d.fechaEjecucion??d.ejecutado_en??d.created_at;return{id:String(d.id??`reporte-${u+1}`),country:S==="TT"?"TT":S==="PA"?"PA":S==="SV"?"SV":S==="HN"?"HN":S==="PR"?"PR":"GT",date:GC(d.fecha??d.period_end??k,e.locale),executionDate:xf(k,e.locale),period:String(d.periodo??d.period_label??d.period??"Período no especificado"),periodStart:d.period_start?String(d.period_start):void 0,periodEnd:d.period_end?String(d.period_end):void 0,payrollFile:String(d.archivoNomina??d.payroll_file_name??d.payrollFile??""),bankFiles:T,matches:Pn(d.coincidencias??d.matches),discrepancies:m,bankWithoutPayroll:p,bankWithoutBamboo:y,payrollWithoutAccount:v,nameDifferences:x,totalPending:_,totalPayroll:Pn(d.totalNomina??d.total_nomina),totalBank:Pn(d.totalBanco??d.total_banco),totalDifference:Pn(d.diferenciaTotal??d.diferencia_total),status:KC(d.estado??d.status??d.estadoLabel),reportUrl:d.reportUrl||d.report_url?String(d.reportUrl??d.report_url):void 0,spreadsheetId:d.spreadsheetId||d.spreadsheet_id?String(d.spreadsheetId??d.spreadsheet_id):void 0,executedBy:d.ejecutadoPorNombre||d.ejecutado_por_nombre?String(d.ejecutadoPorNombre??d.ejecutado_por_nombre):void 0,executedByEmail:d.ejecutadoPorEmail||d.ejecutado_por_email?String(d.ejecutadoPorEmail??d.ejecutado_por_email):void 0,resolvedBy:d.resueltoPorNombre||d.resuelto_por_nombre?String(d.resueltoPorNombre??d.resuelto_por_nombre):void 0,resolvedByEmail:d.resueltoPorEmail||d.resuelto_por_email?String(d.resueltoPorEmail??d.resuelto_por_email):void 0,resolvedAt:xf(d.resueltoEn??d.resuelto_en,e.locale),resolutionComment:d.comentarioResolucion||d.comentario_resolucion?String(d.comentarioResolucion??d.comentario_resolucion):void 0,_sortTime:new Date(String(k??d.period_end??"")).getTime()||0}}).filter(c=>c.country===e.code).sort((c,u)=>u._sortTime-c._sortTime).map(({_sortTime:c,...u})=>u)}function wf(n){return String(n??"").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9 ]/g," ").replace(/\s+/g," ").trim()}function _f(n){return String(n??"").replace(/\.0$/g,"").replace(/\D/g,"").trim()}function vv(n,e){for(const i of e){const a=n[i];if(typeof a=="number"&&Number.isFinite(a))return a;if(typeof a=="string"&&a.trim()){const l=Number(a.replace(/[A-Za-z$Q,\s]/g,"").trim());if(Number.isFinite(l))return l}}return null}function YC(n){return Array.isArray(n)?n.map((e,i)=>{const a=e??{},l=String(a.employee??a.employee_name??a.bank_name_file??a.bank_account_holder??"Sin nombre"),c=vv(a,["bankAmount","bank_amount","amount","monto_banco"]),u=a.best_bamboo_candidate&&typeof a.best_bamboo_candidate=="object"?a.best_bamboo_candidate:null;return{id:String(a.id??`historical-bamboo-${wf(l)}-${_f(a.account??a.bank_account)}-${i+1}`),employee:l,employeeNumber:"",account:String(a.account??a.bank_account??a.bankAccount??""),amountPayroll:0,amountBank:c,difference:c===null?null:0-c,status:"Pendiente revisión",category:"banco_sin_bamboo",source:"banco_sin_bamboo",observation:String(a.observation??a.observacion??"Pago bancario sin empleado identificado de forma confiable en BambooHR."),bankNameFile:String(a.bank_name_file??a.bankNameFile??""),bankAccountHolder:String(a.bank_account_holder??a.bankAccountHolder??""),bestBambooCandidate:u?{employeeNumber:String(u.employee_number??u.employeeNumber??""),employeeName:String(u.employee_name??u.employeeName??""),score:vv(u,["score"])}:null,ambiguousBambooMatch:!!a.ambiguous_bamboo_match}}):[]}function XC(n){return Array.isArray(n)?n.map(e=>{const i=e??{};return{id:String(i.id??""),sourceName:String(i.nombre_origen_principal??i.source_name??""),sourceNames:Array.isArray(i.nombres_origen)?i.nombres_origen.map(String):[],bankAccount:String(i.cuenta_bancaria??i.bank_account??""),status:String(i.estado??""),emailSent:i.correo_enviado===!0}}):[]}function JC(n,e){const i=n.status.toLowerCase();if(!(n.emailSent||i==="notificado_rrhh"||i==="corregido_bamboo"))return!1;const l=_f(n.bankAccount),c=_f(e.account);if(l&&c&&l!==c)return!1;const u=new Set([n.sourceName,...n.sourceNames].map(wf).filter(Boolean));return[e.employee,e.bankNameFile,e.bankAccountHolder].map(wf).filter(Boolean).some(m=>u.has(m))}function QC(n){return n==="Resuelto"?"bg-blue-50 text-blue-800 border-blue-200":"bg-amber-50 text-amber-800 border-amber-200"}function ZC(n){return n==="Resuelto"?"RESUELTO":"PENDIENTE REVISIÓN"}function WC({onShowToast:n,onOpenSidebar:e,operatorName:i,operatorEmail:a,country:l}){const[c,u]=D.useState(""),[d,m]=D.useState("todos"),[p,y]=D.useState([]),[v,x]=D.useState(1),[_,S]=D.useState(!1),[T,k]=D.useState(!1),[R,M]=D.useState(""),[N,P]=D.useState(null),[G,X]=D.useState(""),[q,Y]=D.useState(null),[se,J]=D.useState([]),[he,fe]=D.useState(new Set),[Ae,ve]=D.useState(!1),[Ce,L]=D.useState(""),[K,Z]=D.useState(null),[pe,ie]=D.useState(0),[E,H]=D.useState(0),W=D.useRef(0),Q=D.useRef(null),oe=Math.max(1,Math.ceil(E/cc)),re=p;D.useEffect(()=>{x(1)},[c,d,l.code]),D.useEffect(()=>{v>oe&&x(oe)},[v,oe]);const _e=async()=>{var xe;const te=++W.current;(xe=Q.current)==null||xe.abort();const Be=new AbortController;Q.current=Be;const Ue=()=>W.current===te&&!Be.signal.aborted;S(!0),M("");try{if(ut){const Gt=d==="pendiente"?"pendiente_revision":d==="resuelto"?"resuelto":null,{data:kn,error:fn}=await ut.rpc("cruce_cuentas_get_historicos",{p_country:l.code,p_page:v,p_page_size:cc,p_search:c.trim()||null,p_status:Gt});if(!Ue())return;if(!fn&&kn){const Rn=Array.isArray(kn)?kn[0]:kn,Cs=bv(Rn,l),Os=Pn(Rn==null?void 0:Rn.total),qn=Pn((Rn==null?void 0:Rn.filtered_total)??Os);y(Cs),ie(Os),H(qn);return}}if(!Ue())return;const $e=FC(l.code);if(!$e)throw new Error(`No está configurado el origen histórico de ${l.name}.`);const kt=new URL($e);kt.searchParams.set("country",l.code);const xt=await fetch(kt.toString(),{method:"GET",signal:Be.signal}),Et=await xt.json().catch(()=>null);if(!Ue())return;if(!xt.ok||(Et==null?void 0:Et.ok)===!1)throw new Error("No se pudieron cargar los reportes históricos.");const Ht=bv(Et,l),Nn=c.trim().toLowerCase(),We=Ht.filter(Gt=>{const kn=d==="todos"||d==="pendiente"&&Gt.status==="Pendiente revisión"||d==="resuelto"&&Gt.status==="Resuelto",fn=!Nn||Gt.period.toLowerCase().includes(Nn)||Gt.payrollFile.toLowerCase().includes(Nn)||Gt.id.toLowerCase().includes(Nn)||(Gt.resolvedBy??"").toLowerCase().includes(Nn)||(Gt.executedBy??"").toLowerCase().includes(Nn);return kn&&fn});if(!Ue())return;const tn=(v-1)*cc;y(We.slice(tn,tn+cc)),ie(Ht.length),H(We.length)}catch($e){if(!Ue()||$e instanceof DOMException&&$e.name==="AbortError")return;y([]),ie(0),H(0),M($e instanceof Error?$e.message:"No se pudieron cargar los reportes históricos. Intenta nuevamente."),n("No se pudieron cargar los reportes históricos.","error")}finally{Ue()&&(S(!1),Q.current===Be&&(Q.current=null))}};D.useEffect(()=>{const te=window.setTimeout(()=>{_e()},c.trim()?300:0);return()=>{var Be;window.clearTimeout(te),W.current+=1,(Be=Q.current)==null||Be.abort(),Q.current=null}},[l.code,v,c,d]);const ke=()=>{Z(null),Y(null),J([]),fe(new Set),L("")},He=async te=>{if(te.bankWithoutBamboo<=0){n("Este reporte no tiene casos de Banco sin Bamboo.","info");return}if(!ut){n("Supabase no está configurado en esta aplicación.","error");return}Y(te),Z(null),J([]),fe(new Set),L(""),ve(!0);try{const{data:Be,error:Ue}=await ut.rpc("cruce_cuentas_get_banco_sin_bamboo_reporte",{p_report_id:te.id,p_country:l.code});if(Ue)throw Ue;const xe=Array.isArray(Be)?Be[0]:Be;if(!xe||xe.ok===!1)throw new Error(String((xe==null?void 0:xe.message)||"No se pudieron recuperar los casos de Banco sin Bamboo."));const $e=YC(xe.cases),kt=XC(xe.corrections),xt=new Set($e.filter(Et=>kt.some(Ht=>JC(Ht,Et))).map(Et=>Et.id));J($e),fe(xt),$e.length===0&&L("Este reporte fue creado antes de que se guardara el detalle de Banco sin Bamboo. Los reportes nuevos conservarán los casos para revisarlos desde Históricos.")}catch(Be){const Ue=Be instanceof Error?Be.message:"No se pudieron recuperar los casos de Banco sin Bamboo.";L(Ue),n(Ue,"error")}finally{ve(!1)}},en={label:(q==null?void 0:q.period)||"Período no especificado",start:(q==null?void 0:q.periodStart)||"",end:(q==null?void 0:q.periodEnd)||""},jn=te=>{if(te.reportUrl){window.open(te.reportUrl,"_blank","noopener,noreferrer");return}n("Este reporte aún no tiene un Google Sheet disponible.","info")},Rs=te=>{if(te.status==="Resuelto"){n("Este reporte ya fue marcado como resuelto.","info");return}P(te),X("")},is=async()=>{if(!N)return;const te=G.trim();if(te.length<10){n("Debes escribir un comentario de resolución más descriptivo.","error");return}const Be=IC(l.code);if(!Be){n(`La resolución de reportes para ${l.name} estará disponible próximamente.`,"info");return}k(!0);try{const Ue=await fetch(Be,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reportId:N.id,comentarioResolucion:te,resueltoPorNombre:i,resueltoPorEmail:a,country:l.code,countryName:l.name})}),xe=await Ue.json().catch(()=>null);if(!Ue.ok||(xe==null?void 0:xe.ok)===!1){const xt=Array.isArray(xe==null?void 0:xe.errors)?xe.errors.join(` +`):(xe==null?void 0:xe.message)||"No se pudo marcar el reporte como resuelto.";throw new Error(xt)}const $e=xe.report??{},kt=xf($e.resueltoEn??new Date().toISOString(),l.locale);y(xt=>xt.map(Et=>Et.id===N.id?{...Et,status:"Resuelto",resolvedBy:String($e.resueltoPorNombre??i),resolvedByEmail:String($e.resueltoPorEmail??a),resolvedAt:kt,resolutionComment:String($e.comentarioResolucion??te)}:Et)),P(null),X(""),n("Reporte marcado como resuelto correctamente.","success")}catch(Ue){const xe=Ue instanceof Error?Ue.message:"No se pudo marcar el reporte como resuelto.";n(xe,"error")}finally{k(!1)}};return g.jsxs("div",{className:"flex-1 flex flex-col min-h-screen bg-neutral-50 text-neutral-800 font-sans",children:[g.jsxs("header",{className:"bg-white border-b-4 border-[#6CC24A] flex justify-between items-center w-full px-4 sm:px-8 h-[84px] sticky top-0 z-40 shadow-sm gap-x-4",children:[g.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 min-w-0",children:[g.jsx("button",{onClick:e,className:"md:hidden p-2 -ml-2 text-neutral-600 hover:text-neutral-800 hover:bg-neutral-100 transition-colors flex-shrink-0","aria-label":"Abrir menú",children:g.jsx(dw,{className:"w-6 h-6"})}),g.jsx("h1",{className:"text-xs sm:text-sm md:text-lg lg:text-2xl font-black text-[#4F758B] tracking-tight uppercase flex items-center gap-1.5 sm:gap-2 min-w-0",children:g.jsxs("span",{className:"truncate",children:["Reportes Históricos - ",l.name]})})]}),g.jsxs("div",{className:"flex items-center gap-2 sm:gap-3 flex-shrink-0",children:[g.jsx(Pw,{country:l,operatorName:i,operatorEmail:a,section:"Reportes Históricos",onShowToast:n}),g.jsx("div",{className:"hidden sm:flex items-center border border-neutral-200 bg-white px-2.5 py-2 shadow-sm","aria-label":`Bandera de ${l.name}`,children:g.jsx("img",{src:l.flag,alt:`Bandera de ${l.name}`,className:"h-5 sm:h-6 w-auto object-contain"})}),g.jsxs("button",{onClick:_e,disabled:_,className:"hidden sm:inline-flex items-center gap-2 border border-[#4F758B]/20 text-[#4F758B] px-3 py-2 text-xs font-bold hover:border-[#4F758B]/50 disabled:opacity-50",children:[g.jsx(eN,{className:`w-4 h-4 ${_?"animate-spin":""}`}),"Actualizar"]})]})]}),g.jsxs("main",{className:"flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 py-8 flex flex-col gap-6",children:[g.jsxs("div",{className:"bg-white border border-[#D0D0D0] p-4 flex flex-col lg:flex-row items-center justify-between gap-4 shadow-sm",children:[g.jsxs("div",{className:"relative w-full lg:max-w-md",children:[g.jsx(Jr,{className:"absolute left-3.5 top-1/2 transform -translate-y-1/2 text-neutral-400 w-4 h-4"}),g.jsx("input",{type:"text",placeholder:"Buscar por período, reporte, archivo o resolutor...",value:c,onChange:te=>u(te.target.value),className:"w-full pl-10 pr-4 py-2 border border-[#D0D0D0] text-xs font-medium placeholder-neutral-400 focus:outline-none focus:border-[#4F758B] transition-colors"})]}),g.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center gap-3 w-full lg:w-auto",children:[g.jsx("div",{className:"flex flex-wrap gap-2",children:[["todos","Todos"],["pendiente","Pendiente revisión"],["resuelto","Resuelto"]].map(([te,Be])=>g.jsx("button",{onClick:()=>m(te),className:`px-3 py-1.5 text-[10px] uppercase tracking-wider font-black border transition-colors ${d===te?"bg-[#4F758B] text-white border-[#4F758B]":"bg-white text-neutral-500 border-neutral-300 hover:border-[#4F758B] hover:text-[#4F758B]"}`,children:Be},te))}),g.jsxs("div",{className:"flex items-center gap-2 text-xs font-bold text-neutral-500",children:[g.jsx(Uj,{className:"w-4 h-4 text-neutral-400"}),g.jsxs("span",{children:["Mostrando: ",E," de ",pe," reportes"]})]})]})]}),R&&g.jsxs("div",{className:"border border-red-200 bg-red-50 text-[#ba1a1a] px-4 py-3 text-xs font-bold flex items-center gap-2",children:[g.jsx(fo,{className:"w-4 h-4"}),R]}),g.jsxs("div",{className:"bg-white border border-[#D0D0D0] shadow-sm overflow-hidden",children:[g.jsx("div",{className:"overflow-x-auto",children:g.jsxs("table",{className:"w-full text-left border-collapse min-w-[960px] xl:min-w-full",children:[g.jsx("thead",{children:g.jsxs("tr",{className:"bg-[#4F758B] text-white text-xs font-bold uppercase tracking-wider",children:[g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Fecha"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Período"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Archivo de Nómina / Bono 14"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Coincidencias"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Banco sin Bamboo"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Pendientes"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Estado"}),g.jsx("th",{className:"py-3 px-3 xl:px-4",children:"Resuelto por"}),g.jsx("th",{className:"py-3 px-3 xl:px-4 text-center",children:"Acciones"})]})}),g.jsx("tbody",{className:"divide-y divide-neutral-200 text-xs",children:_?g.jsx("tr",{children:g.jsx("td",{colSpan:9,className:"py-12 text-center text-neutral-400 font-medium",children:g.jsxs("div",{className:"inline-flex items-center gap-2",children:[g.jsx($a,{className:"w-4 h-4 animate-spin"}),"Cargando reportes históricos..."]})})}):re.length>0?re.map(te=>g.jsxs(un.tr,{initial:{opacity:0},animate:{opacity:1},className:"hover:bg-neutral-50/70 transition-colors",children:[g.jsx("td",{className:"py-3 px-3 xl:px-4 font-medium text-neutral-700",children:g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx(xj,{className:"w-4 h-4 text-neutral-400"}),g.jsxs("div",{children:[g.jsx("span",{children:te.date}),te.executionDate&&g.jsx("p",{className:"text-[9px] text-neutral-400 mt-0.5",children:te.executionDate})]})]})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-bold text-[#4F758B]",children:te.period}),g.jsx("td",{className:"py-3 px-3 xl:px-4 font-mono text-neutral-500 truncate max-w-[260px]",title:te.payrollFile,children:g.jsxs("div",{className:"flex items-center gap-1.5",children:[g.jsx(Oa,{className:"w-4 h-4 text-neutral-400 flex-shrink-0"}),g.jsx("span",{className:"truncate",children:te.payrollFile||"No especificado"})]})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center font-black text-emerald-800",children:te.matches}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:te.bankWithoutBamboo>0?g.jsxs("button",{type:"button",onClick:()=>void He(te),className:"inline-flex items-center gap-1.5 min-w-8 px-2.5 py-1 text-[10px] font-black border bg-orange-50 text-orange-800 border-orange-200 hover:bg-orange-100 hover:border-orange-300 transition-colors",title:"Revisar casos y reportar correcciones a RRHH",children:[g.jsx("span",{children:te.bankWithoutBamboo}),g.jsx("span",{className:"hidden 2xl:inline",children:"Revisar"})]}):g.jsx("span",{className:"inline-block min-w-8 px-2.5 py-1 text-[10px] font-black border bg-neutral-50 text-neutral-400 border-neutral-200",children:"0"})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsx("span",{className:`inline-block px-2.5 py-1 text-[10px] font-bold border ${te.totalPending>0?"bg-red-50 text-[#ba1a1a] border-red-200":"bg-emerald-50 text-emerald-800 border-emerald-200"}`,children:te.totalPending===0?"Sin pendientes":`${te.totalPending} pendiente${te.totalPending>1?"s":""}`})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsx("span",{className:`inline-block px-2.5 py-1 text-[9px] uppercase font-black tracking-wider border ${QC(te.status)}`,children:ZC(te.status)})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-neutral-600 max-w-[220px]",children:te.resolvedBy?g.jsxs("div",{title:te.resolutionComment||"",children:[g.jsx("p",{className:"font-bold text-neutral-700 truncate",children:te.resolvedBy}),g.jsx("p",{className:"text-[10px] text-neutral-400",children:te.resolvedAt})]}):g.jsx("span",{className:"text-neutral-400",children:"Pendiente"})}),g.jsx("td",{className:"py-3 px-3 xl:px-4 text-center",children:g.jsxs("div",{className:"flex justify-center gap-2",children:[g.jsxs("button",{onClick:()=>jn(te),className:"inline-flex items-center gap-1.5 text-[#4F758B] hover:text-[#3d6378] font-bold text-xs border border-[#4F758B]/20 hover:border-[#4F758B]/50 px-3 py-1.5 bg-white hover:bg-neutral-50 transition-colors",children:[g.jsx(Oj,{className:"w-3.5 h-3.5"}),g.jsx("span",{children:"Ver"})]}),g.jsxs("button",{onClick:()=>Rs(te),disabled:te.status==="Resuelto",className:"inline-flex items-center gap-1.5 text-emerald-700 hover:text-emerald-800 font-bold text-xs border border-emerald-200 hover:border-emerald-400 px-3 py-1.5 bg-emerald-50 hover:bg-emerald-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[g.jsx(kc,{className:"w-3.5 h-3.5"}),g.jsx("span",{children:te.status==="Resuelto"?"Resuelto":"Marcar resuelto"})]})]})})]},te.id)):g.jsx("tr",{children:g.jsx("td",{colSpan:9,className:"py-12 text-center text-neutral-400 font-medium",children:"No se encontraron reportes históricos."})})})]})}),g.jsxs("div",{className:"p-4 border-t border-neutral-200 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 text-xs text-neutral-500",children:[g.jsxs("span",{children:["Mostrando ",re.length," de ",E," reporte(s)."]}),g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("button",{onClick:()=>x(te=>Math.max(1,te-1)),disabled:v===1,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Anterior"}),g.jsxs("span",{className:"font-bold text-neutral-700",children:["Página ",v," de ",oe]}),g.jsx("button",{onClick:()=>x(te=>Math.min(oe,te+1)),disabled:v===oe,className:"px-3 py-1.5 border border-neutral-300 bg-white disabled:opacity-40 disabled:cursor-not-allowed hover:border-[#4F758B] font-bold",children:"Siguiente"})]})]})]})]}),g.jsx(si,{children:q&&g.jsx(un.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:"fixed inset-0 z-[80] flex items-center justify-center bg-black/45 px-4 py-6",children:g.jsxs(un.div,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},exit:{opacity:0,y:12},className:"w-full max-w-5xl bg-white border border-[#D0D0D0] shadow-2xl max-h-[92vh] flex flex-col",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A] flex-shrink-0"}),g.jsxs("div",{className:"border-b border-neutral-200 px-5 sm:px-6 py-4 flex items-start justify-between gap-4 flex-shrink-0",children:[g.jsxs("div",{children:[g.jsx("p",{className:"text-[10px] uppercase tracking-[0.18em] font-black text-[#6B8FA3]",children:"Histórico · Banco sin BambooHR"}),g.jsx("h2",{className:"text-lg sm:text-xl font-black text-[#4F758B] mt-1",children:q.period}),g.jsxs("p",{className:"text-xs text-neutral-500 mt-1",children:[l.name," · ",q.bankWithoutBamboo," caso",q.bankWithoutBamboo===1?"":"s"," guardado",q.bankWithoutBamboo===1?"":"s"," en este reporte"]})]}),g.jsx("button",{type:"button",onClick:ke,className:"p-2 text-neutral-400 hover:text-neutral-700 hover:bg-neutral-100","aria-label":"Cerrar revisión de Banco sin Bamboo",children:g.jsx(Ka,{className:"w-5 h-5"})})]}),g.jsxs("div",{className:"px-5 sm:px-6 py-4 overflow-y-auto",children:[g.jsxs("div",{className:"border border-[#D6E8F4] bg-[#F7FBFD] px-4 py-3 text-xs text-neutral-600 leading-relaxed mb-4",children:["Aquí puedes volver días después al reporte y enviar a RRHH únicamente los falsos positivos que ya verificaste manualmente. Los casos previamente enviados se muestran como ",g.jsx("strong",{children:"Reportado a RRHH"}),"."]}),Ae?g.jsxs("div",{className:"py-14 text-center text-neutral-500 text-sm",children:[g.jsx($a,{className:"w-5 h-5 animate-spin mx-auto mb-2 text-[#4F758B]"}),"Recuperando casos del reporte..."]}):Ce?g.jsxs("div",{className:"border border-amber-200 bg-amber-50 text-amber-800 px-4 py-3 text-xs font-medium flex items-start gap-2",children:[g.jsx(fo,{className:"w-4 h-4 mt-0.5 flex-shrink-0"}),g.jsx("span",{children:Ce})]}):se.length>0?g.jsx("div",{className:"overflow-x-auto border border-neutral-200",children:g.jsxs("table",{className:"w-full text-left border-collapse min-w-[760px]",children:[g.jsx("thead",{children:g.jsxs("tr",{className:"bg-[#4F758B] text-white text-[10px] font-black uppercase tracking-wider",children:[g.jsx("th",{className:"px-3 py-3",children:"Nombre detectado"}),g.jsx("th",{className:"px-3 py-3",children:"Cuenta"}),g.jsx("th",{className:"px-3 py-3 text-right",children:"Monto banco"}),g.jsx("th",{className:"px-3 py-3",children:"Candidato BambooHR"}),g.jsx("th",{className:"px-3 py-3 text-center",children:"Acción"})]})}),g.jsx("tbody",{className:"divide-y divide-neutral-200 text-xs",children:se.map(te=>{var xe;const Be=he.has(te.id),Ue=te.amountBank;return g.jsxs("tr",{className:"hover:bg-neutral-50",children:[g.jsxs("td",{className:"px-3 py-3",children:[g.jsx("p",{className:"font-black text-neutral-800",children:te.employee}),(te.bankNameFile||te.bankAccountHolder)&&g.jsx("p",{className:"text-[10px] text-neutral-400 mt-1",children:[te.bankNameFile,te.bankAccountHolder].filter(Boolean).join(" · ")})]}),g.jsx("td",{className:"px-3 py-3 font-mono font-bold text-neutral-600",children:te.account||"—"}),g.jsx("td",{className:"px-3 py-3 text-right font-black text-neutral-700",children:Ue===null?"—":new Intl.NumberFormat(l.locale,{style:"currency",currency:l.currency,maximumFractionDigits:2}).format(Ue)}),g.jsx("td",{className:"px-3 py-3 text-neutral-600 max-w-[260px]",children:(xe=te.bestBambooCandidate)!=null&&xe.employeeName?g.jsxs("div",{children:[g.jsx("p",{className:"font-bold text-[#4F758B]",children:te.bestBambooCandidate.employeeName}),te.bestBambooCandidate.employeeNumber&&g.jsxs("p",{className:"text-[10px] text-neutral-400 mt-0.5",children:["Employee Number ",te.bestBambooCandidate.employeeNumber]})]}):g.jsx("span",{className:"text-neutral-400",children:"Sin candidato confiable"})}),g.jsx("td",{className:"px-3 py-3 text-center",children:Be?g.jsxs("span",{className:"inline-flex items-center gap-1.5 px-3 py-1.5 border border-emerald-200 bg-emerald-50 text-emerald-800 text-[10px] font-black uppercase tracking-wider",children:[g.jsx(kc,{className:"w-3.5 h-3.5"}),"Reportado a RRHH"]}):g.jsx("button",{type:"button",onClick:()=>Z(te),className:"inline-flex items-center gap-1.5 px-3 py-1.5 bg-[#6CC24A] hover:bg-[#5aaa3f] text-white text-[10px] font-black uppercase tracking-wider transition-colors",children:"Reportar a RRHH"})})]},te.id)})})]})}):g.jsx("div",{className:"py-12 text-center text-neutral-400 text-sm",children:"No hay casos guardados en este reporte."})]}),g.jsxs("div",{className:"border-t border-neutral-200 px-5 sm:px-6 py-4 flex items-center justify-between gap-3 flex-shrink-0",children:[g.jsxs("span",{className:"text-[10px] text-neutral-400",children:["ID del reporte: ",q.id]}),g.jsx("button",{type:"button",onClick:ke,className:"border border-neutral-300 bg-white px-4 py-2 text-xs font-bold text-neutral-600 hover:bg-neutral-50",children:"Cerrar"})]})]})})}),K&&q&&g.jsx(zw,{row:K,country:l,periodRange:en,operatorName:i,operatorEmail:a,executionId:q.id,reportUrl:q.reportUrl,onClose:()=>Z(null),onSuccess:te=>{fe(Be=>{const Ue=new Set(Be);return Ue.add(te),Ue})},onShowToast:n}),g.jsx(si,{children:N&&g.jsx(un.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:"fixed inset-0 z-[80] flex items-center justify-center bg-black/45 px-4",children:g.jsxs(un.div,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},exit:{opacity:0,y:12},className:"w-full max-w-lg bg-white border border-[#D0D0D0] shadow-xl",children:[g.jsxs("div",{className:"border-b border-neutral-200 px-5 py-4 flex items-center justify-between",children:[g.jsxs("div",{children:[g.jsx("h2",{className:"text-base font-black text-[#4F758B] uppercase tracking-tight",children:"Marcar reporte como resuelto"}),g.jsx("p",{className:"text-xs text-neutral-500 mt-1",children:N.period})]}),g.jsx("button",{onClick:()=>P(null),disabled:T,className:"p-2 text-neutral-400 hover:text-neutral-700 disabled:opacity-50","aria-label":"Cerrar",children:g.jsx(Ka,{className:"w-4 h-4"})})]}),g.jsxs("div",{className:"p-5 space-y-4",children:[g.jsx("div",{className:"bg-amber-50 border border-amber-200 text-amber-800 p-3 text-xs leading-relaxed",children:"Antes de resolver, confirma que las diferencias fueron revisadas y explica cómo se cerró el caso."}),g.jsxs("label",{className:"block",children:[g.jsx("span",{className:"text-[10px] uppercase tracking-wider font-black text-neutral-500",children:"Comentario de resolución"}),g.jsx("textarea",{value:G,onChange:te=>X(te.target.value),placeholder:"Ejemplo: Se corrigió la cuenta bancaria en nómina y se validó contra el banco.",className:"mt-2 w-full min-h-[130px] border border-[#D0D0D0] px-3 py-2 text-sm focus:outline-none focus:border-[#4F758B]"})]})]}),g.jsxs("div",{className:"border-t border-neutral-200 px-5 py-4 flex justify-end gap-3",children:[g.jsx("button",{onClick:()=>P(null),disabled:T,className:"border border-neutral-300 bg-white px-4 py-2 text-xs font-bold text-neutral-600 hover:bg-neutral-50 disabled:opacity-50",children:"Cancelar"}),g.jsxs("button",{onClick:is,disabled:T,className:"bg-[#4F758B] hover:bg-[#41677b] text-white px-4 py-2 text-xs font-black uppercase tracking-wider disabled:opacity-50 inline-flex items-center gap-2",children:[T&&g.jsx($a,{className:"w-4 h-4 animate-spin"}),"Confirmar resolución"]})]})]})})})]})}function Ud({message:n,type:e,onClose:i}){D.useEffect(()=>{const c=setTimeout(()=>{i()},2e3);return()=>clearTimeout(c)},[i]);const a={success:"bg-[#6CC24A] text-white border-l-4 border-emerald-700",error:"bg-[#ba1a1a] text-white border-l-4 border-red-800",info:"bg-[#4F758B] text-white border-l-4 border-slate-700"},l={success:kc,error:Tj,info:hw}[e];return g.jsx("div",{className:"fixed bottom-6 right-6 z-50 max-w-sm w-full",children:g.jsxs(un.div,{initial:{opacity:0,y:30,scale:.95},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:20,scale:.95},className:`flex items-center gap-3 p-4 shadow-lg rounded-md border border-neutral-200/10 ${a[e]}`,children:[g.jsx("span",{className:"flex-shrink-0",children:g.jsx(l,{className:"w-5 h-5"})}),g.jsx("div",{className:"flex-1 font-medium text-sm leading-tight pr-2",children:n}),g.jsx("button",{onClick:i,className:"flex-shrink-0 hover:bg-white/20 p-1 rounded transition-colors text-white",children:g.jsx(Ka,{className:"w-4 h-4"})})]})})}function eO({onShowToast:n}){const[e,i]=D.useState(!1),a=async()=>{if(!oo||!ut){n("El acceso con Google aún no está disponible. Contacta a IT.","error");return}i(!0),await gC();const{error:l}=await ut.auth.signInWithOAuth({provider:"google",options:{redirectTo:Lw(),queryParams:{access_type:"offline",prompt:"select_account"}}});l&&(i(!1),n("No se pudo iniciar sesión con Google. Intenta nuevamente.","error"))};return g.jsxs("div",{className:"min-h-screen w-full flex flex-col justify-between bg-[#F5F5F5] relative overflow-hidden font-sans",children:[g.jsx("div",{className:"h-1.5 w-full bg-[#4F758B]"}),g.jsx("div",{className:"flex-1 flex items-center justify-center p-4",children:g.jsxs(un.div,{initial:{opacity:0,y:15},animate:{opacity:1,y:0},transition:{duration:.5,ease:"easeOut"},className:"w-full max-w-md bg-white border border-[#D0D0D0] p-8 shadow-xl flex flex-col items-center gap-6",children:[g.jsx("div",{className:"w-full flex justify-center py-2",children:g.jsx("img",{alt:"Logo GOMEZLEE MARKETING",className:"h-14 object-contain",src:"https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png",referrerPolicy:"no-referrer"})}),g.jsx("div",{className:"text-center w-full",children:g.jsx("h1",{className:"text-2xl font-bold text-[#4F758B] tracking-tight",children:"Portal de Cruce de Cuentas GLM"})}),g.jsx("button",{onClick:a,disabled:e||!oo,className:"w-full relative overflow-hidden flex items-center justify-center gap-3 bg-[#6CC24A] hover:bg-[#5bb03c] disabled:bg-neutral-300 disabled:cursor-not-allowed text-white font-semibold py-3.5 px-6 transition-all duration-150 transform active:scale-[0.99] select-none text-sm tracking-wide",children:e?g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsxs("svg",{className:"animate-spin h-5 w-5 text-white",fill:"none",viewBox:"0 0 24 24",children:[g.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),g.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),g.jsx("span",{children:"Redirigiendo a Google..."})]}):g.jsxs(g.Fragment,{children:[g.jsxs("svg",{className:"w-5 h-5 flex-shrink-0 fill-current text-white",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",children:[g.jsx("path",{d:"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"}),g.jsx("path",{d:"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"}),g.jsx("path",{d:"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"}),g.jsx("path",{d:"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"})]}),g.jsx("span",{children:"Continuar con Google"})]})}),!oo&&g.jsxs("div",{className:"w-full flex items-start gap-2 border border-amber-200 bg-amber-50 px-3 py-2 text-[11px] text-amber-800 leading-relaxed",children:[g.jsx(fo,{className:"w-4 h-4 mt-0.5 flex-shrink-0"}),g.jsx("span",{children:"El acceso con Google está pendiente de activación."})]})]})})]})}const tO="https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png";function nO({onSelect:n}){return g.jsxs("div",{className:"min-h-screen bg-[#F5F5F5] flex flex-col font-sans",children:[g.jsx("div",{className:"h-1.5 bg-[#6CC24A]"}),g.jsx("main",{className:"flex-1 flex items-center justify-center px-4 py-10",children:g.jsxs(un.section,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},className:"w-full max-w-3xl bg-white border border-[#D0D0D0] shadow-xl p-6 sm:p-9",children:[g.jsx("img",{src:tO,alt:"GomezLee Marketing",className:"h-14 object-contain mb-7"}),g.jsxs("div",{className:"flex items-start gap-3 mb-7",children:[g.jsx("div",{className:"bg-[#4F758B]/10 p-2.5 rounded-full",children:g.jsx(uw,{className:"w-5 h-5 text-[#4F758B]"})}),g.jsxs("div",{children:[g.jsx("h1",{className:"text-xl sm:text-2xl font-black text-[#4F758B] uppercase tracking-tight",children:"Selecciona el país"}),g.jsx("p",{className:"text-sm text-neutral-500 mt-1",children:"El portal cargará las reglas, la moneda y los reportes correspondientes."})]})]}),g.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",children:mo.map(e=>g.jsxs("button",{type:"button",disabled:!e.enabled,onClick:()=>e.enabled&&n(e.code),className:`text-left border p-5 transition-all min-h-[150px] flex flex-col justify-between ${e.enabled?"border-[#4F758B]/30 hover:border-[#4F758B] hover:shadow-md bg-white":"border-neutral-200 bg-neutral-50 cursor-not-allowed opacity-70"}`,children:[g.jsxs("div",{className:"flex items-start justify-between gap-3",children:[g.jsx("img",{src:e.flag,alt:"",className:"w-12 h-8 sm:w-16 sm:h-10 object-cover shadow-sm border border-neutral-200","aria-hidden":"true"}),e.enabled?g.jsx(pj,{className:"w-5 h-5 text-[#6CC24A]"}):g.jsx(qj,{className:"w-4 h-4 text-neutral-400"})]}),g.jsxs("div",{children:[g.jsx("p",{className:"font-black text-[#4F758B] uppercase tracking-wide",children:e.name}),g.jsx("p",{className:"text-xs text-neutral-500 mt-1",children:e.enabled?`Moneda ${e.currency}`:"Próximamente"})]})]},e.code))})]})})]})}const sO="cruce_cuentas_mi_acceso",xv=2;function $w(n){return(n??"").trim().toLowerCase()}function iO(n){return Array.isArray(n)?Array.from(new Set(n.map(e=>String(e??"").trim().toUpperCase()).filter(Boolean))):[]}function aO(n){const e=Array.isArray(n)?n[0]:n;if(!e||typeof e!="object")throw new Error("Supabase no devolvió una respuesta válida al comprobar el acceso.");return{ok:e.ok===!0,authenticated:e.autenticado===!0,authorized:e.autorizado===!0,email:$w(typeof e.email=="string"?e.email:""),name:typeof e.nombre=="string"?e.nombre.trim():"",active:e.activo===!0,appAccess:e.acceso_app===!0,historyAccess:e.acceso_historicos===!0,receivesGeneratedReports:e.recibe_reportes_generados===!0,receivesResolvedEmails:e.recibe_correos_resuelto===!0,countries:iO(e.paises),reason:typeof e.motivo=="string"?e.motivo.trim():""}}function rO(n){return new Promise(e=>window.setTimeout(e,n))}async function oO(){if(!ut)throw new Error("Supabase no está configurado.");let n=null;for(let i=1;i<=xv;i+=1){const{data:a,error:l}=await ut.rpc(sO);if(!l)return aO(a);n=l,i{if(typeof indexedDB>"u"){e(new Error("IndexedDB no está disponible en este navegador."));return}const i=indexedDB.open(lO,cO);i.onupgradeneeded=()=>{const a=i.result;a.objectStoreNames.contains(ns)||a.createObjectStore(ns,{keyPath:"sessionId"})},i.onsuccess=()=>n(i.result),i.onerror=()=>e(i.error??new Error("No se pudo abrir IndexedDB.")),i.onblocked=()=>e(new Error("La base local está bloqueada por otra pestaña."))}),Ra.catch(()=>{Ra=null}),Ra)}function hO(n){return new Promise((e,i)=>{n.onsuccess=()=>e(n.result),n.onerror=()=>i(n.error??new Error("Falló una operación local."))})}function Yc(n){return new Promise((e,i)=>{n.oncomplete=()=>e(),n.onerror=()=>i(n.error??new Error("Falló la transacción local.")),n.onabort=()=>i(n.error??new Error("La transacción local fue cancelada."))})}function dO(n){if(!n||typeof n!="object")return!1;const e=n;return e.version===qw&&typeof e.sessionId=="string"&&typeof e.updatedAt=="number"&&(e.currentView==="cruce"||e.currentView==="historial")&&Array.isArray(e.uploadedBankFiles)&&typeof e.isCruceExecuted=="boolean"}async function fO(n,e){const i=Date.now()-uO,a=n.transaction(ns,"readwrite"),l=Yc(a),u=a.objectStore(ns).openCursor();await new Promise((d,m)=>{u.onsuccess=()=>{const p=u.result;if(!p){d();return}const y=p.value;y.sessionId!==e&&typeof y.updatedAt=="number"&&y.updatedAtm(u.error??new Error("No se pudieron limpiar borradores antiguos."))}),await l}async function mO(){const n=nm();try{const e=await sm(),i=e.transaction(ns,"readonly"),a=Yc(i),l=await hO(i.objectStore(ns).get(n));return await a,fO(e,n).catch(()=>{}),dO(l)?l:null}catch{return null}}function pO(n){const e=nm(),i={...n,version:qw,sessionId:e,updatedAt:Date.now()};return Fa=Fa.catch(()=>{}).then(async()=>{const l=(await sm()).transaction(ns,"readwrite"),c=Yc(l);l.objectStore(ns).put(i),await c}).catch(()=>{}),Fa}function Tv(){const n=nm();return Fa=Fa.catch(()=>{}).then(async()=>{const i=(await sm()).transaction(ns,"readwrite"),a=Yc(i);i.objectStore(ns).delete(n),await a}).catch(()=>{}),Fa}const Ev=!0,Sf="glm_cruce_selected_country",gO={name:"Isaac Aracena",email:"iaracena@gomezleemarketing.com"};function yO(n){const e=(n==null?void 0:n.user_metadata)??{},i=e.full_name||e.name||e.display_name;if(i)return String(i);const a=String((n==null?void 0:n.email)??"Usuario GLM");return a.includes("@")?a.split("@")[0]:a}function bO(){try{const n=window.localStorage.getItem(Sf),e=mo.find(i=>i.code===n&&i.enabled);return(e==null?void 0:e.code)??null}catch{return null}}function vO(){const[n,e]=D.useState("cruce"),[i,a]=D.useState(!1),[l,c]=D.useState(null),[u,d]=D.useState([]),[m,p]=D.useState([]),[y,v]=D.useState(!1),[x,_]=D.useState(null),[S,T]=D.useState(0),[k,R]=D.useState(()=>bO()),[M,N]=D.useState(null),[P,G]=D.useState(Ev),[X,q]=D.useState(!1),[Y,se]=D.useState(null),J=D.useRef(0),he=D.useMemo(()=>oo&&ut,[]),fe=k?_b(k):null,Ae=(ie,E)=>{se({message:ie,type:E})};D.useEffect(()=>{if(!he||!ut){G(!1);return}let ie=!0;const E=async Q=>{const oe=++J.current;if(!(Q!=null&&Q.email)){ie&&oe===J.current&&N(null);return}try{const re=await oO();if(!ie||oe!==J.current)return;const _e=$w(Q.email),ke=!!re.email&&re.email===_e;if(!re.ok||!re.authenticated||!re.authorized||!re.active||!re.appAccess||!ke){N(null),Ae(re.reason||"Tu usuario no tiene acceso autorizado a este módulo.","error"),ut.auth.signOut();return}N({name:re.name||yO(Q),email:_e})}catch(re){if(!ie||oe!==J.current)return;console.error("No se pudo validar el acceso en Supabase:",re),N(null),Ae("No se pudo validar tu acceso en Supabase. Intenta iniciar sesión nuevamente.","error"),ut.auth.signOut()}};(async()=>{var Q;try{const{data:oe,error:re}=await ut.auth.getSession();if(re)throw re;if(!ie)return;await E((Q=oe.session)==null?void 0:Q.user)}catch(oe){if(!ie)return;console.error("No se pudo recuperar la sesión de Supabase:",oe),N(null),Ae("No se pudo recuperar tu sesión. Intenta iniciar sesión nuevamente.","error")}finally{ie&&G(!1)}})();const{data:W}=ut.auth.onAuthStateChange((Q,oe)=>{if(ie){if(oe!=null&&oe.user){window.setTimeout(()=>{ie&&E(oe.user)},0);return}Q==="SIGNED_OUT"&&(J.current+=1,N(null),G(!1))}});return()=>{ie=!1,J.current+=1,W.subscription.unsubscribe()}},[he]),D.useEffect(()=>{if(!he||!ut)return;let ie=0;const E=()=>{if(document.visibilityState!=="visible")return;const H=Date.now();H-ie<5e3||(ie=H,So(300).catch(W=>{console.warn("No se pudo revalidar la sesión al reanudar la pestaña:",W)}))};return document.addEventListener("visibilitychange",E),window.addEventListener("focus",E),()=>{document.removeEventListener("visibilitychange",E),window.removeEventListener("focus",E)}},[he]),D.useEffect(()=>{let ie=!0;return(async()=>{const H=await mO();ie&&(H&&(!k||H.countryCode===k?(H.countryCode&&R(H.countryCode),e(H.currentView),c(H.uploadedPayroll),d(Array.isArray(H.uploadedPayrollFiles)?H.uploadedPayrollFiles:[]),p(H.uploadedBankFiles),v(H.isCruceExecuted),_(H.cruceResponse)):await Tv()),ie&&q(!0))})(),()=>{ie=!1}},[]),D.useEffect(()=>{X&&pO({countryCode:k,currentView:n,uploadedPayroll:l,uploadedPayrollFiles:u,uploadedBankFiles:m,isCruceExecuted:y,cruceResponse:x})},[X,k,n,l,u,m,y,x]);const ve=()=>{c(null),d([]),p([]),v(!1),_(null),T(ie=>ie+1)},Ce=async()=>{await Tv(),ut&&await ut.auth.signOut(),N(null),R(null),window.localStorage.removeItem(Sf),ve(),e("cruce"),Ae("Sesión cerrada correctamente.","success")},L=()=>{ve(),e("cruce"),Ae("Nuevo cruce iniciado.","success")},K=ie=>{const E=_b(ie);if(!E.enabled){Ae(`${E.name} estará disponible próximamente.`,"info");return}k&&k!==ie&&ve(),R(ie),window.localStorage.setItem(Sf,ie),e("cruce"),a(!1)},Z=()=>{R(null),a(!1)};if(P)return g.jsx("div",{className:"min-h-screen bg-[#F5F5F5] flex items-center justify-center text-[#4F758B] font-black uppercase tracking-wider",children:"Validando acceso..."});if(!M)return g.jsxs(g.Fragment,{children:[g.jsx(eO,{onShowToast:Ae}),g.jsx(si,{children:Y&&g.jsx(Ud,{message:Y.message,type:Y.type,onClose:()=>se(null)})})]});if(!fe)return g.jsxs(g.Fragment,{children:[g.jsx(nO,{onSelect:K}),g.jsx(si,{children:Y&&g.jsx(Ud,{message:Y.message,type:Y.type,onClose:()=>se(null)})})]});const pe=M??gO;return g.jsxs("div",{className:"min-h-screen bg-[#F5F5F5] selection:bg-[#6CC24A] selection:text-white antialiased font-sans flex flex-col",children:[g.jsxs(un.div,{initial:{opacity:0},animate:{opacity:1},className:"min-h-screen flex w-full relative overflow-x-hidden",children:[g.jsx(si,{children:i&&g.jsx(un.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},onClick:()=>a(!1),className:"fixed inset-0 bg-black/45 z-45 md:hidden"},"backdrop")}),g.jsx(lN,{currentView:n,onViewChange:ie=>{e(ie),a(!1)},onNewCruce:()=>{L(),a(!1)},isSidebarOpen:i,onCloseSidebar:()=>a(!1),user:pe,isAuthEnabled:Ev,onLogout:Ce,country:fe,onCountryChange:K,onChooseAnotherCountry:Z}),g.jsx("div",{className:"pl-0 md:pl-64 flex-1 flex flex-col w-full min-h-screen transition-all duration-300",children:g.jsx(si,{mode:"wait",children:n==="cruce"?g.jsx(un.div,{initial:{opacity:0,x:10},animate:{opacity:1,x:0},exit:{opacity:0,x:-10},transition:{duration:.2},className:"flex-1 flex flex-col",children:g.jsx(qC,{onShowToast:Ae,uploadedPayroll:l,setUploadedPayroll:c,uploadedPayrollFiles:u,setUploadedPayrollFiles:d,uploadedBankFiles:m,setUploadedBankFiles:p,isCruceExecuted:y,setIsCruceExecuted:v,response:x,setResponse:_,onOpenSidebar:()=>a(!0),operatorName:pe.name,operatorEmail:pe.email,resetToken:S,country:fe})},`view-cruce-${fe.code}`):g.jsx(un.div,{initial:{opacity:0,x:10},animate:{opacity:1,x:0},exit:{opacity:0,x:-10},transition:{duration:.2},className:"flex-1 flex flex-col",children:g.jsx(WC,{onShowToast:Ae,onOpenSidebar:()=>a(!0),operatorName:pe.name,operatorEmail:pe.email,country:fe})},`view-history-${fe.code}`)})})]},`interface-${fe.code}`),g.jsx(si,{children:Y&&g.jsx(Ud,{message:Y.message,type:Y.type,onClose:()=>se(null)})})]})}window.addEventListener("pageshow",n=>{n.persisted&&window.location.reload()});l1.createRoot(document.getElementById("root")).render(g.jsx(D.StrictMode,{children:g.jsx(vO,{})})); diff --git a/dist/index.html b/dist/index.html index 6d0ac05..6abbf14 100644 --- a/dist/index.html +++ b/dist/index.html @@ -9,7 +9,7 @@ Portal de Cruce de Cuentas GLM - + diff --git a/src/components/BambooCorrectionModal.tsx b/src/components/BambooCorrectionModal.tsx index 39e9698..4027da8 100644 --- a/src/components/BambooCorrectionModal.tsx +++ b/src/components/BambooCorrectionModal.tsx @@ -68,6 +68,7 @@ export default function BambooCorrectionModal({ if (country.code === 'PA') return 'Delarys Peñalba'; if (country.code === 'SV') return 'Carlos Antonio Malta Méndez'; if (country.code === 'HN') return 'Melody Chang Blandin'; + if (country.code === 'PR') return 'Noralmy Laureano'; return 'haguirre@gomezleemarketing.com'; }, [country.code]); diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 19a6797..720c1af 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -98,6 +98,13 @@ function getReconciliationUrl(countryCode: string) { ); } + if (countryCode === 'PR') { + return ( + (import.meta.env.VITE_N8N_PUERTO_RICO_WEBHOOK_URL as string | undefined) || + 'https://agenteit.digitalcompass.agency/webhook/nominapr-bamboo-test' + ); + } + return ( (import.meta.env.VITE_N8N_GUATEMALA_WEBHOOK_URL as string | undefined) || (import.meta.env.VITE_N8N_WEBHOOK_URL as string | undefined) || @@ -152,6 +159,10 @@ function isCsvFile(file: File) { return file.name.toLowerCase().endsWith('.csv'); } +function isPdfFile(file: File) { + return file.name.toLowerCase().endsWith('.pdf'); +} + function toUploadedPayrollFiles(files: FileList | File[]) { return Array.from(files).map((file) => ({ name: file.name, @@ -814,7 +825,10 @@ export default function Dashboard({ const isPanama = country.code === 'PA'; const isElSalvador = country.code === 'SV'; const isHonduras = country.code === 'HN'; + const isPuertoRico = country.code === 'PR'; const isMultiPayrollCountry = isPanama || isElSalvador; + const bankFileLabel = isPuertoRico ? 'PDF' : 'CSV'; + const bankFileExtensionLabel = isPuertoRico ? '.pdf' : '.csv'; const [isProcessing, setIsProcessing] = useState(false); const [processingStep, setProcessingStep] = useState(''); const [filterMode, setFilterMode] = useState< @@ -1084,16 +1098,19 @@ export default function Dashboard({ const handleBankDrop = (e: React.DragEvent) => { e.preventDefault(); const files = Array.from(e.dataTransfer.files) as File[]; - const csvFiles = files.filter(isCsvFile); + const validBankFiles = files.filter(isPuertoRico ? isPdfFile : isCsvFile); - if (csvFiles.length === 0) { - onShowToast('Formato inválido. Por favor carga uno o varios archivos CSV (.csv).', 'error'); + if (validBankFiles.length === 0) { + onShowToast( + `Formato inválido. Por favor carga uno o varios archivos ${bankFileLabel} (${bankFileExtensionLabel}).`, + 'error' + ); return; } - setUploadedBankFiles([...uploadedBankFiles, ...toUploadedBankFiles(csvFiles)]); + setUploadedBankFiles([...uploadedBankFiles, ...toUploadedBankFiles(validBankFiles)]); resetResults(); - onShowToast(`${csvFiles.length} planilla(s) del banco cargada(s) con éxito.`, 'success'); + onShowToast(`${validBankFiles.length} archivo(s) del banco cargado(s) con éxito.`, 'success'); }; const handlePayrollSelect = async (e: React.ChangeEvent) => { @@ -1126,16 +1143,19 @@ export default function Dashboard({ const files = e.target.files; if (!files || files.length === 0) return; - const csvFiles = (Array.from(files) as File[]).filter(isCsvFile); + const validBankFiles = (Array.from(files) as File[]).filter(isPuertoRico ? isPdfFile : isCsvFile); - if (csvFiles.length === 0) { - onShowToast('Formato inválido. Por favor carga uno o varios archivos CSV (.csv).', 'error'); + if (validBankFiles.length === 0) { + onShowToast( + `Formato inválido. Por favor carga uno o varios archivos ${bankFileLabel} (${bankFileExtensionLabel}).`, + 'error' + ); return; } - setUploadedBankFiles([...uploadedBankFiles, ...toUploadedBankFiles(csvFiles)]); + setUploadedBankFiles([...uploadedBankFiles, ...toUploadedBankFiles(validBankFiles)]); resetResults(); - onShowToast(`${csvFiles.length} planilla(s) del banco cargada(s) con éxito.`, 'success'); + onShowToast(`${validBankFiles.length} archivo(s) del banco cargado(s) con éxito.`, 'success'); }; const removeBankFile = (indexToRemove: number) => { @@ -1263,7 +1283,9 @@ export default function Dashboard({ setCurrentPage(1); try { - const duplicatedShipments = await findDuplicateBankShipments(uploadedBankFiles); + const duplicatedShipments = isPuertoRico + ? [] + : await findDuplicateBankShipments(uploadedBankFiles); if (duplicatedShipments.length > 0) { const details = duplicatedShipments .map((item) => `Número de envío ${item.shipmentNumber}: ${item.fileNames.join(', ')}`) @@ -1667,7 +1689,7 @@ export default function Dashboard({ - {(isMultiPayrollCountry || isHonduras) && + {(isMultiPayrollCountry || isHonduras || isPuertoRico) && (uploadedPayroll !== null || uploadedPayrollFiles.length > 0 || uploadedBankFiles.length > 0) && (
@@ -1912,7 +1934,7 @@ export default function Dashboard({
{file.size} - CSV + {bankFileLabel}
@@ -1935,7 +1957,7 @@ export default function Dashboard({

No encontramos archivos con ese nombre.

-

Pruebe con otra parte del nombre del CSV.

+

Pruebe con otra parte del nombre del archivo.

)} diff --git a/src/components/History.tsx b/src/components/History.tsx index d532149..0af92d9 100644 --- a/src/components/History.tsx +++ b/src/components/History.tsx @@ -54,6 +54,13 @@ function getHistoryUrl(countryCode: string) { ); } + if (countryCode === 'PR') { + return ( + (import.meta.env.VITE_HISTORICOS_PR_URL as string | undefined) || + 'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pr-historicos' + ); + } + return ( (import.meta.env.VITE_HISTORICOS_GT_URL as string | undefined) || 'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-historicos' @@ -86,6 +93,13 @@ function getMarkResolvedUrl(countryCode: string) { ); } + if (countryCode === 'PR') { + return ( + (import.meta.env.VITE_MARCAR_RESUELTO_PR_URL as string | undefined) || + 'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-pr-marcar-resuelto' + ); + } + return ( (import.meta.env.VITE_MARCAR_RESUELTO_GT_URL as string | undefined) || 'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-marcar-resuelto' @@ -171,7 +185,7 @@ function normalizeReports(input: unknown, country: CountryConfig): HistoricalRep return { id: String(report.id ?? `reporte-${index + 1}`), - country: (rawCountry === 'TT' ? 'TT' : rawCountry === 'PA' ? 'PA' : rawCountry === 'SV' ? 'SV' : rawCountry === 'HN' ? 'HN' : 'GT') as HistoricalReport['country'], + country: (rawCountry === 'TT' ? 'TT' : rawCountry === 'PA' ? 'PA' : rawCountry === 'SV' ? 'SV' : rawCountry === 'HN' ? 'HN' : rawCountry === 'PR' ? 'PR' : 'GT') as HistoricalReport['country'], date: formatDate(report.fecha ?? report.period_end ?? rawExecutionDate, country.locale), executionDate: formatDateTime(rawExecutionDate, country.locale), period: String(report.periodo ?? report.period_label ?? report.period ?? 'Período no especificado'), @@ -485,7 +499,7 @@ export default function History({ if (!isCurrentRequest()) return; // Respaldo temporal: webhook histórico existente. También se envía el país - // para impedir que se mezclen reportes de Guatemala, Trinidad y Tobago, Panamá, El Salvador y Honduras. + // para impedir que se mezclen reportes de Guatemala, Trinidad y Tobago, Panamá, El Salvador, Honduras y Puerto Rico. const historyUrl = getHistoryUrl(country.code); if (!historyUrl) { throw new Error(`No está configurado el origen histórico de ${country.name}.`); diff --git a/src/config/countries.ts b/src/config/countries.ts index 0dc37d0..58551b7 100644 --- a/src/config/countries.ts +++ b/src/config/countries.ts @@ -41,6 +41,14 @@ export const COUNTRIES: CountryConfig[] = [ locale: 'es-HN', enabled: true, }, + { + code: 'PR', + name: 'Puerto Rico', + flag: 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/bandera-nacional-tela-puerto-rico_113767-1759.avif', + currency: 'USD', + locale: 'es-PR', + enabled: true, + }, ]; export const DEFAULT_COUNTRY_CODE: CountryCode = 'GT'; diff --git a/src/types.ts b/src/types.ts index cfe18ec..52535cc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,7 +4,7 @@ export type ViewType = 'cruce' | 'historial'; export type ToastType = 'success' | 'error' | 'info'; -export type CountryCode = 'GT' | 'TT' | 'PA' | 'SV' | 'HN'; +export type CountryCode = 'GT' | 'TT' | 'PA' | 'SV' | 'HN' | 'PR'; export interface CountryConfig { code: CountryCode; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index ffe0653..e9ca1bc 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -7,6 +7,7 @@ interface ImportMetaEnv { readonly VITE_N8N_PANAMA_WEBHOOK_URL?: string; readonly VITE_N8N_EL_SALVADOR_WEBHOOK_URL?: string; readonly VITE_N8N_HONDURAS_WEBHOOK_URL?: string; + readonly VITE_N8N_PUERTO_RICO_WEBHOOK_URL?: string; readonly VITE_HISTORICOS_GT_URL?: string; readonly VITE_MARCAR_RESUELTO_GT_URL?: string; readonly VITE_HISTORICOS_TT_URL?: string; @@ -17,6 +18,8 @@ interface ImportMetaEnv { readonly VITE_MARCAR_RESUELTO_SV_URL?: string; readonly VITE_HISTORICOS_HN_URL?: string; readonly VITE_MARCAR_RESUELTO_HN_URL?: string; + readonly VITE_HISTORICOS_PR_URL?: string; + readonly VITE_MARCAR_RESUELTO_PR_URL?: string; readonly VITE_ANALIZAR_NOMINA_URL?: string; readonly VITE_REPORTAR_BAMBOO_URL?: string; readonly VITE_REPORTAR_APP_URL?: string; diff --git a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN.json b/workflows_n8n/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN.json deleted file mode 100644 index ab4647f..0000000 --- a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Analizar N├│mina con Gemini GT TT PA SV HN.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "name": "Portal de Verificación de Nómina - Analizar Nómina con Gemini GT TT PA SV HN", - "nodes": [ - { - "parameters": { - "httpMethod": "POST", - "path": "cruce-cuentas-analizar-nomina", - "responseMode": "responseNode", - "options": {} - }, - "type": "n8n-nodes-base.webhook", - "typeVersion": 2.1, - "position": [ - 6432, - 2784 - ], - "id": "8518e0cc-3c55-4c55-9c9b-3498dd6ab7d3", - "name": "Webhook - Analizar Nómina", - "webhookId": "f265d1ec-437f-4a05-a699-76dae8c4d89e" - }, - { - "parameters": { - "jsCode": "const input = $input.first().json || {};\n\nlet body = input.body ?? input;\n\nif (typeof body === 'string') {\n try {\n body = JSON.parse(body);\n } catch (error) {\n body = {};\n }\n}\n\nif (!body || typeof body !== 'object' || Array.isArray(body)) {\n body = {};\n}\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst fileName = clean(\n body.fileName ??\n body.filename ??\n body.file_name ??\n ''\n);\n\nconst country = clean(\n body.country ?? 'GT'\n).toUpperCase();\n\nconst supportedCountries = {\n GT: 'Guatemala',\n TT: 'Trinidad y Tobago',\n PA: 'Panamá',\n SV: 'El Salvador',\n HN: 'Honduras',\n};\n\nconst countryName = clean(\n body.countryName ??\n body.country_name ??\n supportedCountries[country] ??\n ''\n);\n\nconst errors = [];\n\nif (!fileName) {\n errors.push(\n 'No se recibió el nombre del archivo de nómina.'\n );\n}\n\nif (!Object.prototype.hasOwnProperty.call(\n supportedCountries,\n country\n)) {\n errors.push(\n 'El analizador solo está habilitado para Guatemala, Trinidad y Tobago, Panamá, El Salvador y Honduras.'\n );\n}\n\nconst supportedExtensions = [\n '.xlsx',\n '.xls',\n '.xlsm',\n '.csv',\n];\n\nconst lowerFileName = fileName.toLowerCase();\n\nconst hasSupportedExtension =\n supportedExtensions.some((extension) =>\n lowerFileName.endsWith(extension)\n );\n\nif (fileName && !hasSupportedExtension) {\n errors.push(\n 'El archivo recibido no tiene una extensión de nómina compatible.'\n );\n}\n\nreturn [\n {\n json: {\n ok: errors.length === 0,\n errors,\n\n fileName,\n country,\n countryName:\n countryName ||\n supportedCountries[country] ||\n '',\n\n receivedAt: new Date().toISOString(),\n },\n },\n];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 6640, - 2784 - ], - "id": "6f8d45c3-8dc9-4c58-9ba7-93a9a37877cc", - "name": "Validar y preparar solicitud" - }, - { - "parameters": { - "conditions": { - "options": { - "caseSensitive": true, - "leftValue": "", - "typeValidation": "strict", - "version": 3 - }, - "conditions": [ - { - "id": "bd9fd675-d72b-4dba-ac03-a44f1123ecb0", - "leftValue": "={{ $json.ok }}", - "rightValue": "", - "operator": { - "type": "boolean", - "operation": "true", - "singleValue": true - } - } - ], - "combinator": "and" - }, - "options": {} - }, - "type": "n8n-nodes-base.if", - "typeVersion": 2.3, - "position": [ - 6848, - 2784 - ], - "id": "10aaa8bb-d2b7-48b8-b538-d7f968bba2d0", - "name": "¿Solicitud válida?" - }, - { - "parameters": { - "respondWith": "json", - "responseBody": "={{\n{\n ok: false,\n message:\n 'No fue posible analizar el nombre del archivo.',\n errors: Array.isArray($json.errors)\n ? $json.errors\n : ['La solicitud recibida no es válida.'],\n detected: null\n}\n}}", - "options": { - "responseCode": 400, - "responseHeaders": { - "entries": [ - { - "name": "Content-Type", - "value": "application/json" - } - ] - } - } - }, - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.5, - "position": [ - 7056, - 2880 - ], - "id": "3c592f6e-f2c5-4b3e-a1b6-56b86045f8b2", - "name": "Responder error" - }, - { - "parameters": { - "text": "={{\n`Analiza este nombre de archivo de nómina:\n\nPaís seleccionado: ${$json.countryName}\nCódigo del país: ${$json.country}\nNombre del archivo: ${$json.fileName}`\n}}", - "schemaType": "manual", - "inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"country\": {\n \"type\": \"string\",\n \"enum\": [\n \"GT\",\n \"TT\",\n \"PA\",\n \"SV\",\n \"HN\"\n ]\n },\n \"year\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 2100\n },\n \"month\": {\n \"type\": \"integer\",\n \"minimum\": 0,\n \"maximum\": 12\n },\n \"period_type\": {\n \"type\": \"string\",\n \"enum\": [\n \"quincena_15\",\n \"quincena_30\",\n \"mensual\",\n \"desconocido\"\n ]\n },\n \"confidence\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1\n },\n \"explanation\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"country\",\n \"year\",\n \"month\",\n \"period_type\",\n \"confidence\",\n \"explanation\"\n ],\n \"additionalProperties\": false\n}", - "options": { - "systemPromptTemplate": "Eres un clasificador determinista de nombres de archivos de nómina.\n\nTu tarea es identificar el país seleccionado, el año, el mes y el período de pago utilizando únicamente la información presente en el nombre del archivo y el país recibido.\n\nNo inventes datos que no estén contenidos o razonablemente codificados en el nombre.\n\nREGLAS GENERALES:\n\n1. Devuelve exactamente el código de país recibido:\n - GT para Guatemala.\n - TT para Trinidad y Tobago.\n - PA para Panamá.\n - SV para El Salvador.\n - HN para Honduras.\n\n2. Detecta el año de cuatro dígitos. También puede aparecer dentro de una fecha compacta como YYYYMMDD; por ejemplo, 20260617 representa el año 2026.\n\n3. Convierte el mes a un número aceptando nombres en español o inglés:\n Enero / January = 1\n Febrero / February = 2\n Marzo / March = 3\n Abril / April = 4\n Mayo / May = 5\n Junio / June = 6\n Julio / July = 7\n Agosto / August = 8\n Septiembre / September = 9\n Octubre / October = 10\n Noviembre / November = 11\n Diciembre / December = 12\n\n4. Usa period_type = quincena_15 cuando aparezca cualquiera de estas señales:\n - 1Q\n - Q1\n - Primera quincena\n - 1ra quincena\n - First fortnight\n - First half\n - Mid month\n - Día 15\n - 15th\n\n5. Usa period_type = quincena_30 cuando aparezca cualquiera de estas señales:\n - 2Q\n - Q2\n - Segunda quincena\n - 2da quincena\n - Second fortnight\n - Second half\n - Fin de mes\n - End of month\n - Month end\n - Día 30\n - Día 31\n - 30th\n - 31st\n - El último día real del mes, incluyendo 28 o 29 de febrero\n\n6. Aunque el último día real del mes no sea 30, utiliza quincena_30 para representar la segunda quincena o cierre de mes.\n\n7. Si solamente aparecen el mes y el año, sin ninguna señal de quincena ni día de cierre, utiliza mensual.\n\n8. Para nombres como \"GLM_TT_Payroll June 30TH_20260617.xlsx\":\n - country = TT\n - year = 2026\n - month = 6\n - period_type = quincena_30\n\n9. Para Honduras también reconoce nombres como \"Nomina 7.1 2026 GLM HN.xlsx\" (mes 7, primera quincena) y \"Nomina 7.2 2026 GLM HN.xlsx\" (mes 7, segunda quincena). En general, cuando ese patrón forma parte del nombre de nómina, M.1 = quincena_15 y M.2 = quincena_30.\n\n10. Si no puedes identificar de manera confiable el año o el mes, utiliza:\n year = 0\n month = 0\n period_type = desconocido\n\n11. confidence debe estar entre 0 y 1.\n\n12. explanation debe ser breve y mencionar las señales encontradas en el nombre.\n\nNo agregues campos diferentes a los definidos por el esquema." - } - }, - "type": "@n8n/n8n-nodes-langchain.informationExtractor", - "typeVersion": 1.2, - "position": [ - 7056, - 2512 - ], - "id": "95117474-e746-4a8a-9f7f-66eeb536b5d5", - "name": "Extraer período con Gemini", - "retryOnFail": true, - "waitBetweenTries": 2000, - "onError": "continueRegularOutput" - }, - { - "parameters": { - "modelName": "models/gemini-2.5-pro", - "options": {} - }, - "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini", - "typeVersion": 1.1, - "position": [ - 7056, - 2688 - ], - "id": "5f2f1579-5479-4be5-8303-3a03c7f4084c", - "name": "Gemini - Analizar título nómina", - "credentials": { - "googlePalmApi": { - "id": "jvsXYwL6IOoY2DBU", - "name": "Isaac - Gemini Api Pago" - } - } - }, - { - "parameters": { - "jsCode": "const request =\n $('Validar y preparar solicitud').first().json || {};\n\nconst incoming = $input.first().json || {};\n\nfunction cleanText(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeText(value) {\n return cleanText(value)\n .toUpperCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[_\\-.,()[\\]{}]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction parseObject(value) {\n if (\n value &&\n typeof value === 'object' &&\n !Array.isArray(value)\n ) {\n return value;\n }\n\n if (typeof value !== 'string') {\n return {};\n }\n\n const cleaned = value\n .replace(/```json/gi, '')\n .replace(/```/g, '')\n .trim();\n\n try {\n return JSON.parse(cleaned);\n } catch (error) {\n return {};\n }\n}\n\nfunction clamp(value, minimum, maximum) {\n const number = Number(value);\n\n if (!Number.isFinite(number)) {\n return minimum;\n }\n\n return Math.min(\n maximum,\n Math.max(minimum, number)\n );\n}\n\nfunction pad(number) {\n return String(number).padStart(2, '0');\n}\n\nfunction getLastDay(year, month) {\n return new Date(\n Date.UTC(year, month, 0)\n ).getUTCDate();\n}\n\nfunction buildPeriodDates(\n year,\n month,\n periodType\n) {\n if (\n !Number.isInteger(year) ||\n year < 2000 ||\n !Number.isInteger(month) ||\n month < 1 ||\n month > 12\n ) {\n return {\n periodStart: null,\n periodEnd: null,\n };\n }\n\n const lastDay = getLastDay(year, month);\n const yearMonth = `${year}-${pad(month)}`;\n\n if (periodType === 'quincena_15') {\n return {\n periodStart: `${yearMonth}-01`,\n periodEnd: `${yearMonth}-15`,\n };\n }\n\n if (periodType === 'quincena_30') {\n return {\n periodStart: `${yearMonth}-16`,\n periodEnd:\n `${yearMonth}-${pad(lastDay)}`,\n };\n }\n\n if (periodType === 'mensual') {\n return {\n periodStart: `${yearMonth}-01`,\n periodEnd:\n `${yearMonth}-${pad(lastDay)}`,\n };\n }\n\n return {\n periodStart: null,\n periodEnd: null,\n };\n}\n\nconst countryNames = {\n GT: 'Guatemala',\n TT: 'Trinidad y Tobago',\n PA: 'Panamá',\n SV: 'El Salvador',\n HN: 'Honduras',\n};\n\nconst country = ['GT', 'TT', 'PA', 'SV', 'HN'].includes(\n String(request.country || '').toUpperCase()\n)\n ? String(request.country).toUpperCase()\n : 'GT';\n\nconst countryName =\n request.countryName ||\n countryNames[country];\n\nconst monthNames = {\n 1: 'Enero',\n 2: 'Febrero',\n 3: 'Marzo',\n 4: 'Abril',\n 5: 'Mayo',\n 6: 'Junio',\n 7: 'Julio',\n 8: 'Agosto',\n 9: 'Septiembre',\n 10: 'Octubre',\n 11: 'Noviembre',\n 12: 'Diciembre',\n};\n\nconst monthPatterns = [\n ['ENERO', 1],\n ['JANUARY', 1],\n ['JAN', 1],\n\n ['FEBRERO', 2],\n ['FEBRUARY', 2],\n ['FEB', 2],\n\n ['MARZO', 3],\n ['MARCH', 3],\n ['MAR', 3],\n\n ['ABRIL', 4],\n ['APRIL', 4],\n ['APR', 4],\n\n ['MAYO', 5],\n ['MAY', 5],\n\n ['JUNIO', 6],\n ['JUNE', 6],\n ['JUN', 6],\n\n ['JULIO', 7],\n ['JULY', 7],\n ['JUL', 7],\n\n ['AGOSTO', 8],\n ['AUGUST', 8],\n ['AUG', 8],\n\n ['SEPTIEMBRE', 9],\n ['SETIEMBRE', 9],\n ['SEPTEMBER', 9],\n ['SEPT', 9],\n ['SEP', 9],\n\n ['OCTUBRE', 10],\n ['OCTOBER', 10],\n ['OCT', 10],\n\n ['NOVIEMBRE', 11],\n ['NOVEMBER', 11],\n ['NOV', 11],\n\n ['DICIEMBRE', 12],\n ['DECEMBER', 12],\n ['DEC', 12],\n];\n\nconst monthPatternText =\n monthPatterns\n .map(([name]) => name)\n .sort((a, b) => b.length - a.length)\n .join('|');\n\nconst validPeriodTypes = new Set([\n 'quincena_15',\n 'quincena_30',\n 'mensual',\n 'desconocido',\n]);\n\nconst fileName = cleanText(request.fileName);\nconst normalizedFileName =\n normalizeText(fileName);\n\nconst hondurasDecimal = country === 'HN'\n ? fileName.match(/(?:nomina|nómina)?\\s*(\\d{1,2})\\.(1|2)\\s+(20\\d{2})/i)\n : null;\n\nconst compactDateMatch =\n normalizedFileName.match(\n /\\b(20\\d{2})(0[1-9]|1[0-2])([0-2]\\d|3[01])\\b/\n );\n\nconst standaloneYearMatch =\n normalizedFileName.match(/\\b(20\\d{2})\\b/);\n\nconst fallbackYear = hondurasDecimal\n ? Number(hondurasDecimal[3])\n : standaloneYearMatch\n ? Number(standaloneYearMatch[1])\n : compactDateMatch\n ? Number(compactDateMatch[1])\n : 0;\n\nlet fallbackMonth = hondurasDecimal ? Number(hondurasDecimal[1]) : 0;\n\nfor (const [monthName, monthNumber] of monthPatterns) {\n const monthRegex = new RegExp(\n `\\\\b${monthName}\\\\b`\n );\n\n if (monthRegex.test(normalizedFileName)) {\n fallbackMonth = monthNumber;\n break;\n }\n}\n\nif (!fallbackMonth && compactDateMatch) {\n fallbackMonth = Number(compactDateMatch[2]);\n}\n\nconst monthThenDayRegex = new RegExp(\n `\\\\b(?:${monthPatternText})\\\\s+([0-3]?\\\\d)(?:ST|ND|RD|TH)?\\\\b`\n);\n\nconst dayThenMonthRegex = new RegExp(\n `\\\\b([0-3]?\\\\d)(?:ST|ND|RD|TH)?\\\\s+(?:DE\\\\s+)?(?:${monthPatternText})\\\\b`\n);\n\nconst explicitDayRegex =\n /\\b(?:DIA|DAY)\\s+([0-3]?\\d)(?:ST|ND|RD|TH)?\\b/;\n\nconst monthThenDayMatch =\n normalizedFileName.match(monthThenDayRegex);\n\nconst dayThenMonthMatch =\n normalizedFileName.match(dayThenMonthRegex);\n\nconst explicitDayMatch =\n normalizedFileName.match(explicitDayRegex);\n\nconst detectedDay = monthThenDayMatch\n ? Number(monthThenDayMatch[1])\n : dayThenMonthMatch\n ? Number(dayThenMonthMatch[1])\n : explicitDayMatch\n ? Number(explicitDayMatch[1])\n : 0;\n\nconst firstFortnight =\n /\\b(1Q|Q1)\\b/.test(normalizedFileName) ||\n normalizedFileName.includes(\n 'PRIMERA QUINCENA'\n ) ||\n normalizedFileName.includes(\n '1RA QUINCENA'\n ) ||\n normalizedFileName.includes(\n 'FIRST FORTNIGHT'\n ) ||\n normalizedFileName.includes(\n 'FIRST HALF'\n ) ||\n normalizedFileName.includes(\n 'MID MONTH'\n ) ||\n normalizedFileName.includes(\n 'MIDMONTH'\n );\n\nconst secondFortnight =\n /\\b(2Q|Q2)\\b/.test(normalizedFileName) ||\n normalizedFileName.includes(\n 'SEGUNDA QUINCENA'\n ) ||\n normalizedFileName.includes(\n '2DA QUINCENA'\n ) ||\n normalizedFileName.includes(\n 'SECOND FORTNIGHT'\n ) ||\n normalizedFileName.includes(\n 'SECOND HALF'\n ) ||\n normalizedFileName.includes(\n 'FIN DE MES'\n ) ||\n normalizedFileName.includes(\n 'END OF MONTH'\n ) ||\n normalizedFileName.includes(\n 'MONTH END'\n ) ||\n normalizedFileName.includes(\n 'MONTHEND'\n );\n\nlet fallbackPeriodType = 'desconocido';\nlet fallbackConfidence = 0.2;\nlet strongPeriodEvidence = false;\n\nif (hondurasDecimal) {\n fallbackPeriodType = hondurasDecimal[2] === '1' ? 'quincena_15' : 'quincena_30';\n fallbackConfidence = 1;\n strongPeriodEvidence = true;\n} else if (firstFortnight) {\n fallbackPeriodType = 'quincena_15';\n fallbackConfidence = 0.98;\n strongPeriodEvidence = true;\n} else if (secondFortnight) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.98;\n strongPeriodEvidence = true;\n} else if (detectedDay === 15) {\n fallbackPeriodType = 'quincena_15';\n fallbackConfidence = 0.95;\n strongPeriodEvidence = true;\n} else if (\n fallbackYear &&\n fallbackMonth &&\n detectedDay === getLastDay(\n fallbackYear,\n fallbackMonth\n )\n) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.95;\n strongPeriodEvidence = true;\n} else if (\n detectedDay === 30 ||\n detectedDay === 31\n) {\n fallbackPeriodType = 'quincena_30';\n fallbackConfidence = 0.94;\n strongPeriodEvidence = true;\n} else if (\n fallbackYear &&\n fallbackMonth\n) {\n fallbackPeriodType = 'mensual';\n fallbackConfidence = 0.75;\n}\n\nlet rawAI =\n incoming.output ??\n incoming.result ??\n incoming.text ??\n incoming;\n\nrawAI = parseObject(rawAI);\n\nconst aiCountry =\n String(rawAI.country || '').toUpperCase();\n\nconst aiYear = Number(rawAI.year);\nconst aiMonth = Number(rawAI.month);\n\nconst aiPeriodType =\n validPeriodTypes.has(rawAI.period_type)\n ? rawAI.period_type\n : 'desconocido';\n\nconst aiConfidence = clamp(\n rawAI.confidence,\n 0,\n 1\n);\n\nconst aiIsUsable =\n Number.isInteger(aiYear) &&\n aiYear >= 2000 &&\n aiYear <= 2100 &&\n Number.isInteger(aiMonth) &&\n aiMonth >= 1 &&\n aiMonth <= 12 &&\n aiPeriodType !== 'desconocido';\n\nlet year =\n fallbackYear ||\n (aiIsUsable ? aiYear : 0);\n\nlet month =\n fallbackMonth ||\n (aiIsUsable ? aiMonth : 0);\n\nlet periodType =\n aiIsUsable\n ? aiPeriodType\n : fallbackPeriodType;\n\nlet source =\n aiIsUsable\n ? 'gemini'\n : 'local_fallback';\n\nlet confidence =\n aiIsUsable\n ? aiConfidence\n : fallbackConfidence;\n\nlet conflictResolved = false;\n\nif (\n aiCountry &&\n aiCountry !== country\n) {\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n strongPeriodEvidence &&\n periodType !== fallbackPeriodType\n) {\n periodType = fallbackPeriodType;\n source = 'gemini_validated_with_rules';\n confidence = Math.max(\n fallbackConfidence,\n Math.min(aiConfidence, 0.9)\n );\n conflictResolved = true;\n}\n\nif (\n fallbackYear &&\n aiIsUsable &&\n aiYear !== fallbackYear\n) {\n year = fallbackYear;\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n fallbackMonth &&\n aiIsUsable &&\n aiMonth !== fallbackMonth\n) {\n month = fallbackMonth;\n source = 'gemini_validated_with_rules';\n conflictResolved = true;\n}\n\nif (\n !aiIsUsable &&\n fallbackYear &&\n fallbackMonth\n) {\n year = fallbackYear;\n month = fallbackMonth;\n periodType = fallbackPeriodType;\n confidence = fallbackConfidence;\n source = 'local_fallback';\n}\n\nconst complete =\n year >= 2000 &&\n month >= 1 &&\n month <= 12 &&\n periodType !== 'desconocido';\n\nconst dates = buildPeriodDates(\n year,\n month,\n periodType\n);\n\nlet periodDescription = 'Período desconocido';\n\nif (periodType === 'quincena_15') {\n periodDescription = 'Quincena 15';\n}\n\nif (periodType === 'quincena_30') {\n periodDescription =\n 'Quincena 30 / fin de mes';\n}\n\nif (periodType === 'mensual') {\n periodDescription = 'Mensual';\n}\n\nconst monthLabel =\n monthNames[month] || 'Mes desconocido';\n\nconst periodLabel = complete\n ? `${monthLabel} ${year} · ${periodDescription}`\n : 'Período pendiente de confirmar';\n\nconst requiresManualReview =\n !complete ||\n confidence < 0.75;\n\nconst explanation =\n cleanText(rawAI.explanation) ||\n (\n source === 'local_fallback'\n ? 'El período fue identificado mediante las reglas locales de respaldo.'\n : 'El período fue identificado mediante Gemini y validado con las reglas del portal.'\n );\n\nreturn [\n {\n json: {\n ok: true,\n\n message: requiresManualReview\n ? 'El archivo fue analizado, pero el período debe confirmarse manualmente.'\n : 'El período de la nómina fue detectado correctamente.',\n\n detected: {\n country,\n countryName,\n\n fileName,\n\n year,\n month,\n\n periodType,\n period_type: periodType,\n\n periodLabel,\n period_label: periodLabel,\n\n periodStart: dates.periodStart,\n period_start: dates.periodStart,\n\n periodEnd: dates.periodEnd,\n period_end: dates.periodEnd,\n\n confidence:\n Math.round(confidence * 100) / 100,\n\n complete,\n requiresManualReview,\n requires_manual_review:\n requiresManualReview,\n\n source,\n conflictResolved,\n explanation,\n },\n\n debug: {\n fallback: {\n country,\n year: fallbackYear,\n month: fallbackMonth,\n periodType: fallbackPeriodType,\n detectedDay,\n confidence: fallbackConfidence,\n strongPeriodEvidence,\n compactDate:\n compactDateMatch?.[0] || null,\n },\n\n gemini: {\n country: aiCountry || null,\n year: aiYear || 0,\n month: aiMonth || 0,\n periodType: aiPeriodType,\n confidence: aiConfidence,\n explanation:\n cleanText(rawAI.explanation),\n },\n },\n },\n },\n];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 7392, - 2528 - ], - "id": "0c24e1d9-1abb-4c8b-934e-d1cd7d0db5fc", - "name": "Normalizar análisis IA" - }, - { - "parameters": { - "options": { - "responseCode": 200, - "responseHeaders": { - "entries": [ - { - "name": "Content-Type", - "value": "application/json" - } - ] - } - } - }, - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.5, - "position": [ - 7600, - 2528 - ], - "id": "7c889159-aba3-4288-851e-d4c117453465", - "name": "Responder análisis" - }, - { - "parameters": { - "content": "# 🧠 ANÁLISIS AUTOMÁTICO DE NÓMINA — GT, TT, PA y SV\n\nAnaliza el nombre o título del archivo de nómina para identificar automáticamente el período correspondiente antes de ejecutar la verificación.\n\n## Recepción y validación\n\nEl Webhook recibe desde el Portal de Verificación de Nóminas la información del archivo seleccionado.\n\nLa solicitud puede incluir:\n\n- Nombre del archivo.\n- Título o descripción disponible.\n- País o portal de origen.\n- Identificador de la solicitud.\n- Información complementaria enviada por la aplicación.\n\nAntes de utilizar inteligencia artificial, el flujo valida que exista suficiente información para analizar el archivo.\n\n## Extracción con Gemini\n\nCuando la solicitud es válida, Gemini interpreta el nombre o título de la nómina e intenta determinar:\n\n- Año.\n- Mes.\n- Tipo de período.\n- Quincena 15.\n- Quincena 30 o fin de mes.\n- Nómina mensual.\n- País, cuando puede inferirse de forma confiable.\n- Nivel de confianza del análisis.\n\nGemini se utiliza únicamente para interpretar el texto recibido; no ejecuta el cruce de nómina ni modifica información.\n\n## Normalización del resultado\n\nLa respuesta de Gemini se transforma al formato requerido por la aplicación.\n\nEste bloque:\n\n- Limpia texto adicional generado por la IA.\n- Normaliza el año a formato numérico.\n- Convierte el mes al valor esperado por el portal.\n- Estandariza el tipo de período.\n- Corrige variaciones como “segunda quincena”, “fin de mes” o “30”.\n- Conserva los valores originales cuando la IA no puede determinar un dato.\n- Prepara una respuesta consistente para Guatemala, Trinidad y Tobago, Panamá y El Salvador.\n\n## Respuesta al portal\n\nCuando el análisis termina, el workflow devuelve a la aplicación:\n\n- Indicador de éxito.\n- Año detectado.\n- Mes detectado.\n- Tipo de período detectado.\n- País identificado, cuando corresponda.\n- Nivel de confianza.\n- Datos que requieren revisión manual.\n\nLos valores detectados sirven para completar automáticamente el formulario, pero el usuario puede corregirlos antes de ejecutar el proceso.\n\n## Ruta de error\n\nCuando la solicitud es inválida:\n\n- No se llama a Gemini.\n- No se intenta interpretar información incompleta.\n- Se devuelve una respuesta de error al portal.\n- Se indica que falta el nombre del archivo u otro dato obligatorio.\n\n## Reglas\n\n- No inventar un período cuando el título no contiene información suficiente.\n- No ejecutar el cruce de nómina desde este workflow.\n- No tratar una predicción de baja confianza como un dato definitivo.\n- Mantener la opción de corrección manual en la aplicación.\n- Responder siempre al Webhook, tanto en la ruta válida como en la ruta de error.\n- Utilizar una estructura de respuesta compatible con los portales de Guatemala, Trinidad y Tobago, Panamá y El Salvador.", - "height": 1488, - "width": 2256, - "color": 2 - }, - "type": "n8n-nodes-base.stickyNote", - "typeVersion": 1, - "position": [ - 5568, - 1968 - ], - "id": "a32f6dbb-6c5b-4505-a79e-593d8799c41c", - "name": "Sticky Note" - } - ], - "pinData": {}, - "connections": { - "Webhook - Analizar Nómina": { - "main": [ - [ - { - "node": "Validar y preparar solicitud", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validar y preparar solicitud": { - "main": [ - [ - { - "node": "¿Solicitud válida?", - "type": "main", - "index": 0 - } - ] - ] - }, - "¿Solicitud válida?": { - "main": [ - [ - { - "node": "Extraer período con Gemini", - "type": "main", - "index": 0 - } - ], - [ - { - "node": "Responder error", - "type": "main", - "index": 0 - } - ] - ] - }, - "Gemini - Analizar título nómina": { - "ai_languageModel": [ - [ - { - "node": "Extraer período con Gemini", - "type": "ai_languageModel", - "index": 0 - } - ] - ] - }, - "Extraer período con Gemini": { - "main": [ - [ - { - "node": "Normalizar análisis IA", - "type": "main", - "index": 0 - } - ] - ] - }, - "Normalizar análisis IA": { - "main": [ - [ - { - "node": "Responder análisis", - "type": "main", - "index": 0 - } - ] - ] - } - }, - "active": false, - "settings": { - "executionOrder": "v1", - "binaryMode": "separate", - "availableInMCP": true, - "timeSavedMode": "fixed", - "errorWorkflow": "puF4LUczoSz3hcek", - "timezone": "America/Santo_Domingo", - "callerPolicy": "workflowsFromSameOwner" - }, - "versionId": "6bf17633-b996-46d8-8ac2-ee59c9137093", - "meta": { - "templateCredsSetupCompleted": true, - "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" - }, - "tags": [] -} \ No newline at end of file diff --git a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Honduras.json b/workflows_n8n/Portal de Verificaci├│n de N├│mina - Honduras.json index 090230c..d5163e7 100644 --- a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Honduras.json +++ b/workflows_n8n/Portal de Verificaci├│n de N├│mina - Honduras.json @@ -14,7 +14,7 @@ -1088, 512 ], - "id": "9a529a55-ec46-4e0d-8901-d4a2e5c059f9", + "id": "5d3b8ad0-7348-447e-b5e2-7a00235bb69c", "name": "Webhook", "webhookId": "517b24db-a370-4701-8281-06b7d277b403" }, @@ -28,7 +28,7 @@ -848, 512 ], - "id": "7daf7b32-2a8f-4724-b912-da5d6f80c86d", + "id": "0983b28d-86ea-4453-85e2-90f2b0fd159f", "name": "Preparar entrada app" }, { @@ -41,7 +41,7 @@ -608, 352 ], - "id": "73b4ccb3-b19f-415e-8d2b-f7ef1d43dcfe", + "id": "c7d5a5c0-0044-4570-800a-1069eef2a92e", "name": "Parsear CSV banco HN" }, { @@ -54,7 +54,7 @@ -608, 672 ], - "id": "5d72ed5f-ca78-4d72-9b64-2f92bc7735dc", + "id": "01c05dab-e323-4d59-97ab-348cb3e2e25b", "name": "Preparar archivos nómina HN" }, { @@ -62,8 +62,8 @@ "operation": "xlsx", "options": { "headerRow": true, - "sheetName": "={{ $json.sheet_name }}", - "range": "={{ $json.range }}" + "range": "={{ $json.range }}", + "sheetName": "={{ $json.sheet_name }}" } }, "type": "n8n-nodes-base.extractFromFile", @@ -72,7 +72,7 @@ -368, 672 ], - "id": "460f89f9-a7dd-48ab-a6bf-7d96a33c2f8e", + "id": "d10dc605-66f2-4f44-9ed5-60473d57fd24", "name": "Extract - Archivo Nómina HN", "retryOnFail": false, "onError": "continueRegularOutput" @@ -87,7 +87,7 @@ -128, 672 ], - "id": "1495fff4-c34d-4885-b1a9-bf648765b25b", + "id": "da41c543-d4dd-44e1-90fe-39c81866adce", "name": "Normalizar Nómina HN" }, { @@ -102,7 +102,7 @@ 128, 512 ], - "id": "951d4aea-afdd-4e3b-bcff-30ac7232ccbe", + "id": "a0eac81e-b6b4-475a-b9e9-f72e421e052b", "name": "Merge Banco + Nómina HN" }, { @@ -159,7 +159,7 @@ 368, 352 ], - "id": "fe29b239-2467-40f5-b84f-822376bd4b10", + "id": "b0c95a03-b147-4380-beb3-f822cb7025dd", "name": "HTTP - Empleados BambooHR HN", "retryOnFail": true, "maxTries": 3, @@ -181,7 +181,7 @@ 608, 352 ], - "id": "52f020c5-b1d0-4a51-803b-2576f1c7ab4e", + "id": "36f26790-e15c-4900-be40-e6d031053a4f", "name": "Normalizar BambooHR HN" }, { @@ -196,7 +196,7 @@ 848, 512 ], - "id": "87f27436-4cbd-44e6-926e-b66d2998741b", + "id": "318d9c84-08fc-4124-b360-fafecc6ff320", "name": "Merge - Agregar BambooHR HN" }, { @@ -244,7 +244,7 @@ 1088, 512 ], - "id": "ec09ee1f-6974-4ac5-9f24-42c7747375ee", + "id": "e1578802-aadc-4b81-ad17-a623912a20d4", "name": "Obtener alias BambooHR Supabase", "retryOnFail": true, "maxTries": 2, @@ -253,7 +253,7 @@ }, { "parameters": { - "jsCode": "const data = $('Merge - Agregar BambooHR HN').first().json || {};\nconst aliasResponse = $input.first().json || {};\nconst validatedBambooAliases = Array.isArray(aliasResponse.aliases)\n ? aliasResponse.aliases\n : Array.isArray(aliasResponse)\n ? aliasResponse\n : [];\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst normalizedNameCache = new Map();\nconst nameWordsCache = new Map();\nconst nameMatchScoreCache = new Map();\n\nfunction normalizeName(value) {\n const raw = clean(value);\n if (normalizedNameCache.has(raw)) {\n return normalizedNameCache.get(raw);\n }\n\n const normalized = raw\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n normalizedNameCache.set(raw, normalized);\n return normalized;\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction moneyDiff(payroll, bank) {\n return roundMoney((Number(payroll) || 0) - (Number(bank) || 0));\n}\n\n// Los archivos de Honduras presentan diferencias de redondeo de 0.01 y,\n// en algunos pagos USD, de hasta 0.03 entre la hoja y el plano bancario.\n// Esas variaciones no representan una discrepancia operativa.\nfunction sameCent(a, b) {\n return Math.abs(moneyDiff(a, b)) <= 0.05;\n}\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los', 'y', 'e', 'el',\n 'da', 'do', 'dos', 'das', 'van', 'von',\n]);\n\nfunction nameWords(value) {\n const normalized = normalizeName(value);\n if (nameWordsCache.has(normalized)) {\n return nameWordsCache.get(normalized);\n }\n\n const words = normalized\n .split(' ')\n .filter((word) => word.length > 1 && !IGNORED_NAME_TOKENS.has(word));\n\n nameWordsCache.set(normalized, words);\n return words;\n}\n\nfunction editDistance(a, b) {\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n let previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n const current = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);\n }\n previous = current;\n }\n return previous[b.length];\n}\n\nfunction tokenMatches(a, b) {\n if (a === b) return true;\n const minLength = Math.min(a.length, b.length);\n if (minLength >= 8 && editDistance(a, b) <= 2) return true;\n if (minLength >= 5 && editDistance(a, b) <= 1) return true;\n return false;\n}\n\nfunction samePersonName(a, b) {\n const normalizedA = normalizeName(a);\n const normalizedB = normalizeName(b);\n if (!normalizedA || !normalizedB) return false;\n if (normalizedA === normalizedB) return true;\n\n const wordsA = nameWords(a);\n const wordsB = nameWords(b);\n if (!wordsA.length || !wordsB.length) return false;\n\n const usedB = new Set();\n let matches = 0;\n for (const wordA of wordsA) {\n const matchIndex = wordsB.findIndex((wordB, index) =>\n !usedB.has(index) && tokenMatches(wordA, wordB)\n );\n if (matchIndex >= 0) {\n usedB.add(matchIndex);\n matches += 1;\n }\n }\n\n const smallerLength = Math.min(wordsA.length, wordsB.length);\n const ratio = matches / smallerLength;\n if (smallerLength <= 2) return matches === smallerLength && matches >= 2;\n return matches >= 2 && ratio >= 0.6;\n}\n\nfunction bankNameLooksConsistent(fileName, holderName) {\n const fileWords = nameWords(fileName);\n const holderWords = nameWords(holderName);\n if (!fileWords.length || !holderWords.length) return true;\n\n const used = new Set();\n for (const fw of fileWords) {\n let idx = holderWords.findIndex((hw, i) => !used.has(i) && hw === fw);\n\n // Los archivos BAC pueden truncar el último carácter del nombre\n // (ej. ALVARAD vs ALVARADO). Aceptamos solo truncamiento por prefijo,\n // no errores internos/transposiciones; por eso SANTAIGO vs SANTIAGO\n // sigue reportándose como diferencia de nombre.\n if (idx < 0 && fw.length >= 5) {\n idx = holderWords.findIndex((hw, i) =>\n !used.has(i) &&\n Math.abs(hw.length - fw.length) <= 2 &&\n (hw.startsWith(fw) || fw.startsWith(hw))\n );\n }\n\n if (idx < 0 && fw.length <= 3) {\n idx = holderWords.findIndex((hw, i) => !used.has(i) && hw.startsWith(fw));\n }\n\n if (idx < 0) return false;\n used.add(idx);\n }\n return true;\n}\n\nfunction nameMatchScore(a, b) {\n const na = normalizeName(a);\n const nb = normalizeName(b);\n if (!na || !nb) return 0;\n\n const cacheKey = na <= nb ? `${na}\\u0000${nb}` : `${nb}\\u0000${na}`;\n if (nameMatchScoreCache.has(cacheKey)) {\n return nameMatchScoreCache.get(cacheKey);\n }\n\n if (na === nb) {\n nameMatchScoreCache.set(cacheKey, 1);\n return 1;\n }\n\n const wa = nameWords(a);\n const wb = nameWords(b);\n if (!samePersonName(a, b)) {\n nameMatchScoreCache.set(cacheKey, 0);\n return 0;\n }\n\n const setA = new Set(wa);\n const setB = new Set(wb);\n let exact = 0;\n for (const w of setA) if (setB.has(w)) exact += 1;\n const min = Math.max(1, Math.min(setA.size, setB.size));\n const score = Math.min(\n 0.99,\n 0.75 + (exact / min) * 0.20 + (wa.length === wb.length ? 0.04 : 0)\n );\n\n nameMatchScoreCache.set(cacheKey, score);\n return score;\n}\n\nfunction bankNames(row) {\n return [row.bank_name_file, row.bank_account_holder]\n .map(clean)\n .filter(Boolean);\n}\n\nfunction bestNameScore(payrollName, bankRow) {\n return Math.max(0, ...bankNames(bankRow).map((name) => nameMatchScore(payrollName, name)));\n}\n\nfunction bankDisplayName(row) {\n return clean(row.bank_name_file) || clean(row.bank_account_holder) || 'Pago bancario';\n}\n\nfunction bankRowKey(row, index) {\n return clean(row.bank_row_key) || `${clean(row.source_file)}|${row.row_number || index + 1}`;\n}\n\n\nconst metadata = data.metadata || {};\n\nfunction parseBankDate(value, sourceFile = '') {\n const raw = clean(value);\n let match = raw.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (match) {\n return {\n year: Number(match[3]),\n month: Number(match[2]),\n day: Number(match[1]),\n };\n }\n\n match = raw.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})/);\n if (match) {\n return {\n year: Number(match[1]),\n month: Number(match[2]),\n day: Number(match[3]),\n };\n }\n\n // Respaldo por el patrón habitual del nombre: _DDMMYY / _DDMMYYYY.\n const fileMatch = clean(sourceFile).match(/_(\\d{2})(\\d{2})(\\d{2}|\\d{4})(?:\\D|$)/);\n if (!fileMatch) return null;\n\n const yearPart = Number(fileMatch[3]);\n return {\n year: fileMatch[3].length === 2 ? 2000 + yearPart : yearPart,\n month: Number(fileMatch[2]),\n day: Number(fileMatch[1]),\n };\n}\n\nfunction selectedPeriodKind() {\n const type = clean(metadata.period_type).toLowerCase();\n if (type === 'quincena_15') return 'q15';\n if (type === 'quincena_30') return 'q30';\n return 'unknown';\n}\n\nfunction conceptPeriodHint(row) {\n const concept = normalizeName(row.concept || '');\n if (!concept) return 'unknown';\n\n if (\n /(?:^| )1(?:ra|er|era)? quincena(?: |$)/.test(concept) ||\n concept.includes('primera quincena') ||\n concept.includes('quincena 1ra') ||\n concept.includes('quincena 1')\n ) {\n return 'q15';\n }\n\n if (\n /(?:^| )2(?:da|do|nda)? quincena(?: |$)/.test(concept) ||\n concept.includes('segunda quincena') ||\n concept.includes('quincena 2da') ||\n concept.includes('quincena 2') ||\n concept.includes('fin de mes')\n ) {\n return 'q30';\n }\n\n return 'unknown';\n}\n\nfunction periodInfoForBankRow(row) {\n const selected = selectedPeriodKind();\n const rowDate = parseBankDate(row.application_date, row.source_file);\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n const hint = conceptPeriodHint(row);\n\n if (selected === 'unknown') {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (hint !== 'unknown' && hint === selected) {\n return { known: true, selected, inPeriod: true, affinity: 4, rowDate, hint };\n }\n\n if (!rowDate || !selectedYear || !selectedMonth) {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (rowDate.year !== selectedYear || rowDate.month !== selectedMonth) {\n return { known: true, selected, inPeriod: false, affinity: 0, rowDate, hint };\n }\n\n const inHalf = selected === 'q15'\n ? rowDate.day <= 15\n : rowDate.day >= 16;\n\n return {\n known: true,\n selected,\n inPeriod: inHalf,\n affinity: inHalf ? 3 : 0,\n rowDate,\n hint,\n };\n}\n\nfunction isSupplementPayroll(payroll) {\n return clean(payroll?.source_kind).toLowerCase() !== 'quincenal';\n}\n\nfunction bankCandidatePeriodScore(payroll, bank) {\n const info = periodInfoForBankRow(bank);\n if (!info.known || info.inPeriod) return info.affinity || 1;\n\n // Una extraordinaria/vacación/XIII Mes puede pagarse fuera de la mitad\n // estricta del mes. Se permite como respaldo, siempre detrás de cualquier\n // candidato del período seleccionado.\n if (isSupplementPayroll(payroll) && info.rowDate) {\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n if (info.rowDate.year === selectedYear && info.rowDate.month === selectedMonth) {\n return 0.5;\n }\n }\n\n return -1;\n}\n\nconst payrollRows = Array.isArray(data.payroll?.rows) ? data.payroll.rows : [];\nconst bankRows = Array.isArray(data.bank?.rows) ? data.bank.rows : [];\n\n// Guardia de seguridad: nunca generar un reporte \"válido\" cuando el request\n// traía archivos pero el parser no consiguió ninguna fila. Esto evita que\n// todas las personas de nómina aparezcan falsamente como discrepancias.\nif (payrollRows.length === 0) {\n throw new Error(\n 'Honduras: no se pudo leer ninguna fila válida de nómina. ' +\n 'Revisa los archivos Excel de Honduras y las hojas de nómina configuradas.'\n );\n}\n\nif (bankRows.length === 0) {\n const detectedFiles = Number(data.bank?.files_count || data.summary?.bank_files_count || 0);\n const invalidFiles = Array.isArray(data.bank?.file_summaries)\n ? data.bank.file_summaries.filter((file) => file?.ok === false)\n : [];\n\n throw new Error(\n 'Honduras: no se pudo leer ninguna transacción bancaria. ' +\n `Archivos bancarios detectados: ${detectedFiles}. ` +\n (invalidFiles.length\n ? `Archivos con error de formato: ${invalidFiles.map((file) => file.file_name).join(', ')}.`\n : 'Verifica que los CSV sean los planos bancarios esperados.')\n );\n}\n\nconst bambooEmployees = Array.isArray(data.bamboo?.employees) ? data.bamboo.employees : [];\nconst resolvedNameMatches = data.bamboo?.resolved_name_matches || {};\nconst bambooValidationAvailable = data.bamboo?.fetch_complete === true &&\n data.bamboo?.validation_available === true && bambooEmployees.length > 0;\n\nconst bambooByEmployeeNumber = new Map();\nconst bambooById = new Map();\nconst bambooByExactName = new Map();\nfor (const employee of bambooEmployees) {\n const number = clean(employee.employee_number || employee.employeeNumber);\n const id = clean(employee.bamboo_id || employee.id);\n if (number) {\n const arr = bambooByEmployeeNumber.get(number) || [];\n arr.push(employee);\n bambooByEmployeeNumber.set(number, arr);\n }\n if (id) bambooById.set(id, employee);\n for (const alias of employee.aliases || [employee.full_name]) {\n const normalized = normalizeName(alias);\n if (!normalized) continue;\n const arr = bambooByExactName.get(normalized) || [];\n arr.push(employee);\n bambooByExactName.set(normalized, arr);\n }\n}\n\nfunction employeeKey(employee) {\n return clean(employee?.bamboo_id || employee?.id) ||\n clean(employee?.employee_number || employee?.employeeNumber) ||\n normalizeName(employee?.full_name || employee?.displayName);\n}\n\nfunction validatedAliasDecision(row) {\n if (!validatedBambooAliases.length) return null;\n const rowAccount = normalizeAccount(row.account);\n const names = bankNames(row).map(normalizeName).filter(Boolean);\n const candidates = validatedBambooAliases.filter((alias) => {\n const country = clean(alias.pais || alias.country).toUpperCase();\n if (country && country !== 'HN') return false;\n const source = normalizeName(alias.nombre_origen || alias.source_name || alias.alias || '');\n if (!source || !names.includes(source)) return false;\n const aliasAccount = normalizeAccount(alias.cuenta_bancaria || alias.cuenta_banco || alias.bank_account || '');\n return !aliasAccount || !rowAccount || aliasAccount === rowAccount;\n });\n if (candidates.length !== 1) return null;\n const alias = candidates[0];\n let employee = null;\n const employeeNumber = clean(alias.bamboo_employee_number || '');\n const bambooId = clean(alias.bamboo_employee_id || '');\n const bambooName = normalizeName(alias.bamboo_nombre || '');\n if (employeeNumber && (bambooByEmployeeNumber.get(employeeNumber) || []).length === 1) {\n employee = bambooByEmployeeNumber.get(employeeNumber)[0];\n } else if (bambooId && bambooById.has(bambooId)) {\n employee = bambooById.get(bambooId);\n } else if (bambooName && (bambooByExactName.get(bambooName) || []).length === 1) {\n employee = bambooByExactName.get(bambooName)[0];\n }\n if (!employee) return null;\n return {\n found: true,\n matched_by: 'validated_bamboo_alias',\n confidence: 1,\n employee,\n employee_key: employeeKey(employee),\n bamboo_alias: clean(alias.bamboo_nombre) || employee.full_name || '',\n };\n}\n\nfunction precomputedDecision(name) {\n const normalized = normalizeName(name);\n if (!normalized) return null;\n const decision = resolvedNameMatches[normalized];\n if (!decision || decision.found !== true) return null;\n const index = Number(decision.employee_index);\n const employee = Number.isInteger(index) && index >= 0 ? bambooEmployees[index] : null;\n const key = clean(decision.employee_key) || employeeKey(employee);\n if (!key) return null;\n return {\n found: true,\n matched_by: decision.matched_by || 'precomputed_name',\n confidence: Number(decision.confidence || 0),\n employee,\n employee_key: key,\n bamboo_alias: clean(decision.bamboo_alias) || employee?.full_name || '',\n query_name: clean(name),\n };\n}\n\nfunction resolveBamboo(row, linkedPayrollNames = []) {\n if (!bambooValidationAvailable) return { found: false, reason: 'validation_unavailable' };\n const validated = validatedAliasDecision(row);\n if (validated) return validated;\n\n const names = Array.from(new Set([...bankNames(row), ...linkedPayrollNames.map(clean)].filter(Boolean)));\n const decisions = names.map(precomputedDecision).filter(Boolean);\n if (!decisions.length) return { found: false, reason: 'no_confident_name_match' };\n\n const byKey = new Map();\n for (const d of decisions) {\n const current = byKey.get(d.employee_key);\n if (!current || d.confidence > current.confidence) byKey.set(d.employee_key, d);\n }\n const ranked = Array.from(byKey.values()).sort((a, b) => b.confidence - a.confidence);\n if (ranked.length === 1) return ranked[0];\n if ((ranked[0].confidence - ranked[1].confidence) >= 0.08) return ranked[0];\n\n // Si el nombre corto del archivo es ambiguo pero el nombre legal del\n // cuentahabiente apunta de forma única a BambooHR, damos prioridad a este\n // último. Caso real validado: \"Suyapa Palma\" vs\n // \"SUYAPA MELISA PALMA AGUILAR\".\n const holderNormalized = normalizeName(row.bank_account_holder || '');\n if (holderNormalized) {\n const holderMatches = ranked.filter((decision) =>\n normalizeName(decision.query_name || '') === holderNormalized\n );\n if (holderMatches.length === 1) return holderMatches[0];\n }\n\n return { found: false, reason: 'conflicting_precomputed_name_matches', candidates: ranked.slice(0, 3) };\n}\n\nconst usedBankKeys = new Set();\nconst matchedPayrollIndexes = new Set();\nconst matchedPairs = [];\nconst bankIndexByKey = new Map();\n\n// Índice por centavos para que la pasada principal no compare cada fila de\n// nómina contra todas las transacciones del banco. Esto preserva exactamente\n// la lógica original: la primera pasada ya exigía el mismo monto al centavo.\nconst bankIndexesByAmountCents = new Map();\n\nfunction amountCents(value) {\n return Math.round(roundMoney(value) * 100);\n}\n\nbankRows.forEach((row, index) => {\n const key = bankRowKey(row, index);\n bankIndexByKey.set(key, index);\n\n const amountKey = `${clean(row.currency || 'HNL').toUpperCase()}|${amountCents(row.amount)}`;\n const bucket = bankIndexesByAmountCents.get(amountKey) || [];\n bucket.push(index);\n bankIndexesByAmountCents.set(amountKey, bucket);\n});\n\nfunction availableNameCandidates(payroll, requireExactAmount) {\n const candidates = [];\n\n // La pasada exacta solo inspecciona filas con el mismo importe. La segunda\n // pasada conserva el barrido completo y el mismo criterio de persona fuerte\n // + menor diferencia que tenía el workflow validado.\n const candidateIndexes = requireExactAmount\n ? (bankIndexesByAmountCents.get(`${clean(payroll.currency || 'HNL').toUpperCase()}|${amountCents(payroll.payroll_amount)}`) || [])\n : null;\n\n const inspectBank = (bankIndex) => {\n const bank = bankRows[bankIndex];\n if (!bank) return;\n\n const key = bankRowKey(bank, bankIndex);\n if (usedBankKeys.has(key)) return;\n if (clean(payroll.currency || 'HNL').toUpperCase() !== clean(bank.currency || 'HNL').toUpperCase()) return;\n\n if (requireExactAmount && !sameCent(payroll.payroll_amount, bank.amount)) return;\n\n const periodScore = bankCandidatePeriodScore(payroll, bank);\n if (periodScore < 0) return;\n\n const score = bestNameScore(payroll.employee_name, bank);\n if (score <= 0) return;\n\n candidates.push({\n bank,\n bankIndex,\n key,\n score,\n periodScore,\n amountDiff: Math.abs(moneyDiff(payroll.payroll_amount, bank.amount)),\n });\n };\n\n if (candidateIndexes) {\n for (const bankIndex of candidateIndexes) {\n inspectBank(bankIndex);\n }\n } else {\n for (let bankIndex = 0; bankIndex < bankRows.length; bankIndex++) {\n inspectBank(bankIndex);\n }\n }\n\n candidates.sort((a, b) => {\n if (b.periodScore !== a.periodScore) return b.periodScore - a.periodScore;\n if (b.score !== a.score) return b.score - a.score;\n if (a.amountDiff !== b.amountDiff) return a.amountDiff - b.amountDiff;\n return a.bankIndex - b.bankIndex;\n });\n\n return candidates;\n}\n\n// Primera pasada: persona + monto exacto. Es la evidencia más fuerte y evita\n// que extraordinarias/vacaciones de una misma persona se mezclen entre sí.\npayrollRows.forEach((payroll, payrollIndex) => {\n const candidates = availableNameCandidates(payroll, true);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\n// Segunda pasada: persona fuerte aunque el monto sea distinto. Solo se toma\n// una contraparte bancaria por fila de nómina y se prioriza la menor diferencia.\npayrollRows.forEach((payroll, payrollIndex) => {\n if (matchedPayrollIndexes.has(payrollIndex)) return;\n const candidates = availableNameCandidates(payroll, false);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\nconst linkedPayrollNamesByBankKey = new Map();\nfor (const pair of matchedPairs) {\n const current = linkedPayrollNamesByBankKey.get(pair.key) || [];\n current.push(pair.payroll.employee_name);\n linkedPayrollNamesByBankKey.set(pair.key, current);\n}\n\n// Filas bancarias del período de Honduras.\n// Toda fila emparejada entra siempre. Para una fila no emparejada se usa la\n// fecha/concepto del plano para evitar arrastrar accidentalmente otra quincena.\nconst includedBankRows = [];\nconst excludedBankRows = [];\n\nfor (let index = 0; index < bankRows.length; index++) {\n const row = bankRows[index];\n const key = bankRowKey(row, index);\n\n if (usedBankKeys.has(key)) {\n includedBankRows.push({ row, key, index, inclusion: 'matched_payroll' });\n continue;\n }\n\n const periodInfo = periodInfoForBankRow(row);\n if (periodInfo.known && !periodInfo.inPeriod) {\n excludedBankRows.push({ row, key, reason: 'outside_selected_payroll_period' });\n continue;\n }\n\n includedBankRows.push({ row, key, index, inclusion: 'unmatched_current_period' });\n}\n\nconst includedBankKeySet = new Set(includedBankRows.map((entry) => entry.key));\n\nconst coreRows = [];\nconst wrongAccountGroups = new Map();\nfor (const pair of matchedPairs) {\n if (!includedBankKeySet.has(pair.key)) continue;\n const payrollAmount = roundMoney(pair.payroll.payroll_amount);\n const bankAmount = roundMoney(pair.bank.amount);\n const difference = moneyDiff(payrollAmount, bankAmount);\n const currency = clean(pair.payroll.currency || pair.bank.currency || 'HNL').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAccount = normalizeAccount(pair.payroll.account || pair.payroll.payroll_account);\n const bankAccount = normalizeAccount(pair.bank.account);\n const accountMismatch = Boolean(payrollAccount && bankAccount && payrollAccount !== bankAccount);\n if (accountMismatch) {\n const groupKey = `${normalizeName(pair.payroll.employee_name)}|${currency}|${payrollAccount}|${bankAccount}`;\n const current = wrongAccountGroups.get(groupKey) || {\n id: `hn_wrong_account_${wrongAccountGroups.size + 1}`,\n employee: pair.payroll.employee_name,\n employee_name: pair.payroll.employee_name,\n employeeNumber: pair.payroll.employee_number || '', employee_number: pair.payroll.employee_number || '',\n account: bankAccount, payrollAccount, payroll_account: payrollAccount, bankAccount, bank_account: bankAccount,\n currency, payrollAmount:0, payroll_amount:0, bankAmount:0, bank_amount:0, difference:0,\n status:'Pendiente revisión', category:'posible_cuenta_mal_digitada', subcategory:'nombre_y_monto_coinciden_cuenta_diferente',\n observation:`El nombre y el monto coinciden, pero la cuenta de nómina (${payrollAccount}) es diferente a la cuenta del banco (${bankAccount}).`,\n source_rows:[], source_sheets:new Set(), source_files:new Set(), shipment_numbers:new Set(), plan_numbers:new Set(),\n bank_name_file: pair.bank.bank_name_file || '', bank_account_holder: pair.bank.bank_account_holder || ''\n };\n current.payrollAmount=roundMoney(current.payrollAmount+payrollAmount); current.payroll_amount=current.payrollAmount;\n current.bankAmount=roundMoney(current.bankAmount+bankAmount); current.bank_amount=current.bankAmount; current.difference=moneyDiff(current.payrollAmount,current.bankAmount);\n current.source_rows.push(pair.payroll); if(pair.payroll.source_sheet)current.source_sheets.add(pair.payroll.source_sheet); if(pair.bank.source_file)current.source_files.add(pair.bank.source_file); if(pair.bank.shipment_number)current.shipment_numbers.add(pair.bank.shipment_number); if(pair.bank.plan_number)current.plan_numbers.add(pair.bank.plan_number);\n wrongAccountGroups.set(groupKey,current); continue;\n }\n const exact = sameCent(payrollAmount, bankAmount);\n coreRows.push({id:`hn_match_${pair.payrollIndex+1}_${pair.bankIndex+1}`,employee:pair.payroll.employee_name,employee_name:pair.payroll.employee_name,employeeNumber:pair.payroll.employee_number||'',employee_number:pair.payroll.employee_number||'',account:bankAccount||payrollAccount,payrollAccount:payrollAccount,payroll_account:payrollAccount,bankAccount:bankAccount,bank_account:bankAccount,currency,payrollAmount,payroll_amount:payrollAmount,bankAmount,bank_amount:bankAmount,difference,status:exact?'Coincide':'Pendiente revisión',category:exact?'coincidencia':'discrepancia',subcategory:exact?'persona_cuenta_y_monto_exactos':'persona_y_cuenta_coinciden_monto_diferente',observation:exact?'Coincidencia de persona, cuenta y monto.':difference>0?'Se pagó de menos.':'Se pagó de más.',source_kind:pair.payroll.source_kind||'',source_sheet:pair.payroll.source_sheet||'',source_file:pair.bank.source_file||'',shipment_number:pair.bank.shipment_number||'',plan_number:pair.bank.plan_number||'',plan_label:pair.bank.plan_label||'',reference:pair.bank.reference||'',bank_name_file:pair.bank.bank_name_file||'',bank_account_holder:pair.bank.bank_account_holder||''});\n}\nfor (let payrollIndex=0; payrollIndex({...row,source_sheets:Array.from(row.source_sheets),source_files:Array.from(row.source_files),shipment_numbers:Array.from(row.shipment_numbers),plan_numbers:Array.from(row.plan_numbers),source_sheet:Array.from(row.source_sheets).join(' / '),source_file:Array.from(row.source_files).join(' / '),shipment_number:Array.from(row.shipment_numbers).join(' / '),plan_number:Array.from(row.plan_numbers).join(' / ')}));\ncoreRows.push(...wrongAccountRows);\n\n// Banco sin Nómina se agrupa para que repeticiones de la misma persona/cuenta\n// se presenten como un solo hallazgo, tal como en los reportes manuales.\n// Banco sin nómina se conserva por transacción, no agrupado por cuenta.\n// Esto es importante porque una misma persona/cuenta puede aparecer en más de\n// un envío dentro del período y cada pago debe quedar auditado por separado.\nconst bankWithoutPayrollRows = [];\nfor (const entry of includedBankRows) {\n if (usedBankKeys.has(entry.key)) continue;\n const row = entry.row;\n const displayName = bankDisplayName(row);\n const rowCurrency = clean(row.currency || 'HNL').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const bankAmount = roundMoney(Number(row.amount || 0));\n\n bankWithoutPayrollRows.push({\n id: `hn_bank_without_payroll_${entry.index + 1}`,\n employee: displayName,\n employee_name: displayName,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount,\n bank_amount: bankAmount,\n difference: roundMoney(0 - bankAmount),\n status: 'Pendiente revisión',\n category: 'banco_sin_nomina',\n subcategory: 'pago_banco_sin_registro_nomina',\n observation: 'Pago bancario sin registro equivalente en las planillas cargadas.',\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n source_files: row.source_file ? [row.source_file] : [],\n shipment_numbers: row.shipment_number ? [row.shipment_number] : [],\n plan_numbers: row.plan_number ? [row.plan_number] : [],\n source_rows: [row],\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\ncoreRows.push(...bankWithoutPayrollRows);\n\nconst nameDifferenceRows = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n if (!clean(row.bank_name_file) || !clean(row.bank_account_holder)) continue;\n if (bankNameLooksConsistent(row.bank_name_file, row.bank_account_holder)) continue;\n nameDifferenceRows.push({\n id: `hn_bank_name_difference_${entry.index + 1}`,\n employee: row.bank_name_file,\n employee_name: row.bank_name_file,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount: roundMoney(row.amount),\n bank_amount: roundMoney(row.amount),\n difference: 0,\n status: 'Pendiente revisión',\n category: 'diferencia_nombre_banco',\n subcategory: 'nombre_archivo_vs_cuentahabiente',\n observation: `El Nombre en Archivo (${row.bank_name_file}) no coincide con el Nombre del Cuentahabiente (${row.bank_account_holder}).`,\n bank_name_file: row.bank_name_file,\n bank_account_holder: row.bank_account_holder,\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\n\nconst bankWithoutBambooMap = new Map();\nconst bambooMatchDetails = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const linkedNames = linkedPayrollNamesByBankKey.get(entry.key) || [];\n const decision = resolveBamboo(row, linkedNames);\n bambooMatchDetails.push({\n bank_row_key: entry.key,\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n account: row.account || '',\n amount: row.amount || 0,\n linked_payroll_names: linkedNames,\n ...decision,\n });\n if (decision.found) continue;\n\n const displayName = bankDisplayName(row);\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n const groupingKey = `${normalizeAccount(row.account)}|${normalizeName(displayName)}|${rowCurrency}`;\n const current = bankWithoutBambooMap.get(groupingKey) || {\n id: `hn_bank_without_bamboo_${bankWithoutBambooMap.size + 1}`,\n bank_name_file: row.bank_name_file || displayName,\n bank_account_holder: row.bank_account_holder || '',\n employee_name: row.bank_name_file || displayName,\n account: row.account || '',\n currency: rowCurrency,\n bank_amount: 0,\n amount: 0,\n status: 'Pendiente revisión',\n category: 'banco_sin_bamboo',\n shipment_numbers: new Set(),\n references: new Set(),\n source_files: new Set(),\n source_rows: [],\n };\n current.bank_amount = roundMoney(current.bank_amount + Number(row.amount || 0));\n current.amount = current.bank_amount;\n if (row.shipment_number) current.shipment_numbers.add(row.shipment_number);\n if (row.reference) current.references.add(row.reference);\n if (row.source_file) current.source_files.add(row.source_file);\n current.source_rows.push(row);\n bankWithoutBambooMap.set(groupingKey, current);\n}\n\nconst bankWithoutBamboo = Array.from(bankWithoutBambooMap.values()).map((row) => ({\n ...row,\n shipment_numbers: Array.from(row.shipment_numbers),\n shipment_number: Array.from(row.shipment_numbers).join(' / '),\n references: Array.from(row.references),\n source_files: Array.from(row.source_files),\n source_file: Array.from(row.source_files).join(' / '),\n difference: roundMoney(0 - row.bank_amount),\n}));\n\nfunction priority(row) {\n const category = clean(row.category).toLowerCase();\n if (category === 'posible_cuenta_mal_digitada') return 1;\n if (category === 'discrepancia') return 2;\n if (category === 'banco_sin_nomina') return 3;\n if (category === 'diferencia_nombre_banco') return 5;\n if (category === 'coincidencia') return 99;\n return 50;\n}\n\nconst rowsFinales = [...coreRows, ...nameDifferenceRows].sort((a, b) => {\n const p = priority(a) - priority(b);\n if (p !== 0) return p;\n return clean(a.employee_name).localeCompare(clean(b.employee_name), 'es');\n});\n\nconst coincidencias = coreRows.filter((row) => row.category === 'coincidencia').length;\nconst discrepancias = coreRows.filter((row) => row.category === 'discrepancia').length;\nconst bancoSinNomina = bankWithoutPayrollRows.length;\nconst diferenciasNombreBanco = nameDifferenceRows.length;\nconst posiblesCuentasMalDigitadas = wrongAccountRows.length;\nconst pendientes = discrepancias + bancoSinNomina + diferenciasNombreBanco;\nconst totalNominaByCurrency = {HNL:0,USD:0};\nfor(const row of payrollRows){const c=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalNominaByCurrency[c]=roundMoney(totalNominaByCurrency[c]+Number(row.payroll_amount||0));}\nconst totalBancoByCurrency = {HNL:0,USD:0};\nfor(const entry of includedBankRows){const c=clean(entry.row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalBancoByCurrency[c]=roundMoney(totalBancoByCurrency[c]+Number(entry.row.amount||0));}\nconst diferenciaByCurrency={HNL:moneyDiff(totalNominaByCurrency.HNL,totalBancoByCurrency.HNL),USD:moneyDiff(totalNominaByCurrency.USD,totalBancoByCurrency.USD)};\nconst totalNomina=totalNominaByCurrency.HNL || totalNominaByCurrency.USD;\nconst totalBanco=totalBancoByCurrency.HNL || totalBancoByCurrency.USD;\nconst diferenciaTotal=totalNominaByCurrency.HNL||totalBancoByCurrency.HNL?diferenciaByCurrency.HNL:diferenciaByCurrency.USD;\n\nconst bambooSummaryCompact = { ...(data.bamboo || {}) };\ndelete bambooSummaryCompact.employees;\n\nreturn [{\n json: {\n ok: true,\n stage: 'cruce_nomina_banco_honduras',\n errors: [],\n metadata: data.metadata || {},\n summary: {\n coincidencias,\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo: bankWithoutBamboo.length,\n nominaSinCuenta: 0,\n diferenciasNombreBanco,\n posiblesCuentasMalDigitadas,\n pendientes,\n filasNominaValidas: Number(data.payroll?.base_rows_count || data.payroll?.valid_rows_count || 0),\n filasNominaSinCuenta: 0,\n suplementosPotenciales: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaAplicados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAplicados: 0,\n suplementosNominaAdjuntados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAdjuntados: Number(data.payroll?.unattached_supplements_count || 0),\n reconciliacionesExactasFinales: coincidencias,\n cuentasNominaAgrupadas: 0,\n transaccionesBanco: includedBankRows.length,\n cuentasBancoAgrupadas: new Set(includedBankRows.map((entry) => normalizeAccount(entry.row.account)).filter(Boolean)).size,\n empleadosBambooHN: Number(data.bamboo?.honduras_count || 0),\n empleadosBambooEnPeriodo: Number(data.bamboo?.active_in_period_count || 0),\n bambooPaginasDescargadas: Number(data.bamboo?.pages_fetched || 0),\n bambooEmpleadosEsperados: Number(data.bamboo?.expected_total || 0),\n bambooDescargaCompleta: Boolean(data.bamboo?.fetch_complete),\n bambooValidacionDisponible: bambooValidationAvailable,\n totalNominaBase: roundMoney(data.payroll?.base_total || 0),\n totalSuplementosAplicados: roundMoney(data.payroll?.supplements_total || 0),\n totalNomina,\n totalBanco,\n diferenciaTotal,\n totalNominaPorMoneda: totalNominaByCurrency,\n totalBancoPorMoneda: totalBancoByCurrency,\n diferenciaTotalPorMoneda: diferenciaByCurrency,\n },\n rows: rowsFinales,\n bankWithoutBamboo,\n nameDifferences: nameDifferenceRows,\n bambooSummary: bambooSummaryCompact,\n reportUrl: null,\n debug: {\n matched_pairs_count: matchedPairs.length,\n included_bank_rows_count: includedBankRows.length,\n excluded_bank_rows_count: excludedBankRows.length,\n excluded_bank_rows_preview: excludedBankRows.slice(0, 100),\n validated_bamboo_aliases_loaded: validatedBambooAliases.length,\n bamboo_matches: bambooMatchDetails,\n bamboo_validation_available: bambooValidationAvailable,\n banco_sin_bamboo: bankWithoutBamboo,\n payroll_preview: payrollRows.slice(0, 20),\n bank_included_preview: includedBankRows.slice(0, 20).map((entry) => entry.row),\n },\n },\n}];\n" + "jsCode": "const data = $('Merge - Agregar BambooHR HN').first().json || {};\nconst aliasResponse = $input.first().json || {};\nconst validatedBambooAliases = Array.isArray(aliasResponse.aliases)\n ? aliasResponse.aliases\n : Array.isArray(aliasResponse)\n ? aliasResponse\n : [];\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst normalizedNameCache = new Map();\nconst nameWordsCache = new Map();\nconst nameMatchScoreCache = new Map();\n\nfunction normalizeName(value) {\n const raw = clean(value);\n if (normalizedNameCache.has(raw)) {\n return normalizedNameCache.get(raw);\n }\n\n const normalized = raw\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n normalizedNameCache.set(raw, normalized);\n return normalized;\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction moneyDiff(payroll, bank) {\n return roundMoney((Number(payroll) || 0) - (Number(bank) || 0));\n}\n\nconst MONEY_TOLERANCE = 0.05;\nconst MONEY_TOLERANCE_CENTS = Math.round(MONEY_TOLERANCE * 100);\n\nfunction sameCent(a, b) {\n return Math.abs(moneyDiff(a, b)) <= MONEY_TOLERANCE + 1e-9;\n}\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los', 'y', 'e', 'el',\n 'da', 'do', 'dos', 'das', 'van', 'von',\n]);\n\nfunction nameWords(value) {\n const normalized = normalizeName(value);\n if (nameWordsCache.has(normalized)) {\n return nameWordsCache.get(normalized);\n }\n\n const words = normalized\n .split(' ')\n .filter((word) => word.length > 1 && !IGNORED_NAME_TOKENS.has(word));\n\n nameWordsCache.set(normalized, words);\n return words;\n}\n\nfunction editDistance(a, b) {\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n let previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n const current = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);\n }\n previous = current;\n }\n return previous[b.length];\n}\n\nfunction tokenMatches(a, b) {\n if (a === b) return true;\n const minLength = Math.min(a.length, b.length);\n if (minLength >= 8 && editDistance(a, b) <= 2) return true;\n if (minLength >= 5 && editDistance(a, b) <= 1) return true;\n return false;\n}\n\nfunction samePersonName(a, b) {\n const normalizedA = normalizeName(a);\n const normalizedB = normalizeName(b);\n if (!normalizedA || !normalizedB) return false;\n if (normalizedA === normalizedB) return true;\n\n const wordsA = nameWords(a);\n const wordsB = nameWords(b);\n if (!wordsA.length || !wordsB.length) return false;\n\n const usedB = new Set();\n let matches = 0;\n for (const wordA of wordsA) {\n const matchIndex = wordsB.findIndex((wordB, index) =>\n !usedB.has(index) && tokenMatches(wordA, wordB)\n );\n if (matchIndex >= 0) {\n usedB.add(matchIndex);\n matches += 1;\n }\n }\n\n const smallerLength = Math.min(wordsA.length, wordsB.length);\n const ratio = matches / smallerLength;\n if (smallerLength <= 2) return matches === smallerLength && matches >= 2;\n return matches >= 2 && ratio >= 0.6;\n}\n\nfunction bankNameLooksConsistent(fileName, holderName) {\n const fileWords = nameWords(fileName);\n const holderWords = nameWords(holderName);\n\n if (!fileWords.length || !holderWords.length) return true;\n\n const used = new Set();\n\n for (const fileWord of fileWords) {\n let matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord === fileWord\n );\n\n // Algunos planos de Honduras recortan el último carácter del apellido\n // por longitud de campo (ej.: ALVARAD -> ALVARADO). Eso no debe\n // convertirse en una diferencia de nombre. No se aceptan sustituciones\n // ortográficas (ej.: GONZALES -> GONZALEZ), porque sí requieren revisión.\n if (matchIndex < 0) {\n matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord.startsWith(fileWord) &&\n (\n (fileWord.length <= 3) ||\n (\n fileWord.length >= 4 &&\n holderWord.length === fileWord.length + 1\n )\n )\n );\n }\n\n if (matchIndex < 0) return false;\n used.add(matchIndex);\n }\n\n return true;\n}\n\nfunction nameMatchScore(a, b) {\n const na = normalizeName(a);\n const nb = normalizeName(b);\n if (!na || !nb) return 0;\n\n const cacheKey = na <= nb ? `${na}\\u0000${nb}` : `${nb}\\u0000${na}`;\n if (nameMatchScoreCache.has(cacheKey)) {\n return nameMatchScoreCache.get(cacheKey);\n }\n\n if (na === nb) {\n nameMatchScoreCache.set(cacheKey, 1);\n return 1;\n }\n\n const wa = nameWords(a);\n const wb = nameWords(b);\n if (!samePersonName(a, b)) {\n nameMatchScoreCache.set(cacheKey, 0);\n return 0;\n }\n\n const setA = new Set(wa);\n const setB = new Set(wb);\n let exact = 0;\n for (const w of setA) if (setB.has(w)) exact += 1;\n const min = Math.max(1, Math.min(setA.size, setB.size));\n const score = Math.min(\n 0.99,\n 0.75 + (exact / min) * 0.20 + (wa.length === wb.length ? 0.04 : 0)\n );\n\n nameMatchScoreCache.set(cacheKey, score);\n return score;\n}\n\nfunction bankNames(row) {\n return [row.bank_name_file, row.bank_account_holder]\n .map(clean)\n .filter(Boolean);\n}\n\nfunction bestNameScore(payrollName, bankRow) {\n return Math.max(0, ...bankNames(bankRow).map((name) => nameMatchScore(payrollName, name)));\n}\n\nfunction bankDisplayName(row) {\n return clean(row.bank_name_file) || clean(row.bank_account_holder) || 'Pago bancario';\n}\n\nfunction bankRowKey(row, index) {\n return clean(row.bank_row_key) || `${clean(row.source_file)}|${row.row_number || index + 1}`;\n}\n\n\nconst metadata = data.metadata || {};\n\nfunction parseBankDate(value, sourceFile = '') {\n const raw = clean(value);\n let match = raw.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (match) {\n return {\n year: Number(match[3]),\n month: Number(match[2]),\n day: Number(match[1]),\n };\n }\n\n match = raw.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})/);\n if (match) {\n return {\n year: Number(match[1]),\n month: Number(match[2]),\n day: Number(match[3]),\n };\n }\n\n // Respaldo por el patrón habitual del nombre: _DDMMYY / _DDMMYYYY.\n const fileMatch = clean(sourceFile).match(/_(\\d{2})(\\d{2})(\\d{2}|\\d{4})(?:\\D|$)/);\n if (!fileMatch) return null;\n\n const yearPart = Number(fileMatch[3]);\n return {\n year: fileMatch[3].length === 2 ? 2000 + yearPart : yearPart,\n month: Number(fileMatch[2]),\n day: Number(fileMatch[1]),\n };\n}\n\nfunction selectedPeriodKind() {\n const type = clean(metadata.period_type).toLowerCase();\n if (type === 'quincena_15') return 'q15';\n if (type === 'quincena_30') return 'q30';\n return 'unknown';\n}\n\nfunction conceptPeriodHint(row) {\n const concept = normalizeName(row.concept || '');\n if (!concept) return 'unknown';\n\n if (\n /(?:^| )1(?:ra|er|era)? quincena(?: |$)/.test(concept) ||\n concept.includes('primera quincena') ||\n concept.includes('quincena 1ra') ||\n concept.includes('quincena 1')\n ) {\n return 'q15';\n }\n\n if (\n /(?:^| )2(?:da|do|nda)? quincena(?: |$)/.test(concept) ||\n concept.includes('segunda quincena') ||\n concept.includes('quincena 2da') ||\n concept.includes('quincena 2') ||\n concept.includes('fin de mes')\n ) {\n return 'q30';\n }\n\n return 'unknown';\n}\n\nfunction periodInfoForBankRow(row) {\n const selected = selectedPeriodKind();\n const rowDate = parseBankDate(row.application_date, row.source_file);\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n const hint = conceptPeriodHint(row);\n\n if (selected === 'unknown') {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (hint !== 'unknown' && hint === selected) {\n return { known: true, selected, inPeriod: true, affinity: 4, rowDate, hint };\n }\n\n if (!rowDate || !selectedYear || !selectedMonth) {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (rowDate.year !== selectedYear || rowDate.month !== selectedMonth) {\n return { known: true, selected, inPeriod: false, affinity: 0, rowDate, hint };\n }\n\n const inHalf = selected === 'q15'\n ? rowDate.day <= 15\n : rowDate.day >= 16;\n\n return {\n known: true,\n selected,\n inPeriod: inHalf,\n affinity: inHalf ? 3 : 0,\n rowDate,\n hint,\n };\n}\n\nfunction isSupplementPayroll(payroll) {\n return clean(payroll?.source_kind).toLowerCase() !== 'quincenal';\n}\n\nfunction bankCandidatePeriodScore(payroll, bank) {\n const info = periodInfoForBankRow(bank);\n if (!info.known || info.inPeriod) return info.affinity || 1;\n\n // Una extraordinaria/vacación/XIII Mes puede pagarse fuera de la mitad\n // estricta del mes. Se permite como respaldo, siempre detrás de cualquier\n // candidato del período seleccionado.\n if (isSupplementPayroll(payroll) && info.rowDate) {\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n if (info.rowDate.year === selectedYear && info.rowDate.month === selectedMonth) {\n return 0.5;\n }\n }\n\n return -1;\n}\n\nconst payrollRows = Array.isArray(data.payroll?.rows) ? data.payroll.rows : [];\nconst bankRows = Array.isArray(data.bank?.rows) ? data.bank.rows : [];\n\n// Guardia de seguridad: nunca generar un reporte \"válido\" cuando el request\n// traía archivos pero el parser no consiguió ninguna fila. Esto evita que\n// todas las personas de nómina aparezcan falsamente como discrepancias.\nif (payrollRows.length === 0) {\n throw new Error(\n 'Honduras: no se pudo leer ninguna fila válida de nómina. ' +\n 'Revisa los archivos Excel de Honduras y las hojas de nómina configuradas.'\n );\n}\n\nif (bankRows.length === 0) {\n const detectedFiles = Number(data.bank?.files_count || data.summary?.bank_files_count || 0);\n const invalidFiles = Array.isArray(data.bank?.file_summaries)\n ? data.bank.file_summaries.filter((file) => file?.ok === false)\n : [];\n\n throw new Error(\n 'Honduras: no se pudo leer ninguna transacción bancaria. ' +\n `Archivos bancarios detectados: ${detectedFiles}. ` +\n (invalidFiles.length\n ? `Archivos con error de formato: ${invalidFiles.map((file) => file.file_name).join(', ')}.`\n : 'Verifica que los CSV sean los planos bancarios esperados.')\n );\n}\n\nconst bambooEmployees = Array.isArray(data.bamboo?.employees) ? data.bamboo.employees : [];\nconst resolvedNameMatches = data.bamboo?.resolved_name_matches || {};\nconst bambooValidationAvailable = data.bamboo?.fetch_complete === true &&\n data.bamboo?.validation_available === true && bambooEmployees.length > 0;\n\nconst bambooByEmployeeNumber = new Map();\nconst bambooById = new Map();\nconst bambooByExactName = new Map();\nfor (const employee of bambooEmployees) {\n const number = clean(employee.employee_number || employee.employeeNumber);\n const id = clean(employee.bamboo_id || employee.id);\n if (number) {\n const arr = bambooByEmployeeNumber.get(number) || [];\n arr.push(employee);\n bambooByEmployeeNumber.set(number, arr);\n }\n if (id) bambooById.set(id, employee);\n for (const alias of employee.aliases || [employee.full_name]) {\n const normalized = normalizeName(alias);\n if (!normalized) continue;\n const arr = bambooByExactName.get(normalized) || [];\n arr.push(employee);\n bambooByExactName.set(normalized, arr);\n }\n}\n\nfunction employeeKey(employee) {\n return clean(employee?.bamboo_id || employee?.id) ||\n clean(employee?.employee_number || employee?.employeeNumber) ||\n normalizeName(employee?.full_name || employee?.displayName);\n}\n\nfunction validatedAliasDecision(row) {\n if (!validatedBambooAliases.length) return null;\n const rowAccount = normalizeAccount(row.account);\n const names = bankNames(row).map(normalizeName).filter(Boolean);\n const candidates = validatedBambooAliases.filter((alias) => {\n const country = clean(alias.pais || alias.country).toUpperCase();\n if (country && country !== 'HN') return false;\n const source = normalizeName(alias.nombre_origen || alias.source_name || alias.alias || '');\n if (!source || !names.includes(source)) return false;\n const aliasAccount = normalizeAccount(alias.cuenta_bancaria || alias.cuenta_banco || alias.bank_account || '');\n return !aliasAccount || !rowAccount || aliasAccount === rowAccount;\n });\n if (candidates.length !== 1) return null;\n const alias = candidates[0];\n let employee = null;\n const employeeNumber = clean(alias.bamboo_employee_number || '');\n const bambooId = clean(alias.bamboo_employee_id || '');\n const bambooName = normalizeName(alias.bamboo_nombre || '');\n if (employeeNumber && (bambooByEmployeeNumber.get(employeeNumber) || []).length === 1) {\n employee = bambooByEmployeeNumber.get(employeeNumber)[0];\n } else if (bambooId && bambooById.has(bambooId)) {\n employee = bambooById.get(bambooId);\n } else if (bambooName && (bambooByExactName.get(bambooName) || []).length === 1) {\n employee = bambooByExactName.get(bambooName)[0];\n }\n if (!employee) return null;\n return {\n found: true,\n matched_by: 'validated_bamboo_alias',\n confidence: 1,\n employee,\n employee_key: employeeKey(employee),\n bamboo_alias: clean(alias.bamboo_nombre) || employee.full_name || '',\n };\n}\n\nfunction precomputedDecision(name) {\n const normalized = normalizeName(name);\n if (!normalized) return null;\n const decision = resolvedNameMatches[normalized];\n if (!decision || decision.found !== true) return null;\n const index = Number(decision.employee_index);\n const employee = Number.isInteger(index) && index >= 0 ? bambooEmployees[index] : null;\n const key = clean(decision.employee_key) || employeeKey(employee);\n if (!key) return null;\n return {\n found: true,\n matched_by: decision.matched_by || 'precomputed_name',\n confidence: Number(decision.confidence || 0),\n employee,\n employee_key: key,\n bamboo_alias: clean(decision.bamboo_alias) || employee?.full_name || '',\n query_name: clean(name),\n };\n}\n\nfunction resolveBamboo(row, linkedPayrollNames = []) {\n if (!bambooValidationAvailable) {\n return {\n found: false,\n reason: 'validation_unavailable',\n };\n }\n\n const validated = validatedAliasDecision(row);\n if (validated) return validated;\n\n const holderName = clean(row.bank_account_holder);\n const fileName = clean(row.bank_name_file);\n\n // El nombre legal del cuentahabiente es la evidencia bancaria más fuerte.\n // Si identifica exactamente a una persona que pertenecía al período,\n // prevalece sobre un nombre abreviado que pueda coincidir con un perfil\n // histórico distinto (caso real: Suyapa Palma).\n const holderDecision = holderName\n ? precomputedDecision(holderName)\n : null;\n\n if (\n holderDecision?.found &&\n holderDecision.employee?.overlaps_period === true\n ) {\n return {\n ...holderDecision,\n matched_by:\n `${holderDecision.matched_by || 'precomputed_name'}_holder_in_period`,\n };\n }\n\n const names = Array.from(\n new Set(\n [\n holderName,\n fileName,\n ...linkedPayrollNames.map(clean),\n ].filter(Boolean)\n )\n );\n\n const decisions = names\n .map(precomputedDecision)\n .filter(Boolean);\n\n if (!decisions.length) {\n return {\n found: false,\n reason: 'no_confident_name_match',\n };\n }\n\n // Antes de declarar conflicto, descartar perfiles históricos que no\n // pertenecen al período cuando existe una única persona vigente/en período.\n const inPeriodByKey = new Map();\n\n for (const decision of decisions) {\n if (decision.employee?.overlaps_period !== true) continue;\n\n const current =\n inPeriodByKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n inPeriodByKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const inPeriodRanked =\n Array.from(inPeriodByKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (inPeriodRanked.length === 1) {\n return {\n ...inPeriodRanked[0],\n matched_by:\n `${inPeriodRanked[0].matched_by || 'precomputed_name'}_unique_in_period`,\n };\n }\n\n if (\n inPeriodRanked.length >= 2 &&\n (\n inPeriodRanked[0].confidence -\n inPeriodRanked[1].confidence\n ) >= 0.08\n ) {\n return inPeriodRanked[0];\n }\n\n const byKey = new Map();\n\n for (const decision of decisions) {\n const current =\n byKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n byKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const ranked =\n Array.from(byKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (ranked.length === 1) {\n return ranked[0];\n }\n\n if (\n (\n ranked[0].confidence -\n ranked[1].confidence\n ) >= 0.08\n ) {\n return ranked[0];\n }\n\n return {\n found: false,\n reason: 'conflicting_precomputed_name_matches',\n candidates: ranked.slice(0, 3),\n };\n}\n\nconst usedBankKeys = new Set();\nconst matchedPayrollIndexes = new Set();\nconst matchedPairs = [];\nconst bankIndexByKey = new Map();\n\n// Índice por centavos para que la pasada principal no compare cada fila de\n// nómina contra todas las transacciones del banco. Esto preserva exactamente\n// la lógica original: la primera pasada ya exigía el mismo monto al centavo.\nconst bankIndexesByAmountCents = new Map();\n\nfunction amountCents(value) {\n return Math.round(roundMoney(value) * 100);\n}\n\nbankRows.forEach((row, index) => {\n const key = bankRowKey(row, index);\n bankIndexByKey.set(key, index);\n\n const amountKey = `${clean(row.currency || 'HNL').toUpperCase()}|${amountCents(row.amount)}`;\n const bucket = bankIndexesByAmountCents.get(amountKey) || [];\n bucket.push(index);\n bankIndexesByAmountCents.set(amountKey, bucket);\n});\n\nfunction availableNameCandidates(payroll, requireExactAmount) {\n const candidates = [];\n\n // La pasada exacta solo inspecciona filas con el mismo importe. La segunda\n // pasada conserva el barrido completo y el mismo criterio de persona fuerte\n // + menor diferencia que tenía el workflow validado.\n let candidateIndexes = null;\n\n if (requireExactAmount) {\n const currency =\n clean(payroll.currency || 'HNL')\n .toUpperCase();\n\n const center =\n amountCents(payroll.payroll_amount);\n\n const indexSet = new Set();\n\n for (\n let delta = -MONEY_TOLERANCE_CENTS;\n delta <= MONEY_TOLERANCE_CENTS;\n delta++\n ) {\n const bucket =\n bankIndexesByAmountCents.get(\n `${currency}|${center + delta}`\n ) || [];\n\n for (const bankIndex of bucket) {\n indexSet.add(bankIndex);\n }\n }\n\n candidateIndexes =\n Array.from(indexSet);\n }\n\n const inspectBank = (bankIndex) => {\n const bank = bankRows[bankIndex];\n if (!bank) return;\n\n const key = bankRowKey(bank, bankIndex);\n if (usedBankKeys.has(key)) return;\n if (clean(payroll.currency || 'HNL').toUpperCase() !== clean(bank.currency || 'HNL').toUpperCase()) return;\n\n if (requireExactAmount && !sameCent(payroll.payroll_amount, bank.amount)) return;\n\n const periodScore = bankCandidatePeriodScore(payroll, bank);\n if (periodScore < 0) return;\n\n const score = bestNameScore(payroll.employee_name, bank);\n if (score <= 0) return;\n\n candidates.push({\n bank,\n bankIndex,\n key,\n score,\n periodScore,\n amountDiff: Math.abs(moneyDiff(payroll.payroll_amount, bank.amount)),\n });\n };\n\n if (candidateIndexes) {\n for (const bankIndex of candidateIndexes) {\n inspectBank(bankIndex);\n }\n } else {\n for (let bankIndex = 0; bankIndex < bankRows.length; bankIndex++) {\n inspectBank(bankIndex);\n }\n }\n\n candidates.sort((a, b) => {\n if (b.periodScore !== a.periodScore) return b.periodScore - a.periodScore;\n if (b.score !== a.score) return b.score - a.score;\n if (a.amountDiff !== b.amountDiff) return a.amountDiff - b.amountDiff;\n return a.bankIndex - b.bankIndex;\n });\n\n return candidates;\n}\n\n// Primera pasada: persona + monto exacto. Es la evidencia más fuerte y evita\n// que extraordinarias/vacaciones de una misma persona se mezclen entre sí.\npayrollRows.forEach((payroll, payrollIndex) => {\n const candidates = availableNameCandidates(payroll, true);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\n// Segunda pasada: persona fuerte aunque el monto sea distinto. Solo se toma\n// una contraparte bancaria por fila de nómina y se prioriza la menor diferencia.\npayrollRows.forEach((payroll, payrollIndex) => {\n if (matchedPayrollIndexes.has(payrollIndex)) return;\n const candidates = availableNameCandidates(payroll, false);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\nconst linkedPayrollNamesByBankKey = new Map();\nfor (const pair of matchedPairs) {\n const current = linkedPayrollNamesByBankKey.get(pair.key) || [];\n current.push(pair.payroll.employee_name);\n linkedPayrollNamesByBankKey.set(pair.key, current);\n}\n\n// Filas bancarias del período de Honduras.\n// Toda fila emparejada entra siempre. Para una fila no emparejada se usa la\n// fecha/concepto del plano para evitar arrastrar accidentalmente otra quincena.\nconst includedBankRows = [];\nconst excludedBankRows = [];\n\nfor (let index = 0; index < bankRows.length; index++) {\n const row = bankRows[index];\n const key = bankRowKey(row, index);\n\n if (usedBankKeys.has(key)) {\n includedBankRows.push({ row, key, index, inclusion: 'matched_payroll' });\n continue;\n }\n\n const periodInfo = periodInfoForBankRow(row);\n if (periodInfo.known && !periodInfo.inPeriod) {\n excludedBankRows.push({ row, key, reason: 'outside_selected_payroll_period' });\n continue;\n }\n\n includedBankRows.push({ row, key, index, inclusion: 'unmatched_current_period' });\n}\n\nconst includedBankKeySet = new Set(includedBankRows.map((entry) => entry.key));\n\nconst coreRows = [];\nconst wrongAccountGroups = new Map();\nfor (const pair of matchedPairs) {\n if (!includedBankKeySet.has(pair.key)) continue;\n const payrollAmount = roundMoney(pair.payroll.payroll_amount);\n const bankAmount = roundMoney(pair.bank.amount);\n const difference = moneyDiff(payrollAmount, bankAmount);\n const currency = clean(pair.payroll.currency || pair.bank.currency || 'HNL').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAccount = normalizeAccount(pair.payroll.account || pair.payroll.payroll_account);\n const bankAccount = normalizeAccount(pair.bank.account);\n const accountMismatch = Boolean(payrollAccount && bankAccount && payrollAccount !== bankAccount);\n if (accountMismatch) {\n const groupKey = `${normalizeName(pair.payroll.employee_name)}|${currency}|${payrollAccount}|${bankAccount}`;\n const current = wrongAccountGroups.get(groupKey) || {\n id: `hn_wrong_account_${wrongAccountGroups.size + 1}`,\n employee: pair.payroll.employee_name,\n employee_name: pair.payroll.employee_name,\n employeeNumber: pair.payroll.employee_number || '', employee_number: pair.payroll.employee_number || '',\n account: bankAccount, payrollAccount, payroll_account: payrollAccount, bankAccount, bank_account: bankAccount,\n currency, payrollAmount:0, payroll_amount:0, bankAmount:0, bank_amount:0, difference:0,\n status:'Pendiente revisión', category:'posible_cuenta_mal_digitada', subcategory:'nombre_y_monto_coinciden_cuenta_diferente',\n observation:`El nombre y el monto coinciden, pero la cuenta de nómina (${payrollAccount}) es diferente a la cuenta del banco (${bankAccount}).`,\n source_rows:[], source_sheets:new Set(), source_files:new Set(), shipment_numbers:new Set(), plan_numbers:new Set(),\n bank_name_file: pair.bank.bank_name_file || '', bank_account_holder: pair.bank.bank_account_holder || ''\n };\n current.payrollAmount=roundMoney(current.payrollAmount+payrollAmount); current.payroll_amount=current.payrollAmount;\n current.bankAmount=roundMoney(current.bankAmount+bankAmount); current.bank_amount=current.bankAmount; current.difference=moneyDiff(current.payrollAmount,current.bankAmount);\n current.source_rows.push(pair.payroll); if(pair.payroll.source_sheet)current.source_sheets.add(pair.payroll.source_sheet); if(pair.bank.source_file)current.source_files.add(pair.bank.source_file); if(pair.bank.shipment_number)current.shipment_numbers.add(pair.bank.shipment_number); if(pair.bank.plan_number)current.plan_numbers.add(pair.bank.plan_number);\n wrongAccountGroups.set(groupKey,current); continue;\n }\n const exact = sameCent(payrollAmount, bankAmount);\n coreRows.push({id:`hn_match_${pair.payrollIndex+1}_${pair.bankIndex+1}`,employee:pair.payroll.employee_name,employee_name:pair.payroll.employee_name,employeeNumber:pair.payroll.employee_number||'',employee_number:pair.payroll.employee_number||'',account:bankAccount||payrollAccount,payrollAccount:payrollAccount,payroll_account:payrollAccount,bankAccount:bankAccount,bank_account:bankAccount,currency,payrollAmount,payroll_amount:payrollAmount,bankAmount,bank_amount:bankAmount,difference,status:exact?'Coincide':'Pendiente revisión',category:exact?'coincidencia':'discrepancia',subcategory:exact?'persona_cuenta_y_monto_exactos':'persona_y_cuenta_coinciden_monto_diferente',observation:exact?'Coincidencia de persona, cuenta y monto.':difference>0?'Se pagó de menos.':'Se pagó de más.',source_kind:pair.payroll.source_kind||'',source_sheet:pair.payroll.source_sheet||'',source_file:pair.bank.source_file||'',shipment_number:pair.bank.shipment_number||'',plan_number:pair.bank.plan_number||'',plan_label:pair.bank.plan_label||'',reference:pair.bank.reference||'',bank_name_file:pair.bank.bank_name_file||'',bank_account_holder:pair.bank.bank_account_holder||''});\n}\nfor (let payrollIndex=0; payrollIndex({...row,source_sheets:Array.from(row.source_sheets),source_files:Array.from(row.source_files),shipment_numbers:Array.from(row.shipment_numbers),plan_numbers:Array.from(row.plan_numbers),source_sheet:Array.from(row.source_sheets).join(' / '),source_file:Array.from(row.source_files).join(' / '),shipment_number:Array.from(row.shipment_numbers).join(' / '),plan_number:Array.from(row.plan_numbers).join(' / ')}));\ncoreRows.push(...wrongAccountRows);\n\n// Banco sin Nómina se conserva a nivel de TRANSACCIÓN.\n// Una misma persona/cuenta puede tener más de un pago bancario legítimamente\n// separado; agruparlos reduce artificialmente el número de hallazgos.\nconst bankWithoutPayrollRows = [];\n\nfor (const entry of includedBankRows) {\n if (usedBankKeys.has(entry.key)) continue;\n\n const row = entry.row;\n const displayName =\n bankDisplayName(row);\n\n const rowCurrency =\n clean(row.currency || 'HNL')\n .toUpperCase() === 'USD'\n ? 'USD'\n : 'HNL';\n\n const bankAmount =\n roundMoney(row.amount || 0);\n\n bankWithoutPayrollRows.push({\n id:\n `hn_bank_without_payroll_${entry.index + 1}`,\n employee: displayName,\n employee_name: displayName,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount,\n bank_amount: bankAmount,\n difference:\n roundMoney(0 - bankAmount),\n status: 'Pendiente revisión',\n category: 'banco_sin_nomina',\n subcategory:\n 'pago_banco_sin_registro_nomina',\n observation:\n 'Pago bancario sin registro equivalente en las planillas cargadas.',\n bank_name_file:\n row.bank_name_file || '',\n bank_account_holder:\n row.bank_account_holder || '',\n source_file:\n row.source_file || '',\n shipment_number:\n row.shipment_number || '',\n plan_number:\n row.plan_number || '',\n reference:\n row.reference || '',\n row_number:\n row.row_number || null,\n source_rows: [row],\n });\n}\n\ncoreRows.push(...bankWithoutPayrollRows);\n\nconst nameDifferenceRows = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n if (!clean(row.bank_name_file) || !clean(row.bank_account_holder)) continue;\n if (bankNameLooksConsistent(row.bank_name_file, row.bank_account_holder)) continue;\n nameDifferenceRows.push({\n id: `hn_bank_name_difference_${entry.index + 1}`,\n employee: row.bank_name_file,\n employee_name: row.bank_name_file,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount: roundMoney(row.amount),\n bank_amount: roundMoney(row.amount),\n difference: 0,\n status: 'Pendiente revisión',\n category: 'diferencia_nombre_banco',\n subcategory: 'nombre_archivo_vs_cuentahabiente',\n observation: `El Nombre en Archivo (${row.bank_name_file}) no coincide con el Nombre del Cuentahabiente (${row.bank_account_holder}).`,\n bank_name_file: row.bank_name_file,\n bank_account_holder: row.bank_account_holder,\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\n\nconst bankWithoutBambooMap = new Map();\nconst bambooMatchDetails = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const linkedNames = linkedPayrollNamesByBankKey.get(entry.key) || [];\n const decision = resolveBamboo(row, linkedNames);\n bambooMatchDetails.push({\n bank_row_key: entry.key,\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n account: row.account || '',\n amount: row.amount || 0,\n linked_payroll_names: linkedNames,\n ...decision,\n });\n if (decision.found) continue;\n\n const displayName = bankDisplayName(row);\n const rowCurrency=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';\n const groupingKey = `${normalizeAccount(row.account)}|${normalizeName(displayName)}|${rowCurrency}`;\n const current = bankWithoutBambooMap.get(groupingKey) || {\n id: `hn_bank_without_bamboo_${bankWithoutBambooMap.size + 1}`,\n bank_name_file: row.bank_name_file || displayName,\n bank_account_holder: row.bank_account_holder || '',\n employee_name: row.bank_name_file || displayName,\n account: row.account || '',\n currency: rowCurrency,\n bank_amount: 0,\n amount: 0,\n status: 'Pendiente revisión',\n category: 'banco_sin_bamboo',\n shipment_numbers: new Set(),\n references: new Set(),\n source_files: new Set(),\n source_rows: [],\n };\n current.bank_amount = roundMoney(current.bank_amount + Number(row.amount || 0));\n current.amount = current.bank_amount;\n if (row.shipment_number) current.shipment_numbers.add(row.shipment_number);\n if (row.reference) current.references.add(row.reference);\n if (row.source_file) current.source_files.add(row.source_file);\n current.source_rows.push(row);\n bankWithoutBambooMap.set(groupingKey, current);\n}\n\nconst bankWithoutBamboo = Array.from(bankWithoutBambooMap.values()).map((row) => ({\n ...row,\n shipment_numbers: Array.from(row.shipment_numbers),\n shipment_number: Array.from(row.shipment_numbers).join(' / '),\n references: Array.from(row.references),\n source_files: Array.from(row.source_files),\n source_file: Array.from(row.source_files).join(' / '),\n difference: roundMoney(0 - row.bank_amount),\n}));\n\nfunction priority(row) {\n const category = clean(row.category).toLowerCase();\n if (category === 'posible_cuenta_mal_digitada') return 1;\n if (category === 'discrepancia') return 2;\n if (category === 'banco_sin_nomina') return 3;\n if (category === 'diferencia_nombre_banco') return 5;\n if (category === 'coincidencia') return 99;\n return 50;\n}\n\nconst rowsFinales = [...coreRows, ...nameDifferenceRows].sort((a, b) => {\n const p = priority(a) - priority(b);\n if (p !== 0) return p;\n return clean(a.employee_name).localeCompare(clean(b.employee_name), 'es');\n});\n\nconst coincidencias = coreRows.filter((row) => row.category === 'coincidencia').length;\nconst discrepancias = coreRows.filter((row) => row.category === 'discrepancia').length;\nconst bancoSinNomina = bankWithoutPayrollRows.length;\nconst diferenciasNombreBanco = nameDifferenceRows.length;\nconst posiblesCuentasMalDigitadas = wrongAccountRows.length;\nconst pendientes = discrepancias + bancoSinNomina + diferenciasNombreBanco;\nconst totalNominaByCurrency = {HNL:0,USD:0};\nfor(const row of payrollRows){const c=clean(row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalNominaByCurrency[c]=roundMoney(totalNominaByCurrency[c]+Number(row.payroll_amount||0));}\nconst totalBancoByCurrency = {HNL:0,USD:0};\nfor(const entry of includedBankRows){const c=clean(entry.row.currency||'HNL').toUpperCase()==='USD'?'USD':'HNL';totalBancoByCurrency[c]=roundMoney(totalBancoByCurrency[c]+Number(entry.row.amount||0));}\nconst diferenciaByCurrency={HNL:moneyDiff(totalNominaByCurrency.HNL,totalBancoByCurrency.HNL),USD:moneyDiff(totalNominaByCurrency.USD,totalBancoByCurrency.USD)};\nconst totalNomina=totalNominaByCurrency.HNL || totalNominaByCurrency.USD;\nconst totalBanco=totalBancoByCurrency.HNL || totalBancoByCurrency.USD;\nconst diferenciaTotal=totalNominaByCurrency.HNL||totalBancoByCurrency.HNL?diferenciaByCurrency.HNL:diferenciaByCurrency.USD;\n\nconst bambooSummaryCompact = { ...(data.bamboo || {}) };\ndelete bambooSummaryCompact.employees;\n\nreturn [{\n json: {\n ok: true,\n stage: 'cruce_nomina_banco_honduras',\n errors: [],\n metadata: data.metadata || {},\n summary: {\n coincidencias,\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo: bankWithoutBamboo.length,\n nominaSinCuenta: 0,\n diferenciasNombreBanco,\n posiblesCuentasMalDigitadas,\n pendientes,\n filasNominaValidas: Number(data.payroll?.base_rows_count || data.payroll?.valid_rows_count || 0),\n filasNominaSinCuenta: 0,\n suplementosPotenciales: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaAplicados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAplicados: 0,\n suplementosNominaAdjuntados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAdjuntados: Number(data.payroll?.unattached_supplements_count || 0),\n reconciliacionesExactasFinales: coincidencias,\n cuentasNominaAgrupadas: 0,\n transaccionesBanco: includedBankRows.length,\n cuentasBancoAgrupadas: new Set(includedBankRows.map((entry) => normalizeAccount(entry.row.account)).filter(Boolean)).size,\n empleadosBambooHN: Number(data.bamboo?.honduras_count || 0),\n empleadosBambooEnPeriodo: Number(data.bamboo?.active_in_period_count || 0),\n bambooPaginasDescargadas: Number(data.bamboo?.pages_fetched || 0),\n bambooEmpleadosEsperados: Number(data.bamboo?.expected_total || 0),\n bambooDescargaCompleta: Boolean(data.bamboo?.fetch_complete),\n bambooValidacionDisponible: bambooValidationAvailable,\n totalNominaBase: roundMoney(data.payroll?.base_total || 0),\n totalSuplementosAplicados: roundMoney(data.payroll?.supplements_total || 0),\n totalNomina,\n totalBanco,\n diferenciaTotal,\n totalNominaPorMoneda: totalNominaByCurrency,\n totalBancoPorMoneda: totalBancoByCurrency,\n diferenciaTotalPorMoneda: diferenciaByCurrency,\n },\n rows: rowsFinales,\n bankWithoutBamboo,\n nameDifferences: nameDifferenceRows,\n bambooSummary: bambooSummaryCompact,\n reportUrl: null,\n debug: {\n matched_pairs_count: matchedPairs.length,\n included_bank_rows_count: includedBankRows.length,\n excluded_bank_rows_count: excludedBankRows.length,\n excluded_bank_rows_preview: excludedBankRows.slice(0, 100),\n validated_bamboo_aliases_loaded: validatedBambooAliases.length,\n bamboo_matches: bambooMatchDetails,\n bamboo_validation_available: bambooValidationAvailable,\n banco_sin_bamboo: bankWithoutBamboo,\n payroll_preview: payrollRows.slice(0, 20),\n bank_included_preview: includedBankRows.slice(0, 20).map((entry) => entry.row),\n },\n },\n}];\n" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -261,7 +261,7 @@ 1328, 512 ], - "id": "6f3dcbf7-8940-45f7-b3dd-c9b96553d8da", + "id": "c061625b-f317-41d2-b10a-dd0bec96269c", "name": "Cruzar Nómina vs Banco" }, { @@ -274,7 +274,7 @@ 1568, 512 ], - "id": "9b692b9e-bc13-465b-8c91-804d3909752e", + "id": "d27e1baa-53ec-4862-9467-8b708916955b", "name": "Preparar Google Sheet" }, { @@ -320,7 +320,7 @@ 1808, 512 ], - "id": "6f9b4cad-7771-4db4-b3a2-2e62d304147b", + "id": "c9c2472c-8e08-47dc-814c-9f878e11730e", "name": "Buscar reporte existente Supabase" }, { @@ -340,7 +340,7 @@ 2048, 512 ], - "id": "c971dc31-ead1-4f97-82c6-40f8f421bd5c", + "id": "398d6e91-0f62-4d90-a493-a507d7ee9d8a", "name": "Crear Google Sheet", "retryOnFail": true, "credentials": { @@ -365,7 +365,7 @@ 2288, 512 ], - "id": "abe06c36-f6cd-490d-a88b-4363c7d9211f", + "id": "ce89510d-5f8f-4378-a548-f98297279add", "name": "Leer Google Sheet actual", "retryOnFail": true, "credentials": { @@ -385,7 +385,7 @@ 2528, 512 ], - "id": "e185a0db-f278-48df-a45f-bd8954d97531", + "id": "4fab55a2-a11e-4fda-ae21-93147793a8ee", "name": "Preparar actualización Google Sheet" }, { @@ -407,7 +407,7 @@ 2768, 512 ], - "id": "b7c10e3f-e8ce-4c1c-b258-a4bc0f3ee2fb", + "id": "cc3b923a-6c4d-47f0-902e-98f6123947b9", "name": "Actualizar estructura Google Sheet", "retryOnFail": true, "credentials": { @@ -436,7 +436,7 @@ 3008, 512 ], - "id": "9f6ad41c-5ac9-4555-921a-ecd8f122b045", + "id": "407c9c50-6e67-4e06-ad8f-def748f51f5b", "name": "Limpiar Google Sheet", "retryOnFail": true, "credentials": { @@ -465,7 +465,7 @@ 3248, 512 ], - "id": "972a7c61-0537-4aad-8a9c-781fc4d2749d", + "id": "e9262d82-fea5-46b8-a46c-e44503058b3d", "name": "Escribir Google Sheet", "retryOnFail": true, "credentials": { @@ -494,7 +494,7 @@ 3488, 512 ], - "id": "85899c0a-f31a-4911-8d91-554d0da24e78", + "id": "eeff3ec4-35a7-4643-8e44-8ea5644247e1", "name": "Formatear Google Sheet", "retryOnFail": true, "credentials": { @@ -514,7 +514,7 @@ 3728, 512 ], - "id": "0508be37-3d2a-42c2-b7d5-be42098d813a", + "id": "df55c597-5e31-4237-8eac-cd2598993e20", "name": "Preparar permisos Google Sheet" }, { @@ -534,7 +534,7 @@ 3968, 512 ], - "id": "8e29fb34-a901-4bf2-82c0-d4d9c81e35ce", + "id": "083436a6-f137-4679-aa9c-658cece3301b", "name": "Compartir Google Sheet", "credentials": { "googleOAuth2Api": { @@ -554,7 +554,7 @@ 4208, 512 ], - "id": "dca4a992-cb01-42eb-b64c-b6f0c1440c9e", + "id": "a2397022-6257-4617-88e4-1a5859428e2c", "name": "Preparar histórico Supabase" }, { @@ -593,7 +593,7 @@ 4448, 512 ], - "id": "272ca2cf-cf2a-4531-bca3-c9f92753c614", + "id": "0a1e75d6-ec4e-454d-9eb9-34ef27517ea8", "name": "Insertar histórico Supabase", "onError": "continueRegularOutput" }, @@ -607,7 +607,7 @@ 4688, 432 ], - "id": "fd91d3a5-d496-4f08-945a-dac6d3ec8161", + "id": "6776e4cf-01e3-4ece-878a-3b48cef3e2ca", "name": "Preparar respuesta final" }, { @@ -632,12 +632,12 @@ 4928, 432 ], - "id": "edfd725c-d971-4b46-bf64-8f29ea09f7af", + "id": "1f4078d3-7294-4173-8a97-416b2c83f7e9", "name": "Respond to Webhook" }, { "parameters": { - "jsCode": "const prepared = $('Preparar histórico Supabase').first().json || {};\nconst payload = prepared.supabasePayload || {};\nconst metadata = payload.metadata || {};\nconst summary = payload.summary || {};\nconst reportUpdated = prepared.reportUpdated === true;\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatGeneratedAt(date) {\n const timeZone = 'America/Tegucigalpa';\n\n const dateLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(date);\n\n const timeLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n }).format(date);\n\n const hour24Raw = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour: '2-digit',\n hour12: false,\n }).format(date);\n\n const hour24 =\n Number(String(hour24Raw).replace(/\\D/g, '')) % 24;\n\n const hourArticle =\n hour24 === 1 || hour24 === 13\n ? 'a la'\n : 'a las';\n\n return `${dateLabel} ${hourArticle} ${timeLabel}`;\n}\n\nfunction metricCard(label, value, accent) {\n return `\n \n \n \n \n \n ${escapeHtml(label)}\n \n\n \n ${numberValue(value).toLocaleString('en-US')}\n \n \n \n \n \n `;\n}\n\nconst countryCode = 'HN';\nconst countryName = 'Honduras';\n\nconst periodLabel =\n payload.period_label ||\n metadata.period_label ||\n [\n payload.year || metadata.year,\n payload.month || metadata.month,\n payload.period_type || metadata.period_type,\n ]\n .filter(Boolean)\n .join(' · ') ||\n 'Período no especificado';\n\nconst executedByName =\n payload.ejecutado_por_nombre ||\n metadata.requested_by_name ||\n 'Usuario GLM';\n\nconst executedByEmail =\n payload.ejecutado_por_email ||\n metadata.requested_by_email ||\n '';\n\nconst reportUrl =\n payload.report_url ||\n prepared.reportUrl ||\n '';\n\nconst discrepancias = numberValue(\n payload.discrepancias ??\n summary.discrepancias\n);\n\nconst bancoSinNomina = numberValue(\n payload.banco_sin_nomina ??\n summary.bancoSinNomina\n);\n\nconst bancoSinBamboo = numberValue(\n payload.banco_sin_bamboo ??\n summary.bancoSinBamboo\n);\n\nconst diferenciasNombreBanco = numberValue(\n metadata.diferencias_nombre_banco ??\n summary.diferenciasNombreBanco\n);\n\nconst nominaSinCuenta = numberValue(\n payload.nomina_sin_cuenta ??\n summary.nominaSinCuenta\n);\n\nconst coincidencias = numberValue(\n payload.coincidencias ??\n summary.coincidencias\n);\n\nconst generatedAt = new Date();\nconst generatedAtLabel =\n formatGeneratedAt(generatedAt);\n\nconst statusLabel =\n String(payload.estado || '').toLowerCase() === 'resuelto'\n ? 'Resuelto'\n : 'Pendiente de revisión';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp';\n\nconst subject = reportUpdated\n ? `Reporte actualizado - Cruce de Cuentas ${countryName} - ${periodLabel}`\n : `Nuevo reporte generado - Cruce de Cuentas ${countryName} - ${periodLabel}`;\n\n/*\n * Todos son destinatarios directos.\n * Nadie se envía en CC.\n */\n\nconst to = [\n 'iaracena@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n].join(',');\n\n\n// Paleta oficial GLM.\nconst metricCards = [\n metricCard(\n 'Discrepancias',\n discrepancias,\n '#FF6A13'\n ),\n metricCard(\n 'Banco sin nómina',\n bancoSinNomina,\n '#FF8C00'\n ),\n metricCard(\n 'Banco sin Bamboo',\n bancoSinBamboo,\n '#E87722'\n ),\n metricCard(\n 'Diferencias nombre banco',\n diferenciasNombreBanco,\n '#5B7F95'\n ),\n metricCard(\n 'Nómina sin cuenta',\n nominaSinCuenta,\n '#4F758B'\n ),\n].join('');\n\nconst htmlBody = `\n\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n  \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n ${reportUpdated ? 'REPORTE ACTUALIZADO' : 'REPORTE GENERADO'}\n \n \n \n \n\n \n\n \n \n\n \n \n \n\n \n\n \n Cruce de Cuentas · ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n\n \n \n\n \n \n\n \n \n \n\n \n ${reportUpdated ? 'Se actualizó el reporte existente del Portal de Cruce de Cuentas GLM conservando las resoluciones previamente registradas que continúan correspondiendo a los mismos casos.' : 'Se completó una nueva ejecución del Portal de Cruce de Cuentas GLM. Este es el resumen del reporte generado.'}\n \n\n \n \n\n \n \n \n\n \n \n ${metricCards}\n \n \n\n \n \n\n \n \n \n\n \n\n \n\n \n\n \n Ejecutado por\n \n\n \n ${escapeHtml(executedByName)}\n \n\n \n ${escapeHtml(\n executedByEmail ||\n 'Correo no disponible'\n )}\n \n\n \n\n \n\n \n País\n \n\n \n \n\n \n \n \n\n \n\n \n ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(\n countryCode || '—'\n )}\n \n\n \n\n \n \n\n \n\n \n\n \n\n \n\n \n Período\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n ${escapeHtml(generatedAtLabel)}\n \n\n \n\n \n\n \n Estado inicial\n \n\n \n ${escapeHtml(statusLabel)}\n \n\n \n Coincidencias:\n ${coincidencias.toLocaleString('en-US')}\n \n\n \n\n \n\n \n\n \n \n\n \n ${\n reportUrl\n ? `\n \n \n\n \n Abrir reporte en Google Sheets\n \n\n \n \n `\n : ''\n }\n\n \n \n \n\n \n\n \n Notificación automática · Portal Cruce de Cuentas GLM\n
\n Los casos de revisión deben gestionarse directamente en el reporte generado.\n \n\n \n \n\n \n\n \n \n \n\n\n\n`;\n\nreturn [\n {\n json: {\n to,\n subject,\n htmlBody,\n\n notification: {\n country: countryCode,\n countryName,\n flagUrl,\n periodLabel,\n executedByName,\n executedByEmail,\n generatedAt: generatedAt.toISOString(),\n reportUrl,\n\n metrics: {\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo,\n diferenciasNombreBanco,\n nominaSinCuenta,\n coincidencias,\n },\n },\n },\n },\n];" + "jsCode": "const prepared = $('Preparar histórico Supabase').first().json || {};\nconst payload = prepared.supabasePayload || {};\nconst metadata = payload.metadata || {};\nconst summary = payload.summary || {};\nconst reportUpdated = prepared.reportUpdated === true;\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatGeneratedAt(date) {\n const timeZone = 'America/Tegucigalpa';\n\n const dateLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(date);\n\n const timeLabel = new Intl.DateTimeFormat('es-HN', {\n timeZone,\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n }).format(date);\n\n const hour24Raw = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour: '2-digit',\n hour12: false,\n }).format(date);\n\n const hour24 =\n Number(String(hour24Raw).replace(/\\D/g, '')) % 24;\n\n const hourArticle =\n hour24 === 1 || hour24 === 13\n ? 'a la'\n : 'a las';\n\n return `${dateLabel} ${hourArticle} ${timeLabel}`;\n}\n\nfunction metricCard(label, value, accent) {\n return `\n \n \n \n \n \n ${escapeHtml(label)}\n \n\n \n ${numberValue(value).toLocaleString('en-US')}\n \n \n \n \n \n `;\n}\n\nconst countryCode = 'HN';\nconst countryName = 'Honduras';\n\nconst periodLabel =\n payload.period_label ||\n metadata.period_label ||\n [\n payload.year || metadata.year,\n payload.month || metadata.month,\n payload.period_type || metadata.period_type,\n ]\n .filter(Boolean)\n .join(' · ') ||\n 'Período no especificado';\n\nconst executedByName =\n payload.ejecutado_por_nombre ||\n metadata.requested_by_name ||\n 'Usuario GLM';\n\nconst executedByEmail =\n payload.ejecutado_por_email ||\n metadata.requested_by_email ||\n '';\n\nconst reportUrl =\n payload.report_url ||\n prepared.reportUrl ||\n '';\n\nconst discrepancias = numberValue(\n payload.discrepancias ??\n summary.discrepancias\n);\n\nconst bancoSinNomina = numberValue(\n payload.banco_sin_nomina ??\n summary.bancoSinNomina\n);\n\nconst bancoSinBamboo = numberValue(\n payload.banco_sin_bamboo ??\n summary.bancoSinBamboo\n);\n\nconst diferenciasNombreBanco = numberValue(\n metadata.diferencias_nombre_banco ??\n summary.diferenciasNombreBanco\n);\n\nconst nominaSinCuenta = numberValue(\n payload.nomina_sin_cuenta ??\n summary.nominaSinCuenta\n);\n\nconst coincidencias = numberValue(\n payload.coincidencias ??\n summary.coincidencias\n);\n\nconst generatedAt = new Date();\nconst generatedAtLabel =\n formatGeneratedAt(generatedAt);\n\nconst statusLabel =\n String(payload.estado || '').toLowerCase() === 'resuelto'\n ? 'Resuelto'\n : 'Pendiente de revisión';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp';\n\nconst subject = reportUpdated\n ? `Reporte actualizado - Cruce de Cuentas ${countryName} - ${periodLabel}`\n : `Nuevo reporte generado - Cruce de Cuentas ${countryName} - ${periodLabel}`;\n\n/*\n * Todos son destinatarios directos.\n * Nadie se envía en CC.\n */\n\nconst to = [\n 'iaracena@gomezleemarketing.com',\n].join(',');\n\n\n// Paleta oficial GLM.\nconst metricCards = [\n metricCard(\n 'Discrepancias',\n discrepancias,\n '#FF6A13'\n ),\n metricCard(\n 'Banco sin nómina',\n bancoSinNomina,\n '#FF8C00'\n ),\n metricCard(\n 'Banco sin Bamboo',\n bancoSinBamboo,\n '#E87722'\n ),\n metricCard(\n 'Diferencias nombre banco',\n diferenciasNombreBanco,\n '#5B7F95'\n ),\n metricCard(\n 'Nómina sin cuenta',\n nominaSinCuenta,\n '#4F758B'\n ),\n].join('');\n\nconst htmlBody = `\n\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n  \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n ${reportUpdated ? 'REPORTE ACTUALIZADO' : 'REPORTE GENERADO'}\n \n \n \n \n\n \n\n \n \n\n \n \n \n\n \n\n \n Cruce de Cuentas · ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n\n \n \n\n \n \n\n \n \n \n\n \n ${reportUpdated ? 'Se actualizó el reporte existente del Portal de Cruce de Cuentas GLM conservando las resoluciones previamente registradas que continúan correspondiendo a los mismos casos.' : 'Se completó una nueva ejecución del Portal de Cruce de Cuentas GLM. Este es el resumen del reporte generado.'}\n \n\n \n \n\n \n \n \n\n \n \n ${metricCards}\n \n \n\n \n \n\n \n \n \n\n \n\n \n\n \n\n \n Ejecutado por\n \n\n \n ${escapeHtml(executedByName)}\n \n\n \n ${escapeHtml(\n executedByEmail ||\n 'Correo no disponible'\n )}\n \n\n \n\n \n\n \n País\n \n\n \n \n\n \n \n \n\n \n\n \n ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(\n countryCode || '—'\n )}\n \n\n \n\n \n \n\n \n\n \n\n \n\n \n\n \n Período\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n ${escapeHtml(generatedAtLabel)}\n \n\n \n\n \n\n \n Estado inicial\n \n\n \n ${escapeHtml(statusLabel)}\n \n\n \n Coincidencias:\n ${coincidencias.toLocaleString('en-US')}\n \n\n \n\n \n\n \n\n \n \n\n \n ${\n reportUrl\n ? `\n \n \n\n \n Abrir reporte en Google Sheets\n \n\n \n \n `\n : ''\n }\n\n \n \n \n\n \n\n \n Notificación automática · Portal Cruce de Cuentas GLM\n
\n Los casos de revisión deben gestionarse directamente en el reporte generado.\n \n\n \n \n\n \n\n \n \n \n\n\n\n`;\n\nreturn [\n {\n json: {\n to,\n subject,\n htmlBody,\n\n notification: {\n country: countryCode,\n countryName,\n flagUrl,\n periodLabel,\n executedByName,\n executedByEmail,\n generatedAt: generatedAt.toISOString(),\n reportUrl,\n\n metrics: {\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo,\n diferenciasNombreBanco,\n nominaSinCuenta,\n coincidencias,\n },\n },\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -645,7 +645,7 @@ 4688, 672 ], - "id": "a067ffc0-607f-4094-be4b-19bbd95f1171", + "id": "e3ee2bf6-38a8-4e38-8f13-b706aa667650", "name": "Preparar correo reporte generado" }, { @@ -663,7 +663,7 @@ 4928, 672 ], - "id": "c309bd25-b751-46a5-a2fd-fbc4442cb7c0", + "id": "f29d8f4e-5156-41a6-ae07-874de0e8bd83", "name": "Enviar correo reporte generado", "webhookId": "4bcdbd7b-503d-46ac-aeaf-9207f59f01e7", "credentials": { @@ -676,7 +676,7 @@ }, { "parameters": { - "content": "# 🇭🇳 CRUCE DE CUENTAS — HONDURAS\n\nFlujo principal para una nómina quincenal de Honduras + todos los planos bancarios BAC del período + BambooHR.\n\n- Monedas: HNL y USD.\n- BambooHR: `onlyCurrent=false`, filtrado por país/localidad Honduras.\n- Q1: días 1–15. Q2: días 16–último día real del mes.\n- Los meses/años se calculan dinámicamente, incluidos años bisiestos.\n- La nómina usa las hojas operativas validadas de GLM Honduras y omite hojas auxiliares/copias para evitar duplicados.", + "content": "# 🇭🇳 CRUCE DE CUENTAS — HONDURAS\n\nFlujo principal para una nómina quincenal de Honduras + todos los planos bancarios BAC del período + BambooHR.\n\n- Monedas: HNL y USD.\n- BambooHR: `onlyCurrent=false`, filtrado por país/localidad Honduras.\n- Q1: días 1–15. Q2: días 16–último día real del mes.\n- Los meses/años se calculan dinámicamente, incluidos años bisiestos.\n- La nómina usa las hojas operativas validadas de GLM Honduras y omite hojas auxiliares/copias para evitar duplicados.\n\n\n### Correcciones de conciliación HN · 08-09-2026\n- Tolerancia monetaria operativa: ±0.05 por redondeos bancarios.\n- Banco sin Nómina se mantiene por transacción, sin agrupar pagos distintos.\n- Nombre en archivo truncado por un carácter al final no genera falso positivo.\n- BambooHR prioriza el nombre legal del cuentahabiente y perfiles vigentes en el período.\n- La cuenta mal digitada se determina por la cuenta real de la nómina vs. la cuenta real del banco.\n", "height": 260, "width": 520 }, @@ -686,7 +686,7 @@ -1088, 80 ], - "id": "bfd68985-f8c4-4311-bc99-dd4f67042115", + "id": "8a0379c3-9a89-4428-a42d-53f18a7b3250", "name": "📌 HONDURAS · V1" }, { @@ -701,7 +701,7 @@ 0, 0 ], - "id": "91b14935-3200-4f6a-b926-6299f3a6141b", + "id": "8640e03f-2128-4eb4-aae8-a2ee3ec221d3", "name": "📌 REGLA BANCO HN" }, { @@ -716,12 +716,12 @@ -608, 928 ], - "id": "62ec2b77-ecfb-42a1-984c-874f84fd77ba", + "id": "ce7cb681-290e-404b-a3de-fa879e57fb2e", "name": "📌 ARCHIVOS NÓMINA HN" }, { "parameters": { - "content": "# 📊 SALIDA\n\nConserva la misma estructura de reportes de GT/TT/PA/SV/HN, reutiliza el Google Sheet del mismo período si existe, actualiza Supabase y envía correo tanto en creación como en actualización.\n\nLos valores se escriben en Google Sheets con `RAW` para preservar las cuentas bancarias como texto exacto, incluyendo ceros iniciales y evitando notación científica.", + "content": "# 📊 SALIDA\n\nConserva la misma estructura de reportes de GT/TT/PA/SV, reutiliza el Google Sheet del mismo período si existe, actualiza Supabase y envía correo tanto en creación como en actualización.\n\nLos valores se escriben en Google Sheets con `RAW` para preservar las cuentas bancarias como texto exacto, incluyendo ceros iniciales y evitando notación científica.", "height": 250, "width": 720 }, @@ -731,7 +731,7 @@ 3120, 832 ], - "id": "70cdc5fd-3337-43dd-950d-64f68b311936", + "id": "cfcb9b0f-32a1-4859-81ee-910ba6aa89dc", "name": "📌 REPORTES" } ], @@ -1055,9 +1055,10 @@ "executionOrder": "v1", "binaryMode": "separate" }, - "versionId": "a4e7c2cc-6784-43db-a676-1288cf3f718e", + "versionId": "e9ce02ee-af02-439b-8679-30525f6173ba", "meta": { "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" }, + "id": "fu6dsueqDQax1c43", "tags": [] } \ No newline at end of file diff --git a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Puerto Rico.json b/workflows_n8n/Portal de Verificaci├│n de N├│mina - Puerto Rico.json new file mode 100644 index 0000000..c0d421a --- /dev/null +++ b/workflows_n8n/Portal de Verificaci├│n de N├│mina - Puerto Rico.json @@ -0,0 +1,1196 @@ +{ + "name": "Portal de Verificación de Nómina - Puerto Rico", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "nominapr-bamboo-test", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + -2800, + 5200 + ], + "id": "e07fef4a-5007-452d-b75e-975757344c59", + "name": "Webhook", + "webhookId": "00132835-9daf-4f36-95ee-233ff6f7b691" + }, + { + "parameters": { + "jsCode": "const item = $input.first();\nconst body = item.json.body || {};\nconst binary = item.binary || {};\n\nlet metadata = {};\ntry {\n metadata = typeof body.metadata === 'string'\n ? JSON.parse(body.metadata)\n : body.metadata || {};\n} catch (error) {\n metadata = {};\n}\n\nconst binaryKeys = Object.keys(binary);\n\nfunction naturalBinarySort(left, right) {\n return String(left).localeCompare(String(right), undefined, {\n numeric: true,\n sensitivity: 'base',\n });\n}\n\nfunction extensionOf(file) {\n const explicit = String(file?.fileExtension || '').toLowerCase().replace(/^\\./, '');\n if (explicit) return explicit;\n const name = String(file?.fileName || '').toLowerCase();\n const match = name.match(/\\.([a-z0-9]+)$/i);\n return match ? match[1] : '';\n}\n\nconst numberedPayrollKeys = binaryKeys\n .filter((key) => /^payroll_file_\\d+$/i.test(key))\n .sort(naturalBinarySort);\nconst repeatedPayrollKeys = binaryKeys\n .filter((key) => /^payroll_files(?:_?\\d+)?$/i.test(key))\n .sort(naturalBinarySort);\nlet payrollKeys = numberedPayrollKeys.length ? numberedPayrollKeys : repeatedPayrollKeys;\nif (payrollKeys.length === 0 && binary.payroll_file) payrollKeys = ['payroll_file'];\n\nconst numberedBankKeys = binaryKeys\n .filter((key) => /^bank_file_\\d+$/i.test(key))\n .sort(naturalBinarySort);\nconst repeatedBankKeys = binaryKeys\n .filter((key) => /^bank_files(?:_?\\d+)?$/i.test(key))\n .sort(naturalBinarySort);\nlet bankKeys = numberedBankKeys.length ? numberedBankKeys : repeatedBankKeys;\nif (bankKeys.length === 0 && binary.bank_file) bankKeys = ['bank_file'];\n\nconst payrollFiles = payrollKeys.map((key) => ({\n binary_key: key,\n file_name: binary[key]?.fileName || key,\n file_extension: extensionOf(binary[key]),\n mime_type: binary[key]?.mimeType || '',\n file_size: binary[key]?.fileSize || null,\n}));\n\nconst bankFiles = bankKeys.map((key) => ({\n binary_key: key,\n file_name: binary[key]?.fileName || key,\n file_extension: extensionOf(binary[key]),\n mime_type: binary[key]?.mimeType || '',\n file_size: binary[key]?.fileSize || null,\n}));\n\nconst receivedCountry = String(metadata.country || '').trim().toUpperCase();\nconst errors = [];\n\nif (receivedCountry !== 'PR') errors.push('El país recibido no es Puerto Rico.');\nif (!metadata.year) errors.push('No se recibió el año del cruce.');\nif (!metadata.month) errors.push('No se recibió el mes del cruce.');\nif (!metadata.period_type) errors.push('No se recibió el tipo de quincena.');\nif (!metadata.period_start || !metadata.period_end) errors.push('No se recibió el período calculado.');\nif (payrollFiles.length === 0) errors.push('No se recibió el archivo Excel de nómina de Puerto Rico.');\nif (bankFiles.length === 0) errors.push('No se recibió el PDF de pagos de Banco Popular de Puerto Rico.');\n\nconst badPayroll = payrollFiles.filter((file) => !['xlsx', 'xls', 'xlsm'].includes(file.file_extension));\nif (badPayroll.length) {\n errors.push(`Puerto Rico requiere nómina Excel (.xlsx, .xls o .xlsm). Archivo(s) inválido(s): ${badPayroll.map((f) => f.file_name).join(', ')}.`);\n}\nconst badBank = bankFiles.filter((file) => file.file_extension !== 'pdf');\nif (badBank.length) {\n errors.push(`Puerto Rico requiere el Transaction Detail Report de Banco Popular en PDF. Archivo(s) inválido(s): ${badBank.map((f) => f.file_name).join(', ')}.`);\n}\n\nconst mainPayroll = payrollFiles[0] || null;\nconst normalizedMetadata = {\n ...metadata,\n country: 'PR',\n country_name: 'Puerto Rico',\n currency: 'USD',\n source_app: metadata.source_app || 'portal-cruce-cuentas-glm',\n payroll_file_name: metadata.payroll_file_name || mainPayroll?.file_name || '',\n payroll_file_names: Array.isArray(metadata.payroll_file_names) && metadata.payroll_file_names.length\n ? metadata.payroll_file_names\n : payrollFiles.map((file) => file.file_name),\n bank_file_names: Array.isArray(metadata.bank_file_names) && metadata.bank_file_names.length\n ? metadata.bank_file_names\n : bankFiles.map((file) => file.file_name),\n};\n\nreturn [{\n json: {\n ok: errors.length === 0,\n stage: 'entrada_pr_recibida',\n errors,\n metadata: normalizedMetadata,\n payroll_files: payrollFiles,\n bank_files: bankFiles,\n summary: {\n payroll_files_count: payrollFiles.length,\n bank_files_count: bankFiles.length,\n },\n },\n binary,\n}];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2560, + 5200 + ], + "id": "1753550c-cf8b-48eb-ba58-0a8642163242", + "name": "Preparar entrada app" + }, + { + "parameters": { + "jsCode": "const input = $input.first();\nconst base = input.json || {};\nconst binary = input.binary || {};\nconst configuredFiles = Array.isArray(base.bank_files) ? base.bank_files : [];\nconst outputs = [];\n\nfor (const fileInfo of configuredFiles) {\n const key = String(fileInfo.binary_key || '');\n const file = binary[key];\n if (!key || !file) continue;\n outputs.push({\n json: {\n source_file: String(file.fileName || fileInfo.file_name || key),\n source_binary_key: key,\n source_kind: 'bppr_pdf',\n currency: 'USD',\n },\n binary: { data: file },\n });\n}\n\nif (!outputs.length) {\n throw new Error('Puerto Rico: no se pudo preparar el PDF de Banco Popular.');\n}\nreturn outputs;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2080, + 4960 + ], + "id": "eba2da3b-8ca3-49bd-9679-a61a1270c8f8", + "name": "Preparar PDF banco PR" + }, + { + "parameters": { + "jsCode": "const input = $input.first();\nconst base = input.json || {};\nconst binary = input.binary || {};\nconst configuredFiles = Array.isArray(base.payroll_files) ? base.payroll_files : [];\nconst SHEETS = [\n ['Whirlpool', 1, 'USD'],\n ['Personal 2', 1, 'USD'],\n ['Personal 3', 1, 'USD'],\n];\nconst outputs = [];\nfor (const fileInfo of configuredFiles) {\n const key = String(fileInfo.binary_key || '');\n const file = binary[key];\n if (!key || !file) continue;\n const fileName = String(file.fileName || fileInfo.file_name || key);\n for (const [sheetName, headerRow, currency] of SHEETS) {\n outputs.push({\n json: {\n source_file: fileName,\n source_binary_key: key,\n source_kind: 'quincenal',\n payroll_group: 'puerto_rico',\n sheet_name: sheetName,\n header_row: headerRow,\n range: `A${headerRow}:CA2000`,\n currency,\n },\n binary: { data: file },\n });\n }\n}\nif (!outputs.length) {\n throw new Error('Puerto Rico: no se pudo preparar el archivo Excel de nómina.');\n}\nreturn outputs;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -2080, + 5440 + ], + "id": "2b1028fb-bee3-4b82-bb5c-78c534959b20", + "name": "Preparar archivos nómina PR" + }, + { + "parameters": { + "operation": "xlsx", + "options": { + "headerRow": true, + "range": "={{ $json.range }}", + "sheetName": "={{ $json.sheet_name }}" + } + }, + "type": "n8n-nodes-base.extractFromFile", + "typeVersion": 1.1, + "position": [ + -1840, + 5440 + ], + "id": "ec028b3b-a794-462a-a33b-afb839928069", + "name": "Extract - Archivo Nómina PR", + "retryOnFail": false, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const items = $input.all();\nconst base = $('Preparar entrada app').first().json || {};\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\uFEFF/g, '').replace(/\\u00A0/g, ' ').replace(/\\s+/g, ' ').trim();\n}\nfunction normalize(value) {\n return clean(value).toLowerCase().normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').replace(/[^a-z0-9 #]/g, ' ').replace(/\\s+/g, ' ').trim();\n}\nfunction parseMoney(value) {\n if (typeof value === 'number') return Number.isFinite(value) ? value : 0;\n const parsed = Number.parseFloat(String(value ?? '').replace(/USD/gi, '').replace(/\\$/g, '').replace(/,/g, '').replace(/\\s+/g, '').trim());\n return Number.isFinite(parsed) ? parsed : 0;\n}\nfunction roundMoney(value) { return Math.round((Number(value) || 0) * 100) / 100; }\nfunction normalizeAccount(value) {\n const raw = clean(value);\n if (!raw || /[A-Za-zÀ-ÿ]/.test(raw)) return '';\n if (typeof value === 'number' && Number.isFinite(value)) return String(Math.round(value));\n if (/^[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)[eE][+-]?\\d+$/.test(raw)) {\n const number = Number(raw);\n if (Number.isFinite(number)) return String(Math.round(number));\n }\n const account = raw.replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n return account.length >= 5 && !/^0+$/.test(account) ? account : '';\n}\nfunction getValue(row, keys) {\n for (const key of keys) if (row[key] !== undefined && row[key] !== null && row[key] !== '') return row[key];\n const actual = Object.keys(row || {});\n for (const expected of keys) {\n const normalizedExpected = normalize(expected);\n const key = actual.find((candidate) => normalize(candidate) === normalizedExpected);\n if (key) {\n const value = row[key];\n if (value !== undefined && value !== null && value !== '') return value;\n }\n }\n return '';\n}\nfunction validEmployeeName(value) {\n const name = clean(value);\n const normalized = normalize(name);\n if (!name || /^[\\d.,\\s-]+$/.test(name)) return false;\n const bad = ['total', 'subtotal', 'total general', 'gran total', 'nombre', 'empleado', 'colaborador'];\n return !bad.some((term) => normalized === term || normalized.startsWith(`${term} `));\n}\nfunction sourceMeta(index) {\n try { return $('Preparar archivos nómina PR').itemMatching(index).json || {}; }\n catch { return {}; }\n}\n\nconst rows = [];\nconst ignored = [];\nconst counters = new Map();\nconst summaries = new Map();\n\nitems.forEach((item, index) => {\n const source = item.json || {};\n const linked = sourceMeta(index);\n const sourceFile = clean(linked.source_file || '');\n const sourceSheet = clean(linked.sheet_name || '');\n const currency = 'USD';\n const key = `${sourceFile}|${sourceSheet}`;\n const dataIndex = (counters.get(key) || 0) + 1;\n counters.set(key, dataIndex);\n const rowNumber = Number(linked.header_row || 1) + dataIndex;\n\n const employeeName = clean(getValue(source, ['NOMBRE', 'Nombre', 'Nombre del Empleado', 'NOMBRE DEL EMPLEADO']));\n const account = normalizeAccount(getValue(source, ['Cuenta', 'CUENTA', 'Cuenta Banco', 'CUENTA BANCO', 'CUENTA BANCARIA']));\n const amount = roundMoney(parseMoney(getValue(source, ['NETO A PAGAR', 'Neto a Pagar', 'Total a Pagar', 'TOTAL A PAGAR', 'Total Neto'])));\n\n const summary = summaries.get(key) || {\n source_file: sourceFile,\n source_sheet: sourceSheet,\n currency,\n extracted_rows_count: 0,\n accepted_rows_count: 0,\n ignored_rows_count: 0,\n total_amount: 0,\n };\n summary.extracted_rows_count += 1;\n\n if (!validEmployeeName(employeeName) || amount <= 0 || amount > 5000000) {\n summary.ignored_rows_count += 1;\n summaries.set(key, summary);\n if (employeeName || amount) ignored.push({ source_file: sourceFile, source_sheet: sourceSheet, row_number: rowNumber, employee_name: employeeName, account, payroll_amount: amount, currency });\n return;\n }\n\n rows.push({\n payroll_row_key: `PR_PAYROLL_${index + 1}_${rowNumber}`,\n row_number: rowNumber,\n employee_name: employeeName,\n employee_number: '',\n account,\n payroll_account: account,\n payroll_amount: amount,\n currency,\n source_file: sourceFile,\n source_kind: 'quincenal',\n source_sheet: sourceSheet,\n payroll_group: 'puerto_rico',\n is_main_payroll: true,\n raw: source,\n });\n summary.accepted_rows_count += 1;\n summary.total_amount = roundMoney(summary.total_amount + amount);\n summaries.set(key, summary);\n});\n\nif (!rows.length) {\n throw new Error('Puerto Rico: no se encontró ninguna fila válida en las hojas Whirlpool, Personal 2 y Personal 3.');\n}\nconst totalAmount = roundMoney(rows.reduce((sum, row) => sum + row.payroll_amount, 0));\nreturn [{\n json: {\n ...base,\n stage: 'nomina_pr_normalizada',\n payroll: {\n source: 'nomina_puerto_rico_usd',\n files_count: Number(base.summary?.payroll_files_count || 0),\n file_summaries: Array.from(summaries.values()),\n raw_rows_count: items.length,\n rows_count: rows.length,\n valid_rows_count: rows.length,\n base_rows_count: rows.length,\n supplement_rows_count: 0,\n attached_supplements_count: 0,\n unattached_supplements_count: 0,\n no_account_rows_count: rows.filter((row) => !row.account).length,\n ignored_rows_count: ignored.length,\n base_total: totalAmount,\n supplements_total: 0,\n total_amount: totalAmount,\n totals_by_currency: { USD: totalAmount },\n rows,\n no_account_rows: [],\n grouped_by_account: [],\n potential_supplements: [],\n },\n debug_payroll: {\n file_summaries: Array.from(summaries.values()),\n ignored_rows_preview: ignored.slice(0, 100),\n },\n },\n}];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1600, + 5440 + ], + "id": "1e1a261f-8711-49db-9640-aa64f3540fdd", + "name": "Normalizar Nómina PR" + }, + { + "parameters": { + "mode": "combine", + "combineBy": "combineByPosition", + "options": {} + }, + "type": "n8n-nodes-base.merge", + "typeVersion": 3.2, + "position": [ + -1320, + 5200 + ], + "id": "349328dc-e99b-499a-b79b-bbf62918ba68", + "name": "Merge Banco + Nómina PR" + }, + { + "parameters": { + "method": "POST", + "url": "https://glm.bamboohr.com/api/v1/reports/custom?format=JSON&onlyCurrent=false", + "authentication": "genericCredentialType", + "genericAuthType": "httpBasicAuth", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Accept", + "value": "application/json" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": { + "title": "Información de BambooHR - Cruce de Cuentas Puerto Rico", + "fields": [ + "firstName", + "middleName", + "lastName", + "displayName", + "department", + "division", + "location", + "customPosicion-Cliente", + "hireDate", + "originalHireDate", + "status", + "terminationDate", + "employeeNumber", + "country", + "workEmail", + "homeEmail", + "bestEmail" + ] + }, + "options": { + "response": { + "response": { + "responseFormat": "json" + } + }, + "timeout": 300000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + -1080, + 5040 + ], + "id": "396dcc5d-eb94-4f6d-a234-9adaa99baa7b", + "name": "HTTP - Empleados BambooHR PR", + "retryOnFail": true, + "maxTries": 3, + "waitBetweenTries": 3000, + "credentials": { + "httpBasicAuth": { + "id": "7VrpNZ2jBLmiJ35q", + "name": "BambooHR GLM Full Access" + } + } + }, + { + "parameters": { + "jsCode": "const inputItems = $input.all();\nconst base = $('Preparar entrada app').first().json || {};\nconst reconciliationData = $('Merge Banco + Nómina PR').first().json || {};\nconst metadata = base.metadata || {};\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los',\n 'y', 'e', 'el', 'da', 'do',\n 'dos', 'das', 'van', 'von',\n]);\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalize(value) {\n return clean(value)\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction unique(values) {\n const result = [];\n const seen = new Set();\n\n for (const value of values) {\n const cleaned = clean(value);\n\n if (!cleaned || seen.has(cleaned)) {\n continue;\n }\n\n seen.add(cleaned);\n result.push(cleaned);\n }\n\n return result;\n}\n\nfunction nameTokens(value) {\n return normalize(value)\n .split(' ')\n .filter(\n (token) =>\n token.length > 1 &&\n !IGNORED_NAME_TOKENS.has(token)\n );\n}\n\nfunction uniqueNameTokens(value) {\n return Array.from(\n new Set(nameTokens(value))\n );\n}\n\nfunction parseDate(value) {\n const raw = clean(value);\n if (!raw || raw === '0000-00-00') return null;\n\n const direct = raw.match(\n /^(\\d{4})-(\\d{2})-(\\d{2})/\n );\n\n if (direct) {\n return `${direct[1]}-${direct[2]}-${direct[3]}`;\n }\n\n const date = new Date(raw);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toISOString().slice(0, 10);\n}\n\nfunction parseBoolean(value) {\n if (typeof value === 'boolean') return value;\n\n return [\n 'true', 'yes', 'si', 'sí', '1', 'y',\n ].includes(normalize(value));\n}\n\nfunction isTargetCountry(employee) {\n const country = normalize(employee.country);\n const location = normalize(\n employee.location ||\n employee.jobInformationLocation ||\n employee.jobLocation\n );\n\n return (\n country === 'pr' ||\n country.includes('puerto rico') ||\n location === 'pr' ||\n location.includes('puerto rico')\n );\n}\n\nfunction overlapsPeriod(\n hireDate,\n terminationDate,\n periodStart,\n periodEnd\n) {\n if (!periodStart || !periodEnd) return false;\n\n const hiredBeforeEnd =\n !hireDate || hireDate <= periodEnd;\n\n const notTerminatedBeforeStart =\n !terminationDate ||\n terminationDate >= periodStart;\n\n return hiredBeforeEnd && notTerminatedBeforeStart;\n}\n\nfunction collectPageObjects(value, pages) {\n if (!value) return;\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n collectPageObjects(entry, pages);\n }\n return;\n }\n\n if (typeof value !== 'object') return;\n\n if (value.body && typeof value.body === 'object') {\n collectPageObjects(value.body, pages);\n return;\n }\n\n if (\n Array.isArray(value.data) ||\n Array.isArray(value.employees)\n ) {\n pages.push(value);\n return;\n }\n\n if (value.json && typeof value.json === 'object') {\n collectPageObjects(value.json, pages);\n }\n}\n\nfunction profileContains(\n leftTokens,\n leftTokenSet,\n rightTokens,\n rightTokenSet\n) {\n if (\n leftTokens.length < 3 ||\n rightTokens.length < 3\n ) {\n return false;\n }\n\n const leftInsideRight =\n leftTokens.every((token) =>\n rightTokenSet.has(token)\n );\n\n if (leftInsideRight) return true;\n\n return rightTokens.every((token) =>\n leftTokenSet.has(token)\n );\n}\n\nfunction employeeKey(employee) {\n return (\n employee.bamboo_id ||\n employee.employee_number ||\n normalize(employee.full_name)\n );\n}\n\nconst pageObjects = [];\n\nfor (const item of inputItems) {\n collectPageObjects(item.json, pageObjects);\n}\n\nconst employeeMap = new Map();\nlet expectedTotal = 0;\nlet restrictedFields = 0;\n\nfor (const page of pageObjects) {\n const pageEmployees =\n Array.isArray(page.data)\n ? page.data\n : Array.isArray(page.employees)\n ? page.employees\n : [];\n\n const pageTotal = Number(\n page.meta?.total ||\n page.total ||\n 0\n );\n\n if (Number.isFinite(pageTotal)) {\n expectedTotal = Math.max(\n expectedTotal,\n pageTotal\n );\n }\n\n for (const employee of pageEmployees) {\n const key =\n clean(employee.employeeId || employee.id) ||\n clean(employee.employeeNumber) ||\n clean(employee.bestEmail).toLowerCase() ||\n [\n clean(employee.firstName),\n clean(employee.middleName),\n clean(employee.lastName),\n ].filter(Boolean).join('|').toLowerCase();\n\n if (!key) continue;\n\n employeeMap.set(key, employee);\n\n restrictedFields += Array.isArray(\n employee._restrictedFields\n )\n ? employee._restrictedFields.length\n : 0;\n }\n}\n\nconst rawEmployees = Array.from(\n employeeMap.values()\n);\n\nconst periodStart = clean(metadata.period_start);\nconst periodEnd = clean(metadata.period_end);\n\nconst targetEmployees = [];\nconst outsideRecords = [];\nlet normalizedEmployeesCount = 0;\n\nfor (const employee of rawEmployees) {\n const firstName = clean(employee.firstName);\n const middleName = clean(employee.middleName);\n const lastName = clean(employee.lastName);\n const preferredName = clean(\n employee.preferredName\n );\n\n const constructedFullName = [\n firstName,\n middleName,\n lastName,\n ].filter(Boolean).join(' ');\n\n const aliases = unique([\n employee.displayName,\n employee.fullName1,\n employee.fullName2,\n employee.fullName3,\n employee.fullName4,\n employee.fullName5,\n constructedFullName,\n [preferredName, lastName]\n .filter(Boolean)\n .join(' '),\n [firstName, lastName]\n .filter(Boolean)\n .join(' '),\n ]);\n\n const hireDate = parseDate(\n employee.hireDate ||\n employee.originalHireDate\n );\n\n const terminationDate = parseDate(\n employee.terminationDate\n );\n\n const status = clean(\n employee.status ||\n employee.employmentStatus ||\n employee.employmentHistoryStatus\n );\n\n const employeeNumber = clean(\n employee.employeeNumber ||\n employee.employee_number\n );\n\n const normalizedEmployee = {\n bamboo_id: clean(\n employee.employeeId ||\n employee.id\n ),\n employee_number: employeeNumber,\n first_name: firstName,\n middle_name: middleName,\n last_name: lastName,\n preferred_name: preferredName,\n full_name:\n clean(employee.displayName) ||\n clean(employee.fullName1) ||\n constructedFullName,\n aliases,\n normalized_aliases:\n aliases.map(normalize).filter(Boolean),\n status,\n hire_date: hireDate,\n termination_date: terminationDate,\n location: clean(\n employee.location ||\n employee.jobInformationLocation ||\n employee.jobLocation\n ),\n country: clean(employee.country),\n include_in_payroll:\n parseBoolean(employee.includeInPayroll),\n work_email:\n clean(employee.workEmail).toLowerCase(),\n home_email:\n clean(employee.homeEmail).toLowerCase(),\n best_email: clean(\n employee.bestEmail ||\n employee.workEmail ||\n employee.homeEmail\n ).toLowerCase(),\n exists_in_bamboo: true,\n overlaps_period: overlapsPeriod(\n hireDate,\n terminationDate,\n periodStart,\n periodEnd\n ),\n };\n\n normalizedEmployeesCount += 1;\n\n if (isTargetCountry(normalizedEmployee)) {\n targetEmployees.push({\n ...normalizedEmployee,\n validation_eligible: true,\n validation_scope:\n 'puerto_rico_country_or_location',\n });\n continue;\n }\n\n const aliasProfiles = aliases\n .map((rawAlias) => {\n const tokens =\n uniqueNameTokens(rawAlias);\n\n return {\n raw: rawAlias,\n tokens,\n token_set: new Set(tokens),\n };\n })\n .filter(\n (profile) =>\n profile.tokens.length >= 3\n );\n\n const searchTokens = new Set();\n\n for (const profile of aliasProfiles) {\n for (const token of profile.tokens) {\n searchTokens.add(token);\n }\n }\n\n outsideRecords.push({\n employee: normalizedEmployee,\n alias_profiles: aliasProfiles,\n search_tokens: searchTokens,\n });\n}\n\nconst relevantNameMap = new Map();\n\nfunction addRelevantName(value) {\n const cleaned = clean(value);\n const normalized = normalize(cleaned);\n\n if (!normalized) return;\n\n const tokens = uniqueNameTokens(cleaned);\n const current =\n relevantNameMap.get(normalized);\n\n if (\n !current ||\n tokens.length > current.tokens.length\n ) {\n relevantNameMap.set(\n normalized,\n {\n raw: cleaned,\n tokens,\n token_set: new Set(tokens),\n }\n );\n }\n}\n\nfor (const row of reconciliationData.bank?.rows || []) {\n addRelevantName(row.bank_name_file);\n addRelevantName(row.bank_account_holder);\n addRelevantName(row.participant_name);\n}\n\nfor (\n const row of\n reconciliationData.bank?.grouped_by_account || []\n) {\n addRelevantName(row.bank_name_file);\n addRelevantName(row.bank_account_holder);\n\n for (const name of row.bank_name_files || []) {\n addRelevantName(name);\n }\n\n for (\n const name of\n row.bank_account_holders || []\n ) {\n addRelevantName(name);\n }\n}\n\nfor (const row of [\n ...(reconciliationData.payroll?.rows || []),\n ...(reconciliationData.payroll?.grouped_by_account || []),\n ...(reconciliationData.payroll?.no_account_rows || []),\n]) {\n addRelevantName(\n row.employee_name ||\n row.employee ||\n ''\n );\n}\n\n/*\n * Índice invertido para rescatar perfiles con país/localidad incorrectos.\n *\n * La versión anterior comparaba cada nombre relevante contra todos los\n * empleados fuera de Puerto Rico y todas sus variantes de nombre. Con casi\n * 10,000 perfiles, eso provocaba millones de normalizaciones y bloqueaba el\n * task runner. Aquí cada palabra apunta directamente a los pocos empleados\n * que la contienen; luego solo se revisan candidatos con al menos tres\n * palabras compartidas.\n */\nconst outsideTokenIndex = new Map();\n\nfor (\n let index = 0;\n index < outsideRecords.length;\n index++\n) {\n for (\n const token of\n outsideRecords[index].search_tokens\n ) {\n let bucket =\n outsideTokenIndex.get(token);\n\n if (!bucket) {\n bucket = [];\n outsideTokenIndex.set(\n token,\n bucket\n );\n }\n\n bucket.push(index);\n }\n}\n\nconst targetExactAliasSet = new Set();\n\nfor (const employee of targetEmployees) {\n for (\n const normalizedAlias of\n employee.normalized_aliases || []\n ) {\n if (normalizedAlias) {\n targetExactAliasSet.add(\n normalizedAlias\n );\n }\n }\n}\n\nlet contextualOutsideSkippedByTargetExact = 0;\nconst contextualOutsideMap = new Map();\n\nfor (\n const relevantProfile of\n relevantNameMap.values()\n) {\n if (relevantProfile.tokens.length < 3) {\n continue;\n }\n\n /*\n * Prioridad absoluta al país objetivo:\n *\n * Cuando el nombre completo recibido existe exactamente en Puerto Rico,\n * no se incorpora un perfil externo cuyo nombre más corto esté contenido\n * dentro de ese mismo texto. Esto evita conflictos entre dos personas\n * distintas, por ejemplo:\n *\n * - NOMBRE COMPLETO · Honduras\n * - un perfil con nombre más corto · otro país\n *\n * El rescate fuera del país permanece activo cuando no existe una\n * coincidencia exacta entre los perfiles de Puerto Rico.\n */\n if (\n targetExactAliasSet.has(\n normalize(relevantProfile.raw)\n )\n ) {\n contextualOutsideSkippedByTargetExact += 1;\n continue;\n }\n\n const sharedTokenCounts = new Map();\n\n for (const token of relevantProfile.tokens) {\n for (\n const outsideIndex of\n outsideTokenIndex.get(token) || []\n ) {\n sharedTokenCounts.set(\n outsideIndex,\n (\n sharedTokenCounts.get(\n outsideIndex\n ) || 0\n ) + 1\n );\n }\n }\n\n const uniqueMatches = new Map();\n\n for (\n const [\n outsideIndex,\n sharedTokenCount,\n ] of sharedTokenCounts\n ) {\n if (sharedTokenCount < 3) {\n continue;\n }\n\n const record =\n outsideRecords[outsideIndex];\n\n const matches =\n record.alias_profiles.some(\n (aliasProfile) =>\n profileContains(\n relevantProfile.tokens,\n relevantProfile.token_set,\n aliasProfile.tokens,\n aliasProfile.token_set\n )\n );\n\n if (!matches) continue;\n\n const key =\n employeeKey(record.employee);\n\n if (key) {\n uniqueMatches.set(\n key,\n record.employee\n );\n }\n }\n\n /*\n * Solo se rescata un perfil fuera del país cuando un nombre informativo\n * identifica exactamente a una única persona. Así se corrigen localidades\n * erróneas sin convertir nombres comunes en falsos positivos.\n */\n if (uniqueMatches.size !== 1) {\n continue;\n }\n\n const employee =\n uniqueMatches.values().next().value;\n\n const key = employeeKey(employee);\n\n if (!key) continue;\n\n contextualOutsideMap.set(key, {\n ...employee,\n validation_eligible: true,\n validation_scope:\n 'outside_country_unique_informative_name',\n });\n}\n\nconst validationEmployeeMap = new Map();\n\nfor (const employee of [\n ...targetEmployees,\n ...contextualOutsideMap.values(),\n]) {\n const key = employeeKey(employee);\n\n if (key) {\n validationEmployeeMap.set(\n key,\n employee\n );\n }\n}\n\nconst validationEmployees =\n Array.from(\n validationEmployeeMap.values()\n );\n\n\n/*\n * Resolución previa de nombres contra BambooHR.\n *\n * Cada nombre distinto recibido desde banco y nómina se resuelve una sola\n * vez, usando índices de alias y palabras. El resultado queda disponible\n * para el nodo de cruce mediante resolved_name_matches.\n */\nconst CONFIRMED_BAMBOO_NAME_ALIASES = new Map([]);\n\nfunction relevantEntryRaw(entry) {\n if (typeof entry === 'string') return clean(entry);\n return clean(entry?.raw || entry?.name || '');\n}\n\nfunction bambooResolutionEmployeeKey(employee) {\n return (\n clean(employee.bamboo_id) ||\n clean(employee.employee_number) ||\n normalize(employee.full_name)\n );\n}\n\nfunction bambooResolutionEditDistance(left, right) {\n const a = String(left || '');\n const b = String(right || '');\n\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n\n let previous = Array.from(\n { length: b.length + 1 },\n (_, index) => index\n );\n\n for (let row = 1; row <= a.length; row++) {\n const current = [row];\n\n for (let column = 1; column <= b.length; column++) {\n const cost =\n a[row - 1] === b[column - 1]\n ? 0\n : 1;\n\n current[column] = Math.min(\n current[column - 1] + 1,\n previous[column] + 1,\n previous[column - 1] + cost\n );\n }\n\n previous = current;\n }\n\n return previous[b.length];\n}\nfunction bambooResolutionTokenSimilarity(left, right) {\n const a = String(left || '');\n const b = String(right || '');\n\n if (!a || !b) return 0;\n if (a === b) return 1;\n\n const minimumLength = Math.min(\n a.length,\n b.length\n );\n\n const maximumLength = Math.max(\n a.length,\n b.length\n );\n\n const distance =\n bambooResolutionEditDistance(a, b);\n\n if (\n minimumLength >= 4 &&\n distance <= 1\n ) {\n return Math.max(\n 0.90,\n 1 - distance / maximumLength\n );\n }\n\n if (\n minimumLength >= 6 &&\n distance <= 2\n ) {\n return Math.max(\n 0.82,\n 1 - distance / maximumLength\n );\n }\n\n const prefixOrSuffix =\n a.startsWith(b) ||\n b.startsWith(a) ||\n a.endsWith(b) ||\n b.endsWith(a);\n\n if (\n prefixOrSuffix &&\n minimumLength >= 4\n ) {\n return Math.max(\n 0.78,\n minimumLength / maximumLength\n );\n }\n\n return 0;\n}\n\nfunction bambooResolutionAliasDetails(\n queryName,\n aliasProfile\n) {\n const queryWords = Array.from(\n new Set(nameTokens(queryName))\n );\n\n const aliasWords =\n aliasProfile.words;\n\n if (\n queryWords.length < 2 ||\n aliasWords.length < 2\n ) {\n return null;\n }\n\n const aliasWordSet =\n aliasProfile.word_set;\n\n const queryWordSet =\n new Set(queryWords);\n\n const queryInsideAlias =\n queryWords.every((word) =>\n aliasWordSet.has(word)\n );\n\n const aliasInsideQuery =\n aliasWords.every((word) =>\n queryWordSet.has(word)\n );\n\n const usedAliasIndexes = new Set();\n const usedQueryIndexes = new Set();\n const similarities = new Array(\n queryWords.length\n ).fill(0);\n\n let exactMatches = 0;\n\n for (\n let queryIndex = 0;\n queryIndex < queryWords.length;\n queryIndex++\n ) {\n const aliasIndex =\n aliasWords.findIndex(\n (aliasWord, currentAliasIndex) =>\n !usedAliasIndexes.has(\n currentAliasIndex\n ) &&\n aliasWord ===\n queryWords[queryIndex]\n );\n\n if (aliasIndex < 0) continue;\n\n usedQueryIndexes.add(queryIndex);\n usedAliasIndexes.add(aliasIndex);\n similarities[queryIndex] = 1;\n exactMatches += 1;\n }\n\n const remainingQueryIndexes =\n queryWords\n .map((word, index) => ({\n word,\n index,\n }))\n .filter((entry) =>\n !usedQueryIndexes.has(entry.index)\n )\n .sort((left, right) =>\n right.word.length -\n left.word.length\n );\n\n for (const queryEntry of remainingQueryIndexes) {\n let bestSimilarity = 0;\n let bestAliasIndex = -1;\n\n for (\n let aliasIndex = 0;\n aliasIndex < aliasWords.length;\n aliasIndex++\n ) {\n if (\n usedAliasIndexes.has(\n aliasIndex\n )\n ) {\n continue;\n }\n\n const similarity =\n bambooResolutionTokenSimilarity(\n queryEntry.word,\n aliasWords[aliasIndex]\n );\n\n if (similarity > bestSimilarity) {\n bestSimilarity = similarity;\n bestAliasIndex = aliasIndex;\n }\n }\n\n if (\n bestAliasIndex >= 0 &&\n bestSimilarity >= 0.78\n ) {\n usedAliasIndexes.add(\n bestAliasIndex\n );\n similarities[queryEntry.index] =\n bestSimilarity;\n }\n }\n\n const matchedTokens =\n similarities.filter(\n (value) => value >= 0.78\n ).length;\n\n const queryCoverage =\n similarities.reduce(\n (sum, value) => sum + value,\n 0\n ) / queryWords.length;\n\n const aliasCoverage =\n matchedTokens /\n aliasWords.length;\n\n const lengthBalance =\n Math.min(\n queryWords.length,\n aliasWords.length\n ) /\n Math.max(\n queryWords.length,\n aliasWords.length\n );\n\n const score =\n queryCoverage * 0.65 +\n aliasCoverage * 0.20 +\n (\n exactMatches /\n queryWords.length\n ) * 0.10 +\n lengthBalance * 0.05;\n\n return {\n score,\n exact_matches: exactMatches,\n matched_tokens: matchedTokens,\n query_tokens:\n queryWords.length,\n alias_tokens:\n aliasWords.length,\n query_coverage:\n queryCoverage,\n alias_coverage:\n aliasCoverage,\n containment:\n queryInsideAlias ||\n aliasInsideQuery,\n };\n}\n\nconst bambooResolutionProfiles =\n validationEmployees.map(\n (employee, employeeIndex) => {\n const aliases = [];\n const seenAliases = new Set();\n\n for (\n const rawAlias of\n employee.aliases || []\n ) {\n const normalizedAlias =\n normalize(rawAlias);\n\n if (\n !normalizedAlias ||\n seenAliases.has(\n normalizedAlias\n )\n ) {\n continue;\n }\n\n seenAliases.add(\n normalizedAlias\n );\n\n const words = Array.from(\n new Set(nameTokens(rawAlias))\n );\n\n if (!words.length) continue;\n\n aliases.push({\n raw: clean(rawAlias),\n normalized:\n normalizedAlias,\n words,\n word_set:\n new Set(words),\n });\n }\n\n return {\n employee,\n employee_index:\n employeeIndex,\n employee_key:\n bambooResolutionEmployeeKey(\n employee\n ),\n aliases,\n };\n }\n );\n\nconst bambooResolutionExactAliasSets =\n new Map();\n\nconst bambooResolutionTokenSets =\n new Map();\n\nconst bambooResolutionTokenShapeSets =\n new Map();\n\nfor (\n let employeeIndex = 0;\n employeeIndex <\n bambooResolutionProfiles.length;\n employeeIndex++\n) {\n const profile =\n bambooResolutionProfiles[\n employeeIndex\n ];\n\n for (const alias of profile.aliases) {\n let exactSet =\n bambooResolutionExactAliasSets\n .get(alias.normalized);\n\n if (!exactSet) {\n exactSet = new Set();\n bambooResolutionExactAliasSets\n .set(\n alias.normalized,\n exactSet\n );\n }\n\n exactSet.add(employeeIndex);\n\n for (const token of alias.words) {\n if (token.length < 3) continue;\n\n let tokenSet =\n bambooResolutionTokenSets\n .get(token);\n\n if (!tokenSet) {\n tokenSet = new Set();\n bambooResolutionTokenSets\n .set(token, tokenSet);\n }\n\n tokenSet.add(employeeIndex);\n\n const tokenShape =\n `${token[0]}:${token.length}`;\n\n let shapeSet =\n bambooResolutionTokenShapeSets\n .get(tokenShape);\n\n if (!shapeSet) {\n shapeSet = new Set();\n bambooResolutionTokenShapeSets\n .set(\n tokenShape,\n shapeSet\n );\n }\n\n shapeSet.add(employeeIndex);\n }\n }\n}\n\nconst bambooResolutionExactAliasMap =\n new Map();\n\nfor (\n const [alias, indexes] of\n bambooResolutionExactAliasSets\n) {\n bambooResolutionExactAliasMap.set(\n alias,\n Array.from(indexes)\n );\n}\n\nfunction bambooResolutionDecision(\n queryName\n) {\n const rawQuery = clean(queryName);\n const normalizedQuery =\n normalize(rawQuery);\n\n const queryWords = Array.from(\n new Set(nameTokens(rawQuery))\n );\n\n if (\n !normalizedQuery ||\n queryWords.length < 2\n ) {\n return {\n found: false,\n matched_by: null,\n confidence: 0,\n reason:\n 'insufficient_name_tokens',\n };\n }\n\n const confirmedCanonical =\n CONFIRMED_BAMBOO_NAME_ALIASES\n .get(normalizedQuery);\n\n if (confirmedCanonical) {\n const confirmedIndexes =\n bambooResolutionExactAliasMap\n .get(confirmedCanonical) || [];\n\n if (confirmedIndexes.length === 1) {\n const employeeIndex =\n confirmedIndexes[0];\n\n return {\n found: true,\n matched_by:\n 'confirmed_alias_catalog',\n confidence: 1,\n employee_index:\n employeeIndex,\n employee_key:\n bambooResolutionProfiles[\n employeeIndex\n ].employee_key,\n query_name:\n rawQuery,\n bamboo_alias:\n bambooResolutionProfiles[\n employeeIndex\n ].aliases.find(\n (alias) =>\n alias.normalized ===\n confirmedCanonical\n )?.raw ||\n bambooResolutionProfiles[\n employeeIndex\n ].employee.full_name ||\n '',\n };\n }\n }\n\n const exactIndexes =\n bambooResolutionExactAliasMap\n .get(normalizedQuery) || [];\n\n if (exactIndexes.length === 1) {\n const employeeIndex =\n exactIndexes[0];\n\n return {\n found: true,\n matched_by:\n 'exact_precomputed_name',\n confidence: 1,\n employee_index:\n employeeIndex,\n employee_key:\n bambooResolutionProfiles[\n employeeIndex\n ].employee_key,\n query_name:\n rawQuery,\n bamboo_alias:\n bambooResolutionProfiles[\n employeeIndex\n ].aliases.find(\n (alias) =>\n alias.normalized ===\n normalizedQuery\n )?.raw ||\n bambooResolutionProfiles[\n employeeIndex\n ].employee.full_name ||\n '',\n };\n }\n\n const candidateVotes = new Map();\n\n function addCandidateVotes(\n indexes,\n weight\n ) {\n for (const employeeIndex of indexes) {\n candidateVotes.set(\n employeeIndex,\n (\n candidateVotes.get(\n employeeIndex\n ) || 0\n ) + weight\n );\n }\n }\n\n for (const token of queryWords) {\n addCandidateVotes(\n bambooResolutionTokenSets\n .get(token) || [],\n 4\n );\n\n for (\n let lengthOffset = -2;\n lengthOffset <= 2;\n lengthOffset++\n ) {\n const candidateLength =\n token.length + lengthOffset;\n\n if (candidateLength < 3) {\n continue;\n }\n\n addCandidateVotes(\n bambooResolutionTokenShapeSets\n .get(\n `${token[0]}:${candidateLength}`\n ) || [],\n 1\n );\n }\n }\n\n const candidateIndexes =\n Array.from(\n candidateVotes.entries()\n )\n .sort((left, right) =>\n right[1] - left[1]\n )\n .slice(0, 120)\n .map(([employeeIndex]) =>\n employeeIndex\n );\n\n const rankedCandidates = [];\n\n for (\n const employeeIndex of\n candidateIndexes\n ) {\n const profile =\n bambooResolutionProfiles[\n employeeIndex\n ];\n\n let bestDetails = null;\n let bestAlias = '';\n\n for (const alias of profile.aliases) {\n const details =\n bambooResolutionAliasDetails(\n rawQuery,\n alias\n );\n\n if (\n details &&\n (\n !bestDetails ||\n details.score >\n bestDetails.score\n )\n ) {\n bestDetails = details;\n bestAlias = alias.raw;\n }\n }\n\n if (!bestDetails) continue;\n\n rankedCandidates.push({\n employee_index:\n employeeIndex,\n employee_key:\n profile.employee_key,\n details:\n bestDetails,\n bamboo_alias:\n bestAlias,\n });\n }\n\n rankedCandidates.sort(\n (left, right) => {\n if (\n right.details.score !==\n left.details.score\n ) {\n return (\n right.details.score -\n left.details.score\n );\n }\n\n if (\n right.details.exact_matches !==\n left.details.exact_matches\n ) {\n return (\n right.details.exact_matches -\n left.details.exact_matches\n );\n }\n\n return (\n right.details.query_coverage -\n left.details.query_coverage\n );\n }\n );\n\n const best =\n rankedCandidates[0] || null;\n\n const second =\n rankedCandidates[1] || null;\n\n const margin =\n best\n ? best.details.score -\n (\n second?.details.score ||\n 0\n )\n : 0;\n\n const details =\n best?.details || null;\n\n const exactContainment =\n Boolean(\n details?.containment &&\n details.exact_matches >= 2\n );\n\n const strongTwoTokenName =\n Boolean(\n details &&\n details.query_tokens === 2 &&\n details.matched_tokens === 2 &&\n details.exact_matches >= 1 &&\n details.query_coverage >= 0.90 &&\n details.score >= 0.88\n );\n\n const strongLongName =\n Boolean(\n details &&\n details.query_tokens >= 3 &&\n details.matched_tokens >=\n Math.min(\n 3,\n details.query_tokens\n ) &&\n details.exact_matches >= 2 &&\n details.query_coverage >= 0.85 &&\n details.score >= 0.84\n );\n\n const acceptableMargin =\n !second ||\n margin >= (\n exactContainment\n ? 0.04\n : 0.06\n ) ||\n (\n details?.exact_matches || 0\n ) >\n (\n second?.details\n ?.exact_matches || 0\n );\n\n if (\n best &&\n acceptableMargin &&\n (\n exactContainment ||\n strongTwoTokenName ||\n strongLongName\n )\n ) {\n return {\n found: true,\n matched_by:\n exactContainment\n ? 'unique_precomputed_containment'\n : 'strong_precomputed_fuzzy_name',\n confidence:\n Math.min(\n 1,\n details.score\n ),\n employee_index:\n best.employee_index,\n employee_key:\n best.employee_key,\n query_name:\n rawQuery,\n bamboo_alias:\n best.bamboo_alias,\n margin,\n exact_matches:\n details.exact_matches,\n matched_tokens:\n details.matched_tokens,\n };\n }\n\n return {\n found: false,\n matched_by: null,\n confidence:\n details?.score || 0,\n reason:\n best\n ? (\n acceptableMargin\n ? 'insufficient_name_evidence'\n : 'ambiguous_name'\n )\n : 'no_candidate',\n best_candidate:\n best\n ? {\n employee_index:\n best.employee_index,\n employee_key:\n best.employee_key,\n bamboo_alias:\n best.bamboo_alias,\n score:\n best.details.score,\n }\n : null,\n second_candidate:\n second\n ? {\n employee_index:\n second.employee_index,\n employee_key:\n second.employee_key,\n bamboo_alias:\n second.bamboo_alias,\n score:\n second.details.score,\n }\n : null,\n };\n}\n\nconst resolvedNameMatches = {};\nlet resolvedNameMatchesFound = 0;\n\nfor (\n const [\n normalizedRelevantName,\n relevantEntry,\n ] of relevantNameMap\n) {\n const rawRelevantName =\n relevantEntryRaw(relevantEntry);\n\n const decision =\n bambooResolutionDecision(\n rawRelevantName\n );\n\n resolvedNameMatches[\n normalizedRelevantName\n ] = decision;\n\n if (decision.found) {\n resolvedNameMatchesFound += 1;\n }\n}\n\n\nconst fetchedEmployeesCount =\n rawEmployees.length;\n\nconst fetchComplete =\n expectedTotal > 0\n ? fetchedEmployeesCount >= expectedTotal\n : (\n pageObjects.length > 0 &&\n !pageObjects.some(\n (page) =>\n Boolean(\n page?._links?.next?.href\n )\n )\n );\n\nconst errors = [];\n\nif (!pageObjects.length) {\n errors.push(\n 'BambooHR no devolvió páginas de empleados.'\n );\n}\n\nif (!fetchedEmployeesCount) {\n errors.push(\n 'BambooHR no devolvió empleados.'\n );\n}\n\nif (\n expectedTotal > 0 &&\n fetchedEmployeesCount < expectedTotal\n) {\n errors.push(\n `La descarga de BambooHR quedó incompleta: ` +\n `${fetchedEmployeesCount} de ${expectedTotal} empleados.`\n );\n}\n\nif (!targetEmployees.length) {\n errors.push(\n 'No se encontraron empleados de Puerto Rico en BambooHR.'\n );\n}\n\nreturn [\n {\n json: {\n ...base,\n ok:\n Boolean(base.ok ?? true) &&\n errors.length === 0,\n stage:\n errors.length === 0\n ? 'bamboohr_pr_normalizado'\n : 'bamboohr_pr_incompleto',\n errors: [\n ...(Array.isArray(base.errors)\n ? base.errors\n : []),\n ...errors,\n ],\n bamboo: {\n source:\n 'bamboohr_custom_report_only_current_false',\n period_start: periodStart,\n period_end: periodEnd,\n pages_fetched: pageObjects.length,\n expected_total: expectedTotal,\n raw_employees_count:\n fetchedEmployeesCount,\n employees_count:\n normalizedEmployeesCount,\n puerto_rico_count:\n targetEmployees.length,\n active_in_period_count:\n targetEmployees.filter(\n (employee) =>\n employee.overlaps_period\n ).length,\n active_status_count:\n targetEmployees.filter(\n (employee) =>\n normalize(employee.status) ===\n 'active'\n ).length,\n contextual_outside_country_count:\n contextualOutsideMap.size,\n contextual_outside_skipped_by_target_exact_count:\n contextualOutsideSkippedByTargetExact,\n validation_candidates_count:\n validationEmployees.length,\n resolved_name_matches:\n resolvedNameMatches,\n resolved_name_matches_count:\n Object.keys(\n resolvedNameMatches\n ).length,\n resolved_name_matches_found:\n resolvedNameMatchesFound,\n name_resolution_strategy:\n 'precomputed_indexed_fuzzy_matching_with_target_exact_precedence',\n fetch_complete: fetchComplete,\n validation_available:\n fetchComplete &&\n validationEmployees.length > 0,\n validation_rule:\n 'Target country/location first; outside-country contextual rescue only when no exact target alias exists',\n performance_strategy:\n 'precomputed_alias_tokens_and_inverted_index',\n employees:\n validationEmployees,\n restricted_fields:\n restrictedFields,\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -840, + 5040 + ], + "id": "c9609568-0db8-42c7-8629-601841c2c3d7", + "name": "Normalizar BambooHR PR" + }, + { + "parameters": { + "mode": "combine", + "combineBy": "combineByPosition", + "options": {} + }, + "type": "n8n-nodes-base.merge", + "typeVersion": 3.2, + "position": [ + -600, + 5200 + ], + "id": "0d44d9e2-bf49-400c-873c-c9adb8bcbbd3", + "name": "Merge - Agregar BambooHR PR" + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/cruce_cuentas_bamboo_aliases_activos", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": { + "p_pais": "PR" + }, + "options": { + "response": { + "response": { + "responseFormat": "json" + } + }, + "timeout": 30000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + -360, + 5200 + ], + "id": "b8e487d1-2745-4fce-a4ae-00a1ceb44bab", + "name": "Obtener alias BambooHR Supabase", + "retryOnFail": true, + "maxTries": 2, + "waitBetweenTries": 1000, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const data = $('Merge - Agregar BambooHR PR').first().json || {};\nconst aliasResponse = $input.first().json || {};\nconst validatedBambooAliases = Array.isArray(aliasResponse.aliases)\n ? aliasResponse.aliases\n : Array.isArray(aliasResponse)\n ? aliasResponse\n : [];\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst normalizedNameCache = new Map();\nconst nameWordsCache = new Map();\nconst nameMatchScoreCache = new Map();\n\nfunction normalizeName(value) {\n const raw = clean(value);\n if (normalizedNameCache.has(raw)) {\n return normalizedNameCache.get(raw);\n }\n\n const normalized = raw\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/['’`-]/g, ' ')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n\n normalizedNameCache.set(raw, normalized);\n return normalized;\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\.0$/g, '').replace(/\\D/g, '').trim();\n}\n\nfunction normalizeEmployeeNumber(value) {\n return String(value ?? '')\n .replace(/\\u00A0/g, '')\n .replace(/\\D/g, '')\n .trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction moneyDiff(payroll, bank) {\n return roundMoney((Number(payroll) || 0) - (Number(bank) || 0));\n}\n\nconst MONEY_TOLERANCE = 0.01;\nconst MONEY_TOLERANCE_CENTS = Math.round(MONEY_TOLERANCE * 100);\n\nfunction sameCent(a, b) {\n return Math.abs(moneyDiff(a, b)) <= MONEY_TOLERANCE + 1e-9;\n}\n\nconst IGNORED_NAME_TOKENS = new Set([\n 'de', 'del', 'la', 'las', 'los', 'y', 'e', 'el',\n 'da', 'do', 'dos', 'das', 'van', 'von',\n]);\n\nfunction nameWords(value) {\n const normalized = normalizeName(value);\n if (nameWordsCache.has(normalized)) {\n return nameWordsCache.get(normalized);\n }\n\n const words = normalized\n .split(' ')\n .filter((word) => word.length > 1 && !IGNORED_NAME_TOKENS.has(word));\n\n nameWordsCache.set(normalized, words);\n return words;\n}\n\nfunction editDistance(a, b) {\n if (a === b) return 0;\n if (!a) return b.length;\n if (!b) return a.length;\n let previous = Array.from({ length: b.length + 1 }, (_, index) => index);\n for (let i = 1; i <= a.length; i++) {\n const current = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);\n }\n previous = current;\n }\n return previous[b.length];\n}\n\nfunction tokenMatches(a, b) {\n if (a === b) return true;\n const minLength = Math.min(a.length, b.length);\n if (minLength >= 8 && editDistance(a, b) <= 2) return true;\n if (minLength >= 5 && editDistance(a, b) <= 1) return true;\n return false;\n}\n\nfunction samePersonName(a, b) {\n const normalizedA = normalizeName(a);\n const normalizedB = normalizeName(b);\n if (!normalizedA || !normalizedB) return false;\n if (normalizedA === normalizedB) return true;\n\n const wordsA = nameWords(a);\n const wordsB = nameWords(b);\n if (!wordsA.length || !wordsB.length) return false;\n\n const usedB = new Set();\n let matches = 0;\n for (const wordA of wordsA) {\n const matchIndex = wordsB.findIndex((wordB, index) =>\n !usedB.has(index) && tokenMatches(wordA, wordB)\n );\n if (matchIndex >= 0) {\n usedB.add(matchIndex);\n matches += 1;\n }\n }\n\n const smallerLength = Math.min(wordsA.length, wordsB.length);\n const ratio = matches / smallerLength;\n if (smallerLength <= 2) return matches === smallerLength && matches >= 2;\n return matches >= 2 && ratio >= 0.6;\n}\n\nfunction bankNameLooksConsistent(fileName, holderName) {\n const fileWords = nameWords(fileName);\n const holderWords = nameWords(holderName);\n\n if (!fileWords.length || !holderWords.length) return true;\n\n const used = new Set();\n\n for (const fileWord of fileWords) {\n let matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord === fileWord\n );\n\n // Algunos planos de Puerto Rico recortan el último carácter del apellido\n // por longitud de campo (ej.: ALVARAD -> ALVARADO). Eso no debe\n // convertirse en una diferencia de nombre. No se aceptan sustituciones\n // ortográficas (ej.: GONZALES -> GONZALEZ), porque sí requieren revisión.\n if (matchIndex < 0) {\n matchIndex = holderWords.findIndex(\n (holderWord, index) =>\n !used.has(index) &&\n holderWord.startsWith(fileWord) &&\n (\n (fileWord.length <= 3) ||\n (\n fileWord.length >= 4 &&\n holderWord.length === fileWord.length + 1\n )\n )\n );\n }\n\n if (matchIndex < 0) return false;\n used.add(matchIndex);\n }\n\n return true;\n}\n\nfunction nameMatchScore(a, b) {\n const na = normalizeName(a);\n const nb = normalizeName(b);\n if (!na || !nb) return 0;\n\n const cacheKey = na <= nb ? `${na}\\u0000${nb}` : `${nb}\\u0000${na}`;\n if (nameMatchScoreCache.has(cacheKey)) {\n return nameMatchScoreCache.get(cacheKey);\n }\n\n if (na === nb) {\n nameMatchScoreCache.set(cacheKey, 1);\n return 1;\n }\n\n const wa = nameWords(a);\n const wb = nameWords(b);\n if (!samePersonName(a, b)) {\n nameMatchScoreCache.set(cacheKey, 0);\n return 0;\n }\n\n const setA = new Set(wa);\n const setB = new Set(wb);\n let exact = 0;\n for (const w of setA) if (setB.has(w)) exact += 1;\n const min = Math.max(1, Math.min(setA.size, setB.size));\n const score = Math.min(\n 0.99,\n 0.75 + (exact / min) * 0.20 + (wa.length === wb.length ? 0.04 : 0)\n );\n\n nameMatchScoreCache.set(cacheKey, score);\n return score;\n}\n\nfunction bankNames(row) {\n return [row.bank_name_file, row.bank_account_holder]\n .map(clean)\n .filter(Boolean);\n}\n\nfunction bestNameScore(payrollName, bankRow) {\n return Math.max(0, ...bankNames(bankRow).map((name) => nameMatchScore(payrollName, name)));\n}\n\nfunction bankDisplayName(row) {\n return clean(row.bank_name_file) || clean(row.bank_account_holder) || 'Pago bancario';\n}\n\nfunction bankRowKey(row, index) {\n return clean(row.bank_row_key) || `${clean(row.source_file)}|${row.row_number || index + 1}`;\n}\n\n\nconst metadata = data.metadata || {};\n\nfunction parseBankDate(value, sourceFile = '') {\n const raw = clean(value);\n let match = raw.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/);\n if (match) {\n return {\n year: Number(match[3]),\n month: Number(match[2]),\n day: Number(match[1]),\n };\n }\n\n match = raw.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})/);\n if (match) {\n return {\n year: Number(match[1]),\n month: Number(match[2]),\n day: Number(match[3]),\n };\n }\n\n // Respaldo por el patrón habitual del nombre: _DDMMYY / _DDMMYYYY.\n const fileMatch = clean(sourceFile).match(/_(\\d{2})(\\d{2})(\\d{2}|\\d{4})(?:\\D|$)/);\n if (!fileMatch) return null;\n\n const yearPart = Number(fileMatch[3]);\n return {\n year: fileMatch[3].length === 2 ? 2000 + yearPart : yearPart,\n month: Number(fileMatch[2]),\n day: Number(fileMatch[1]),\n };\n}\n\nfunction selectedPeriodKind() {\n const type = clean(metadata.period_type).toLowerCase();\n if (type === 'quincena_15') return 'q15';\n if (type === 'quincena_30') return 'q30';\n return 'unknown';\n}\n\nfunction conceptPeriodHint(row) {\n const concept = normalizeName(row.concept || '');\n if (!concept) return 'unknown';\n\n if (\n /(?:^| )1(?:ra|er|era)? quincena(?: |$)/.test(concept) ||\n concept.includes('primera quincena') ||\n concept.includes('quincena 1ra') ||\n concept.includes('quincena 1')\n ) {\n return 'q15';\n }\n\n if (\n /(?:^| )2(?:da|do|nda)? quincena(?: |$)/.test(concept) ||\n concept.includes('segunda quincena') ||\n concept.includes('quincena 2da') ||\n concept.includes('quincena 2') ||\n concept.includes('fin de mes')\n ) {\n return 'q30';\n }\n\n return 'unknown';\n}\n\nfunction periodInfoForBankRow(row) {\n const selected = selectedPeriodKind();\n const rowDate = parseBankDate(row.application_date, row.source_file);\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n const hint = conceptPeriodHint(row);\n\n if (selected === 'unknown') {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (hint !== 'unknown' && hint === selected) {\n return { known: true, selected, inPeriod: true, affinity: 4, rowDate, hint };\n }\n\n if (!rowDate || !selectedYear || !selectedMonth) {\n return { known: false, selected, inPeriod: true, affinity: 1, rowDate, hint };\n }\n\n if (rowDate.year !== selectedYear || rowDate.month !== selectedMonth) {\n return { known: true, selected, inPeriod: false, affinity: 0, rowDate, hint };\n }\n\n const inHalf = selected === 'q15'\n ? rowDate.day <= 15\n : rowDate.day >= 16;\n\n return {\n known: true,\n selected,\n inPeriod: inHalf,\n affinity: inHalf ? 3 : 0,\n rowDate,\n hint,\n };\n}\n\nfunction isSupplementPayroll(payroll) {\n return clean(payroll?.source_kind).toLowerCase() !== 'quincenal';\n}\n\nfunction bankCandidatePeriodScore(payroll, bank) {\n const info = periodInfoForBankRow(bank);\n if (!info.known || info.inPeriod) return info.affinity || 1;\n\n // Una extraordinaria/vacación/XIII Mes puede pagarse fuera de la mitad\n // estricta del mes. Se permite como respaldo, siempre detrás de cualquier\n // candidato del período seleccionado.\n if (isSupplementPayroll(payroll) && info.rowDate) {\n const selectedYear = Number(metadata.year || 0);\n const selectedMonth = Number(metadata.month || 0);\n if (info.rowDate.year === selectedYear && info.rowDate.month === selectedMonth) {\n return 0.5;\n }\n }\n\n return -1;\n}\n\nconst payrollRows = Array.isArray(data.payroll?.rows) ? data.payroll.rows : [];\nconst bankRows = Array.isArray(data.bank?.rows) ? data.bank.rows : [];\n\n// Guardia de seguridad: nunca generar un reporte \"válido\" cuando el request\n// traía archivos pero el parser no consiguió ninguna fila. Esto evita que\n// todas las personas de nómina aparezcan falsamente como discrepancias.\nif (payrollRows.length === 0) {\n throw new Error(\n 'Puerto Rico: no se pudo leer ninguna fila válida de nómina. ' +\n 'Revisa los archivo Excel de Puerto Rico y las hojas Whirlpool, Personal 2 y Personal 3.'\n );\n}\n\nif (bankRows.length === 0) {\n const detectedFiles = Number(data.bank?.files_count || data.summary?.bank_files_count || 0);\n const invalidFiles = Array.isArray(data.bank?.file_summaries)\n ? data.bank.file_summaries.filter((file) => file?.ok === false)\n : [];\n\n throw new Error(\n 'Puerto Rico: no se pudo leer ninguna transacción bancaria. ' +\n `Archivos bancarios detectados: ${detectedFiles}. ` +\n (invalidFiles.length\n ? `Archivos con error de formato: ${invalidFiles.map((file) => file.file_name).join(', ')}.`\n : 'Verifica que el PDF sea el Transaction Detail Report de Banco Popular de Puerto Rico.')\n );\n}\n\nconst bambooEmployees = Array.isArray(data.bamboo?.employees) ? data.bamboo.employees : [];\nconst resolvedNameMatches = data.bamboo?.resolved_name_matches || {};\nconst bambooValidationAvailable = data.bamboo?.fetch_complete === true &&\n data.bamboo?.validation_available === true && bambooEmployees.length > 0;\n\nconst bambooByEmployeeNumber = new Map();\nconst bambooById = new Map();\nconst bambooByExactName = new Map();\nfor (const employee of bambooEmployees) {\n const number = normalizeEmployeeNumber(\n employee.employee_number || employee.employeeNumber\n );\n const id = clean(employee.bamboo_id || employee.id);\n if (number) {\n const arr = bambooByEmployeeNumber.get(number) || [];\n arr.push(employee);\n bambooByEmployeeNumber.set(number, arr);\n }\n if (id) bambooById.set(id, employee);\n for (const alias of employee.aliases || [employee.full_name]) {\n const normalized = normalizeName(alias);\n if (!normalized) continue;\n const arr = bambooByExactName.get(normalized) || [];\n arr.push(employee);\n bambooByExactName.set(normalized, arr);\n }\n}\n\nfunction employeeKey(employee) {\n return clean(employee?.bamboo_id || employee?.id) ||\n clean(employee?.employee_number || employee?.employeeNumber) ||\n normalizeName(employee?.full_name || employee?.displayName);\n}\n\nfunction validatedAliasDecision(row) {\n if (!validatedBambooAliases.length) return null;\n const rowAccount = normalizeAccount(row.account);\n const names = bankNames(row).map(normalizeName).filter(Boolean);\n const candidates = validatedBambooAliases.filter((alias) => {\n const country = clean(alias.pais || alias.country).toUpperCase();\n if (country && country !== 'PR') return false;\n const source = normalizeName(alias.nombre_origen || alias.source_name || alias.alias || '');\n if (!source || !names.includes(source)) return false;\n const aliasAccount = normalizeAccount(alias.cuenta_bancaria || alias.cuenta_banco || alias.bank_account || '');\n return !aliasAccount || !rowAccount || aliasAccount === rowAccount;\n });\n if (candidates.length !== 1) return null;\n const alias = candidates[0];\n let employee = null;\n const employeeNumber = normalizeEmployeeNumber(\n alias.bamboo_employee_number || ''\n );\n const bambooId = clean(alias.bamboo_employee_id || '');\n const bambooName = normalizeName(alias.bamboo_nombre || '');\n if (employeeNumber && (bambooByEmployeeNumber.get(employeeNumber) || []).length === 1) {\n employee = bambooByEmployeeNumber.get(employeeNumber)[0];\n } else if (bambooId && bambooById.has(bambooId)) {\n employee = bambooById.get(bambooId);\n } else if (bambooName && (bambooByExactName.get(bambooName) || []).length === 1) {\n employee = bambooByExactName.get(bambooName)[0];\n }\n if (!employee) return null;\n return {\n found: true,\n matched_by: 'validated_bamboo_alias',\n confidence: 1,\n employee,\n employee_key: employeeKey(employee),\n bamboo_alias: clean(alias.bamboo_nombre) || employee.full_name || '',\n };\n}\n\nfunction precomputedDecision(name) {\n const normalized = normalizeName(name);\n if (!normalized) return null;\n const decision = resolvedNameMatches[normalized];\n if (!decision || decision.found !== true) return null;\n const index = Number(decision.employee_index);\n const employee = Number.isInteger(index) && index >= 0 ? bambooEmployees[index] : null;\n const key = clean(decision.employee_key) || employeeKey(employee);\n if (!key) return null;\n return {\n found: true,\n matched_by: decision.matched_by || 'precomputed_name',\n confidence: Number(decision.confidence || 0),\n employee,\n employee_key: key,\n bamboo_alias: clean(decision.bamboo_alias) || employee?.full_name || '',\n query_name: clean(name),\n };\n}\n\nfunction resolveBamboo(\n row,\n linkedPayrollNames = [],\n linkedPayrollEmployeeNumbers = []\n) {\n if (!bambooValidationAvailable) {\n return {\n found: false,\n reason: 'validation_unavailable',\n };\n }\n\n const validated = validatedAliasDecision(row);\n if (validated) return validated;\n\n /*\n * Puerto Rico: si existe un identificador fuerte procedente de nómina se puede usar como respaldo para determinar\n * si el pago pertenece a una persona existente en BambooHR.\n *\n * Se normalizan guiones/espacios tanto en banco como en BambooHR:\n * 0611198400414 <-> 0611-1984-00414\n *\n * Como respaldo, cuando la fila bancaria ya fue conciliada con nómina,\n * también se usa el Employee Number/Identidad de esa fila de nómina.\n * Solo se acepta el identificador cuando apunta a UNA sola persona.\n */\n const referenceEmployeeNumbers = Array.from(\n new Set(\n [\n row.reference,\n row.participant_id,\n row.employee_number,\n row.employeeNumber,\n ...linkedPayrollEmployeeNumbers,\n ]\n .flatMap((value) =>\n String(value ?? '').match(/\\d{6,20}/g) || []\n )\n .map(normalizeEmployeeNumber)\n .filter((value) => value.length >= 6)\n )\n );\n\n const employeesByStrongIdentifier = new Map();\n\n for (const employeeNumber of referenceEmployeeNumbers) {\n const matches =\n bambooByEmployeeNumber.get(employeeNumber) || [];\n\n if (matches.length !== 1) continue;\n\n const employee = matches[0];\n const key = employeeKey(employee);\n\n if (key) {\n employeesByStrongIdentifier.set(\n key,\n {\n employee,\n employee_number: employeeNumber,\n }\n );\n }\n }\n\n if (employeesByStrongIdentifier.size === 1) {\n const resolved =\n employeesByStrongIdentifier.values().next().value;\n\n return {\n found: true,\n matched_by: 'bank_or_payroll_employee_number',\n confidence: 1,\n employee: resolved.employee,\n employee_key: employeeKey(resolved.employee),\n bamboo_alias:\n clean(resolved.employee.full_name) ||\n clean(resolved.employee.displayName) ||\n '',\n matched_employee_number:\n resolved.employee_number,\n };\n }\n\n const holderName = clean(row.bank_account_holder);\n const fileName = clean(row.bank_name_file);\n\n // El nombre legal del cuentahabiente es la evidencia bancaria más fuerte.\n // Si identifica exactamente a una persona que pertenecía al período,\n // prevalece sobre un nombre abreviado que pueda coincidir con un perfil\n // histórico distinto (caso real: Suyapa Palma).\n const holderDecision = holderName\n ? precomputedDecision(holderName)\n : null;\n\n const holderMatchedBy =\n clean(holderDecision?.matched_by).toLowerCase();\n\n const holderIsExactUnique =\n holderDecision?.found === true &&\n (\n holderDecision.confidence >= 0.999 ||\n holderMatchedBy.includes('exact_precomputed_name') ||\n holderMatchedBy.includes('confirmed_alias_catalog')\n );\n\n // \"Banco sin Bamboo\" valida existencia en BambooHR, no que la persona\n // siguiera activa durante toda la quincena. Un finiquito/pago final puede\n // ocurrir después de la fecha de terminación. Por eso un nombre legal\n // completo y único del cuentahabiente es suficiente evidencia.\n if (holderIsExactUnique) {\n return {\n ...holderDecision,\n matched_by:\n `${holderDecision.matched_by || 'precomputed_name'}_holder_exact`,\n };\n }\n\n if (\n holderDecision?.found &&\n holderDecision.employee?.overlaps_period === true\n ) {\n return {\n ...holderDecision,\n matched_by:\n `${holderDecision.matched_by || 'precomputed_name'}_holder_in_period`,\n };\n }\n\n const names = Array.from(\n new Set(\n [\n holderName,\n fileName,\n ...linkedPayrollNames.map(clean),\n ].filter(Boolean)\n )\n );\n\n const decisions = names\n .map(precomputedDecision)\n .filter(Boolean);\n\n if (!decisions.length) {\n return {\n found: false,\n reason: 'no_confident_name_match',\n };\n }\n\n // Antes de declarar conflicto, descartar perfiles históricos que no\n // pertenecen al período cuando existe una única persona vigente/en período.\n const inPeriodByKey = new Map();\n\n for (const decision of decisions) {\n if (decision.employee?.overlaps_period !== true) continue;\n\n const current =\n inPeriodByKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n inPeriodByKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const inPeriodRanked =\n Array.from(inPeriodByKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (inPeriodRanked.length === 1) {\n return {\n ...inPeriodRanked[0],\n matched_by:\n `${inPeriodRanked[0].matched_by || 'precomputed_name'}_unique_in_period`,\n };\n }\n\n if (\n inPeriodRanked.length >= 2 &&\n (\n inPeriodRanked[0].confidence -\n inPeriodRanked[1].confidence\n ) >= 0.08\n ) {\n return inPeriodRanked[0];\n }\n\n const byKey = new Map();\n\n for (const decision of decisions) {\n const current =\n byKey.get(decision.employee_key);\n\n if (\n !current ||\n decision.confidence > current.confidence\n ) {\n byKey.set(\n decision.employee_key,\n decision\n );\n }\n }\n\n const ranked =\n Array.from(byKey.values())\n .sort(\n (left, right) =>\n right.confidence - left.confidence\n );\n\n if (ranked.length === 1) {\n return ranked[0];\n }\n\n if (\n (\n ranked[0].confidence -\n ranked[1].confidence\n ) >= 0.08\n ) {\n return ranked[0];\n }\n\n return {\n found: false,\n reason: 'conflicting_precomputed_name_matches',\n candidates: ranked.slice(0, 3),\n };\n}\n\nconst usedBankKeys = new Set();\nconst matchedPayrollIndexes = new Set();\nconst matchedPairs = [];\nconst bankIndexByKey = new Map();\n\n// Índice por centavos para que la pasada principal no compare cada fila de\n// nómina contra todas las transacciones del banco. Esto preserva exactamente\n// la lógica original: la primera pasada ya exigía el mismo monto al centavo.\nconst bankIndexesByAmountCents = new Map();\n\nfunction amountCents(value) {\n return Math.round(roundMoney(value) * 100);\n}\n\nbankRows.forEach((row, index) => {\n const key = bankRowKey(row, index);\n bankIndexByKey.set(key, index);\n\n const amountKey = `${clean(row.currency || 'USD').toUpperCase()}|${amountCents(row.amount)}`;\n const bucket = bankIndexesByAmountCents.get(amountKey) || [];\n bucket.push(index);\n bankIndexesByAmountCents.set(amountKey, bucket);\n});\n\nfunction availableNameCandidates(payroll, requireExactAmount) {\n const candidates = [];\n\n // La pasada exacta solo inspecciona filas con el mismo importe. La segunda\n // pasada conserva el barrido completo y el mismo criterio de persona fuerte\n // + menor diferencia que tenía el workflow validado.\n let candidateIndexes = null;\n\n if (requireExactAmount) {\n const currency =\n clean(payroll.currency || 'USD')\n .toUpperCase();\n\n const center =\n amountCents(payroll.payroll_amount);\n\n const indexSet = new Set();\n\n for (\n let delta = -MONEY_TOLERANCE_CENTS;\n delta <= MONEY_TOLERANCE_CENTS;\n delta++\n ) {\n const bucket =\n bankIndexesByAmountCents.get(\n `${currency}|${center + delta}`\n ) || [];\n\n for (const bankIndex of bucket) {\n indexSet.add(bankIndex);\n }\n }\n\n candidateIndexes =\n Array.from(indexSet);\n }\n\n const inspectBank = (bankIndex) => {\n const bank = bankRows[bankIndex];\n if (!bank) return;\n\n const key = bankRowKey(bank, bankIndex);\n if (usedBankKeys.has(key)) return;\n if (clean(payroll.currency || 'USD').toUpperCase() !== clean(bank.currency || 'HNL').toUpperCase()) return;\n\n if (requireExactAmount && !sameCent(payroll.payroll_amount, bank.amount)) return;\n\n const periodScore = bankCandidatePeriodScore(payroll, bank);\n if (periodScore < 0) return;\n\n const score = bestNameScore(payroll.employee_name, bank);\n if (score <= 0) return;\n\n candidates.push({\n bank,\n bankIndex,\n key,\n score,\n periodScore,\n amountDiff: Math.abs(moneyDiff(payroll.payroll_amount, bank.amount)),\n });\n };\n\n if (candidateIndexes) {\n for (const bankIndex of candidateIndexes) {\n inspectBank(bankIndex);\n }\n } else {\n for (let bankIndex = 0; bankIndex < bankRows.length; bankIndex++) {\n inspectBank(bankIndex);\n }\n }\n\n candidates.sort((a, b) => {\n if (b.periodScore !== a.periodScore) return b.periodScore - a.periodScore;\n if (b.score !== a.score) return b.score - a.score;\n if (a.amountDiff !== b.amountDiff) return a.amountDiff - b.amountDiff;\n return a.bankIndex - b.bankIndex;\n });\n\n return candidates;\n}\n\n// Primera pasada: persona + monto exacto. Es la evidencia más fuerte y evita\n// que extraordinarias/vacaciones de una misma persona se mezclen entre sí.\npayrollRows.forEach((payroll, payrollIndex) => {\n const candidates = availableNameCandidates(payroll, true);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\n// Segunda pasada: persona fuerte aunque el monto sea distinto. Solo se toma\n// una contraparte bancaria por fila de nómina y se prioriza la menor diferencia.\npayrollRows.forEach((payroll, payrollIndex) => {\n if (matchedPayrollIndexes.has(payrollIndex)) return;\n const candidates = availableNameCandidates(payroll, false);\n if (!candidates.length) return;\n const chosen = candidates[0];\n usedBankKeys.add(chosen.key);\n matchedPayrollIndexes.add(payrollIndex);\n matchedPairs.push({ payroll, payrollIndex, ...chosen });\n});\n\nconst linkedPayrollNamesByBankKey = new Map();\nconst linkedPayrollEmployeeNumbersByBankKey = new Map();\n\nfor (const pair of matchedPairs) {\n const currentNames =\n linkedPayrollNamesByBankKey.get(pair.key) || [];\n\n currentNames.push(pair.payroll.employee_name);\n\n linkedPayrollNamesByBankKey.set(\n pair.key,\n currentNames\n );\n\n const employeeNumber =\n normalizeEmployeeNumber(\n pair.payroll.employee_number ||\n pair.payroll.employeeNumber ||\n ''\n );\n\n if (employeeNumber) {\n const currentNumbers =\n linkedPayrollEmployeeNumbersByBankKey.get(\n pair.key\n ) || [];\n\n currentNumbers.push(employeeNumber);\n\n linkedPayrollEmployeeNumbersByBankKey.set(\n pair.key,\n currentNumbers\n );\n }\n}\n\n// Filas bancarias del período de Puerto Rico.\n// Toda fila emparejada entra siempre. Para una fila no emparejada se usa la\n// fecha/concepto del plano para evitar arrastrar accidentalmente otra quincena.\nconst includedBankRows = [];\nconst excludedBankRows = [];\n\nfor (let index = 0; index < bankRows.length; index++) {\n const row = bankRows[index];\n const key = bankRowKey(row, index);\n\n if (usedBankKeys.has(key)) {\n includedBankRows.push({ row, key, index, inclusion: 'matched_payroll' });\n continue;\n }\n\n const periodInfo = periodInfoForBankRow(row);\n if (periodInfo.known && !periodInfo.inPeriod) {\n excludedBankRows.push({ row, key, reason: 'outside_selected_payroll_period' });\n continue;\n }\n\n includedBankRows.push({ row, key, index, inclusion: 'unmatched_current_period' });\n}\n\nconst includedBankKeySet = new Set(includedBankRows.map((entry) => entry.key));\n\nconst coreRows = [];\nconst wrongAccountGroups = new Map();\nfor (const pair of matchedPairs) {\n if (!includedBankKeySet.has(pair.key)) continue;\n const payrollAmount = roundMoney(pair.payroll.payroll_amount);\n const bankAmount = roundMoney(pair.bank.amount);\n const difference = moneyDiff(payrollAmount, bankAmount);\n const currency = clean(pair.payroll.currency || pair.bank.currency || 'USD').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAccount = normalizeAccount(pair.payroll.account || pair.payroll.payroll_account);\n const bankAccount = normalizeAccount(pair.bank.account);\n const accountMismatch = Boolean(payrollAccount && bankAccount && payrollAccount !== bankAccount);\n if (accountMismatch) {\n const groupKey = `${normalizeName(pair.payroll.employee_name)}|${currency}|${payrollAccount}|${bankAccount}`;\n const current = wrongAccountGroups.get(groupKey) || {\n id: `pr_wrong_account_${wrongAccountGroups.size + 1}`,\n employee: pair.payroll.employee_name,\n employee_name: pair.payroll.employee_name,\n employeeNumber: pair.payroll.employee_number || '', employee_number: pair.payroll.employee_number || '',\n account: bankAccount, payrollAccount, payroll_account: payrollAccount, bankAccount, bank_account: bankAccount,\n currency, payrollAmount:0, payroll_amount:0, bankAmount:0, bank_amount:0, difference:0,\n status:'Pendiente revisión', category:'posible_cuenta_mal_digitada', subcategory:'nombre_y_monto_coinciden_cuenta_diferente',\n observation:`El nombre y el monto coinciden, pero la cuenta de nómina (${payrollAccount}) es diferente a la cuenta del banco (${bankAccount}).`,\n source_rows:[], source_sheets:new Set(), source_files:new Set(), shipment_numbers:new Set(), plan_numbers:new Set(),\n bank_name_file: pair.bank.bank_name_file || '', bank_account_holder: pair.bank.bank_account_holder || ''\n };\n current.payrollAmount=roundMoney(current.payrollAmount+payrollAmount); current.payroll_amount=current.payrollAmount;\n current.bankAmount=roundMoney(current.bankAmount+bankAmount); current.bank_amount=current.bankAmount; current.difference=moneyDiff(current.payrollAmount,current.bankAmount);\n current.source_rows.push(pair.payroll); if(pair.payroll.source_sheet)current.source_sheets.add(pair.payroll.source_sheet); if(pair.bank.source_file)current.source_files.add(pair.bank.source_file); if(pair.bank.shipment_number)current.shipment_numbers.add(pair.bank.shipment_number); if(pair.bank.plan_number)current.plan_numbers.add(pair.bank.plan_number);\n wrongAccountGroups.set(groupKey,current); continue;\n }\n const exact = sameCent(payrollAmount, bankAmount);\n coreRows.push({id:`pr_match_${pair.payrollIndex+1}_${pair.bankIndex+1}`,employee:pair.payroll.employee_name,employee_name:pair.payroll.employee_name,employeeNumber:pair.payroll.employee_number||'',employee_number:pair.payroll.employee_number||'',account:bankAccount||payrollAccount,payrollAccount:payrollAccount,payroll_account:payrollAccount,bankAccount:bankAccount,bank_account:bankAccount,currency,payrollAmount,payroll_amount:payrollAmount,bankAmount,bank_amount:bankAmount,difference,status:exact?'Coincide':'Pendiente revisión',category:exact?'coincidencia':'discrepancia',subcategory:exact?'persona_cuenta_y_monto_exactos':'persona_y_cuenta_coinciden_monto_diferente',observation:exact?'Coincidencia de persona, cuenta y monto.':difference>0?'Se pagó de menos.':'Se pagó de más.',source_kind:pair.payroll.source_kind||'',source_sheet:pair.payroll.source_sheet||'',source_file:pair.bank.source_file||'',shipment_number:pair.bank.shipment_number||'',plan_number:pair.bank.plan_number||'',plan_label:pair.bank.plan_label||'',reference:pair.bank.reference||'',bank_name_file:pair.bank.bank_name_file||'',bank_account_holder:pair.bank.bank_account_holder||''});\n}\nfor (let payrollIndex=0; payrollIndex({...row,source_sheets:Array.from(row.source_sheets),source_files:Array.from(row.source_files),shipment_numbers:Array.from(row.shipment_numbers),plan_numbers:Array.from(row.plan_numbers),source_sheet:Array.from(row.source_sheets).join(' / '),source_file:Array.from(row.source_files).join(' / '),shipment_number:Array.from(row.shipment_numbers).join(' / '),plan_number:Array.from(row.plan_numbers).join(' / ')}));\ncoreRows.push(...wrongAccountRows);\n\n// Banco sin Nómina se conserva a nivel de TRANSACCIÓN.\n// Una misma persona/cuenta puede tener más de un pago bancario legítimamente\n// separado; agruparlos reduce artificialmente el número de hallazgos.\nconst bankWithoutPayrollRows = [];\n\nfor (const entry of includedBankRows) {\n if (usedBankKeys.has(entry.key)) continue;\n\n const row = entry.row;\n const displayName =\n bankDisplayName(row);\n\n const rowCurrency =\n clean(row.currency || 'USD')\n .toUpperCase() === 'USD'\n ? 'USD'\n : 'HNL';\n\n const bankAmount =\n roundMoney(row.amount || 0);\n\n bankWithoutPayrollRows.push({\n id:\n `pr_bank_without_payroll_${entry.index + 1}`,\n employee: displayName,\n employee_name: displayName,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount,\n bank_amount: bankAmount,\n difference:\n roundMoney(0 - bankAmount),\n status: 'Pendiente revisión',\n category: 'banco_sin_nomina',\n subcategory:\n 'pago_banco_sin_registro_nomina',\n observation:\n 'Pago bancario sin registro equivalente en las planillas cargadas.',\n bank_name_file:\n row.bank_name_file || '',\n bank_account_holder:\n row.bank_account_holder || '',\n source_file:\n row.source_file || '',\n shipment_number:\n row.shipment_number || '',\n plan_number:\n row.plan_number || '',\n reference:\n row.reference || '',\n row_number:\n row.row_number || null,\n source_rows: [row],\n });\n}\n\ncoreRows.push(...bankWithoutPayrollRows);\n\nconst nameDifferenceRows = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const rowCurrency=clean(row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';\n if (!clean(row.bank_name_file) || !clean(row.bank_account_holder)) continue;\n if (bankNameLooksConsistent(row.bank_name_file, row.bank_account_holder)) continue;\n nameDifferenceRows.push({\n id: `pr_bank_name_difference_${entry.index + 1}`,\n employee: row.bank_name_file,\n employee_name: row.bank_name_file,\n employeeNumber: '',\n employee_number: '',\n account: row.account || '',\n payrollAccount: '',\n payroll_account: '',\n bankAccount: row.account || '',\n bank_account: row.account || '',\n currency: rowCurrency,\n payrollAmount: 0,\n payroll_amount: 0,\n bankAmount: roundMoney(row.amount),\n bank_amount: roundMoney(row.amount),\n difference: 0,\n status: 'Pendiente revisión',\n category: 'diferencia_nombre_banco',\n subcategory: 'nombre_archivo_vs_cuentahabiente',\n observation: `El Nombre en Archivo (${row.bank_name_file}) no coincide con el Nombre del Cuentahabiente (${row.bank_account_holder}).`,\n bank_name_file: row.bank_name_file,\n bank_account_holder: row.bank_account_holder,\n source_file: row.source_file || '',\n shipment_number: row.shipment_number || '',\n plan_number: row.plan_number || '',\n reference: row.reference || '',\n row_number: row.row_number || null,\n });\n}\n\nconst bankWithoutBambooMap = new Map();\nconst bambooMatchDetails = [];\nfor (const entry of includedBankRows) {\n const row = entry.row;\n const linkedNames =\n linkedPayrollNamesByBankKey.get(entry.key) || [];\n\n const linkedEmployeeNumbers =\n linkedPayrollEmployeeNumbersByBankKey.get(\n entry.key\n ) || [];\n\n const decision = resolveBamboo(\n row,\n linkedNames,\n linkedEmployeeNumbers\n );\n bambooMatchDetails.push({\n bank_row_key: entry.key,\n bank_name_file: row.bank_name_file || '',\n bank_account_holder: row.bank_account_holder || '',\n account: row.account || '',\n amount: row.amount || 0,\n linked_payroll_names: linkedNames,\n ...decision,\n });\n if (decision.found) continue;\n\n const displayName = bankDisplayName(row);\n const rowCurrency=clean(row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';\n const groupingKey = `${normalizeAccount(row.account)}|${normalizeName(displayName)}|${rowCurrency}`;\n const current = bankWithoutBambooMap.get(groupingKey) || {\n id: `pr_bank_without_bamboo_${bankWithoutBambooMap.size + 1}`,\n bank_name_file: row.bank_name_file || displayName,\n bank_account_holder: row.bank_account_holder || '',\n employee_name: row.bank_name_file || displayName,\n account: row.account || '',\n currency: rowCurrency,\n bank_amount: 0,\n amount: 0,\n status: 'Pendiente revisión',\n category: 'banco_sin_bamboo',\n shipment_numbers: new Set(),\n references: new Set(),\n source_files: new Set(),\n source_rows: [],\n };\n current.bank_amount = roundMoney(current.bank_amount + Number(row.amount || 0));\n current.amount = current.bank_amount;\n if (row.shipment_number) current.shipment_numbers.add(row.shipment_number);\n if (row.reference) current.references.add(row.reference);\n if (row.source_file) current.source_files.add(row.source_file);\n current.source_rows.push(row);\n bankWithoutBambooMap.set(groupingKey, current);\n}\n\nconst bankWithoutBamboo = Array.from(bankWithoutBambooMap.values()).map((row) => ({\n ...row,\n shipment_numbers: Array.from(row.shipment_numbers),\n shipment_number: Array.from(row.shipment_numbers).join(' / '),\n references: Array.from(row.references),\n source_files: Array.from(row.source_files),\n source_file: Array.from(row.source_files).join(' / '),\n difference: roundMoney(0 - row.bank_amount),\n}));\n\nfunction priority(row) {\n const category = clean(row.category).toLowerCase();\n if (category === 'posible_cuenta_mal_digitada') return 1;\n if (category === 'discrepancia') return 2;\n if (category === 'banco_sin_nomina') return 3;\n if (category === 'diferencia_nombre_banco') return 5;\n if (category === 'coincidencia') return 99;\n return 50;\n}\n\nconst rowsFinales = [...coreRows, ...nameDifferenceRows].sort((a, b) => {\n const p = priority(a) - priority(b);\n if (p !== 0) return p;\n return clean(a.employee_name).localeCompare(clean(b.employee_name), 'es');\n});\n\nconst coincidencias = coreRows.filter((row) => row.category === 'coincidencia').length;\nconst discrepancias = coreRows.filter((row) => row.category === 'discrepancia').length;\nconst bancoSinNomina = bankWithoutPayrollRows.length;\nconst diferenciasNombreBanco = nameDifferenceRows.length;\nconst posiblesCuentasMalDigitadas = wrongAccountRows.length;\nconst pendientes = discrepancias + bancoSinNomina + diferenciasNombreBanco;\nconst totalNominaByCurrency = {HNL:0,USD:0};\nfor(const row of payrollRows){const c=clean(row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';totalNominaByCurrency[c]=roundMoney(totalNominaByCurrency[c]+Number(row.payroll_amount||0));}\nconst totalBancoByCurrency = {HNL:0,USD:0};\nfor(const entry of includedBankRows){const c=clean(entry.row.currency||'USD').toUpperCase()==='USD'?'USD':'HNL';totalBancoByCurrency[c]=roundMoney(totalBancoByCurrency[c]+Number(entry.row.amount||0));}\nconst diferenciaByCurrency={HNL:moneyDiff(totalNominaByCurrency.HNL,totalBancoByCurrency.HNL),USD:moneyDiff(totalNominaByCurrency.USD,totalBancoByCurrency.USD)};\nconst totalNomina=totalNominaByCurrency.HNL || totalNominaByCurrency.USD;\nconst totalBanco=totalBancoByCurrency.HNL || totalBancoByCurrency.USD;\nconst diferenciaTotal=totalNominaByCurrency.HNL||totalBancoByCurrency.HNL?diferenciaByCurrency.HNL:diferenciaByCurrency.USD;\n\nconst bambooSummaryCompact = { ...(data.bamboo || {}) };\ndelete bambooSummaryCompact.employees;\n\nreturn [{\n json: {\n ok: true,\n stage: 'cruce_nomina_banco_puerto_rico',\n errors: [],\n metadata: data.metadata || {},\n summary: {\n coincidencias,\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo: bankWithoutBamboo.length,\n nominaSinCuenta: 0,\n diferenciasNombreBanco,\n posiblesCuentasMalDigitadas,\n pendientes,\n filasNominaValidas: Number(data.payroll?.base_rows_count || data.payroll?.valid_rows_count || 0),\n filasNominaSinCuenta: 0,\n suplementosPotenciales: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaAplicados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAplicados: 0,\n suplementosNominaAdjuntados: Number(data.payroll?.supplement_rows_count || 0),\n suplementosNominaNoAdjuntados: Number(data.payroll?.unattached_supplements_count || 0),\n reconciliacionesExactasFinales: coincidencias,\n cuentasNominaAgrupadas: 0,\n transaccionesBanco: includedBankRows.length,\n cuentasBancoAgrupadas: new Set(includedBankRows.map((entry) => normalizeAccount(entry.row.account)).filter(Boolean)).size,\n empleadosBambooPR: Number(data.bamboo?.puerto_rico_count || 0),\n empleadosBambooEnPeriodo: Number(data.bamboo?.active_in_period_count || 0),\n bambooPaginasDescargadas: Number(data.bamboo?.pages_fetched || 0),\n bambooEmpleadosEsperados: Number(data.bamboo?.expected_total || 0),\n bambooDescargaCompleta: Boolean(data.bamboo?.fetch_complete),\n bambooValidacionDisponible: bambooValidationAvailable,\n totalNominaBase: roundMoney(data.payroll?.base_total || 0),\n totalSuplementosAplicados: roundMoney(data.payroll?.supplements_total || 0),\n totalNomina,\n totalBanco,\n diferenciaTotal,\n totalNominaPorMoneda: totalNominaByCurrency,\n totalBancoPorMoneda: totalBancoByCurrency,\n diferenciaTotalPorMoneda: diferenciaByCurrency,\n },\n rows: rowsFinales,\n bankWithoutBamboo,\n nameDifferences: nameDifferenceRows,\n bambooSummary: bambooSummaryCompact,\n reportUrl: null,\n debug: {\n matched_pairs_count: matchedPairs.length,\n included_bank_rows_count: includedBankRows.length,\n excluded_bank_rows_count: excludedBankRows.length,\n excluded_bank_rows_preview: excludedBankRows.slice(0, 100),\n validated_bamboo_aliases_loaded: validatedBambooAliases.length,\n bamboo_matches: bambooMatchDetails,\n bamboo_validation_available: bambooValidationAvailable,\n banco_sin_bamboo: bankWithoutBamboo,\n payroll_preview: payrollRows.slice(0, 20),\n bank_included_preview: includedBankRows.slice(0, 20).map((entry) => entry.row),\n },\n },\n}];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -120, + 5200 + ], + "id": "f7befa53-9b77-44d3-b0fb-e456328163e5", + "name": "Cruzar Nómina vs Banco" + }, + { + "parameters": { + "jsCode": "const data = $input.first().json || {};\n\nfunction normalizeText(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\n\nfunction readableCategory(value) {\n const map = {\n coincidencia: 'Coincidencia',\n discrepancia: 'Discrepancia',\n posible_cuenta_mal_digitada: 'Posible cuenta mal digitada',\n banco_sin_nomina: 'Banco sin nómina',\n banco_sin_bamboo: 'Banco sin Bamboo',\n nomina_sin_cuenta: 'Nómina sin cuenta',\n diferencia_nombre_banco: 'Diferencia nombre banco',\n };\n\n return map[value] || normalizeText(value).replace(/_/g, ' ');\n}\n\nfunction firstValue(value) {\n if (Array.isArray(value)) {\n return value.filter(Boolean).join(' / ');\n }\n\n return normalizeText(value);\n}\n\nfunction formatPeriodEnd(value) {\n const raw = normalizeText(value);\n\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(raw)) {\n return raw;\n }\n\n const [year, month, day] = raw.split('-');\n const monthNames = {\n '01': 'ene',\n '02': 'feb',\n '03': 'mar',\n '04': 'abr',\n '05': 'may',\n '06': 'jun',\n '07': 'jul',\n '08': 'ago',\n '09': 'sep',\n '10': 'oct',\n '11': 'nov',\n '12': 'dic',\n };\n\n return `${day}-${monthNames[month] || month}-${year}`;\n}\n\nfunction mainReportSense(row, difference) {\n const category = normalizeText(row.category).toLowerCase();\n const subcategory = normalizeText(row.subcategory).toLowerCase();\n\n if (category === 'posible_cuenta_mal_digitada') {\n return 'Revisar cuenta';\n }\n\n if (\n subcategory === 'nomina_con_cuenta_sin_pago_banco' ||\n (Number(row.bank_amount ?? row.bankAmount ?? 0) === 0 &&\n Number(row.payroll_amount ?? row.payrollAmount ?? 0) > 0)\n ) {\n return 'No aparece pagado en banco';\n }\n\n if (difference > 0) {\n return 'Se pagó de menos';\n }\n\n if (difference < 0) {\n return 'Se pagó de más';\n }\n\n return 'Revisar';\n}\n\nconst metadata = data.metadata || {};\nconst summary = data.summary || {};\nconst rows = Array.isArray(data.rows) ? data.rows : [];\nconst bankWithoutBamboo = Array.isArray(data.bankWithoutBamboo)\n ? data.bankWithoutBamboo\n : [];\n\nconst periodLabel =\n metadata.period_label ||\n `${metadata.year || ''}-${metadata.month || ''}-${metadata.period_type || ''}`;\n\nconst spreadsheetTitle =\n `Cruce de Cuentas GLM PR - ${periodLabel}`;\n\nconst mainReportSubtitle =\n `Diferencias de Monto Nómina vs. Banco · Puerto Rico · ${\n formatPeriodEnd(metadata.period_end || '')\n }`;\n\nconst bancoSinBambooSubtitle =\n `Pagos en banco sin empleado identificado en BambooHR · Puerto Rico · ${\n formatPeriodEnd(metadata.period_end || '')\n }`;\n\nconst cuentaMalDigitadaSubtitle =\n `Cuenta Mal Digitada en Nómina · Puerto Rico · ${\n formatPeriodEnd(metadata.period_end || '')\n }`;\n\nconst cuentaMalDigitadaCases = rows.filter(\n (row) => row.category === 'posible_cuenta_mal_digitada'\n);\n\nconst hasCuentaMalDigitada =\n cuentaMalDigitadaCases.length > 0;\n\nconst sheetIds = {\n nominaVsBanco: 101,\n bancoSinNomina: 102,\n bancoSinBamboo: 103,\n diferenciasNombreBanco: 104,\n cuentaMalDigitada: 105,\n resumen: 106,\n};\n\nconst sheetTitles = {\n nominaVsBanco: '01 Nómina vs Banco',\n bancoSinNomina: '02 Banco sin Nómina',\n bancoSinBamboo: '03 Banco sin Bamboo',\n diferenciasNombreBanco: '04 Diferencias nombre banco',\n cuentaMalDigitada: '05 Cuenta Mal Digitada',\n resumen: hasCuentaMalDigitada\n ? '06 Resumen'\n : '05 Resumen',\n};\n\nconst nominaVsBancoHeader = [\n '#',\n 'Empleado',\n 'Cuenta',\n 'Monto en Nómina',\n 'Monto en Banco',\n 'Diferencia',\n 'Sentido',\n 'Estado',\n 'Resolución',\n];\n\nconst mainReportRows = rows\n .filter((row) => row.category === 'discrepancia')\n .map((row, index) => {\n const payrollAmount = roundMoney(\n row.payroll_amount ?? row.payrollAmount ?? 0\n );\n const bankAmount = roundMoney(\n row.bank_amount ?? row.bankAmount ?? 0\n );\n const difference = roundMoney(\n row.difference ?? (payrollAmount - bankAmount)\n );\n\n return [\n index + 1,\n normalizeText(row.employee_name || row.employee || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.payroll_account ||\n row.payrollAccount ||\n row.account ||\n ''\n ),\n payrollAmount,\n bankAmount,\n difference,\n mainReportSense(row, difference),\n normalizeText(row.status || 'Riesgo').toUpperCase(),\n '',\n ];\n });\n\nconst nominaVsBancoValues = [\n [\n 'GOMEZLEE MARKETING',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n [\n mainReportSubtitle,\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n ['', '', '', '', '', '', '', '', ''],\n nominaVsBancoHeader,\n ...mainReportRows,\n];\n\nconst bancoSinNominaHeader = [\n 'Empleado Banco',\n 'Cuenta Banco',\n 'Moneda',\n 'Monto Banco',\n 'Estado',\n 'Observación',\n 'Resolución',\n];\n\nconst bancoSinNominaRows = rows\n .filter((row) => row.category === 'banco_sin_nomina')\n .map((row) => [\n normalizeText(row.employee_name || row.employee || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n ),\n normalizeText(row.currency || 'USD'),\n roundMoney(row.bank_amount ?? row.bankAmount ?? 0),\n normalizeText(row.status || ''),\n normalizeText(row.observation || ''),\n '',\n ]);\n\nconst bancoSinBambooHeader = [\n '#',\n 'Nombre en banco',\n 'Nombre del cuentahabiente',\n 'Cuenta',\n 'Monto en banco',\n 'Número de envío',\n 'Estado',\n 'Resolución',\n];\n\nconst bancoSinBambooRows = bankWithoutBamboo.map((row, index) => [\n index + 1,\n normalizeText(row.bank_name_file || row.employee_name || ''),\n normalizeText(row.bank_account_holder || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n ),\n roundMoney(row.bank_amount ?? row.bankAmount ?? 0),\n firstValue(row.shipment_numbers || row.shipment_number || ''),\n 'PENDIENTE REVISIÓN',\n '',\n]);\n\nconst bancoSinBambooValues = [\n [\n 'GOMEZLEE MARKETING',\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n [\n bancoSinBambooSubtitle,\n '',\n '',\n '',\n '',\n '',\n '',\n '',\n ],\n ['', '', '', '', '', '', '', ''],\n bancoSinBambooHeader,\n ...bancoSinBambooRows,\n];\n\nconst diferenciasNombreHeader = [\n 'Nombre en Archivo',\n 'Nombre del Cuentahabiente',\n 'Cuenta Destino',\n 'Moneda',\n 'Monto',\n 'Número de envío',\n 'Número de plan',\n 'Archivo',\n 'Estado',\n 'Observación',\n 'Resolución',\n];\n\nconst diferenciasNombreRows = rows\n .filter((row) => row.category === 'diferencia_nombre_banco')\n .map((row) => [\n normalizeText(row.bank_name_file || row.employee_name || ''),\n normalizeText(row.bank_account_holder || ''),\n normalizeText(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n ),\n normalizeText(row.currency || 'USD'),\n roundMoney(row.bank_amount ?? row.bankAmount ?? 0),\n normalizeText(row.shipment_number || ''),\n normalizeText(row.plan_number || ''),\n normalizeText(row.source_file || ''),\n normalizeText(row.status || ''),\n normalizeText(row.observation || ''),\n '',\n ]);\n\n\nfunction accountValues(value) {\n const values = Array.isArray(value)\n ? value\n : String(value ?? '')\n .split(/\\s*(?:\\/|;|,|\\by\\b)\\s*/i);\n\n return values\n .map((item) =>\n String(item ?? '')\n .replace(/\\.0$/g, '')\n .replace(/\\D/g, '')\n .trim()\n )\n .filter((item) =>\n item.length >= 7 &&\n !/^0+$/.test(item)\n );\n}\n\nfunction payrollAccountsForWrongAccount(row) {\n const candidates = [\n row.payroll_account,\n row.payrollAccount,\n ...(Array.isArray(row.source_rows)\n ? row.source_rows.flatMap((sourceRow) => [\n sourceRow.account,\n sourceRow.payroll_account,\n sourceRow.payrollAccount,\n ])\n : []),\n ];\n\n return Array.from(new Set(\n candidates.flatMap(accountValues)\n ));\n}\n\nfunction bankAccountForWrongAccount(row) {\n return firstValue(\n row.bank_account ||\n row.bankAccount ||\n row.account ||\n ''\n );\n}\n\nfunction wrongAccountTotalStatus(row) {\n const currency = normalizeText(row.currency || 'USD').toUpperCase() === 'USD' ? 'USD' : 'HNL';\n const payrollAmount = roundMoney(\n row.payroll_amount ?? row.payrollAmount ?? 0\n );\n const bankAmount = roundMoney(\n row.bank_amount ?? row.bankAmount ?? 0\n );\n const difference = roundMoney(\n payrollAmount - bankAmount\n );\n\n if (Math.abs(difference) <= 0.02) {\n return 'El total de nómina coincide con el total del banco.';\n }\n\n if (difference > 0) {\n return (\n `El total de nómina supera el total del banco por ` +\n `${currency} ${Math.abs(difference).toLocaleString('en-US', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}.`\n );\n }\n\n return (\n `El total pagado por el banco supera el total de nómina por ` +\n `${currency} ${Math.abs(difference).toLocaleString('en-US', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}.`\n );\n}\n\nfunction wrongAccountFinding(row) {\n const existing = normalizeText(row.observation || '');\n\n if (existing) {\n return existing;\n }\n\n const payrollAccounts =\n payrollAccountsForWrongAccount(row);\n const bankAccount =\n bankAccountForWrongAccount(row);\n\n return (\n `El empleado presenta una posible inconsistencia entre ` +\n `la cuenta registrada en nómina ` +\n `(${payrollAccounts.join(' y ') || 'sin cuenta identificada'}) ` +\n `y la cuenta utilizada por el banco ` +\n `(${bankAccount || 'sin cuenta identificada'}).`\n );\n}\n\nconst cuentaMalDigitadaHeader = [\n '#',\n 'Campo',\n 'Detalle',\n 'Resolución',\n];\n\nconst cuentaMalDigitadaRows = [];\n\nfor (\n let index = 0;\n index < cuentaMalDigitadaCases.length;\n index++\n) {\n const row = cuentaMalDigitadaCases[index];\n const payrollAccounts =\n payrollAccountsForWrongAccount(row);\n const bankAccount =\n bankAccountForWrongAccount(row);\n\n const fields = [\n [\n 'Empleado',\n normalizeText(\n row.employee_name ||\n row.employee ||\n ''\n ),\n ],\n [\n 'Cuentas registradas en las hojas de nómina',\n payrollAccounts.join(' y ') ||\n 'No se identificó una cuenta válida en la nómina.',\n ],\n [\n 'Cuenta utilizada por el banco',\n bankAccount ||\n 'No se identificó una cuenta válida en el banco.',\n ],\n [\n 'Estado del total',\n wrongAccountTotalStatus(row),\n ],\n [\n 'Hallazgo',\n wrongAccountFinding(row),\n ],\n [\n 'Clasificación',\n 'Posible cuenta mal digitada — revisar y unificar la cuenta en las hojas de nómina.',\n ],\n ];\n\n fields.forEach((field, fieldIndex) => {\n cuentaMalDigitadaRows.push([\n fieldIndex === 0 ? index + 1 : '',\n field[0],\n field[1],\n '',\n ]);\n });\n}\n\nconst cuentaMalDigitadaValues = [\n [\n 'GOMEZLEE MARKETING',\n '',\n '',\n '',\n ],\n [\n cuentaMalDigitadaSubtitle,\n '',\n '',\n '',\n ],\n ['', '', '', ''],\n cuentaMalDigitadaHeader,\n ...cuentaMalDigitadaRows,\n];\n\nconst resumenHeader = ['Indicador', 'Valor'];\nconst resumenRows = [\n ['Período', periodLabel],\n ['Coincidencias', Number(summary.coincidencias || 0)],\n ['Discrepancias', Number(summary.discrepancias || 0)],\n ['Banco sin nómina', Number(summary.bancoSinNomina || 0)],\n ['Banco sin Bamboo', Number(summary.bancoSinBamboo || 0)],\n ['Nómina sin cuenta', Number(summary.nominaSinCuenta || 0)],\n [\n 'Diferencias nombre banco',\n Number(summary.diferenciasNombreBanco || 0),\n ],\n [\n 'Posibles cuentas mal digitadas',\n Number(summary.posiblesCuentasMalDigitadas || 0),\n ],\n ['Pendientes cruce principal', Number(summary.pendientes || 0)],\n [\n 'Empleados BambooHR Puerto Rico',\n Number(summary.empleadosBambooPR || 0),\n ],\n [\n 'Empleados BambooHR en el período',\n Number(summary.empleadosBambooEnPeriodo || 0),\n ],\n ['Filas válidas de nómina', Number(summary.filasNominaValidas || 0)],\n [\n 'Suplementos adjuntados',\n Number(summary.suplementosNominaAdjuntados || 0),\n ],\n [\n 'Suplementos sin coincidencia',\n Number(summary.suplementosNominaNoAdjuntados || 0),\n ],\n ['Transacciones bancarias', Number(summary.transaccionesBanco || 0)],\n ['Total nómina', `USD ${roundMoney(summary.totalNominaPorMoneda?.USD || summary.totalNomina || 0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`],\n ['Total banco', `USD ${roundMoney(summary.totalBancoPorMoneda?.USD || summary.totalBanco || 0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`],\n ['Diferencia total', `USD ${roundMoney(summary.diferenciaTotalPorMoneda?.USD || summary.diferenciaTotal || 0).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2})}`],\n];\n\nfunction buildSheetValues(header, bodyRows) {\n return [header, ...bodyRows];\n}\n\nconst valueData = [\n {\n range: `'${sheetTitles.nominaVsBanco}'!A1:I`,\n values: nominaVsBancoValues,\n },\n {\n range: `'${sheetTitles.bancoSinNomina}'!A1:G`,\n values: buildSheetValues(\n bancoSinNominaHeader,\n bancoSinNominaRows\n ),\n },\n {\n range: `'${sheetTitles.bancoSinBamboo}'!A1:H`,\n values: bancoSinBambooValues,\n },\n {\n range: `'${sheetTitles.diferenciasNombreBanco}'!A1:K`,\n values: buildSheetValues(\n diferenciasNombreHeader,\n diferenciasNombreRows\n ),\n },\n ...(hasCuentaMalDigitada\n ? [\n {\n range:\n `'${sheetTitles.cuentaMalDigitada}'!A1:D`,\n values: cuentaMalDigitadaValues,\n },\n ]\n : []),\n {\n range: `'${sheetTitles.resumen}'!A1:B`,\n values: buildSheetValues(\n resumenHeader,\n resumenRows\n ),\n },\n];\n\nfunction headerFormatRequest(\n sheetId,\n endColumnIndex,\n startRowIndex = 0\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex: startRowIndex + 1,\n startColumnIndex: 0,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 0.29,\n green: 0.49,\n blue: 0.58,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 1,\n green: 1,\n blue: 1,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction freezeHeaderRequest(sheetId, frozenRowCount = 1) {\n return {\n updateSheetProperties: {\n properties: {\n sheetId,\n gridProperties: {\n frozenRowCount,\n },\n },\n fields: 'gridProperties.frozenRowCount',\n },\n };\n}\n\nfunction autoResizeRequest(sheetId, endColumnIndex) {\n return {\n autoResizeDimensions: {\n dimensions: {\n sheetId,\n dimension: 'COLUMNS',\n startIndex: 0,\n endIndex: endColumnIndex,\n },\n },\n };\n}\n\nfunction moneyFormatRequest(\n sheetId,\n startColumnIndex,\n endColumnIndex,\n startRowIndex = 1,\n pattern = '#,##0.00',\n endRowIndex = null\n) {\n const range = {\n sheetId,\n startRowIndex,\n startColumnIndex,\n endColumnIndex,\n };\n\n if (Number.isInteger(endRowIndex)) {\n range.endRowIndex = endRowIndex;\n }\n\n return {\n repeatCell: {\n range,\n cell: {\n userEnteredFormat: {\n numberFormat: {\n type: 'NUMBER',\n pattern,\n },\n },\n },\n fields: 'userEnteredFormat.numberFormat',\n },\n };\n}\n\nfunction basicFilterRequest(\n sheetId,\n endColumnIndex,\n startRowIndex = 0,\n endRowIndex = null\n) {\n const range = {\n sheetId,\n startRowIndex,\n startColumnIndex: 0,\n endColumnIndex,\n };\n\n if (Number.isInteger(endRowIndex)) {\n range.endRowIndex = endRowIndex;\n }\n\n return {\n setBasicFilter: {\n filter: {\n range,\n },\n },\n };\n}\n\nfunction mergeRowRequest(\n sheetId,\n rowIndex,\n endColumnIndex\n) {\n return {\n mergeCells: {\n range: {\n sheetId,\n startRowIndex: rowIndex,\n endRowIndex: rowIndex + 1,\n startColumnIndex: 0,\n endColumnIndex,\n },\n mergeType: 'MERGE_ALL',\n },\n };\n}\n\nfunction titleRowFormatRequest(\n sheetId,\n rowIndex,\n endColumnIndex,\n options = {}\n) {\n const {\n fontSize = 12,\n bold = true,\n italic = false,\n horizontalAlignment = 'LEFT',\n } = options;\n\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: rowIndex,\n endRowIndex: rowIndex + 1,\n startColumnIndex: 0,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 0.29,\n green: 0.49,\n blue: 0.58,\n },\n textFormat: {\n bold,\n italic,\n fontSize,\n foregroundColor: {\n red: 1,\n green: 1,\n blue: 1,\n },\n },\n horizontalAlignment,\n verticalAlignment: 'MIDDLE',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment)',\n },\n };\n}\n\nfunction columnWidthRequest(\n sheetId,\n startIndex,\n endIndex,\n pixelSize\n) {\n return {\n updateDimensionProperties: {\n range: {\n sheetId,\n dimension: 'COLUMNS',\n startIndex,\n endIndex,\n },\n properties: {\n pixelSize,\n },\n fields: 'pixelSize',\n },\n };\n}\n\nfunction rowHeightRequest(\n sheetId,\n startIndex,\n endIndex,\n pixelSize\n) {\n return {\n updateDimensionProperties: {\n range: {\n sheetId,\n dimension: 'ROWS',\n startIndex,\n endIndex,\n },\n properties: {\n pixelSize,\n },\n fields: 'pixelSize',\n },\n };\n}\n\nfunction bodyAlignmentRequest(\n sheetId,\n startColumnIndex,\n endColumnIndex,\n horizontalAlignment,\n endRowIndex\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: 4,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n horizontalAlignment,\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction statusFormatRequest(sheetId, endRowIndex) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: 4,\n endRowIndex,\n startColumnIndex: 7,\n endColumnIndex: 8,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 1,\n green: 0.92,\n blue: 0.92,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.85,\n green: 0.08,\n blue: 0.08,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment)',\n },\n };\n}\n\nfunction bambooStatusFormatRequest(\n sheetId,\n endRowIndex\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex: 4,\n endRowIndex,\n startColumnIndex: 6,\n endColumnIndex: 7,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 1,\n green: 0.97,\n blue: 0.82,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.70,\n green: 0.30,\n blue: 0.00,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\n\nfunction mergeVerticalRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n columnIndex\n) {\n return {\n mergeCells: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex: columnIndex,\n endColumnIndex: columnIndex + 1,\n },\n mergeType: 'MERGE_ALL',\n },\n };\n}\n\nfunction rangeFormatRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n userEnteredFormat\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat,\n },\n fields:\n 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment,verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction conditionalDifferenceRequest(\n sheetId,\n formula,\n backgroundColor,\n textColor,\n ranges,\n index\n) {\n return {\n addConditionalFormatRule: {\n index,\n rule: {\n ranges,\n booleanRule: {\n condition: {\n type: 'CUSTOM_FORMULA',\n values: [\n {\n userEnteredValue: formula,\n },\n ],\n },\n format: {\n backgroundColor,\n textFormat: {\n bold: true,\n foregroundColor: textColor,\n },\n },\n },\n },\n },\n };\n}\n\nfunction borderRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n endColumnIndex\n) {\n const border = {\n style: 'SOLID',\n color: {\n red: 0.72,\n green: 0.82,\n blue: 0.76,\n },\n };\n\n return {\n updateBorders: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex: 0,\n endColumnIndex,\n },\n top: border,\n bottom: border,\n left: border,\n right: border,\n innerHorizontal: border,\n innerVertical: border,\n },\n };\n}\n\nconst mainReportEndRow = Math.max(\n 4 + mainReportRows.length,\n 4\n);\n\nconst bancoSinBambooEndRow = Math.max(\n 4 + bancoSinBambooRows.length,\n 4\n);\n\nconst mainConditionalRanges = mainReportRows.length\n ? [\n {\n sheetId: sheetIds.nominaVsBanco,\n startRowIndex: 4,\n endRowIndex: mainReportEndRow,\n startColumnIndex: 5,\n endColumnIndex: 7,\n },\n ]\n : [];\n\nconst mainDataFormatRequests = mainReportRows.length\n ? [\n moneyFormatRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 4,\n 'Q#,##0.00;[Red](Q#,##0.00)',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 0,\n 1,\n 'CENTER',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 'LEFT',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 2,\n 3,\n 'CENTER',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 'RIGHT',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 6,\n 8,\n 'CENTER',\n mainReportEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.nominaVsBanco,\n 8,\n 9,\n 'LEFT',\n mainReportEndRow\n ),\n statusFormatRequest(\n sheetIds.nominaVsBanco,\n mainReportEndRow\n ),\n conditionalDifferenceRequest(\n sheetIds.nominaVsBanco,\n '=$F5>0',\n {\n red: 1,\n green: 0.97,\n blue: 0.82,\n },\n {\n red: 0.25,\n green: 0.25,\n blue: 0.25,\n },\n mainConditionalRanges,\n 0\n ),\n conditionalDifferenceRequest(\n sheetIds.nominaVsBanco,\n '=$F5<0',\n {\n red: 1,\n green: 0.89,\n blue: 0.89,\n },\n {\n red: 0.9,\n green: 0.05,\n blue: 0.05,\n },\n mainConditionalRanges,\n 1\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 4,\n mainReportEndRow,\n 28\n ),\n ]\n : [];\n\n\nconst cuentaMalDigitadaEndRow = Math.max(\n 4 + cuentaMalDigitadaRows.length,\n 4\n);\n\nconst cuentaMalDigitadaCaseRequests =\n hasCuentaMalDigitada\n ? cuentaMalDigitadaCases.flatMap(\n (_, caseIndex) => {\n const startRowIndex =\n 4 + caseIndex * 6;\n const endRowIndex =\n startRowIndex + 6;\n\n return [\n mergeVerticalRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex,\n endRowIndex,\n 0\n ),\n mergeVerticalRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex,\n endRowIndex,\n 3\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex,\n startRowIndex + 4,\n 34\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 4,\n startRowIndex + 5,\n 76\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 5,\n endRowIndex,\n 54\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 4,\n startRowIndex + 5,\n 2,\n 3,\n {\n backgroundColor: {\n red: 0.97,\n green: 0.97,\n blue: 0.97,\n },\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n startRowIndex + 5,\n endRowIndex,\n 2,\n 3,\n {\n backgroundColor: {\n red: 1,\n green: 0.97,\n blue: 0.82,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.55,\n green: 0.30,\n blue: 0.00,\n },\n },\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n ];\n }\n )\n : [];\n\nconst cuentaMalDigitadaFormatRequests =\n hasCuentaMalDigitada\n ? [\n mergeRowRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 4\n ),\n mergeRowRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 4\n ),\n titleRowFormatRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 4,\n {\n fontSize: 12,\n bold: true,\n italic: false,\n horizontalAlignment: 'LEFT',\n }\n ),\n titleRowFormatRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 4,\n {\n fontSize: 10,\n bold: false,\n italic: true,\n horizontalAlignment: 'LEFT',\n }\n ),\n headerFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n 3\n ),\n freezeHeaderRequest(\n sheetIds.cuentaMalDigitada,\n 4\n ),\n borderRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n cuentaMalDigitadaEndRow,\n 4\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 1,\n 30\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 2,\n 26\n ),\n rowHeightRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n 40\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 0,\n 1,\n {\n backgroundColor: {\n red: 0.91,\n green: 0.95,\n blue: 0.99,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.20,\n green: 0.36,\n blue: 0.45,\n },\n },\n horizontalAlignment: 'CENTER',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 1,\n 2,\n {\n backgroundColor: {\n red: 0.93,\n green: 0.97,\n blue: 0.90,\n },\n textFormat: {\n bold: true,\n foregroundColor: {\n red: 0.20,\n green: 0.36,\n blue: 0.45,\n },\n },\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 2,\n 3,\n {\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n rangeFormatRequest(\n sheetIds.cuentaMalDigitada,\n 4,\n cuentaMalDigitadaEndRow,\n 3,\n 4,\n {\n horizontalAlignment: 'LEFT',\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n }\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 0,\n 1,\n 48\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 2,\n 285\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 2,\n 3,\n 520\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n 260\n ),\n ...cuentaMalDigitadaCaseRequests,\n ]\n : [];\n\n\nfunction wrapRangeRequest(\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex\n) {\n return {\n repeatCell: {\n range: {\n sheetId,\n startRowIndex,\n endRowIndex,\n startColumnIndex,\n endColumnIndex,\n },\n cell: {\n userEnteredFormat: {\n verticalAlignment: 'MIDDLE',\n wrapStrategy: 'WRAP',\n },\n },\n fields:\n 'userEnteredFormat(verticalAlignment,wrapStrategy)',\n },\n };\n}\n\nfunction autoResizeRowsRequest(\n sheetId,\n startIndex,\n endIndex\n) {\n return {\n autoResizeDimensions: {\n dimensions: {\n sheetId,\n dimension: 'ROWS',\n startIndex,\n endIndex,\n },\n },\n };\n}\n\nconst formatRequests = [\n mergeRowRequest(sheetIds.nominaVsBanco, 0, 9),\n mergeRowRequest(sheetIds.nominaVsBanco, 1, 9),\n titleRowFormatRequest(\n sheetIds.nominaVsBanco,\n 0,\n 9,\n {\n fontSize: 12,\n bold: true,\n italic: false,\n horizontalAlignment: 'LEFT',\n }\n ),\n titleRowFormatRequest(\n sheetIds.nominaVsBanco,\n 1,\n 9,\n {\n fontSize: 10,\n bold: false,\n italic: true,\n horizontalAlignment: 'LEFT',\n }\n ),\n headerFormatRequest(\n sheetIds.nominaVsBanco,\n 9,\n 3\n ),\n freezeHeaderRequest(\n sheetIds.nominaVsBanco,\n 4\n ),\n basicFilterRequest(\n sheetIds.nominaVsBanco,\n 9,\n 3,\n mainReportEndRow\n ),\n borderRequest(\n sheetIds.nominaVsBanco,\n 3,\n mainReportEndRow,\n 9\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 0,\n 1,\n 30\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 26\n ),\n rowHeightRequest(\n sheetIds.nominaVsBanco,\n 3,\n 4,\n 42\n ),\n ...mainDataFormatRequests,\n\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 0,\n 1,\n 48\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 260\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 2,\n 3,\n 130\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 130\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 6,\n 7,\n 155\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 7,\n 8,\n 110\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 8,\n 9,\n 260\n ),\n\n headerFormatRequest(sheetIds.bancoSinNomina, 7),\n freezeHeaderRequest(sheetIds.bancoSinNomina),\n autoResizeRequest(sheetIds.bancoSinNomina, 7),\n moneyFormatRequest(sheetIds.bancoSinNomina, 3, 4),\n basicFilterRequest(sheetIds.bancoSinNomina, 7),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 6,\n 7,\n 260\n ),\n\n mergeRowRequest(sheetIds.bancoSinBamboo, 0, 8),\n mergeRowRequest(sheetIds.bancoSinBamboo, 1, 8),\n titleRowFormatRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 8,\n {\n fontSize: 12,\n bold: true,\n italic: false,\n horizontalAlignment: 'LEFT',\n }\n ),\n titleRowFormatRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 8,\n {\n fontSize: 10,\n bold: false,\n italic: true,\n horizontalAlignment: 'LEFT',\n }\n ),\n headerFormatRequest(\n sheetIds.bancoSinBamboo,\n 8,\n 3\n ),\n freezeHeaderRequest(\n sheetIds.bancoSinBamboo,\n 4\n ),\n basicFilterRequest(\n sheetIds.bancoSinBamboo,\n 8,\n 3,\n bancoSinBambooEndRow\n ),\n borderRequest(\n sheetIds.bancoSinBamboo,\n 3,\n bancoSinBambooEndRow,\n 8\n ),\n rowHeightRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 1,\n 30\n ),\n rowHeightRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 2,\n 26\n ),\n rowHeightRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 42\n ),\n moneyFormatRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 4,\n 'Q#,##0.00',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 1,\n 'CENTER',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 3,\n 'LEFT',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 'CENTER',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 'RIGHT',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 5,\n 7,\n 'CENTER',\n bancoSinBambooEndRow\n ),\n bodyAlignmentRequest(\n sheetIds.bancoSinBamboo,\n 7,\n 8,\n 'LEFT',\n bancoSinBambooEndRow\n ),\n bambooStatusFormatRequest(\n sheetIds.bancoSinBamboo,\n bancoSinBambooEndRow\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 0,\n 1,\n 48\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 2,\n 220\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 2,\n 3,\n 270\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 135\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 130\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 5,\n 6,\n 110\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 6,\n 7,\n 140\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 7,\n 8,\n 260\n ),\n\n headerFormatRequest(\n sheetIds.diferenciasNombreBanco,\n 11\n ),\n freezeHeaderRequest(\n sheetIds.diferenciasNombreBanco\n ),\n autoResizeRequest(\n sheetIds.diferenciasNombreBanco,\n 11\n ),\n moneyFormatRequest(\n sheetIds.diferenciasNombreBanco,\n 4,\n 5\n ),\n basicFilterRequest(\n sheetIds.diferenciasNombreBanco,\n 11\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 10,\n 11,\n 260\n ),\n\n ...cuentaMalDigitadaFormatRequests,\n\n headerFormatRequest(sheetIds.resumen, 2),\n freezeHeaderRequest(sheetIds.resumen),\n autoResizeRequest(sheetIds.resumen, 2),\n moneyFormatRequest(sheetIds.resumen, 1, 2),\n];\n\n\nconst bancoSinNominaEndRow =\n 1 + bancoSinNominaRows.length;\n\nconst diferenciasNombreEndRow =\n 1 + diferenciasNombreRows.length;\n\nconst resumenReadabilityEndRow =\n 1 + resumenRows.length;\n\n/*\n * Ajuste final de legibilidad.\n *\n * Se ejecuta al final para que los anchos definitivos ya estén aplicados\n * cuando Google Sheets calcule automáticamente la altura de cada fila.\n * Así, cualquier texto largo queda envuelto y visible sin que el usuario\n * tenga que expandir columnas o filas manualmente.\n */\nformatRequests.push(\n // 01 Nómina vs Banco\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 1,\n 2,\n 300\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 2,\n 3,\n 150\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 3,\n 6,\n 145\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 6,\n 7,\n 220\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 7,\n 8,\n 140\n ),\n columnWidthRequest(\n sheetIds.nominaVsBanco,\n 8,\n 9,\n 320\n ),\n ...(mainReportRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.nominaVsBanco,\n 4,\n mainReportEndRow,\n 0,\n 9\n ),\n autoResizeRowsRequest(\n sheetIds.nominaVsBanco,\n 4,\n mainReportEndRow\n ),\n ]\n : []),\n\n // 02 Banco sin Nómina\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 0,\n 1,\n 320\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 1,\n 2,\n 165\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 2,\n 3,\n 95\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 3,\n 4,\n 140\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 4,\n 5,\n 170\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 5,\n 6,\n 560\n ),\n columnWidthRequest(\n sheetIds.bancoSinNomina,\n 6,\n 7,\n 320\n ),\n autoResizeRowsRequest(\n sheetIds.bancoSinNomina,\n 0,\n 1\n ),\n ...(bancoSinNominaRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.bancoSinNomina,\n 1,\n bancoSinNominaEndRow,\n 0,\n 7\n ),\n autoResizeRowsRequest(\n sheetIds.bancoSinNomina,\n 1,\n bancoSinNominaEndRow\n ),\n ]\n : []),\n\n // 03 Banco sin Bamboo\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 1,\n 2,\n 300\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 2,\n 3,\n 330\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 3,\n 4,\n 150\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 4,\n 5,\n 145\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 5,\n 6,\n 150\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 6,\n 7,\n 170\n ),\n columnWidthRequest(\n sheetIds.bancoSinBamboo,\n 7,\n 8,\n 320\n ),\n ...(bancoSinBambooRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.bancoSinBamboo,\n 4,\n bancoSinBambooEndRow,\n 0,\n 8\n ),\n autoResizeRowsRequest(\n sheetIds.bancoSinBamboo,\n 4,\n bancoSinBambooEndRow\n ),\n ]\n : []),\n\n // 04 Diferencias nombre banco\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 0,\n 1,\n 300\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 1,\n 2,\n 330\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 2,\n 3,\n 155\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 3,\n 4,\n 95\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 4,\n 5,\n 140\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 5,\n 7,\n 150\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 7,\n 8,\n 260\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 8,\n 9,\n 170\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 9,\n 10,\n 650\n ),\n columnWidthRequest(\n sheetIds.diferenciasNombreBanco,\n 10,\n 11,\n 320\n ),\n autoResizeRowsRequest(\n sheetIds.diferenciasNombreBanco,\n 0,\n 1\n ),\n ...(diferenciasNombreRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.diferenciasNombreBanco,\n 1,\n diferenciasNombreEndRow,\n 0,\n 11\n ),\n autoResizeRowsRequest(\n sheetIds.diferenciasNombreBanco,\n 1,\n diferenciasNombreEndRow\n ),\n ]\n : []),\n\n // 05 Cuenta Mal Digitada\n ...(hasCuentaMalDigitada\n ? [\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 1,\n 2,\n 300\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 2,\n 3,\n 600\n ),\n columnWidthRequest(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n 320\n ),\n ]\n : []),\n\n // Resumen\n columnWidthRequest(\n sheetIds.resumen,\n 0,\n 1,\n 380\n ),\n columnWidthRequest(\n sheetIds.resumen,\n 1,\n 2,\n 320\n ),\n autoResizeRowsRequest(\n sheetIds.resumen,\n 0,\n 1\n ),\n ...(resumenRows.length > 0\n ? [\n wrapRangeRequest(\n sheetIds.resumen,\n 1,\n resumenReadabilityEndRow,\n 0,\n 2\n ),\n autoResizeRowsRequest(\n sheetIds.resumen,\n 1,\n resumenReadabilityEndRow\n ),\n ]\n : [])\n);\n\n\n\n/*\n * Protección de integridad del reporte.\n *\n * La hoja completa queda protegida para impedir que los usuarios con acceso\n * al reporte modifiquen datos calculados, nombres, cuentas, montos, estados,\n * observaciones o cualquier otro resultado del cruce.\n *\n * Únicamente las celdas de datos de la columna \"Resolución\" quedan fuera de\n * la protección para que puedan completarse después de recibir el reporte.\n * Los encabezados y las filas fuera de la tabla permanecen protegidos.\n *\n * El propietario de la hoja conserva siempre el control. Además, el usuario\n * administrativo iaracena@gomezleemarketing.com queda registrado como editor explícito del\n * rango protegido para mantenimiento de IT.\n */\nfunction protectedSheetRequest(\n sheetId,\n description,\n resolutionColumnIndex = null,\n resolutionStartRowIndex = null,\n resolutionRowsCount = 0\n) {\n const protectedRange = {\n range: {\n sheetId,\n },\n description,\n warningOnly: false,\n editors: {\n users: [\n 'iaracena@gomezleemarketing.com',\n ],\n domainUsersCanEdit: false,\n },\n };\n\n if (\n Number.isInteger(resolutionColumnIndex) &&\n Number.isInteger(resolutionStartRowIndex) &&\n Number(resolutionRowsCount) > 0\n ) {\n protectedRange.unprotectedRanges = [\n {\n sheetId,\n startRowIndex:\n resolutionStartRowIndex,\n endRowIndex:\n resolutionStartRowIndex +\n Number(resolutionRowsCount),\n startColumnIndex:\n resolutionColumnIndex,\n endColumnIndex:\n resolutionColumnIndex + 1,\n },\n ];\n }\n\n return {\n addProtectedRange: {\n protectedRange,\n },\n };\n}\n\n\nfunction protectedResolutionRequest(\n sheetId,\n resolutionColumnIndex,\n resolutionStartRowIndex,\n resolutionRowsCount\n) {\n if (\n !Number.isInteger(resolutionColumnIndex) ||\n !Number.isInteger(resolutionStartRowIndex) ||\n Number(resolutionRowsCount) <= 0\n ) {\n return null;\n }\n\n return {\n addProtectedRange: {\n protectedRange: {\n range: {\n sheetId,\n startRowIndex:\n resolutionStartRowIndex,\n endRowIndex:\n resolutionStartRowIndex +\n Number(resolutionRowsCount),\n startColumnIndex:\n resolutionColumnIndex,\n endColumnIndex:\n resolutionColumnIndex + 1,\n },\n description:\n 'Protección GLM — Resolución. Solo Viviana Páramo y Liliana Benítez pueden editar.',\n warningOnly: false,\n editors: {\n users: [\n 'vparamo@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n ],\n domainUsersCanEdit: false,\n },\n },\n },\n };\n}\n\nfunction addResolutionProtection(\n sheetId,\n resolutionColumnIndex,\n resolutionStartRowIndex,\n resolutionRowsCount\n) {\n const request =\n protectedResolutionRequest(\n sheetId,\n resolutionColumnIndex,\n resolutionStartRowIndex,\n resolutionRowsCount\n );\n\n if (request) {\n formatRequests.push(request);\n }\n}\n\n\nformatRequests.push(\n protectedSheetRequest(\n sheetIds.nominaVsBanco,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 8,\n 4,\n mainReportRows.length\n ),\n protectedSheetRequest(\n sheetIds.bancoSinNomina,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 6,\n 1,\n bancoSinNominaRows.length\n ),\n protectedSheetRequest(\n sheetIds.bancoSinBamboo,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 7,\n 4,\n bancoSinBambooRows.length\n ),\n protectedSheetRequest(\n sheetIds.diferenciasNombreBanco,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 10,\n 1,\n diferenciasNombreRows.length\n ),\n ...(hasCuentaMalDigitada\n ? [\n protectedSheetRequest(\n sheetIds.cuentaMalDigitada,\n 'Protección GLM — datos del cruce. Solo Resolución es editable.',\n 3,\n 4,\n cuentaMalDigitadaRows.length\n ),\n ]\n : []),\n protectedSheetRequest(\n sheetIds.resumen,\n 'Protección GLM — hoja de resumen de solo lectura.'\n )\n);\n\n\naddResolutionProtection(\n sheetIds.nominaVsBanco,\n 8,\n 4,\n mainReportRows.length\n);\naddResolutionProtection(\n sheetIds.bancoSinNomina,\n 6,\n 1,\n bancoSinNominaRows.length\n);\naddResolutionProtection(\n sheetIds.bancoSinBamboo,\n 7,\n 4,\n bancoSinBambooRows.length\n);\naddResolutionProtection(\n sheetIds.diferenciasNombreBanco,\n 10,\n 1,\n diferenciasNombreRows.length\n);\nif (hasCuentaMalDigitada) {\n addResolutionProtection(\n sheetIds.cuentaMalDigitada,\n 3,\n 4,\n cuentaMalDigitadaRows.length\n );\n}\n\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'preparar_google_sheet',\n metadata,\n summary,\n spreadsheetTitle,\n sheetIds,\n sheetTitles,\n createSpreadsheetBody: {\n properties: {\n title: spreadsheetTitle,\n },\n sheets: [\n {\n properties: {\n sheetId: sheetIds.nominaVsBanco,\n title: sheetTitles.nominaVsBanco,\n },\n },\n {\n properties: {\n sheetId: sheetIds.bancoSinNomina,\n title: sheetTitles.bancoSinNomina,\n },\n },\n {\n properties: {\n sheetId: sheetIds.bancoSinBamboo,\n title: sheetTitles.bancoSinBamboo,\n },\n },\n {\n properties: {\n sheetId:\n sheetIds.diferenciasNombreBanco,\n title:\n sheetTitles.diferenciasNombreBanco,\n },\n },\n ...(hasCuentaMalDigitada\n ? [\n {\n properties: {\n sheetId:\n sheetIds.cuentaMalDigitada,\n title:\n sheetTitles.cuentaMalDigitada,\n },\n },\n ]\n : []),\n {\n properties: {\n sheetId: sheetIds.resumen,\n title: sheetTitles.resumen,\n },\n },\n ],\n },\n valueBatchBody: {\n // RAW conserva los números de cuenta como texto exacto (incluidos\n // ceros iniciales) y mantiene los montos numéricos porque ya se\n // envían como números en el payload.\n valueInputOption: 'RAW',\n data: valueData,\n },\n formatBatchBody: {\n requests: formatRequests,\n },\n originalResponse: data,\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 120, + 5200 + ], + "id": "ffde494e-1ffa-41e6-a76d-3322f684dec7", + "name": "Preparar Google Sheet" + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/cruce_cuentas_get_reporte_periodo", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ { p_country: $('Preparar Google Sheet').first().json.metadata.country || 'PR', p_year: Number($('Preparar Google Sheet').first().json.metadata.year || 0), p_month: Number($('Preparar Google Sheet').first().json.metadata.month || 0), p_period_type: $('Preparar Google Sheet').first().json.metadata.period_type || '' } }}", + "options": { + "response": { + "response": { + "responseFormat": "json" + } + }, + "timeout": 30000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 360, + 5200 + ], + "id": "81fd24de-a18d-4efd-b030-c8f193673745", + "name": "Buscar reporte existente Supabase" + }, + { + "parameters": { + "method": "POST", + "url": "={{ (() => { const lookup = $('Buscar reporte existente Supabase').first().json || {}; const report = lookup.report || {}; const id = String(report.spreadsheet_id || '').trim(); return (lookup.found === true && id) ? 'https://sheets.googleapis.com/v4/spreadsheets/' + id + ':batchUpdate' : 'https://sheets.googleapis.com/v4/spreadsheets'; })() }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{\n(() => {\n const prepared = $('Preparar Google Sheet').first().json || {};\n const lookup = $('Buscar reporte existente Supabase').first().json || {};\n const report = lookup.report || {};\n const existingId = String(report.spreadsheet_id || '').trim();\n\n if (lookup.found === true && existingId) {\n return {\n requests: [\n {\n updateSpreadsheetProperties: {\n properties: {\n title: prepared.spreadsheetTitle || prepared.createSpreadsheetBody?.properties?.title || 'Cruce de Cuentas GLM',\n },\n fields: 'title',\n },\n },\n ],\n };\n }\n\n const createBody = prepared.createSpreadsheetBody || {};\n\n if (!Array.isArray(createBody.sheets) || createBody.sheets.length === 0) {\n throw new Error('Preparar Google Sheet no devolvió las hojas que deben crearse.');\n }\n\n return {\n properties: {\n ...(createBody.properties || {}),\n timeZone: 'America/Puerto_Rico',\n },\n sheets: createBody.sheets.map((sheet) => ({\n properties: {\n ...(sheet.properties || {}),\n gridProperties: {\n ...((sheet.properties || {}).gridProperties || {}),\n frozenRowCount: 1,\n },\n },\n })),\n };\n})()\n}}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 600, + 5200 + ], + "id": "170ec328-0a9f-401b-9852-ca472d6ac14d", + "name": "Crear Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "eHseMeH39kRcXgOF", + "name": "Google account 2" + } + } + }, + { + "parameters": { + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Crear Google Sheet').first().json.spreadsheetId + '?includeGridData=true' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 840, + 5200 + ], + "id": "5d4d04ba-ee83-4a46-a419-f028729bb7bc", + "name": "Leer Google Sheet actual", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "jsCode": "const prepared =\n $('Preparar Google Sheet').first().json || {};\n\nconst lookup =\n $('Buscar reporte existente Supabase').first().json || {};\n\nconst resolvedSheet =\n $('Crear Google Sheet').first().json || {};\n\nconst spreadsheet =\n $input.first().json || {};\n\nconst report =\n lookup && typeof lookup.report === 'object' && lookup.report\n ? lookup.report\n : {};\n\nconst historyExists =\n lookup.found === true &&\n Boolean(report.id);\n\nconst reusingSheet =\n historyExists &&\n Boolean(\n String(\n report.spreadsheet_id ||\n ''\n ).trim()\n );\n\nconst spreadsheetId =\n String(\n resolvedSheet.spreadsheetId ||\n report.spreadsheet_id ||\n spreadsheet.spreadsheetId ||\n ''\n ).trim();\n\nif (!spreadsheetId) {\n throw new Error(\n 'No se pudo resolver el spreadsheetId del reporte.'\n );\n}\n\nfunction normalizeText(value) {\n return String(value ?? '')\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeAccount(value) {\n return String(value ?? '')\n .replace(/\\.0$/g, '')\n .replace(/\\D/g, '')\n .trim();\n}\n\nfunction cellValue(cell) {\n if (!cell || typeof cell !== 'object') {\n return '';\n }\n\n const entered =\n cell.userEnteredValue || {};\n\n if (\n entered.stringValue !== undefined\n ) {\n return String(entered.stringValue);\n }\n\n if (\n entered.numberValue !== undefined\n ) {\n return String(entered.numberValue);\n }\n\n if (\n entered.boolValue !== undefined\n ) {\n return entered.boolValue\n ? 'TRUE'\n : 'FALSE';\n }\n\n if (\n entered.formulaValue !== undefined\n ) {\n return String(\n cell.formattedValue ??\n entered.formulaValue\n );\n }\n\n return String(\n cell.formattedValue ?? ''\n );\n}\n\nfunction sheetMatrix(sheet) {\n const matrix = [];\n const blocks =\n Array.isArray(sheet?.data)\n ? sheet.data\n : [];\n\n for (const block of blocks) {\n const startRow =\n Number(block.startRow || 0);\n const rowData =\n Array.isArray(block.rowData)\n ? block.rowData\n : [];\n\n for (\n let rowOffset = 0;\n rowOffset < rowData.length;\n rowOffset++\n ) {\n const rowIndex =\n startRow + rowOffset;\n\n const cells =\n Array.isArray(\n rowData[rowOffset]?.values\n )\n ? rowData[rowOffset].values\n : [];\n\n matrix[rowIndex] =\n cells.map(cellValue);\n }\n }\n\n return matrix;\n}\n\nfunction findHeaderInfo(matrix) {\n for (\n let rowIndex = 0;\n rowIndex < matrix.length;\n rowIndex++\n ) {\n const row =\n Array.isArray(matrix[rowIndex])\n ? matrix[rowIndex]\n : [];\n\n const normalized =\n row.map(normalizeText);\n\n const resolutionIndex =\n normalized.findIndex(\n (value) =>\n value === 'resolucion'\n );\n\n if (resolutionIndex >= 0) {\n return {\n rowIndex,\n header: row,\n normalized,\n resolutionIndex,\n };\n }\n }\n\n return null;\n}\n\nfunction indexMatching(\n normalizedHeader,\n candidates\n) {\n for (const candidate of candidates) {\n const normalizedCandidate =\n normalizeText(candidate);\n\n const exact =\n normalizedHeader.findIndex(\n (header) =>\n header === normalizedCandidate\n );\n\n if (exact >= 0) {\n return exact;\n }\n }\n\n for (const candidate of candidates) {\n const normalizedCandidate =\n normalizeText(candidate);\n\n const partial =\n normalizedHeader.findIndex(\n (header) =>\n header.includes(\n normalizedCandidate\n )\n );\n\n if (partial >= 0) {\n return partial;\n }\n }\n\n return -1;\n}\n\nfunction firstAt(row, indexes) {\n for (const index of indexes) {\n if (\n index >= 0 &&\n String(row[index] ?? '').trim()\n ) {\n return String(row[index]);\n }\n }\n\n return '';\n}\n\nconst ids =\n prepared.sheetIds || {};\n\nfunction rowIdentity(\n sheetId,\n row,\n normalizedHeader\n) {\n const accountIndex =\n indexMatching(\n normalizedHeader,\n [\n 'Cuenta',\n 'Cuenta Banco',\n 'Cuenta Destino',\n ]\n );\n\n const account =\n accountIndex >= 0\n ? normalizeAccount(\n row[accountIndex]\n )\n : '';\n\n if (account) {\n return `account:${account}`;\n }\n\n if (\n Number(sheetId) ===\n Number(ids.nominaVsBanco)\n ) {\n const employeeIndex =\n indexMatching(\n normalizedHeader,\n ['Empleado', 'Nombre en nómina']\n );\n\n return `employee:${normalizeText(\n firstAt(\n row,\n [employeeIndex]\n )\n )}`;\n }\n\n if (\n Number(sheetId) ===\n Number(ids.bancoSinNomina)\n ) {\n const nameIndex =\n indexMatching(\n normalizedHeader,\n [\n 'Empleado Banco',\n 'Nombre en banco',\n ]\n );\n\n const currencyIndex =\n indexMatching(\n normalizedHeader,\n ['Moneda']\n );\n\n return [\n 'bank-no-payroll',\n normalizeText(\n firstAt(row, [nameIndex])\n ),\n normalizeText(\n firstAt(row, [currencyIndex])\n ),\n ].join(':');\n }\n\n if (\n Number(sheetId) ===\n Number(ids.bancoSinBamboo)\n ) {\n const nameIndex =\n indexMatching(\n normalizedHeader,\n ['Nombre en banco']\n );\n\n const holderIndex =\n indexMatching(\n normalizedHeader,\n ['Nombre del cuentahabiente']\n );\n\n return [\n 'bank-no-bamboo',\n normalizeText(\n firstAt(row, [nameIndex])\n ),\n normalizeText(\n firstAt(row, [holderIndex])\n ),\n ].join(':');\n }\n\n if (\n Number(sheetId) ===\n Number(ids.diferenciasNombreBanco)\n ) {\n const nameOne =\n indexMatching(\n normalizedHeader,\n [\n 'Nombre en Archivo',\n 'Nombre en nómina',\n ]\n );\n\n const nameTwo =\n indexMatching(\n normalizedHeader,\n [\n 'Nombre del Cuentahabiente',\n 'Nombre en banco',\n ]\n );\n\n return [\n 'name-difference',\n normalizeText(\n firstAt(row, [nameOne])\n ),\n normalizeText(\n firstAt(row, [nameTwo])\n ),\n ].join(':');\n }\n\n return '';\n}\n\nfunction collectRegularResolutions(\n sheetId,\n matrix\n) {\n const result = new Map();\n const info =\n findHeaderInfo(matrix);\n\n if (!info) {\n return result;\n }\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex < matrix.length;\n rowIndex++\n ) {\n const row =\n Array.isArray(matrix[rowIndex])\n ? matrix[rowIndex]\n : [];\n\n const resolution =\n String(\n row[\n info.resolutionIndex\n ] ?? ''\n ).trim();\n\n if (!resolution) {\n continue;\n }\n\n const key =\n rowIdentity(\n sheetId,\n row,\n info.normalized\n );\n\n if (key) {\n result.set(\n key,\n resolution\n );\n }\n }\n\n return result;\n}\n\nfunction collectWrongAccountResolutions(\n matrix\n) {\n const result = new Map();\n const info =\n findHeaderInfo(matrix);\n\n if (!info) {\n return result;\n }\n\n const fieldIndex =\n indexMatching(\n info.normalized,\n ['Campo']\n );\n\n const detailIndex =\n indexMatching(\n info.normalized,\n ['Detalle']\n );\n\n let currentEmployee = '';\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex < matrix.length;\n rowIndex++\n ) {\n const row =\n Array.isArray(matrix[rowIndex])\n ? matrix[rowIndex]\n : [];\n\n const field =\n normalizeText(\n row[fieldIndex] ?? ''\n );\n\n const detail =\n String(\n row[detailIndex] ?? ''\n );\n\n if (field === 'empleado') {\n currentEmployee =\n normalizeText(detail);\n }\n\n const resolution =\n String(\n row[\n info.resolutionIndex\n ] ?? ''\n ).trim();\n\n if (\n resolution &&\n currentEmployee &&\n field\n ) {\n result.set(\n `${currentEmployee}|${field}`,\n resolution\n );\n }\n }\n\n return result;\n}\n\nconst existingSheets =\n Array.isArray(spreadsheet.sheets)\n ? spreadsheet.sheets\n : [];\n\nconst sheetById =\n new Map();\n\nfor (const sheet of existingSheets) {\n const sheetId =\n sheet?.properties?.sheetId;\n\n if (\n sheetId !== null &&\n sheetId !== undefined\n ) {\n sheetById.set(\n Number(sheetId),\n sheet\n );\n }\n}\n\nconst resolutionMaps =\n new Map();\n\nif (reusingSheet) {\n for (\n const [key, rawId]\n of Object.entries(ids)\n ) {\n const sheetId =\n Number(rawId);\n\n const sheet =\n sheetById.get(sheetId);\n\n if (!sheet) {\n continue;\n }\n\n const matrix =\n sheetMatrix(sheet);\n\n const map =\n key === 'cuentaMalDigitada'\n ? collectWrongAccountResolutions(\n matrix\n )\n : collectRegularResolutions(\n sheetId,\n matrix\n );\n\n resolutionMaps.set(\n sheetId,\n map\n );\n }\n}\n\nfunction titleFromRange(range) {\n const match =\n String(range || '')\n .match(/^'([^']+)'!/);\n\n return match?.[1] || '';\n}\n\nconst desiredTitles =\n prepared.sheetTitles || {};\n\nconst titleToId =\n new Map();\n\nfor (\n const [key, title]\n of Object.entries(desiredTitles)\n) {\n if (\n ids[key] !== undefined\n ) {\n titleToId.set(\n String(title),\n Number(ids[key])\n );\n }\n}\n\nlet preservedResolutionsCount = 0;\n\nconst valueBatchBody =\n JSON.parse(\n JSON.stringify(\n prepared.valueBatchBody || {\n valueInputOption:\n 'USER_ENTERED',\n data: [],\n }\n )\n );\n\nfor (\n const dataEntry\n of valueBatchBody.data || []\n) {\n const title =\n titleFromRange(\n dataEntry.range\n );\n\n const sheetId =\n titleToId.get(title);\n\n if (\n sheetId === undefined ||\n !Array.isArray(\n dataEntry.values\n )\n ) {\n continue;\n }\n\n const info =\n findHeaderInfo(\n dataEntry.values\n );\n\n if (!info) {\n continue;\n }\n\n const oldMap =\n resolutionMaps.get(\n Number(sheetId)\n ) || new Map();\n\n if (\n Number(sheetId) ===\n Number(ids.cuentaMalDigitada)\n ) {\n const fieldIndex =\n indexMatching(\n info.normalized,\n ['Campo']\n );\n\n const detailIndex =\n indexMatching(\n info.normalized,\n ['Detalle']\n );\n\n let currentEmployee = '';\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex <\n dataEntry.values.length;\n rowIndex++\n ) {\n const row =\n dataEntry.values[\n rowIndex\n ];\n\n const field =\n normalizeText(\n row[fieldIndex] ?? ''\n );\n\n const detail =\n String(\n row[detailIndex] ?? ''\n );\n\n if (field === 'empleado') {\n currentEmployee =\n normalizeText(detail);\n }\n\n const key =\n currentEmployee && field\n ? `${currentEmployee}|${field}`\n : '';\n\n if (\n key &&\n oldMap.has(key)\n ) {\n while (\n row.length <=\n info.resolutionIndex\n ) {\n row.push('');\n }\n\n row[\n info.resolutionIndex\n ] = oldMap.get(key);\n\n preservedResolutionsCount++;\n }\n }\n\n continue;\n }\n\n for (\n let rowIndex =\n info.rowIndex + 1;\n rowIndex <\n dataEntry.values.length;\n rowIndex++\n ) {\n const row =\n dataEntry.values[\n rowIndex\n ];\n\n const key =\n rowIdentity(\n sheetId,\n row,\n info.normalized\n );\n\n if (\n key &&\n oldMap.has(key)\n ) {\n while (\n row.length <=\n info.resolutionIndex\n ) {\n row.push('');\n }\n\n row[\n info.resolutionIndex\n ] = oldMap.get(key);\n\n preservedResolutionsCount++;\n }\n }\n}\n\nconst desiredSheets =\n Array.isArray(\n prepared\n .createSpreadsheetBody\n ?.sheets\n )\n ? prepared\n .createSpreadsheetBody\n .sheets\n : [];\n\nconst existingIds =\n new Set(\n existingSheets\n .map(\n (sheet) =>\n Number(\n sheet?.properties\n ?.sheetId\n )\n )\n .filter(\n Number.isFinite\n )\n );\n\nconst desiredIds =\n new Set(\n desiredSheets\n .map(\n (sheet) =>\n Number(\n sheet?.properties\n ?.sheetId\n )\n )\n .filter(\n Number.isFinite\n )\n );\n\nconst newlyAddedSheetIds =\n new Set();\n\nconst structureRequests = [\n {\n updateSpreadsheetProperties: {\n properties: {\n title:\n prepared.spreadsheetTitle ||\n prepared\n .createSpreadsheetBody\n ?.properties?.title ||\n spreadsheet.properties\n ?.title ||\n 'Cruce de Cuentas GLM',\n },\n fields: 'title',\n },\n },\n];\n\nif (reusingSheet) {\n const optionalId =\n Number(\n ids.cuentaMalDigitada\n );\n\n if (\n Number.isFinite(optionalId) &&\n existingIds.has(optionalId) &&\n !desiredIds.has(optionalId)\n ) {\n structureRequests.push({\n deleteSheet: {\n sheetId: optionalId,\n },\n });\n\n existingIds.delete(\n optionalId\n );\n }\n\n for (\n let desiredIndex = 0;\n desiredIndex <\n desiredSheets.length;\n desiredIndex++\n ) {\n const desired =\n desiredSheets[\n desiredIndex\n ];\n\n const props =\n desired.properties || {};\n\n const desiredId =\n Number(props.sheetId);\n\n if (\n !Number.isFinite(\n desiredId\n )\n ) {\n continue;\n }\n\n const existing =\n existingSheets.find(\n (sheet) =>\n Number(\n sheet?.properties\n ?.sheetId\n ) === desiredId\n );\n\n if (!existing) {\n const sameTitle =\n existingSheets.find(\n (sheet) =>\n String(\n sheet?.properties\n ?.title || ''\n ) ===\n String(\n props.title || ''\n )\n );\n\n if (\n sameTitle &&\n Number(\n sameTitle\n ?.properties\n ?.sheetId\n ) !== desiredId\n ) {\n structureRequests.push({\n deleteSheet: {\n sheetId:\n Number(\n sameTitle\n .properties\n .sheetId\n ),\n },\n });\n }\n\n structureRequests.push({\n addSheet: {\n properties: {\n ...props,\n // El índice es 0-based. Así \"05 Cuenta Mal Digitada\"\n // vuelve exactamente antes de \"06 Resumen\" cuando reaparece.\n index:\n desiredIndex,\n gridProperties: {\n ...(props\n .gridProperties ||\n {}),\n frozenRowCount: 1,\n },\n },\n },\n });\n\n newlyAddedSheetIds.add(\n desiredId\n );\n\n continue;\n }\n\n const currentTitle =\n String(\n existing.properties\n ?.title || ''\n );\n\n const desiredTitle =\n String(\n props.title || ''\n );\n\n const currentIndex =\n Number(\n existing.properties\n ?.index\n );\n\n const titleNeedsUpdate =\n Boolean(\n desiredTitle &&\n currentTitle !==\n desiredTitle\n );\n\n const indexNeedsUpdate =\n Number.isFinite(\n currentIndex\n ) &&\n currentIndex !==\n desiredIndex;\n\n if (\n titleNeedsUpdate ||\n indexNeedsUpdate\n ) {\n const properties = {\n sheetId:\n desiredId,\n };\n\n const fields = [];\n\n if (titleNeedsUpdate) {\n properties.title =\n desiredTitle;\n fields.push('title');\n }\n\n if (indexNeedsUpdate) {\n properties.index =\n desiredIndex;\n fields.push('index');\n }\n\n structureRequests.push({\n updateSheetProperties: {\n properties,\n fields:\n fields.join(','),\n },\n });\n }\n }\n}\n\nconst originalFormatRequests =\n Array.isArray(\n prepared\n .formatBatchBody\n ?.requests\n )\n ? prepared\n .formatBatchBody\n .requests\n : [];\n\n/*\n * IMPORTANTE AL REUTILIZAR UN REPORTE:\n *\n * \"Actualizar estructura Google Sheet\" se ejecuta ANTES de\n * \"Formatear Google Sheet\". Cuando la estructura elimina una hoja\n * (por ejemplo, \"05 Cuenta Mal Digitada\" deja de ser necesaria),\n * Google elimina automáticamente también los ProtectedRange de esa hoja.\n *\n * Por eso NO debemos intentar borrar después esos protectedRangeId en el\n * batch de formato, porque ya dejaron de existir y Google responde:\n * \"Invalid requests[x].deleteProtectedRange: No protected range with id\".\n *\n * Se identifican aquí todas las hojas que serán eliminadas por el batch\n * de estructura y se excluyen sus protecciones del batch de formato.\n */\nconst sheetIdsScheduledForDeletion =\n new Set(\n structureRequests\n .map((request) =>\n Number(\n request?.deleteSheet?.sheetId\n )\n )\n .filter(Number.isFinite)\n );\n\nconst formatRequests = [];\nconst protectedRangeIdsScheduled =\n new Set();\n\nif (reusingSheet) {\n for (\n const sheet\n of existingSheets\n ) {\n const currentSheetId =\n Number(\n sheet?.properties?.sheetId\n );\n\n // Si esta hoja será eliminada en el paso de estructura, sus\n // protecciones también desaparecerán automáticamente.\n if (\n Number.isFinite(currentSheetId) &&\n sheetIdsScheduledForDeletion.has(\n currentSheetId\n )\n ) {\n continue;\n }\n\n const protections =\n Array.isArray(\n sheet.protectedRanges\n )\n ? sheet.protectedRanges\n : [];\n\n for (\n const protection\n of protections\n ) {\n const description =\n String(\n protection.description ||\n ''\n );\n\n const protectedRangeId =\n Number(\n protection\n .protectedRangeId\n );\n\n if (\n description.startsWith(\n 'Protección GLM'\n ) &&\n Number.isFinite(\n protectedRangeId\n ) &&\n !protectedRangeIdsScheduled.has(\n protectedRangeId\n )\n ) {\n protectedRangeIdsScheduled.add(\n protectedRangeId\n );\n\n formatRequests.push({\n deleteProtectedRange: {\n protectedRangeId,\n },\n });\n }\n }\n }\n}\n\n\n/*\n * Limpieza visual al reutilizar un reporte.\n *\n * values:batchClear elimina el CONTENIDO de las filas antiguas, pero Google\n * Sheets conserva su formato. Si una ejecución anterior tenía cientos de\n * discrepancias y la nueva tiene pocas, podían quedar celdas vacías con el\n * fondo rojo/amarillo aplicado por la ejecución anterior.\n *\n * La corrección es deliberadamente limitada: solamente se limpia el\n * background de las filas que quedan DESPUÉS de la última fila escrita por\n * la nueva ejecución. No se toca ninguna fila vigente, encabezado,\n * Resolución preservada, protección, borde, ancho, fórmula ni lógica del\n * cruce.\n */\nif (reusingSheet) {\n for (\n const dataEntry\n of valueBatchBody.data || []\n ) {\n const title =\n titleFromRange(\n dataEntry.range\n );\n\n const currentSheetId =\n Number(\n titleToId.get(title)\n );\n\n if (\n !Number.isFinite(\n currentSheetId\n ) ||\n sheetIdsScheduledForDeletion.has(\n currentSheetId\n ) ||\n newlyAddedSheetIds.has(\n currentSheetId\n )\n ) {\n continue;\n }\n\n const existingSheet =\n sheetById.get(\n currentSheetId\n );\n\n if (!existingSheet) {\n continue;\n }\n\n const currentValues =\n Array.isArray(\n dataEntry.values\n )\n ? dataEntry.values\n : [];\n\n const writtenRowCount =\n currentValues.length;\n\n const writtenColumnCount =\n currentValues.reduce(\n (maximum, row) =>\n Math.max(\n maximum,\n Array.isArray(row)\n ? row.length\n : 0\n ),\n 0\n );\n\n const gridRowCount =\n Number(\n existingSheet\n ?.properties\n ?.gridProperties\n ?.rowCount || 0\n );\n\n if (\n writtenRowCount <= 0 ||\n writtenColumnCount <= 0 ||\n !Number.isFinite(\n gridRowCount\n ) ||\n gridRowCount <=\n writtenRowCount\n ) {\n continue;\n }\n\n formatRequests.push({\n repeatCell: {\n range: {\n sheetId:\n currentSheetId,\n startRowIndex:\n writtenRowCount,\n endRowIndex:\n gridRowCount,\n startColumnIndex: 0,\n endColumnIndex:\n writtenColumnCount,\n },\n cell: {\n userEnteredFormat: {\n backgroundColor: {\n red: 1,\n green: 1,\n blue: 1,\n },\n },\n },\n fields:\n 'userEnteredFormat.backgroundColor',\n },\n });\n }\n}\n\nfor (\n const request\n of originalFormatRequests\n) {\n if (\n reusingSheet &&\n request.mergeCells\n ) {\n const mergeSheetId =\n Number(\n request\n .mergeCells\n ?.range\n ?.sheetId\n );\n\n if (\n !newlyAddedSheetIds.has(\n mergeSheetId\n )\n ) {\n continue;\n }\n }\n\n formatRequests.push(\n request\n );\n}\n\nconst clearRanges =\n (valueBatchBody.data || [])\n .map(\n (entry) =>\n entry.range\n )\n .filter(Boolean);\n\nreturn [\n {\n json: {\n ok: true,\n stage:\n 'google_sheet_destino_preparado',\n spreadsheetId,\n reportUrl:\n `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit`,\n historyExists,\n reportUpdated:\n historyExists,\n reusingSheet,\n existingHistoricalId:\n report.id\n ? String(report.id)\n : '',\n reportIdentity:\n String(\n report.report_identity ||\n ''\n ),\n previousReport:\n report,\n preservedResolutionsCount,\n valueBatchBody,\n structureBatchBody: {\n requests:\n structureRequests,\n },\n clearBatchBody: {\n ranges: clearRanges,\n },\n formatBatchBody: {\n requests:\n formatRequests,\n },\n originalResponse:\n prepared.originalResponse ||\n {},\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1080, + 5200 + ], + "id": "81decb9b-a5ca-4c4b-836f-41a1389cf472", + "name": "Preparar actualización Google Sheet" + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + ':batchUpdate' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.structureBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1320, + 5200 + ], + "id": "973e9e0e-a044-4870-8f41-1c02f1ff5412", + "name": "Actualizar estructura Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + '/values:batchClear' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.clearBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1560, + 5200 + ], + "id": "174a00f9-d50b-439a-ae7f-844d3c5cc53e", + "name": "Limpiar Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + '/values:batchUpdate' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.valueBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1800, + 5200 + ], + "id": "7731ef3c-018c-4a73-b4c2-11247e0142b8", + "name": "Escribir Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Preparar actualización Google Sheet').first().json.spreadsheetId + ':batchUpdate' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $('Preparar actualización Google Sheet').first().json.formatBatchBody }}", + "options": { + "timeout": 60000 + } + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 2040, + 5200 + ], + "id": "0e27f441-b958-44ad-8f90-b33449c42d03", + "name": "Formatear Google Sheet", + "retryOnFail": true, + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + } + }, + { + "parameters": { + "jsCode": "const context =\n $('Preparar actualización Google Sheet').first().json || {};\n\nconst spreadsheetId =\n context.spreadsheetId;\n\nif (!spreadsheetId) {\n throw new Error(\n 'No se recibió spreadsheetId para configurar permisos.'\n );\n}\n\nconst allowedEmails = [\n 'iaracena@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com'\n];\n\nreturn allowedEmails.map((email) => ({\n json: {\n spreadsheetId,\n email,\n permissionBody: {\n type: 'user',\n role: 'writer',\n emailAddress: email,\n },\n reportUpdated:\n context.reportUpdated === true,\n },\n}));" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2280, + 5200 + ], + "id": "24e9bed0-49df-488f-8b07-d9027c62bf9f", + "name": "Preparar permisos Google Sheet" + }, + { + "parameters": { + "method": "POST", + "url": "={{ 'https://www.googleapis.com/drive/v3/files/' + $json.spreadsheetId + '/permissions?sendNotificationEmail=false' }}", + "authentication": "predefinedCredentialType", + "nodeCredentialType": "googleOAuth2Api", + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $json.permissionBody }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 2520, + 5200 + ], + "id": "88b2fb70-5a7b-4343-bc45-b84fb664b21b", + "name": "Compartir Google Sheet", + "credentials": { + "googleOAuth2Api": { + "id": "dQ1MJSJSWcoWYcb8", + "name": "Google account - Isaac Producción" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const cruce = $('Cruzar Nómina vs Banco').first().json || {};\nconst sheetContext =\n $('Preparar actualización Google Sheet').first().json || {};\n\nconst metadata = cruce.metadata || {};\nconst summary = cruce.summary || {};\nconst debug = cruce.debug || {};\n\nconst spreadsheetId = sheetContext.spreadsheetId || cruce.spreadsheetId || '';\nconst reportUrl =\n sheetContext.reportUrl ||\n (spreadsheetId ? `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit` : null);\n\nfunction toNumber(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction buildPeriodKey(periodMetadata) {\n const country = periodMetadata.country || 'PR';\n const year = periodMetadata.year || '';\n const month = String(periodMetadata.month || '').padStart(2, '0');\n const periodType = periodMetadata.period_type || 'periodo';\n return `${country}-${year}-${month}-${periodType}`;\n}\n\nconst discrepancias = toNumber(summary.discrepancias);\nconst bancoSinNomina = toNumber(summary.bancoSinNomina);\nconst nominaSinCuenta = toNumber(summary.nominaSinCuenta);\nconst diferenciasNombreBanco =\n toNumber(summary.diferenciasNombreBanco);\nconst bancoSinBamboo =\n toNumber(summary.bancoSinBamboo);\nconst pendientes = toNumber(summary.pendientes) || (\n discrepancias +\n bancoSinNomina +\n nominaSinCuenta +\n diferenciasNombreBanco\n);\n\n// Banco sin Bamboo es una revisión independiente del cruce principal.\nconst requiereRevision =\n pendientes > 0 || bancoSinBamboo > 0;\n\n// La app final solo manejará Pendiente revisión y Resuelto.\nconst estado = requiereRevision\n ? 'pendiente_revision'\n : 'resuelto';\n\nconst reportIdentity = [\n 'PR',\n String(metadata.year || ''),\n String(metadata.month || '').padStart(2, '0'),\n String(metadata.period_type || '').trim().toLowerCase(),\n].join('-');\n\nconst reportUpdated =\n sheetContext.reportUpdated === true;\n\nconst preservedResolutionsCount =\n Number(\n sheetContext.preservedResolutionsCount || 0\n );\n\nconst payload = {\n source_app: metadata.source_app || 'cruce-cuentas-glm-puerto-rico',\n country: metadata.country || 'PR',\n country_name: metadata.country_name || 'Puerto Rico',\n client_request_id: metadata.client_request_id || null,\n report_identity: reportIdentity,\n\n year: toNumber(metadata.year),\n month: toNumber(metadata.month),\n period_type: metadata.period_type || '',\n period_label: metadata.period_label || '',\n period_start: metadata.period_start || null,\n period_end: metadata.period_end || null,\n period_key: buildPeriodKey(metadata),\n\n payroll_file_name: metadata.payroll_file_name || '',\n bank_file_names: metadata.bank_file_names || [],\n\n coincidencias: toNumber(summary.coincidencias),\n discrepancias,\n\n banco_sin_bamboo: bancoSinBamboo,\n detalle_banco_sin_bamboo:\n Array.isArray(cruce.bankWithoutBamboo)\n ? cruce.bankWithoutBamboo\n : [],\n banco_sin_nomina: bancoSinNomina,\n nomina_sin_cuenta: nominaSinCuenta,\n nomina_sin_bamboo: 0,\n bamboo_sin_nomina: 0,\n\n filas_nomina_validas: toNumber(summary.filasNominaValidas),\n cuentas_nomina_agrupadas: toNumber(summary.cuentasNominaAgrupadas),\n transacciones_banco: toNumber(summary.transaccionesBanco),\n cuentas_banco_agrupadas: toNumber(summary.cuentasBancoAgrupadas),\n\n total_nomina: toNumber(summary.totalNomina),\n total_banco: toNumber(summary.totalBanco),\n diferencia_total: toNumber(summary.diferenciaTotal),\n\n report_url: reportUrl,\n spreadsheet_id: spreadsheetId,\n estado,\n\n ejecutado_por_nombre: metadata.requested_by_name || 'Usuario GLM',\n ejecutado_por_email: metadata.requested_by_email || '',\n\n metadata: {\n ...metadata,\n report_identity: reportIdentity,\n report_updated: reportUpdated,\n preserved_resolutions_count: preservedResolutionsCount,\n diferencias_nombre_banco:\n diferenciasNombreBanco,\n banco_sin_bamboo:\n bancoSinBamboo,\n pendientes_cruce_principal:\n pendientes,\n requiere_revision:\n requiereRevision,\n },\n summary,\n debug: {\n sheet_summaries: debug.sheet_summaries || [],\n attached_supplements: debug.attached_supplements || [],\n unattached_supplements: debug.unattached_supplements || [],\n bank_name_differences_preview:\n debug.bank_name_differences_preview || [],\n bamboo_matches:\n debug.bamboo_matches || [],\n bamboo_excluded_payments:\n debug.bamboo_excluded_payments || [],\n banco_sin_bamboo:\n cruce.bankWithoutBamboo || [],\n },\n};\n\n\nif (\n reportUpdated &&\n estado !== 'resuelto'\n) {\n payload.resuelto_por_nombre = null;\n payload.resuelto_por_email = null;\n payload.resuelto_en = null;\n payload.comentario_resolucion = null;\n}\n\nreturn [\n {\n json: {\n ...cruce,\n reportUpdated,\n preservedResolutionsCount,\n existingHistoricalId: sheetContext.existingHistoricalId || '',\n reportIdentity,\n supabaseTable: 'cruces_cuentas_gt_reportes',\n supabasePayload: payload,\n reportUrl,\n spreadsheetId,\n },\n },\n];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2760, + 5200 + ], + "id": "0534fec7-fb94-4893-9e50-9a1afdfb843f", + "name": "Preparar histórico Supabase" + }, + { + "parameters": { + "method": "POST", + "url": "https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?on_conflict=report_identity", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "resolution=merge-duplicates,return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{ $json.supabasePayload }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 3000, + 5200 + ], + "id": "c87f31ff-6179-42c7-904c-e2fc684685f9", + "name": "Insertar histórico Supabase", + "onError": "continueRegularOutput" + }, + { + "parameters": { + "jsCode": "const prepared = $('Preparar Google Sheet').first().json || {};\nconst sheetContext = $('Preparar actualización Google Sheet').first().json || {};\n\nconst original =\n prepared.originalResponse ||\n prepared.original_response ||\n prepared.response ||\n {};\n\nconst spreadsheetId = sheetContext.spreadsheetId || '';\nconst reportUrl =\n sheetContext.reportUrl ||\n (spreadsheetId ? `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit` : null);\n\n\nfunction collectHistoricalObjects(value, output) {\n if (Array.isArray(value)) {\n for (const item of value) collectHistoricalObjects(item, output);\n return;\n }\n\n if (!value || typeof value !== 'object') return;\n\n output.push(value);\n\n if (Array.isArray(value.data)) {\n for (const item of value.data) collectHistoricalObjects(item, output);\n }\n}\n\nconst historicalObjects = [];\ntry {\n for (const item of $items('Insertar histórico Supabase')) {\n collectHistoricalObjects(item.json, historicalObjects);\n }\n} catch (error) {\n // Si Supabase no devolvió representación, el reporte sigue respondiendo\n // igual que antes; únicamente executionId quedará vacío.\n}\n\nconst insertedHistoricalReport =\n historicalObjects.find((item) => item && item.id) || null;\n\nconst executionId = insertedHistoricalReport?.id\n ? String(insertedHistoricalReport.id)\n : '';\n\nreturn [\n {\n json: {\n ok: original.ok ?? true,\n message: reportUrl\n ? (sheetContext.reportUpdated === true\n ? `Reporte existente actualizado correctamente. Se conservaron ${Number(sheetContext.preservedResolutionsCount || 0)} resolución(es) previamente registradas.`\n : 'Cruce procesado correctamente. Google Sheet generado.')\n : 'Cruce procesado correctamente, pero no se recibió URL del Google Sheet.',\n stage: reportUrl ? 'cruce_completado_con_reporte' : 'cruce_completado_sin_reporte',\n errors: original.errors || [],\n metadata: original.metadata || {},\n summary: original.summary || {},\n rows: original.rows || [],\n bankWithoutBamboo:\n original.bankWithoutBamboo || [],\n bambooSummary:\n original.bambooSummary || {},\n reportUrl,\n executionId: executionId || undefined,\n reportUpdated: sheetContext.reportUpdated === true,\n reusingSheet: sheetContext.reusingSheet === true,\n preservedResolutionsCount: Number(sheetContext.preservedResolutionsCount || 0),\n clientRequestId: original.metadata?.client_request_id || '',\n googleSheet: {\n spreadsheetId,\n spreadsheetUrl: reportUrl,\n },\n debug: {\n rows_returned: Array.isArray(original.rows) ? original.rows.length : 0,\n coincidencias: original.summary?.coincidencias ?? 0,\n discrepancias: original.summary?.discrepancias ?? 0,\n bancoSinBamboo:\n original.summary?.bancoSinBamboo ?? 0,\n bancoSinBambooRows:\n Array.isArray(original.bankWithoutBamboo)\n ? original.bankWithoutBamboo.length\n : 0,\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3240, + 5120 + ], + "id": "aaa3a012-5f5f-412d-93fb-890f7093b906", + "name": "Preparar respuesta final" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{\n(() => {\n const data = $json || {};\n\n const original =\n data.originalResponse ||\n data.original_response ||\n data.response ||\n data.cruceResponse ||\n data.cruce_response ||\n data;\n\n const summary = original.summary || data.summary || {};\n const rows = original.rows || data.rows || [];\n const bankWithoutBamboo =\n original.bankWithoutBamboo ||\n data.bankWithoutBamboo ||\n [];\n const bambooSummary =\n original.bambooSummary ||\n data.bambooSummary ||\n {};\n\n const reportUrl =\n data.reportUrl ||\n data.report_url ||\n data.googleSheetUrl ||\n data.google_sheet_url ||\n data.spreadsheetUrl ||\n data.spreadsheet_url ||\n original.reportUrl ||\n original.report_url ||\n null;\n\n return {\n ok: original.ok ?? data.ok ?? true,\n message: original.message || data.message || (reportUrl\n ? 'Cruce procesado correctamente. Google Sheet generado.'\n : 'Cruce procesado correctamente.'),\n stage: reportUrl ? 'cruce_completado_con_reporte' : 'cruce_completado',\n errors: original.errors || data.errors || [],\n metadata: original.metadata || data.metadata || {},\n summary,\n rows,\n bankWithoutBamboo,\n bambooSummary,\n reportUrl,\n executionId: original.executionId || data.executionId || null,\n reportUpdated: original.reportUpdated ?? data.reportUpdated ?? false,\n reusingSheet: original.reusingSheet ?? data.reusingSheet ?? false,\n preservedResolutionsCount: Number(original.preservedResolutionsCount ?? data.preservedResolutionsCount ?? 0),\n clientRequestId: original.clientRequestId || data.clientRequestId || original.metadata?.client_request_id || data.metadata?.client_request_id || null,\n debug: {\n source_stage: data.stage || null,\n rows_returned:\n Array.isArray(rows) ? rows.length : 0,\n banco_sin_bamboo_rows:\n Array.isArray(bankWithoutBamboo)\n ? bankWithoutBamboo.length\n : 0,\n report_url_found: Boolean(reportUrl),\n },\n };\n})()\n}}", + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 3480, + 5120 + ], + "id": "3278f6df-9a9a-443d-a8a5-5267cf38286c", + "name": "Respond to Webhook" + }, + { + "parameters": { + "jsCode": "const prepared = $('Preparar histórico Supabase').first().json || {};\nconst payload = prepared.supabasePayload || {};\nconst metadata = payload.metadata || {};\nconst summary = payload.summary || {};\nconst reportUpdated = prepared.reportUpdated === true;\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatGeneratedAt(date) {\n const timeZone = 'America/Puerto_Rico';\n\n const dateLabel = new Intl.DateTimeFormat('es-PR', {\n timeZone,\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n }).format(date);\n\n const timeLabel = new Intl.DateTimeFormat('es-PR', {\n timeZone,\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n }).format(date);\n\n const hour24Raw = new Intl.DateTimeFormat('en-US', {\n timeZone,\n hour: '2-digit',\n hour12: false,\n }).format(date);\n\n const hour24 =\n Number(String(hour24Raw).replace(/\\D/g, '')) % 24;\n\n const hourArticle =\n hour24 === 1 || hour24 === 13\n ? 'a la'\n : 'a las';\n\n return `${dateLabel} ${hourArticle} ${timeLabel}`;\n}\n\nfunction metricCard(label, value, accent) {\n return `\n \n \n \n \n \n ${escapeHtml(label)}\n \n\n \n ${numberValue(value).toLocaleString('en-US')}\n \n \n \n \n \n `;\n}\n\nconst countryCode = 'PR';\nconst countryName = 'Puerto Rico';\n\nconst periodLabel =\n payload.period_label ||\n metadata.period_label ||\n [\n payload.year || metadata.year,\n payload.month || metadata.month,\n payload.period_type || metadata.period_type,\n ]\n .filter(Boolean)\n .join(' · ') ||\n 'Período no especificado';\n\nconst executedByName =\n payload.ejecutado_por_nombre ||\n metadata.requested_by_name ||\n 'Usuario GLM';\n\nconst executedByEmail =\n payload.ejecutado_por_email ||\n metadata.requested_by_email ||\n '';\n\nconst reportUrl =\n payload.report_url ||\n prepared.reportUrl ||\n '';\n\nconst discrepancias = numberValue(\n payload.discrepancias ??\n summary.discrepancias\n);\n\nconst bancoSinNomina = numberValue(\n payload.banco_sin_nomina ??\n summary.bancoSinNomina\n);\n\nconst bancoSinBamboo = numberValue(\n payload.banco_sin_bamboo ??\n summary.bancoSinBamboo\n);\n\nconst diferenciasNombreBanco = numberValue(\n metadata.diferencias_nombre_banco ??\n summary.diferenciasNombreBanco\n);\n\nconst nominaSinCuenta = numberValue(\n payload.nomina_sin_cuenta ??\n summary.nominaSinCuenta\n);\n\nconst coincidencias = numberValue(\n payload.coincidencias ??\n summary.coincidencias\n);\n\nconst generatedAt = new Date();\nconst generatedAtLabel =\n formatGeneratedAt(generatedAt);\n\nconst statusLabel =\n String(payload.estado || '').toLowerCase() === 'resuelto'\n ? 'Resuelto'\n : 'Pendiente de revisión';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/bandera-nacional-tela-puerto-rico_113767-1759.avif';\n\nconst subject = reportUpdated\n ? `Reporte actualizado - Cruce de Cuentas ${countryName} - ${periodLabel}`\n : `Nuevo reporte generado - Cruce de Cuentas ${countryName} - ${periodLabel}`;\n\n/*\n * Todos son destinatarios directos.\n * Nadie se envía en CC.\n */\n\nconst to = [\n 'iaracena@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n].join(',');\n\n\n// Paleta oficial GLM.\nconst metricCards = [\n metricCard(\n 'Discrepancias',\n discrepancias,\n '#FF6A13'\n ),\n metricCard(\n 'Banco sin nómina',\n bancoSinNomina,\n '#FF8C00'\n ),\n metricCard(\n 'Banco sin Bamboo',\n bancoSinBamboo,\n '#E87722'\n ),\n metricCard(\n 'Diferencias nombre banco',\n diferenciasNombreBanco,\n '#5B7F95'\n ),\n metricCard(\n 'Nómina sin cuenta',\n nominaSinCuenta,\n '#4F758B'\n ),\n].join('');\n\nconst htmlBody = `\n\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n  \n \n \n\n \n \n \n\n \n \n \n \n \n\n \n \n ${reportUpdated ? 'REPORTE ACTUALIZADO' : 'REPORTE GENERADO'}\n \n \n \n \n\n \n\n \n \n\n \n \n \n\n \n\n \n Cruce de Cuentas · ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n\n \n \n\n \n \n\n \n \n \n\n \n ${reportUpdated ? 'Se actualizó el reporte existente del Portal de Cruce de Cuentas GLM conservando las resoluciones previamente registradas que continúan correspondiendo a los mismos casos.' : 'Se completó una nueva ejecución del Portal de Cruce de Cuentas GLM. Este es el resumen del reporte generado.'}\n \n\n \n \n\n \n \n \n\n \n \n ${metricCards}\n \n \n\n \n \n\n \n \n \n\n \n\n \n\n \n\n \n Ejecutado por\n \n\n \n ${escapeHtml(executedByName)}\n \n\n \n ${escapeHtml(\n executedByEmail ||\n 'Correo no disponible'\n )}\n \n\n \n\n \n\n \n País\n \n\n \n \n\n \n \n \n\n \n\n \n ${escapeHtml(countryName)}\n \n\n \n ${escapeHtml(\n countryCode || '—'\n )}\n \n\n \n\n \n \n\n \n\n \n\n \n\n \n\n \n Período\n \n\n \n ${escapeHtml(periodLabel)}\n \n\n \n ${escapeHtml(generatedAtLabel)}\n \n\n \n\n \n\n \n Estado inicial\n \n\n \n ${escapeHtml(statusLabel)}\n \n\n \n Coincidencias:\n ${coincidencias.toLocaleString('en-US')}\n \n\n \n\n \n\n \n\n \n \n\n \n ${\n reportUrl\n ? `\n \n \n\n \n Abrir reporte en Google Sheets\n \n\n \n \n `\n : ''\n }\n\n \n \n \n\n \n\n \n Notificación automática · Portal Cruce de Cuentas GLM\n
\n Los casos de revisión deben gestionarse directamente en el reporte generado.\n \n\n \n \n\n \n\n \n \n \n\n\n\n`;\n\nreturn [\n {\n json: {\n to,\n subject,\n htmlBody,\n\n notification: {\n country: countryCode,\n countryName,\n flagUrl,\n periodLabel,\n executedByName,\n executedByEmail,\n generatedAt: generatedAt.toISOString(),\n reportUrl,\n\n metrics: {\n discrepancias,\n bancoSinNomina,\n bancoSinBamboo,\n diferenciasNombreBanco,\n nominaSinCuenta,\n coincidencias,\n },\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3240, + 5360 + ], + "id": "6875ed54-851a-44b3-b998-25735b5464dd", + "name": "Preparar correo reporte generado" + }, + { + "parameters": { + "sendTo": "={{$json.to}}", + "subject": "={{$json.subject}}", + "message": "={{ $json.htmlBody }}", + "options": { + "appendAttribution": false + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 3480, + 5360 + ], + "id": "442d0a94-4369-4bfd-8d8e-dbb9f77e4df3", + "name": "Enviar correo reporte generado", + "webhookId": "4bcdbd7b-503d-46ac-aeaf-9207f59f01e7", + "credentials": { + "gmailOAuth2": { + "id": "UDcO1FLJqA453V2D", + "name": "Gmail account 3" + } + }, + "onError": "continueRegularOutput" + }, + { + "parameters": { + "content": "# 🇭🇳 CRUCE DE CUENTAS — PUERTO RICO\n\nFlujo principal para una nómina quincenal de Puerto Rico + todos los planos bancarios BAC del período + BambooHR.\n\n- Monedas: PRL y USD.\n- BambooHR: `onlyCurrent=false`, filtrado por país/localidad Puerto Rico.\n- Q1: días 1–15. Q2: días 16–último día real del mes.\n- Los meses/años se calculan dinámicamente, incluidos años bisiestos.\n- La nómina usa las hojas operativas validadas de GLM Puerto Rico y omite hojas auxiliares/copias para evitar duplicados.\n\n\n### Correcciones de conciliación PR · 08-09-2026\n- Tolerancia monetaria operativa: ±0.05 por redondeos bancarios.\n- Banco sin Nómina se mantiene por transacción, sin agrupar pagos distintos.\n- Nombre en archivo truncado por un carácter al final no genera falso positivo.\n- BambooHR prioriza el nombre legal del cuentahabiente y perfiles vigentes en el período.\n- La cuenta mal digitada se determina por la cuenta real de la nómina vs. la cuenta real del banco.\n", + "height": 260, + "width": 520 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -2800, + 4768 + ], + "id": "ebbfb113-b7a1-42ff-bedb-bb02ad43ac33", + "name": "📌 PUERTO RICO · V1" + }, + { + "parameters": { + "content": "# 🏦 REGLA DE PLANOS — PUERTO RICO\n\nSe procesan todos los PDF BPPR `Consulta Detalle de Envío_HON_...` cargados. Se reconocen planes AR94 (PRL) y AR95US (USD), además de la moneda informada dentro del archivo.\n\nEl período se filtra por la fecha de aplicación de cada transacción: Q1 = 1–15 y Q2 = 16–fin de mes. El cruce se realiza por persona, cuenta, moneda y monto, con una segunda pasada controlada para detectar diferencias o posibles cuentas mal digitadas.", + "height": 300, + "width": 700 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -1712, + 4688 + ], + "id": "3f985792-728c-4f1a-baae-18e963312f62", + "name": "📌 REGLA BANCO PR" + }, + { + "parameters": { + "content": "# 📄 ARCHIVO DE NÓMINA — PUERTO RICO\n\nLa app recibe un solo Excel por quincena, por ejemplo `Nomina 7.1 2026 GLM PR.xlsx` o `Nomina 7.2 2026 GLM PR.xlsx`.\n\nEl flujo extrae únicamente las hojas de pago validadas (Colgate, POP Helper, Motorola, Nestlé, Whirlpool, FF Regional y GLM) y normaliza `NOMBRE`, `Identidad`, `# de Cuenta Bac` y `NETO A PAGAR` / `Total a Pagar`. Las hojas de copia/auxiliares no se contabilizan.", + "height": 240, + "width": 620 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + -2320, + 5616 + ], + "id": "c1e22489-fa4a-49f8-984d-fa2c497620de", + "name": "📌 ARCHIVOS NÓMINA PR" + }, + { + "parameters": { + "content": "# 📊 SALIDA\n\nConserva la misma estructura de reportes de GT/TT/PA/SV, reutiliza el Google Sheet del mismo período si existe, actualiza Supabase y envía correo tanto en creación como en actualización.\n\nLos valores se escriben en Google Sheets con `RAW` para preservar las cuentas bancarias como texto exacto, incluyendo ceros iniciales y evitando notación científica.", + "height": 250, + "width": 720 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 1672, + 5520 + ], + "id": "dd6b181f-198e-4a8a-a363-a0db9d5741b5", + "name": "📌 REPORTES" + }, + { + "parameters": { + "operation": "pdf", + "options": { + "joinPages": true, + "keepSource": "json" + } + }, + "type": "n8n-nodes-base.extractFromFile", + "typeVersion": 1.1, + "position": [ + -1840, + 4960 + ], + "id": "d9f7fced-ae32-4065-87fb-9f86c8c286f2", + "name": "Extract - PDF Banco PR" + }, + { + "parameters": { + "jsCode": "const items = $input.all();\nconst base = $('Preparar entrada app').first().json || {};\n\nfunction clean(value) {\n return String(value ?? '')\n .replace(/\\uFEFF/g, '')\n .replace(/\\u00A0/g, ' ')\n .replace(/\\r/g, '')\n .replace(/[ \\t]+/g, ' ')\n .trim();\n}\nfunction normalizeAccount(value) {\n return String(value ?? '').replace(/\\D/g, '').trim();\n}\nfunction roundMoney(value) {\n return Math.round((Number(value) || 0) * 100) / 100;\n}\nfunction parseMoney(value) {\n const parsed = Number.parseFloat(String(value ?? '').replace(/[$,\\s]/g, ''));\n return Number.isFinite(parsed) ? parsed : 0;\n}\nfunction toIsoDate(raw) {\n const value = clean(raw);\n let match = value.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/);\n if (match) return `${match[1]}-${String(match[2]).padStart(2, '0')}-${String(match[3]).padStart(2, '0')}`;\n match = value.match(/^(\\d{1,2})\\/(\\d{1,2})\\/(\\d{2}|\\d{4})$/);\n if (!match) return '';\n const year = match[3].length === 2 ? 2000 + Number(match[3]) : Number(match[3]);\n return `${year}-${String(match[1]).padStart(2, '0')}-${String(match[2]).padStart(2, '0')}`;\n}\nfunction cleanNameFragment(value) {\n let text = clean(value).replace(/\\f/g, ' ');\n if (!text) return '';\n const normalized = text.toLowerCase();\n const noise = [\n 'transaction details:', 'transaction details', 'name', 'id', 'number',\n 'amount d/c bank id account#', 'acct', 'type', 'trace # addenda',\n 'batch status', 'extracted entry class: directdeposit', 'entry class: directdeposit',\n 'company', 'company name:', 'name:', 'effective', 'date:', 'template',\n 'release date', 'and time:', 'extract file', 'total # credits:',\n 'total # debits:', 'total $ offset credit:', 'subtotal # credits:',\n 'grand totals:', 'grand total # credits:'\n ];\n if (noise.some((entry) => normalized === entry || normalized.startsWith(entry))) return '';\n if (/^[\\d\\-\\s]+$/.test(text)) return '';\n text = text.replace(/\\s*\\d[\\d-]{2,}\\s*$/, '').trim();\n if (!text || /^[\\d\\-\\s]+$/.test(text)) return '';\n return text;\n}\nfunction sourceMeta(index) {\n try { return $('Preparar PDF banco PR').itemMatching(index).json || {}; }\n catch { return {}; }\n}\nfunction templateLabel(templateId) {\n const id = clean(templateId).toUpperCase();\n if (id === 'MOTOP') return 'Motorola Payroll';\n if (id === 'APPLE PAYR') return 'APPLE PAYR';\n if (id === 'PAYROLLV2') return 'PayrollV2';\n return clean(templateId) || 'BPPR';\n}\nfunction expectedTotals(text) {\n const grandCount = text.match(/Grand Total # Credits:\\s*(\\d+)/i);\n const grandAmount = text.match(/Grand Total Credit Amt:\\s*\\$([\\d,]+\\.\\d{2})/i);\n if (grandCount && grandAmount) {\n return { count: Number(grandCount[1]), amount: roundMoney(parseMoney(grandAmount[1])), source: 'grand_total' };\n }\n let count = 0;\n let amount = 0;\n const regex = /(?:^|\\n)Total # Credits:\\s*(\\d+)[^\\n]*Total Credit Amt:\\s*\\$([\\d,]+\\.\\d{2})/gim;\n for (const match of text.matchAll(regex)) {\n count += Number(match[1] || 0);\n amount = roundMoney(amount + parseMoney(match[2]));\n }\n return count > 0 ? { count, amount, source: 'batch_totals' } : null;\n}\nfunction effectiveDateFromText(text) {\n const subtotal = text.match(/Subtotals? for Effective Date:\\s*(\\d{4}-\\d{2}-\\d{2})/i);\n if (subtotal) return toIsoDate(subtotal[1]);\n const raw = text.match(/Effective\\s*(?:\\n|\\s)*Date:\\s*(?:\\n|\\s)*(\\d{1,2}\\/\\d{1,2}\\/\\d{2,4})/i);\n if (raw) return toIsoDate(raw[1]);\n return '';\n}\nfunction inSelectedPeriod(dateValue) {\n const start = clean(base.metadata?.period_start);\n const end = clean(base.metadata?.period_end);\n if (!dateValue || !start || !end) return true;\n return dateValue >= start && dateValue <= end;\n}\n\nconst allRows = [];\nconst fileSummaries = [];\nconst seenTransactions = new Set();\n\nfor (let itemIndex = 0; itemIndex < items.length; itemIndex++) {\n const item = items[itemIndex] || {};\n const linked = sourceMeta(itemIndex);\n const sourceFile = clean(linked.source_file || item.json?.source_file || `Banco PR ${itemIndex + 1}.pdf`);\n const text = String(item.json?.text ?? item.json?.data ?? item.json?.content ?? '');\n if (!text.trim()) {\n throw new Error(`Puerto Rico: no se pudo extraer texto del PDF bancario ${sourceFile}.`);\n }\n\n const effectiveDate = effectiveDateFromText(text);\n if (!effectiveDate) {\n throw new Error(`Puerto Rico: no se encontró la Effective Date en ${sourceFile}.`);\n }\n if (!inSelectedPeriod(effectiveDate)) {\n throw new Error(`Puerto Rico: ${sourceFile} tiene Effective Date ${effectiveDate}, fuera del período seleccionado ${base.metadata?.period_start || ''} a ${base.metadata?.period_end || ''}.`);\n }\n\n const expected = expectedTotals(text);\n const lines = text.split(/\\n+/).map((line) => clean(line)).filter(Boolean);\n let currentTemplate = '';\n let pendingLines = [];\n const rowsFromFile = [];\n\n for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {\n const line = lines[lineIndex];\n const templateMatch = line.match(/^Template ID:\\s*(.+)$/i);\n if (templateMatch) {\n currentTemplate = clean(templateMatch[1]);\n pendingLines = [];\n continue;\n }\n\n if (/^(?:Total|Subtotal|Grand Total)/i.test(line)) {\n pendingLines = [];\n continue;\n }\n\n const tx = line.match(/^(.*?)\\$([\\d,]+\\.\\d{2})\\s+([CD])\\s+(\\d{6,12})\\s+([0-9-]{5,25})\\s+(Checking|Savings|CHK)\\s+(\\d{6,30})(?:\\s+(.*))?$/i);\n if (!tx) {\n const fragment = cleanNameFragment(line);\n if (fragment) pendingLines.push(fragment);\n if (pendingLines.length > 8) pendingLines = pendingLines.slice(-8);\n continue;\n }\n\n let prefix = clean(tx[1]);\n let idNumber = '';\n const idAtEnd = prefix.match(/(\\d[\\d-]{5,})\\s*$/);\n if (idAtEnd) {\n idNumber = normalizeAccount(idAtEnd[1]);\n prefix = clean(prefix.slice(0, idAtEnd.index));\n }\n\n const debitCredit = String(tx[3]).toUpperCase();\n if (debitCredit !== 'C') {\n pendingLines = [];\n continue;\n }\n\n const prefixName = cleanNameFragment(prefix);\n const nameParts = [...pendingLines, prefixName].filter(Boolean);\n const name = clean(nameParts.join(' '));\n const amount = roundMoney(parseMoney(tx[2]));\n const bankId = normalizeAccount(tx[4]);\n const account = normalizeAccount(tx[5]);\n const accountType = clean(tx[6]);\n const trace = normalizeAccount(tx[7]);\n const addenda = clean(tx[8]);\n const templateId = currentTemplate || 'BPPR';\n const duplicateKey = `${effectiveDate}|${trace}|${account}|${amount.toFixed(2)}`;\n\n if (seenTransactions.has(duplicateKey)) {\n throw new Error(`Puerto Rico: se detectó una transacción bancaria duplicada (${trace}) al consolidar los PDF cargados.`);\n }\n seenTransactions.add(duplicateKey);\n\n const row = {\n source_file: sourceFile,\n row_number: lineIndex + 1,\n bank_row_key: `PR|${sourceFile}|${templateId}|${trace}|${lineIndex + 1}`,\n account,\n raw_account: account,\n account_is_valid: account.length >= 5,\n reference: '',\n participant_id: '',\n bank_id_number: idNumber,\n bank_id: bankId,\n shipment_number: trace,\n plan_number: templateId,\n plan_label: templateLabel(templateId),\n template_id: templateId,\n bank_name_file: name || `Pago BPPR ${trace}`,\n bank_account_holder: '',\n amount,\n currency: 'USD',\n application_date: effectiveDate,\n concept: '',\n addenda,\n account_type: accountType,\n status: 'C',\n };\n rowsFromFile.push(row);\n allRows.push(row);\n pendingLines = [];\n }\n\n const parsedTotal = roundMoney(rowsFromFile.reduce((sum, row) => sum + row.amount, 0));\n if (expected) {\n if (rowsFromFile.length !== expected.count) {\n throw new Error(`Puerto Rico: ${sourceFile} reporta ${expected.count} créditos, pero se pudieron leer ${rowsFromFile.length}. No se generará un reporte incompleto.`);\n }\n if (Math.abs(parsedTotal - expected.amount) > 0.01) {\n throw new Error(`Puerto Rico: el total leído de ${sourceFile} (USD ${parsedTotal.toFixed(2)}) no coincide con el total del PDF (USD ${expected.amount.toFixed(2)}).`);\n }\n }\n\n const batchSummaryMap = new Map();\n for (const row of rowsFromFile) {\n const key = row.template_id;\n const current = batchSummaryMap.get(key) || { template_id: key, template_name: row.plan_label, rows_count: 0, total_amount: 0 };\n current.rows_count += 1;\n current.total_amount = roundMoney(current.total_amount + row.amount);\n batchSummaryMap.set(key, current);\n }\n\n fileSummaries.push({\n file_name: sourceFile,\n ok: true,\n effective_date: effectiveDate,\n rows_count: rowsFromFile.length,\n total_amount: parsedTotal,\n currency: 'USD',\n expected_count: expected?.count ?? null,\n expected_total: expected?.amount ?? null,\n expected_source: expected?.source ?? null,\n batches: Array.from(batchSummaryMap.values()),\n });\n}\n\nif (!allRows.length) {\n throw new Error('Puerto Rico: no se encontró ninguna transacción bancaria válida en los PDF cargados.');\n}\n\nconst totalAmount = roundMoney(allRows.reduce((sum, row) => sum + row.amount, 0));\nreturn [{\n json: {\n ...base,\n stage: 'banco_pr_normalizado',\n bank: {\n source: 'bppr_batch_summary_pdf',\n files_count: fileSummaries.length,\n file_summaries: fileSummaries,\n rows_count: allRows.length,\n valid_rows_count: allRows.length,\n total_amount: totalAmount,\n totals_by_currency: { USD: totalAmount },\n rows: allRows,\n },\n debug_bank: {\n file_summaries: fileSummaries,\n parsed_rows_preview: allRows.slice(0, 100),\n },\n },\n}];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + -1600, + 4960 + ], + "id": "aaead29e-f149-443f-80ab-7d6bad0a2b6f", + "name": "Parsear PDF banco PR" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "ed579987-a9c3-404e-a548-8c66fae47745", + "leftValue": "={{ $json.ok }}", + "rightValue": "", + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + -2320, + 5200 + ], + "id": "e48a9f66-61b5-4880-8f51-4032744e9f8b", + "name": "¿Entrada PR válida?" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ { ok: false, stage: $json.stage || 'entrada_pr_invalida', message: 'No fue posible iniciar el cruce de Puerto Rico.', errors: Array.isArray($json.errors) ? $json.errors : ['Solicitud inválida.'] } }}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + -2080, + 5720 + ], + "id": "9a663a01-3ad7-4904-a2ac-239c9c6a2ef8", + "name": "Responder error entrada PR" + } + ], + "pinData": {}, + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "Preparar entrada app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar entrada app": { + "main": [ + [ + { + "node": "¿Entrada PR válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Entrada PR válida?": { + "main": [ + [ + { + "node": "Preparar PDF banco PR", + "type": "main", + "index": 0 + }, + { + "node": "Preparar archivos nómina PR", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder error entrada PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar PDF banco PR": { + "main": [ + [ + { + "node": "Extract - PDF Banco PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract - PDF Banco PR": { + "main": [ + [ + { + "node": "Parsear PDF banco PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parsear PDF banco PR": { + "main": [ + [ + { + "node": "Merge Banco + Nómina PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar archivos nómina PR": { + "main": [ + [ + { + "node": "Extract - Archivo Nómina PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract - Archivo Nómina PR": { + "main": [ + [ + { + "node": "Normalizar Nómina PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar Nómina PR": { + "main": [ + [ + { + "node": "Merge Banco + Nómina PR", + "type": "main", + "index": 1 + } + ] + ] + }, + "Merge Banco + Nómina PR": { + "main": [ + [ + { + "node": "HTTP - Empleados BambooHR PR", + "type": "main", + "index": 0 + }, + { + "node": "Merge - Agregar BambooHR PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "HTTP - Empleados BambooHR PR": { + "main": [ + [ + { + "node": "Normalizar BambooHR PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar BambooHR PR": { + "main": [ + [ + { + "node": "Merge - Agregar BambooHR PR", + "type": "main", + "index": 1 + } + ] + ] + }, + "Merge - Agregar BambooHR PR": { + "main": [ + [ + { + "node": "Obtener alias BambooHR Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Obtener alias BambooHR Supabase": { + "main": [ + [ + { + "node": "Cruzar Nómina vs Banco", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cruzar Nómina vs Banco": { + "main": [ + [ + { + "node": "Preparar Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar Google Sheet": { + "main": [ + [ + { + "node": "Buscar reporte existente Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Buscar reporte existente Supabase": { + "main": [ + [ + { + "node": "Crear Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Crear Google Sheet": { + "main": [ + [ + { + "node": "Leer Google Sheet actual", + "type": "main", + "index": 0 + } + ] + ] + }, + "Leer Google Sheet actual": { + "main": [ + [ + { + "node": "Preparar actualización Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar actualización Google Sheet": { + "main": [ + [ + { + "node": "Actualizar estructura Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Actualizar estructura Google Sheet": { + "main": [ + [ + { + "node": "Limpiar Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Limpiar Google Sheet": { + "main": [ + [ + { + "node": "Escribir Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Escribir Google Sheet": { + "main": [ + [ + { + "node": "Formatear Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Formatear Google Sheet": { + "main": [ + [ + { + "node": "Preparar permisos Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar permisos Google Sheet": { + "main": [ + [ + { + "node": "Compartir Google Sheet", + "type": "main", + "index": 0 + } + ] + ] + }, + "Compartir Google Sheet": { + "main": [ + [ + { + "node": "Preparar histórico Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar histórico Supabase": { + "main": [ + [ + { + "node": "Insertar histórico Supabase", + "type": "main", + "index": 0 + } + ] + ] + }, + "Insertar histórico Supabase": { + "main": [ + [ + { + "node": "Preparar respuesta final", + "type": "main", + "index": 0 + }, + { + "node": "Preparar correo reporte generado", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar respuesta final": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar correo reporte generado": { + "main": [ + [ + { + "node": "Enviar correo reporte generado", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "1a174b21-9f77-443a-9616-ef028022f4d8", + "meta": { + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "tags": [] +} diff --git a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV HN.json b/workflows_n8n/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV HN.json deleted file mode 100644 index 5f85045..0000000 --- a/workflows_n8n/Portal de Verificaci├│n de N├│mina - Reportar correcci├│n BambooHR a RRHH - GT TT PA SV HN.json +++ /dev/null @@ -1,514 +0,0 @@ -{ - "name": "Portal de Verificación de Nómina - Reportar corrección BambooHR a RRHH - GT TT PA SV HN", - "nodes": [ - { - "parameters": { - "httpMethod": "POST", - "path": "cruce-cuentas-bamboo-correccion", - "responseMode": "responseNode", - "options": {} - }, - "type": "n8n-nodes-base.webhook", - "typeVersion": 2.1, - "position": [ - 768, - 1984 - ], - "id": "e5de6fd4-03ff-4d15-b567-24209b9596cc", - "name": "Webhook", - "webhookId": "821fa25f-8b4c-417b-870c-73183194a709" - }, - { - "parameters": { - "jsCode": "const incoming = $input.first().json || {};\nconst rawBody = incoming.body ?? incoming;\n\nlet body = rawBody;\n\nif (rawBody && typeof rawBody === 'object' && typeof rawBody.payload === 'string') {\n try {\n body = JSON.parse(rawBody.payload);\n } catch (error) {\n throw new Error('El payload recibido no contiene JSON válido.');\n }\n}\n\nif (!body || typeof body !== 'object') {\n throw new Error('No se recibió una solicitud válida.');\n}\n\nconst accessToken = String(\n (rawBody && typeof rawBody === 'object' ? rawBody.access_token : '') ||\n body.access_token ||\n ''\n).trim();\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction normalizeName(value) {\n return clean(value)\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst country = clean(body.country).toUpperCase();\nif (!['GT', 'TT', 'PA', 'SV', 'HN'].includes(country)) {\n throw new Error('País no permitido para reportar correcciones BambooHR.');\n}\n\nif (!accessToken) {\n throw new Error('No se recibió una sesión autenticada de Supabase.');\n}\n\nif (body.confirmed_same_person !== true) {\n throw new Error('La revisión manual debe confirmar que se trata de la misma persona.');\n}\n\nconst sourceNames = Array.from(new Set([\n ...(Array.isArray(body.source_names) ? body.source_names : []),\n body.source_name,\n body.bank_name_file,\n body.bank_account_holder,\n]\n .map(clean)\n .filter(Boolean)\n .filter((value) => normalizeName(value))));\n\nif (!sourceNames.length) {\n throw new Error('No se recibió el nombre detectado en Banco sin BambooHR.');\n}\n\nconst bambooName = clean(body.bamboo_name);\nif (!bambooName) {\n throw new Error('Debes indicar el nombre correcto en BambooHR.');\n}\n\nreturn [{\n json: {\n accessToken,\n request: {\n country,\n countryName: ({ GT: 'Guatemala', TT: 'Trinidad y Tobago', PA: 'Panamá', SV: 'El Salvador', HN: 'Honduras' })[country],\n sourceName: clean(body.source_name) || sourceNames[0],\n sourceNames,\n bankNameFile: clean(body.bank_name_file),\n bankAccountHolder: clean(body.bank_account_holder),\n bankAccount: clean(body.bank_account),\n bankAmount: Number.isFinite(Number(body.bank_amount)) ? Number(body.bank_amount) : null,\n currency: clean(body.currency) || ({ GT: 'GTQ', TT: 'TTD', PA: 'USD', SV: 'USD', HN: 'HNL' })[country],\n bambooName,\n bambooEmployeeNumber: clean(body.bamboo_employee_number),\n reason: clean(body.reason) || 'Corrección de identidad en BambooHR',\n comment: clean(body.comment),\n executionId: clean(body.execution_id),\n reportUrl: clean(body.report_url),\n periodLabel: clean(body.period_label),\n periodStart: clean(body.period_start),\n periodEnd: clean(body.period_end),\n requestedByNameClaimed: clean(body.requested_by_name),\n requestedByEmailClaimed: clean(body.requested_by_email).toLowerCase(),\n },\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1008, - 1984 - ], - "id": "569fbf65-f297-416b-988a-c8ab108899bb", - "name": "Preparar solicitud" - }, - { - "parameters": { - "method": "POST", - "url": "https://dbit.digitalcompass.agency/rest/v1/rpc/cruce_cuentas_mi_acceso", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE" - }, - { - "name": "Authorization", - "value": "={{ 'Bearer ' + $('Preparar solicitud').first().json.accessToken }}" - }, - { - "name": "Content-Type", - "value": "application/json" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": {}, - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 1248, - 1984 - ], - "id": "4b3bd35e-8097-4b1b-a665-b482d887e8d7", - "name": "Validar acceso Supabase", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "jsCode": "const raw = $input.first().json || {};\nconst access = Array.isArray(raw) ? (raw[0] || {}) : raw;\nconst prepared = $('Preparar solicitud').first().json || {};\nconst request = prepared.request || {};\n\nconst email = String(access.email || '').trim().toLowerCase();\nconst countries = Array.isArray(access.paises)\n ? access.paises.map((value) => String(value || '').trim().toUpperCase()).filter(Boolean)\n : [];\n\nif (\n access.ok !== true ||\n access.autenticado !== true ||\n access.autorizado !== true ||\n access.activo !== true ||\n access.acceso_app !== true ||\n !email\n) {\n throw new Error(access.motivo || 'El usuario no tiene acceso autorizado al Cruce de Cuentas.');\n}\n\nif (countries.length && !countries.includes(request.country)) {\n throw new Error(`El usuario autenticado no tiene acceso al país ${request.country}.`);\n}\n\nreturn [{\n json: {\n ...request,\n requestedByName: String(access.nombre || request.requestedByNameClaimed || email).trim(),\n requestedByEmail: email,\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1488, - 1984 - ], - "id": "67e4c922-7c87-4567-a1d9-4d9b676684db", - "name": "Validar usuario" - }, - { - "parameters": { - "method": "POST", - "url": "https://glm.bamboohr.com/api/v1/reports/custom?format=JSON&onlyCurrent=false", - "authentication": "genericCredentialType", - "genericAuthType": "httpBasicAuth", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "Accept", - "value": "application/json" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": { - "title": "Validación manual - Corrección identidad Cruce de Cuentas", - "fields": [ - "firstName", - "middleName", - "lastName", - "displayName", - "location", - "status", - "employeeNumber", - "country" - ] - }, - "options": { - "response": { - "response": { - "responseFormat": "json" - } - }, - "timeout": 300000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 1728, - 1984 - ], - "id": "9d9b8120-c80b-456c-8bbe-c945755c1507", - "name": "Consultar empleado BambooHR", - "retryOnFail": true, - "maxTries": 3, - "waitBetweenTries": 3000, - "credentials": { - "httpBasicAuth": { - "id": "7VrpNZ2jBLmiJ35q", - "name": "BambooHR GLM Full Access" - } - } - }, - { - "parameters": { - "jsCode": "const request = $('Validar usuario').first().json || {};\nconst raw = $input.first().json || {};\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction normalizeName(value) {\n return clean(value)\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/[^a-z0-9 ]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction normalizeIdentifier(value) {\n return clean(value).toUpperCase().replace(/[^A-Z0-9]/g, '');\n}\n\nfunction countryMatches(employee) {\n const location = normalizeName(\n employee.location ||\n employee.jobInformationLocation ||\n employee.jobLocation ||\n ''\n );\n const country = normalizeName(employee.country || '');\n const combined = `${location} ${country}`.trim();\n\n if (request.country === 'GT') {\n return combined.includes('guatemala');\n }\n\n if (request.country === 'PA') {\n return combined.includes('panama');\n }\n\n if (request.country === 'SV') {\n return combined.includes('el salvador') || combined.includes('salvador');\n }\n\n if (request.country === 'HN') {\n return combined.includes('honduras');\n }\n\n return combined.includes('trinidad') || combined.includes('tobago');\n}\n\nfunction employeeAliases(employee) {\n const first = clean(employee.firstName || employee.first_name);\n const middle = clean(employee.middleName || employee.middle_name);\n const last = clean(employee.lastName || employee.last_name);\n const display = clean(employee.displayName || employee.fullName || employee.full_name);\n\n return Array.from(new Set([\n display,\n [first, middle, last].filter(Boolean).join(' '),\n [first, last].filter(Boolean).join(' '),\n ].map(clean).filter(Boolean)));\n}\n\nconst employees = Array.isArray(raw.employees)\n ? raw.employees\n : Array.isArray(raw.data?.employees)\n ? raw.data.employees\n : [];\n\nif (!employees.length) {\n throw new Error('BambooHR no devolvió empleados para validar la corrección.');\n}\n\nconst candidates = employees.filter(countryMatches);\nif (!candidates.length) {\n throw new Error(`No se encontraron empleados de ${request.countryName} en BambooHR.`);\n}\n\nconst requestedNumber = normalizeIdentifier(request.bambooEmployeeNumber);\nlet matches = [];\nlet matchedBy = '';\n\nif (requestedNumber) {\n matches = candidates.filter((employee) =>\n normalizeIdentifier(employee.employeeNumber || employee.employee_number) === requestedNumber\n );\n matchedBy = 'employee_number';\n}\n\nif (!matches.length) {\n const targetName = normalizeName(request.bambooName);\n matches = candidates.filter((employee) =>\n employeeAliases(employee).some((alias) => normalizeName(alias) === targetName)\n );\n matchedBy = 'exact_bamboo_name';\n}\n\nif (matches.length !== 1) {\n if (matches.length > 1) {\n throw new Error('La identificación en BambooHR es ambigua. Indica el Employee Number para seleccionar una sola persona.');\n }\n\n throw new Error(\n 'No se encontró exactamente ese empleado en BambooHR para el país seleccionado. Revisa el nombre o indica el Employee Number.'\n );\n}\n\nconst employee = matches[0];\nconst aliases = employeeAliases(employee);\nconst actualName = clean(employee.displayName) || aliases[0] || request.bambooName;\nconst actualEmployeeNumber = clean(employee.employeeNumber || employee.employee_number);\nconst bambooId = clean(employee.employeeId || employee.id);\nconst now = new Date().toISOString();\n\nconst recipientsByCountry = {\n GT: ['haguirre@gomezleemarketing.com'],\n TT: ['lnicomejack@gomezleemarketing.com'],\n PA: ['dpenalba@gomezleemarketing.com'],\n SV: ['cmalta@gomezleemarketing.com'],\n HN: ['mchang@gomezleemarketing.com'],\n};\nconst recipients = recipientsByCountry[request.country] || [];\n\nconst aliasPayload = request.sourceNames.map((sourceName) => ({\n pais: request.country,\n nombre_origen: sourceName,\n nombre_origen_normalizado: normalizeName(sourceName),\n cuenta_bancaria: String(request.bankAccount || '').replace(/[^0-9A-Za-z]+/g, ''),\n bamboo_employee_number: actualEmployeeNumber || null,\n bamboo_employee_id: bambooId || null,\n bamboo_nombre: actualName,\n motivo: request.reason,\n comentario: request.comment || null,\n activo: true,\n validado_por_email: request.requestedByEmail,\n validado_por_nombre: request.requestedByName,\n last_reported_at: now,\n updated_at: now,\n}));\n\nreturn [{\n json: {\n request,\n matchedBy,\n employee: {\n bambooId,\n employeeNumber: actualEmployeeNumber,\n name: actualName,\n status: clean(employee.status),\n location: clean(employee.location),\n },\n recipients,\n aliasPayload,\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 1968, - 1984 - ], - "id": "780c274d-995c-496f-bccf-70eee565bb00", - "name": "Resolver empleado BambooHR" - }, - { - "parameters": { - "method": "POST", - "url": "https://dbit.digitalcompass.agency/rest/v1/cruce_cuentas_bamboo_aliases?on_conflict=pais,nombre_origen_normalizado,cuenta_bancaria", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "Prefer", - "value": "resolution=merge-duplicates,return=representation" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={{ $('Resolver empleado BambooHR').first().json.aliasPayload }}", - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 2208, - 1984 - ], - "id": "15016826-fa2c-4ca1-a103-a2dd00299c66", - "name": "Guardar alias Supabase", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "jsCode": "const resolved = $('Resolver empleado BambooHR').first().json || {};\nconst request = resolved.request || {};\nconst employee = resolved.employee || {};\nconst aliasResponseItems = $input.all().map((item) => item.json || {});\n\nfunction flattenRows(items) {\n const rows = [];\n for (const item of items) {\n if (Array.isArray(item)) rows.push(...item);\n else if (Array.isArray(item.data)) rows.push(...item.data);\n else rows.push(item);\n }\n return rows.filter((row) => row && typeof row === 'object');\n}\n\nconst aliasRows = flattenRows(aliasResponseItems);\nconst aliasIds = aliasRows\n .map((row) => row.id)\n .filter(Boolean);\n\nconst requestPayload = {\n pais: request.country,\n nombre_origen_principal: request.sourceName,\n nombres_origen: request.sourceNames,\n cuenta_bancaria: request.bankAccount || null,\n monto_banco: request.bankAmount,\n moneda: request.currency,\n bamboo_employee_number: employee.employeeNumber || null,\n bamboo_employee_id: employee.bambooId || null,\n bamboo_nombre: employee.name,\n motivo: request.reason,\n comentario: request.comment || null,\n execution_id: request.executionId || null,\n report_url: request.reportUrl || null,\n period_label: request.periodLabel || null,\n period_start: request.periodStart || null,\n period_end: request.periodEnd || null,\n solicitado_por_email: request.requestedByEmail,\n solicitado_por_nombre: request.requestedByName,\n destinatarios_rrhh: resolved.recipients || [],\n alias_ids: aliasIds,\n estado: 'pendiente_notificacion',\n correo_enviado: false,\n};\n\nreturn [{ json: { requestPayload, aliasIds } }];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 2448, - 1984 - ], - "id": "51f90afa-04d0-4d8d-80c2-dbc8278b6ad7", - "name": "Preparar auditoría" - }, - { - "parameters": { - "method": "POST", - "url": "https://dbit.digitalcompass.agency/rest/v1/cruce_cuentas_bamboo_correcciones", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "Prefer", - "value": "return=representation" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={{ $json.requestPayload }}", - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 2688, - 1984 - ], - "id": "e105a2df-aa71-4e0c-b895-87f2f6709d82", - "name": "Guardar solicitud Supabase", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "jsCode": "const resolved = $('Resolver empleado BambooHR').first().json || {};\nconst request = resolved.request || {};\nconst employee = resolved.employee || {};\nconst auditInput = $input.all().map((item) => item.json || {});\n\nfunction firstObject(value) {\n if (Array.isArray(value)) return value[0] || {};\n if (value?.data && Array.isArray(value.data)) return value.data[0] || {};\n return value || {};\n}\n\nconst auditRow = firstObject(auditInput[0]);\nconst requestId = String(auditRow.id || '');\nif (!requestId) {\n throw new Error('Supabase no devolvió el ID de la solicitud de corrección.');\n}\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nconst logoUrl = 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\nconst flagUrl = request.country === 'TT'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Trinidad_and_Tobago.svg.webp'\n : request.country === 'PA'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Panama.svg'\n : request.country === 'SV'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_El_Salvador.svg.webp'\n : request.country === 'HN'\n ? 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp'\n : 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Flag_of_Guatemala.svg.webp';\n\nconst to = (resolved.recipients || []).join(',');\nconst subject = `Corrección requerida en BambooHR - ${request.countryName} - ${request.sourceName}`;\n\nconst reportButton = request.reportUrl\n ? `Abrir reporte de Cruce de Cuentas`\n : '';\n\nconst htmlBody = `\n\n\n\n\n
\n\n\n
 
\n\n\n\n
\"GomezLee\"Bandera
\n
\n
Corrección de identidad · BambooHR
\n

Revisión solicitada para ${escapeHtml(request.countryName)}

\n

Durante la revisión manual de Banco sin BambooHR se confirmó que el pago corresponde a una persona existente en BambooHR, pero su nombre no está permitiendo identificarla correctamente en el cruce.

\n\n\n\n\n\n\n\n\n
Nombre detectado${escapeHtml(request.sourceName)}
Empleado en BambooHR${escapeHtml(employee.name)}
Employee Number${escapeHtml(employee.employeeNumber || 'No disponible')}
Motivo${escapeHtml(request.reason)}
Período${escapeHtml(request.periodLabel || 'No especificado')}
Reportado por${escapeHtml(request.requestedByName)} · ${escapeHtml(request.requestedByEmail)}
\n\n${request.comment ? `
Comentario: ${escapeHtml(request.comment)}
` : ''}\n\n

Por favor, revisen y corrijan el nombre o la información correspondiente en BambooHR. La equivalencia validada ya quedó registrada en el Cruce de Cuentas para evitar que este mismo falso positivo se repita mientras se realiza la corrección.

\n\n${reportButton ? `
${reportButton}
` : ''}\n\n
\n
Notificación automática · Portal Cruce de Cuentas GLM · Uso interno
\n
\n
\n`;\n\nreturn [{\n json: {\n requestId,\n to,\n subject,\n htmlBody,\n country: request.country,\n countryName: request.countryName,\n aliasCount: Array.isArray(resolved.aliasPayload) ? resolved.aliasPayload.length : 0,\n },\n}];" - }, - "type": "n8n-nodes-base.code", - "typeVersion": 2, - "position": [ - 2928, - 1984 - ], - "id": "8a6facdd-60ee-4713-bb2c-5121d8449fea", - "name": "Preparar correo RRHH" - }, - { - "parameters": { - "sendTo": "={{$json.to}}", - "subject": "={{$json.subject}}", - "message": "={{$json.htmlBody}}", - "options": { - "appendAttribution": false - } - }, - "type": "n8n-nodes-base.gmail", - "typeVersion": 2.2, - "position": [ - 3168, - 1984 - ], - "id": "8cbb778d-b36e-4ae9-b55e-79bf8dca9d28", - "name": "Enviar correo RRHH", - "retryOnFail": true, - "maxTries": 3, - "waitBetweenTries": 2000, - "webhookId": "f1d0bb21-fe2a-4742-9dcf-6d6f5eb60fa3", - "credentials": { - "gmailOAuth2": { - "id": "UDcO1FLJqA453V2D", - "name": "Gmail account 3" - } - } - }, - { - "parameters": { - "method": "PATCH", - "url": "={{ 'https://dbit.digitalcompass.agency/rest/v1/cruce_cuentas_bamboo_correcciones?id=eq.' + $('Preparar correo RRHH').first().json.requestId }}", - "sendHeaders": true, - "headerParameters": { - "parameters": [ - { - "name": "apikey", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" - }, - { - "name": "Content-Type", - "value": "application/json" - }, - { - "name": "Prefer", - "value": "return=minimal" - } - ] - }, - "sendBody": true, - "specifyBody": "json", - "jsonBody": "={{ { correo_enviado: true, correo_enviado_at: new Date().toISOString(), estado: 'notificado_rrhh' } }}", - "options": { - "timeout": 30000 - } - }, - "type": "n8n-nodes-base.httpRequest", - "typeVersion": 4.4, - "position": [ - 3408, - 1984 - ], - "id": "8dbc3f8e-0ec7-48b2-95a5-6c9905cb6580", - "name": "Marcar correo enviado", - "retryOnFail": true, - "maxTries": 2, - "waitBetweenTries": 1000 - }, - { - "parameters": { - "respondWith": "json", - "responseBody": "={{ { ok: true, message: 'Caso validado, equivalencia registrada y correo enviado a RRHH.', country: $('Preparar correo RRHH').first().json.country, countryName: $('Preparar correo RRHH').first().json.countryName, aliasCount: $('Preparar correo RRHH').first().json.aliasCount, requestId: $('Preparar correo RRHH').first().json.requestId } }}", - "options": { - "responseCode": 200, - "responseHeaders": { - "entries": [ - { - "name": "Content-Type", - "value": "application/json" - } - ] - } - } - }, - "type": "n8n-nodes-base.respondToWebhook", - "typeVersion": 1.5, - "position": [ - 3648, - 1984 - ], - "id": "ad4e2147-041e-43d6-b952-303d991635ff", - "name": "Respond to Webhook" - } - ], - "pinData": {}, - "connections": { - "Webhook": { - "main": [ - [ - { - "node": "Preparar solicitud", - "type": "main", - "index": 0 - } - ] - ] - }, - "Preparar solicitud": { - "main": [ - [ - { - "node": "Validar acceso Supabase", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validar acceso Supabase": { - "main": [ - [ - { - "node": "Validar usuario", - "type": "main", - "index": 0 - } - ] - ] - }, - "Validar usuario": { - "main": [ - [ - { - "node": "Consultar empleado BambooHR", - "type": "main", - "index": 0 - } - ] - ] - }, - "Consultar empleado BambooHR": { - "main": [ - [ - { - "node": "Resolver empleado BambooHR", - "type": "main", - "index": 0 - } - ] - ] - }, - "Resolver empleado BambooHR": { - "main": [ - [ - { - "node": "Guardar alias Supabase", - "type": "main", - "index": 0 - } - ] - ] - }, - "Guardar alias Supabase": { - "main": [ - [ - { - "node": "Preparar auditoría", - "type": "main", - "index": 0 - } - ] - ] - }, - "Preparar auditoría": { - "main": [ - [ - { - "node": "Guardar solicitud Supabase", - "type": "main", - "index": 0 - } - ] - ] - }, - "Guardar solicitud Supabase": { - "main": [ - [ - { - "node": "Preparar correo RRHH", - "type": "main", - "index": 0 - } - ] - ] - }, - "Preparar correo RRHH": { - "main": [ - [ - { - "node": "Enviar correo RRHH", - "type": "main", - "index": 0 - } - ] - ] - }, - "Enviar correo RRHH": { - "main": [ - [ - { - "node": "Marcar correo enviado", - "type": "main", - "index": 0 - } - ] - ] - }, - "Marcar correo enviado": { - "main": [ - [ - { - "node": "Respond to Webhook", - "type": "main", - "index": 0 - } - ] - ] - } - }, - "active": false, - "settings": { - "executionOrder": "v1", - "binaryMode": "separate" - }, - "versionId": "a6157be9-0bb2-4937-845f-1769af596051", - "meta": { - "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" - }, - "tags": [] -} \ No newline at end of file diff --git a/workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json b/workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json new file mode 100644 index 0000000..f32fed8 --- /dev/null +++ b/workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Env├¡o de Reporte.json @@ -0,0 +1,347 @@ +{ + "name": "Portal de Verificación de Nómina Puerto Rico - Envío de Reporte", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "cruce-cuentas-pr-marcar-resuelto", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + 784, + 592 + ], + "id": "67ea20ff-7e47-4605-be07-b9c1fea50d78", + "name": "Webhook Marcar Resuelto", + "webhookId": "9b76b71d-729d-4559-a6d8-4321986e4e5d" + }, + { + "parameters": { + "jsCode": "const body = $input.first().json.body || $input.first().json || {};\n\nfunction clean(value) {\n return String(value ?? '').replace(/\\s+/g, ' ').trim();\n}\n\nconst reportId = clean(body.reportId || body.id);\nconst comentarioResolucion = clean(body.comentarioResolucion || body.comentario || '');\nconst resueltoPorNombre = clean(body.resueltoPorNombre || body.userName || 'Usuario GLM');\nconst resueltoPorEmail = clean(body.resueltoPorEmail || body.userEmail || '');\n\nconst errors = [];\n\nif (!reportId) {\n errors.push('No se recibió el ID del reporte.');\n}\n\nif (!comentarioResolucion) {\n errors.push('Debe indicar un comentario de resolución.');\n}\n\nif (comentarioResolucion.length < 10) {\n errors.push('El comentario de resolución debe ser más descriptivo.');\n}\n\nif (errors.length > 0) {\n return [\n {\n json: {\n ok: false,\n stage: 'validacion_resolucion',\n errors,\n reportId,\n },\n },\n ];\n}\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'resolucion_validada',\n reportId,\n comentarioResolucion,\n resueltoPorNombre,\n resueltoPorEmail,\n resolvedAt: new Date().toISOString(),\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 992, + 592 + ], + "id": "57d081f4-b2ff-4272-b660-84b71692c0b8", + "name": "Validar resolución" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "c89884c6-de96-4b08-bd20-54644bb95bc7", + "leftValue": "={{ $json.ok }}", + "rightValue": "", + "operator": { + "type": "boolean", + "operation": "true", + "singleValue": true + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + 1200, + 592 + ], + "id": "0e91368c-172f-44bf-bf5f-0c1e4e4842e7", + "name": "¿Solicitud válida?" + }, + { + "parameters": { + "method": "PATCH", + "url": "={{ 'https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?id=eq.' + encodeURIComponent($json.reportId) + '&country=eq.PR' }}", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Prefer", + "value": "return=representation" + } + ] + }, + "sendBody": true, + "specifyBody": "json", + "jsonBody": "={{\n {\n estado: 'resuelto',\n resuelto_por_nombre: $json.resueltoPorNombre,\n resuelto_por_email: $json.resueltoPorEmail,\n resuelto_en: $json.resolvedAt,\n comentario_resolucion: $json.comentarioResolucion,\n updated_at: $json.resolvedAt\n }\n}}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1536, + 480 + ], + "id": "13114fe0-2945-432d-ac06-02b46682b9c0", + "name": "Actualizar reporte Supabase" + }, + { + "parameters": { + "jsCode": "const input = $input.first().json || {};\nconst report = Array.isArray(input) ? input[0] : input;\n\nif (!report || !report.id) {\n throw new Error(\n 'No se encontró un reporte de Puerto Rico con el ID recibido.'\n );\n}\n\nif (String(report.country || '').toUpperCase() !== 'PR') {\n throw new Error(\n 'El reporte localizado no pertenece a Puerto Rico.'\n );\n}\nconst original = $('Validar resolución').first().json || {};\n\n\nfunction escapeHtml(value) {\n return String(value ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction numberValue(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nconst periodo = report.period_label || 'Período no especificado';\nconst reportUrl = report.report_url || '';\n\nconst discrepancias = numberValue(report.discrepancias);\nconst bancoSinNomina = numberValue(report.banco_sin_nomina);\nconst bancoSinBamboo = numberValue(report.banco_sin_bamboo);\nconst nominaSinCuenta = numberValue(report.nomina_sin_cuenta);\n\nconst resolvedAt = original.resolvedAt || new Date().toISOString();\n\nconst resolvedDateLabel = new Date(resolvedAt).toLocaleString('es-PR', {\n timeZone: 'America/Puerto_Rico',\n dateStyle: 'long',\n timeStyle: 'short',\n});\n\nconst resolvedByName =\n original.resueltoPorNombre || 'Usuario GLM';\n\nconst resolvedByEmail =\n original.resueltoPorEmail || '';\n\nconst resolutionComment =\n original.comentarioResolucion || 'Sin comentario registrado.';\n\nconst logoUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';\n\nconst flagUrl =\n 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/bandera-nacional-tela-puerto-rico_113767-1759.avif';\n\nconst subject =\n `Reporte resuelto - Cruce de Cuentas Puerto Rico - ${periodo}`;\n\nconst textBody = `\nHola equipo,\n\nSe marcó como resuelto un reporte de Cruce de Cuentas Puerto Rico.\n\nPeríodo:\n${periodo}\n\nResuelto por:\n${resolvedByName}${resolvedByEmail ? ` (${resolvedByEmail})` : ''}\n\nFecha/hora:\n${resolvedDateLabel}\n\nResumen:\n- Discrepancias: ${discrepancias}\n- Banco sin nómina: ${bancoSinNomina}\n- Banco sin Bamboo: ${bancoSinBamboo}\n- Nómina sin cuenta: ${nominaSinCuenta}\n\nComentario:\n${resolutionComment}\n\nGoogle Sheet:\n${reportUrl || 'No disponible'}\n\nSaludos,\nPortal Cruce de Cuentas GLM\n`.trim();\n\nconst htmlBody = `\n\n\n\n \n \n ${escapeHtml(subject)}\n\n\n\n\n \n \n \n\n \n\n \n \n \n \n \n\n \n  \n \n\n \n \n
\n Cruce de Cuentas Puerto Rico\n \"Bandera\n
\n\n

\n Reporte marcado como resuelto\n

\n \n \n\n \n \n

\n Hola equipo,\n

\n\n

\n El siguiente reporte fue revisado y marcado como\n resuelto.\n

\n \n \n\n \n \n \n \n \n Período\n \n\n \n ${escapeHtml(periodo)}\n \n \n\n \n \n Resuelto por\n \n\n \n ${escapeHtml(resolvedByName)}\n ${\n resolvedByEmail\n ? `
${escapeHtml(resolvedByEmail)}`\n : ''\n }\n \n \n\n \n \n Fecha y hora\n \n\n \n ${escapeHtml(resolvedDateLabel)}\n \n \n \n \n \n\n \n \n
\n Resumen del reporte\n
\n \n \n\n \n \n \n \n \n
\n ${discrepancias}\n
\n\n
\n Discrepancias\n
\n \n\n \n
\n ${bancoSinBamboo}\n
\n\n
\n Banco sin Bamboo\n
\n \n\n \n
\n ${bancoSinNomina}\n
\n\n
\n Banco sin nómina\n
\n \n\n \n
\n ${nominaSinCuenta}\n
\n\n
\n Nómina sin cuenta\n
\n \n \n \n \n \n\n \n \n
\n Comentario de resolución\n
\n \n \n\n \n \n
\n ${escapeHtml(resolutionComment).replace(/\\n/g, '
')}\n
\n \n \n\n ${\n reportUrl\n ? `\n \n \n \n Abrir Google Sheet\n \n \n \n `\n : ''\n }\n\n \n \n Mensaje automático del Portal Cruce de Cuentas GLM.\n \n \n\n \n \n \n \n\n\n\n`.trim();\n\nreturn [\n {\n json: {\n to: [\n 'iaracena@gomezleemarketing.com',\n 'jgomez@gomezleemarketing.com',\n 'mgomez@gomezleemarketing.com',\n 'vparamo@gomezleemarketing.com',\n 'ymadera@gomezleemarketing.com',\n 'adminregional@gomezleemarketing.com',\n ].join(','),\n subject,\n textBody,\n htmlBody,\n report,\n resolution: original,\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1744, + 480 + ], + "id": "f756f935-f1ab-4c87-b46a-eb159ce95cef", + "name": "Preparar correo equipo PR" + }, + { + "parameters": { + "sendTo": "={{$json.to}}", + "subject": "={{$json.subject}}", + "message": "={{ $json.htmlBody }}", + "options": { + "appendAttribution": false, + "ccList": "={{ $json.cc }}" + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 1952, + 480 + ], + "id": "127fa51c-cc53-41c9-8c5f-6c0cd65e8ca9", + "name": "Enviar correo equipo PR", + "webhookId": "ad819366-b163-4e58-86de-a7cd158630b1", + "credentials": { + "gmailOAuth2": { + "id": "UDcO1FLJqA453V2D", + "name": "Gmail account 3" + } + } + }, + { + "parameters": { + "jsCode": "const original = $('Validar resolución').first().json || {};\nconst updatedResponse = $('Actualizar reporte Supabase').first().json || {};\nconst updatedReport = Array.isArray(updatedResponse) ? updatedResponse[0] : updatedResponse;\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'reporte_resuelto',\n message: 'Reporte marcado como resuelto correctamente.',\n report: {\n id: original.reportId,\n estado: 'resuelto',\n estadoLabel: 'RESUELTO',\n country: 'PR',\n countryName: 'Puerto Rico',\n resueltoPorNombre: original.resueltoPorNombre,\n resueltoPorEmail: original.resueltoPorEmail,\n resueltoEn: original.resolvedAt,\n comentarioResolucion: original.comentarioResolucion,\n reportUrl: updatedReport.report_url || '',\n },\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2160, + 480 + ], + "id": "7f19e2dd-b8a2-427c-83d3-345d18fbf267", + "name": "Preparar respuesta resuelto" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": { + "responseCode": 200, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "name": "Access-Control-Allow-Methods", + "value": "GET,POST,OPTIONS" + }, + { + "name": "Access-Control-Allow-Headers", + "value": "Content-Type, Authorization" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 2368, + 480 + ], + "id": "ebb6b338-b872-4167-acea-85b9e235cb38", + "name": "Responder resuelto app" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": { + "responseCode": 400, + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "name": "Access-Control-Allow-Methods", + "value": "GET,POST,OPTIONS" + }, + { + "name": "Access-Control-Allow-Headers", + "value": "Content-Type, Authorization" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 1408, + 688 + ], + "id": "cf478ef5-e94d-4fce-afe6-2a5a2b16c673", + "name": "Responder error resolución" + }, + { + "parameters": { + "content": "# ✅ CIERRE DE REPORTE — PUERTO RICO\n\nRecibe desde el Portal de Verificación de Nóminas la solicitud para marcar un reporte de Puerto Rico como resuelto.\n\n## Validación inicial\n\nEl Webhook recibe la información enviada por la aplicación y valida datos como:\n\n- Identificador del reporte.\n- Usuario que realizó el cierre.\n- Estado solicitado.\n- Comentario o detalle de resolución.\n- Datos necesarios para localizar el registro en Supabase.\n\nLa solicitud solamente continúa cuando contiene información válida y suficiente.\n\n## Actualización del reporte\n\nCuando la solicitud es correcta:\n\n1. Localiza el reporte correspondiente en Supabase.\n2. Cambia su estado a RESUELTO.\n3. Registra quién realizó la resolución.\n4. Guarda la fecha y hora del cierre.\n5. Conserva el comentario o información de seguimiento disponible.\n\nSupabase es la fuente oficial del estado del reporte dentro del portal.\n\n## Notificación al equipo de Puerto Rico\n\nDespués de actualizar el registro:\n\n1. Se prepara un correo para el equipo responsable de Puerto Rico.\n2. Se incluyen los datos principales del reporte.\n3. Se informa que el caso fue marcado como resuelto.\n4. Se identifica al usuario que realizó el cierre.\n5. Se envía el correo mediante Gmail.\n\nLa notificación permite que el equipo conozca el cambio sin tener que revisar manualmente el portal.\n\n## Respuesta a la aplicación\n\nCuando el proceso termina correctamente:\n\n- Se construye una respuesta de éxito.\n- Se confirma que el reporte fue actualizado.\n- Se devuelve el nuevo estado a la aplicación.\n- El portal puede actualizar inmediatamente su interfaz.\n\n## Ruta de error\n\nCuando la solicitud es inválida:\n\n- No se modifica Supabase.\n- No se envía el correo al equipo de Puerto Rico.\n- Se devuelve una respuesta de error a la aplicación.\n- Se informa que faltan datos o que la solicitud no pudo procesarse.\n\n## Reglas\n\n- No marcar un reporte como resuelto sin un identificador válido.\n- No enviar la notificación antes de confirmar la actualización en Supabase.\n- No declarar éxito cuando la base de datos no fue actualizada.\n- Toda respuesta del Webhook debe cerrarse correctamente para evitar solicitudes abiertas.\n- La ruta de error debe terminar sin realizar cambios parciales.", + "height": 1264, + "width": 2624, + "color": "#28765F" + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "e8c96878-0be9-4c70-bea5-e445119cad84", + "name": "Sticky Note" + } + ], + "pinData": {}, + "connections": { + "Webhook Marcar Resuelto": { + "main": [ + [ + { + "node": "Validar resolución", + "type": "main", + "index": 0 + } + ] + ] + }, + "Validar resolución": { + "main": [ + [ + { + "node": "¿Solicitud válida?", + "type": "main", + "index": 0 + } + ] + ] + }, + "¿Solicitud válida?": { + "main": [ + [ + { + "node": "Actualizar reporte Supabase", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Responder error resolución", + "type": "main", + "index": 0 + } + ] + ] + }, + "Actualizar reporte Supabase": { + "main": [ + [ + { + "node": "Preparar correo equipo PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar respuesta resuelto": { + "main": [ + [ + { + "node": "Responder resuelto app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Preparar correo equipo PR": { + "main": [ + [ + { + "node": "Enviar correo equipo PR", + "type": "main", + "index": 0 + } + ] + ] + }, + "Enviar correo equipo PR": { + "main": [ + [ + { + "node": "Preparar respuesta resuelto", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "b51451ef-379a-479a-88b4-cfcd3e183c20", + "meta": { + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "tags": [] +} diff --git a/workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json b/workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json new file mode 100644 index 0000000..c5b7fa1 --- /dev/null +++ b/workflows_n8n/Portal de Verificaci├│n de N├│mina Puerto Rico - Hist├│ricos.json @@ -0,0 +1,163 @@ +{ + "name": "Portal de Verificación de Nómina Puerto Rico - Históricos", + "nodes": [ + { + "parameters": { + "path": "cruce-cuentas-pr-historicos", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + 304, + 272 + ], + "id": "dff3b1cf-ebbe-4525-b367-0479d76a102d", + "name": "Webhook Históricos PR", + "webhookId": "24baab72-bc55-439d-96db-94780a3dffdf" + }, + { + "parameters": { + "jsCode": "const items = $input.all();\n\nfunction formatDate(value) {\n if (!value) return '';\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return String(value).slice(0, 10);\n return date.toISOString().slice(0, 10);\n}\n\nfunction toNumber(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction normalizeStatus(value, discrepancias) {\n const status = String(value || '').toLowerCase();\n\n if (status === 'resuelto') return 'resuelto';\n if (status === 'procesado' && toNumber(discrepancias) === 0) return 'procesado';\n\n return 'pendiente_revision';\n}\n\nfunction statusLabel(status) {\n if (status === 'resuelto') return 'RESUELTO';\n if (status === 'procesado') return 'PROCESADO';\n return 'PENDIENTE REVISIÓN';\n}\n\nconst reports = items.map((item) => {\n const row = item.json || {};\n\n const discrepancias = toNumber(row.discrepancias);\n const bancoSinNomina = toNumber(row.banco_sin_nomina);\n const nominaSinCuenta = toNumber(row.nomina_sin_cuenta);\n const bancoSinBamboo = toNumber(row.banco_sin_bamboo);\n const nominaSinBamboo = toNumber(row.nomina_sin_bamboo);\n const bambooSinNomina = toNumber(row.bamboo_sin_nomina);\n\n const totalPendientes =\n discrepancias +\n bancoSinNomina +\n nominaSinCuenta +\n bancoSinBamboo +\n nominaSinBamboo +\n bambooSinNomina;\n\n const estado = normalizeStatus(row.estado, totalPendientes);\n\n return {\n id: row.id,\n\n fecha: formatDate(row.period_end || row.ejecutado_en || row.created_at),\n fechaEjecucion: row.ejecutado_en || row.created_at,\n\n periodo: row.period_label || '',\n year: row.year,\n month: row.month,\n periodType: row.period_type,\n periodStart: row.period_start,\n periodEnd: row.period_end,\n\n archivoNomina: row.payroll_file_name || '',\n archivosBanco: row.bank_file_names || [],\n\n coincidencias: toNumber(row.coincidencias),\n discrepancias,\n bancoSinNomina,\n nominaSinCuenta,\n bancoSinBamboo,\n nominaSinBamboo,\n bambooSinNomina,\n totalPendientes,\n\n totalNomina: toNumber(row.total_nomina),\n totalBanco: toNumber(row.total_banco),\n diferenciaTotal: toNumber(row.diferencia_total),\n\n estado,\n estadoLabel: statusLabel(estado),\n\n reportUrl: row.report_url || '',\n spreadsheetId: row.spreadsheet_id || '',\n\n ejecutadoPorNombre: row.ejecutado_por_nombre || '',\n ejecutadoPorEmail: row.ejecutado_por_email || '',\n\n resueltoPorNombre: row.resuelto_por_nombre || '',\n resueltoPorEmail: row.resuelto_por_email || '',\n resueltoEn: row.resuelto_en || null,\n comentarioResolucion: row.comentario_resolucion || '',\n };\n});\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'historicos_pr_listos',\n count: reports.length,\n reports,\n },\n },\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 720, + 272 + ], + "id": "9c4a3deb-631f-409f-b3ac-a86c75514f14", + "name": "Normalizar históricos para app" + }, + { + "parameters": { + "url": "https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?select=*&country=eq.PR&order=ejecutado_en.desc&limit=100", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "apikey", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Authorization", + "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q" + }, + { + "name": "Content-Type", + "value": "application/json" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 512, + 272 + ], + "id": "70248bd5-e8ff-4ca5-9770-3a68ca418a12", + "name": "Consultar históricos Supabase" + }, + { + "parameters": { + "respondWith": "json", + "responseBody": "={{ $json }}", + "options": { + "responseHeaders": { + "entries": [ + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "name": "Access-Control-Allow-Methods", + "value": "GET,POST,OPTIONS" + }, + { + "name": "Access-Control-Allow-Headers", + "value": "Content-Type, Authorization" + } + ] + } + } + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 944, + 272 + ], + "id": "65b35ac9-9272-4abf-86e4-c9330322c8ab", + "name": "Responder históricos app" + }, + { + "parameters": { + "content": "# 📚 HISTÓRICOS DE VERIFICACIÓN — PUERTO RICO\n\nEntrega a la aplicación el historial de reportes procesados para Puerto Rico.\n\n- Consulta únicamente registros con `country = PR`.\n- Supabase continúa usando la tabla histórica compartida `cruces_cuentas_gt_reportes`; el nombre de la tabla es legado y funciona como tabla multi-país.\n- Devuelve la misma estructura de históricos utilizada por los demás países.\n- Este webhook sirve como respaldo; la app intenta primero la RPC paginada de Supabase.", + "height": 496, + "width": 1360, + "color": 6 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "8c0b0a43-8bfe-443b-8795-06d79512ca64", + "name": "Sticky Note" + } + ], + "pinData": {}, + "connections": { + "Consultar históricos Supabase": { + "main": [ + [ + { + "node": "Normalizar históricos para app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Normalizar históricos para app": { + "main": [ + [ + { + "node": "Responder históricos app", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook Históricos PR": { + "main": [ + [ + { + "node": "Consultar históricos Supabase", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "ffb6f922-6c37-498e-9dec-0011deddb49e", + "meta": { + "instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70" + }, + "tags": [] +}