diff --git a/glmway-workflow.json b/glmway-workflow.json index a3b9697..01eccfb 100644 --- a/glmway-workflow.json +++ b/glmway-workflow.json @@ -1,6 +1,6 @@ { "$json": { - "updatedAt": "2026-07-01T21:16:20.279Z", + "updatedAt": "2026-07-02T16:33:12.932Z", "createdAt": "2026-06-08T13:50:12.174Z", "id": "F1iPg8hktNAebZua", "name": "GLMWay", @@ -576,8 +576,8 @@ "type": "n8n-nodes-base.googleCalendarTrigger", "typeVersion": 1, "position": [ - -320, - 2848 + -256, + 3168 ], "id": "e0c4da60-bbbe-4cc1-b340-1749e204b6b2", "name": "Termino evento español", @@ -611,8 +611,8 @@ "type": "n8n-nodes-base.googleCalendarTrigger", "typeVersion": 1, "position": [ - -320, - 2672 + -256, + 2992 ], "id": "cd1a89d4-21e5-475e-93ed-4f00bbfd0190", "name": "Termino evento ingles", @@ -694,8 +694,8 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 1440, - 928 + -336, + 2144 ], "id": "7f7d71b2-6c53-49a2-92d8-e66b2d0bff54", "name": "Schedule Trigger" @@ -1037,8 +1037,7 @@ -144 ], "id": "09ce1f03-3dfb-4108-9d25-6c05ac622a6f", - "name": "Alerta no hay coordinador RRHH", - "disabled": true + "name": "Alerta no hay coordinador RRHH" }, { "parameters": { @@ -1109,10 +1108,10 @@ }, { "parameters": { - "sendTo": "=eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee').item.json.workEmail }}", "subject": "Entrenamiento #TheGLMWay", "emailType": "text", - "message": "=Hola, GLMer!\n\nEste {{ $json.fechaFormateada }} a las {{ $('Obtener sesiones español').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español').item.json.enlace_reunion }}", + "message": "=Hola, GLMer!\n\nEste {{ $json.hora_convertida }} a las {{ $('Obtener sesiones español').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -1120,7 +1119,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2480, + 2672, -400 ], "id": "e6c3acf5-d54b-4e00-b7a9-74038d27e387", @@ -1131,15 +1130,14 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { - "sendTo": "=eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee').item.json.workEmail }}", "subject": "#TheGLMWay Training", "emailType": "text", - "message": "=Hello, GLMer!\n\nThis {{ $json.fechaFormateada }} at {{ $('Obtener sesiones ingles').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles').item.json.enlace_reunion }}", + "message": "=Hello, GLMer!\n\nThis {{ $json.hora_convertida }} at {{ $('Obtener sesiones ingles').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -1147,7 +1145,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2480, + 2672, -224 ], "id": "f173c8d1-9311-405c-a7ee-164726df32f1", @@ -1158,8 +1156,7 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { @@ -1202,8 +1199,8 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - -384, - 2464 + -320, + 2784 ], "typeVersion": 1, "id": "6d5c0fe5-5ab1-4cdc-bfd2-197dc2144274", @@ -1276,8 +1273,8 @@ "type": "n8n-nodes-base.googleCalendarTrigger", "typeVersion": 1, "position": [ - -656, - 2800 + -592, + 3120 ], "id": "35ec709a-30fd-4e3b-8492-cf6d8928e372", "name": "Prueba", @@ -1472,7 +1469,7 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 2816, + 3008, 352 ], "id": "30b3a4dd-6d50-405d-9c77-27febcc50aa4", @@ -1517,19 +1514,18 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ - 2992, + 3184, 432 ], "id": "67928478-7604-45a4-9ab0-851079e1470c", - "name": "Alerta no hay coordinador RRHH1", - "disabled": true + "name": "Alerta no hay coordinador RRHH1" }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee1').item.json.workEmail }}", "subject": "Entrenamiento #TheGLMWay", "emailType": "text", - "message": "=Hola, GLMer!\n\nEste {{ $json.fechaFormateada }} a las {{ $('Obtener sesiones español1').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español1').item.json.enlace_reunion }}", + "message": "=Hola, GLMer!\n\nEste {{ $json.hora_convertida }} a las {{ $('Obtener sesiones español1').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español1').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -1537,7 +1533,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2464, + 2656, 272 ], "id": "32302b24-aa4c-4f9a-afc8-ef7e86cad56b", @@ -1548,15 +1544,14 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee1').item.json.workEmail }}", "subject": "#TheGLMWay Training", "emailType": "text", - "message": "=Hello, GLMer!\n\nThis {{ $json.fechaFormateada }} at {{ $('Obtener sesiones ingles1').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles1').item.json.enlace_reunion }}", + "message": "=Hello, GLMer!\n\nThis {{ $json.hora_convertida }} at {{ $('Obtener sesiones ingles1').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles1').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -1564,7 +1559,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2464, + 2656, 448 ], "id": "db2cb6a3-1da8-472a-95eb-1a02de2a5fa4", @@ -1575,8 +1570,7 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { @@ -1730,7 +1724,7 @@ "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ - 3168, + 3360, 352 ], "id": "98185de1-e81a-440f-8951-f98bd78c707b", @@ -1739,10 +1733,10 @@ }, { "parameters": { - "content": "## Enviar listado de inscritos al facilitador antes de iniciar el evento", - "height": 544, + "content": "## Notificacion a facilitador\n\n- Manda un listado de las personas inscritas en la sesion del miercoles al facilitador.\n- Si no hay personas inscritas envia un alerta al facilitador.", + "height": 768, "width": 1392, - "color": 6 + "color": 3 }, "type": "n8n-nodes-base.stickyNote", "position": [ @@ -1755,7 +1749,7 @@ }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Loop Over Items1').item.json.email }}", "subject": "Entrenamiento #TheGLMWay", "emailType": "text", "message": "=¡Hola, GLMer!\n\nEste es un recordatorio de tu entrenamiento, donde te transmitiremos nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\n¡Te esperamos puntual y con toda la energía para aprender!\n\nÚnete aquí:\n{{ $json.conferenceData.entryPoints[0].uri }}", @@ -1767,7 +1761,7 @@ "typeVersion": 2.2, "position": [ 800, - 928 + 736 ], "id": "10b2cb4e-274d-4913-9345-2c024c071f4c", "name": "Send a message espanol2", @@ -1782,7 +1776,7 @@ }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Loop Over Items1').item.json.email }}", "subject": "#TheGLMWay Training", "emailType": "text", "message": "=Hello, GLMer!\n\nThis is a reminder about your training session, where we'll share our culture, our DNA, and what makes us different from the rest.\n\nWe're looking forward to seeing you on time and ready to learn!\n\nJoin here:\n{{ $json.conferenceData.entryPoints[0].uri }}", @@ -1794,7 +1788,7 @@ "typeVersion": 2.2, "position": [ 800, - 752 + 944 ], "id": "1dc76989-8c58-4ffe-9e1c-5113a59d346c", "name": "Send a message ingles2", @@ -1812,7 +1806,11 @@ "rule": { "interval": [ { - "field": "hours" + "field": "weeks", + "triggerAtDay": [ + 2 + ], + "triggerAtHour": 17 } ] } @@ -1925,7 +1923,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 2656, + 2848, 352 ], "id": "3a6f198b-e136-49fb-a171-78164e10b600", @@ -1977,7 +1975,7 @@ "conditions": [ { "id": "9674fd7e-dd62-42d4-8931-6c67e59e3dd7", - "leftValue": "={{ !['Jamaica', 'Trinidad and Tobago', 'Barbados', 'Saint Thomas', 'St. Croix', 'Tobago', 'Guyana', 'Aruba'].includes($('Obtener notificados').item.json.location) }}", + "leftValue": "={{ !['Jamaica', 'Trinidad and Tobago', 'Barbados', 'Saint Thomas', 'St. Croix', 'Tobago', 'Guyana', 'Aruba'].includes($('Obtener notificados').item.json.pais) }}", "rightValue": "Jamaica", "operator": { "type": "boolean", @@ -3168,7 +3166,7 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 2704, + 2976, -352 ], "id": "ca136518-b776-45ba-b1a2-ad5327dd4cca", @@ -3219,7 +3217,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 3600, + 3872, -352 ], "id": "7c321f7d-bce9-447c-b94c-35f6967a48e3", @@ -3237,7 +3235,7 @@ "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ - 3424, + 3696, -352 ], "id": "27531838-5dc7-4169-a08a-f4da93899580", @@ -3267,7 +3265,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 3248, + 3520, -352 ], "id": "bd8cb535-6aa3-46df-9ca0-6efc0555f2e4", @@ -3288,7 +3286,7 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 2672, + 2944, -480 ], "typeVersion": 1, @@ -3329,8 +3327,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 576, - 2816 + 640, + 3136 ], "id": "3c22bc65-799f-4432-86fe-10ae92e036de", "name": "Send a message espanol4", @@ -3356,8 +3354,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 576, - 2640 + 640, + 2960 ], "id": "4dfc0135-6886-4d33-9b2a-a26efbe72832", "name": "Send a message ingles4", @@ -3409,8 +3407,8 @@ "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ - 832, - 2736 + 896, + 3056 ], "id": "b7c02e2f-1382-4086-9059-cf75fee9d278", "name": "Actualizar estado a recordado2", @@ -3431,8 +3429,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 448, - 3088 + 512, + 3408 ], "id": "ef89ad57-bdc8-4d9d-a250-41706ec900c1", "name": "HTTP Request", @@ -3455,8 +3453,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ - 272, - 3248 + 336, + 3568 ], "credentials": { "googleOAuth2Api": { @@ -3491,8 +3489,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ - 96, - 2656 + 160, + 2976 ], "credentials": { "googleOAuth2Api": { @@ -3513,8 +3511,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ - 256, - 2976 + 320, + 3296 ], "credentials": { "googleOAuth2Api": { @@ -3542,8 +3540,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - -144, - 2848 + -80, + 3168 ], "id": "955614d8-60a7-4762-8c02-86ac33e54cb3", "name": "HTTP Request2", @@ -3568,8 +3566,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 64, - 2848 + 128, + 3168 ], "id": "a84173c0-db9f-4251-af6b-6e475541fc52", "name": "HTTP Request3", @@ -3580,22 +3578,6 @@ } } }, - { - "parameters": { - "content": "## Aviso a instructor si no hay nadie inscrito a la sesion del dia\n", - "height": 464, - "width": 1392, - "color": 6 - }, - "type": "n8n-nodes-base.stickyNote", - "position": [ - 1392, - 736 - ], - "typeVersion": 1, - "id": "de09868e-4a06-4c64-9e66-aae9e14b9896", - "name": "Sticky Note11" - }, { "parameters": { "operation": "getAll", @@ -3616,7 +3598,7 @@ "type": "n8n-nodes-base.googleCalendar", "typeVersion": 1.3, "position": [ - 2896, + 3168, -352 ], "id": "b2b03ce6-6010-4e4f-be7a-d9040286e295", @@ -3635,7 +3617,7 @@ "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 3072, + 3344, -352 ], "id": "73c39af9-bc01-4d97-81ee-846aff70b3a6", @@ -3654,7 +3636,7 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 2704, + 2976, -48 ], "id": "55a755d8-8b74-440f-93c6-8a1ecba4c666", @@ -3668,7 +3650,7 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 2672, + 2944, -160 ], "typeVersion": 1, @@ -3695,7 +3677,7 @@ "type": "n8n-nodes-base.googleCalendar", "typeVersion": 1.3, "position": [ - 4144, + 4320, -208 ], "id": "3c32f96e-cd34-4be4-8c24-b4be7a8f29d9", @@ -3728,7 +3710,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 2880, + 3152, -48 ], "id": "73089147-ba1f-4fb4-ac17-d5670b263fd8", @@ -3766,7 +3748,7 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 3216, + 3488, -48 ], "id": "231f396b-855a-4c50-8d6d-096018e6d2ac", @@ -3781,7 +3763,7 @@ "type": "n8n-nodes-base.aggregate", "typeVersion": 1, "position": [ - 3040, + 3312, -48 ], "id": "e51bd2d5-22b5-46cf-b346-c1ba16bb5c34", @@ -3812,7 +3794,7 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 4944, + 5120, -304 ], "id": "3f4a0807-5f29-49cb-a851-41100e04f5bf", @@ -3827,7 +3809,7 @@ "type": "n8n-nodes-base.aggregate", "typeVersion": 1, "position": [ - 4800, + 4976, -304 ], "id": "3ecbb0c7-f5f5-4644-9f42-8be9502e814d", @@ -3842,7 +3824,7 @@ "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ - 4656, + 4832, -304 ], "id": "a099259d-36e3-48b6-aa77-d8132f33f85e", @@ -3865,7 +3847,7 @@ "type": "n8n-nodes-base.merge", "typeVersion": 3.2, "position": [ - 4512, + 4688, -304 ], "id": "456bcbe4-cc98-48fc-bcd2-40f8961934ae", @@ -3879,7 +3861,7 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 3936, + 4112, -480 ], "typeVersion": 1, @@ -3903,7 +3885,7 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 3984, + 4160, -288 ], "id": "6ef829bf-1d52-4093-bcc5-45d922393611", @@ -3930,7 +3912,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 4144, + 4320, -384 ], "id": "f1bf8c51-43cf-41d4-93c4-1a1d0a0dac7a", @@ -3955,8 +3937,8 @@ "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ - 1600, - 928 + -176, + 2144 ], "id": "d34e225b-25bb-4a0a-845c-b9e274529000", "name": "Get many rows", @@ -3974,7 +3956,7 @@ "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 4320, + 4496, -208 ], "id": "8f80d9ca-1e4b-4753-b482-df7b0fd24f61", @@ -4013,8 +3995,8 @@ "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 1776, - 928 + 0, + 2144 ], "id": "0db9e6a4-2e5e-4513-97fd-71d22aa90822", "name": "Calcular sesion mas cercana4" @@ -4032,8 +4014,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2640, - 1008 + 624, + 2432 ], "id": "0cf2219c-db12-4231-bac6-8e5f2816530f", "name": "Send a message espanol5", @@ -4059,8 +4041,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2640, - 832 + 624, + 2256 ], "id": "96bac1b9-d921-418d-947d-f07980bb76aa", "name": "Send a message ingles5", @@ -4101,8 +4083,8 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 2416, - 928 + 416, + 2352 ], "id": "748f3ae7-7e6d-41f5-be1d-861a9566ec00", "name": "Es espanol o ingles" @@ -4113,8 +4095,8 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 2784, - 896 + 912, + 1904 ], "typeVersion": 1, "id": "2d9a27b5-9ca6-4518-bd06-3a91389c4736", @@ -4161,8 +4143,8 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 992, - 2656 + 1056, + 2976 ], "typeVersion": 1, "id": "a9266141-49ab-41f7-b8ab-dc45e746ed1c", @@ -4214,8 +4196,8 @@ "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ - 3344, - 176 + 3744, + 272 ], "id": "df865311-bf25-4b35-ac8a-da71a3d93e05", "name": "Añadir a listado de participantes1", @@ -4413,8 +4395,8 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 1968, - 928 + 192, + 2144 ], "id": "d1472787-4c2f-4e1d-8673-5c2368d469ab", "name": "Hay inscritos?" @@ -4545,7 +4527,7 @@ }, { "parameters": { - "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana1').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" + "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -4571,7 +4553,7 @@ }, { "parameters": { - "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana1').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" + "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana2').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -4584,7 +4566,7 @@ }, { "parameters": { - "jsCode": "function getOrdinalSuffix(dia) {\n const d = parseInt(dia, 10);\n if (d > 3 && d < 21) return `${d}th`; // 11th-13th excepción cubierta aquí\n switch (d % 10) {\n case 1: return `${d}st`;\n case 2: return `${d}nd`;\n case 3: return `${d}rd`;\n default: return `${d}th`;\n }\n}\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana1').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mes = partes[1];\n const diaConSufijo = getOrdinalSuffix(dia);\n\n item.json.fechaFormateada = `${mes} ${diaConSufijo}`;\n}\n\nreturn $input.all();" + "jsCode": "function getOrdinalSuffix(dia) {\n const d = parseInt(dia, 10);\n if (d > 3 && d < 21) return `${d}th`; // 11th-13th excepción cubierta aquí\n switch (d % 10) {\n case 1: return `${d}st`;\n case 2: return `${d}nd`;\n case 3: return `${d}rd`;\n default: return `${d}th`;\n }\n}\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana3').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mes = partes[1];\n const diaConSufijo = getOrdinalSuffix(dia);\n\n item.json.fechaFormateada = `${mes} ${diaConSufijo}`;\n}\n\nreturn $input.all();" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -4594,6 +4576,146 @@ ], "id": "4abc2638-3ae3-4aba-b45f-0b60e73e3267", "name": "Convertir fecha a ingles1" + }, + { + "parameters": { + "sendTo": "eidan.then14@gmail.com", + "subject": "Entrenamiento #TheGLMWay", + "emailType": "text", + "message": "=
\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

Sesión de entrenamiento

\n

{{ $json.session.titulo }}

\n
\n

📅 {{ $json.session.fecha_sesion }}  |  🕒 {{ $json.session.hora_inicio }} - {{ $json.session.hora_fin }}

\n

🎤 Instructor: {{ $json.session.instructor }}

\n

🔗 Unirse a la reunión

\n
\n

Hola,

\n

Aquí tienes el listado de participantes para la sesión de hoy:

\n
\n {{ (function(){\n const data = $json.session.participantes || {};\n const colores = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#db2777'];\n let i = 0;\n let html = '';\n\n for (const pais in data) {\n const nombrePais = pais && pais.trim() !== '' ? pais : 'Sin país asignado';\n const color = colores[i % colores.length];\n i++;\n const lista = data[pais];\n\n html += `\n \n \n \n `;\n\n lista.forEach((p, idx) => {\n const bg = idx % 2 === 0 ? '#ffffff' : '#fafafa';\n html += `\n \n \n `;\n });\n\n html += `
\n ${nombrePais}\n (${lista.length} participante${lista.length !== 1 ? 's' : ''})\n
\n

${p.nombre}

\n

${p.correo}${p.division ? '  •  ' + p.division : ''}${p.departamento ? '  •  ' + p.departamento : ''}

\n
`;\n }\n\n return html || '

No hay participantes registrados.

';\n })() }}\n
\n

Saludos,
#TheGLMWay

\n
\n
", + "options": { + "appendAttribution": false + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 624, + 2032 + ], + "id": "3167fd45-4eae-4db4-92fb-9c80223cb4fa", + "name": "Send a message espanol6", + "webhookId": "a6043707-89d1-4c99-8de2-e60d9aaf7ed2", + "credentials": { + "gmailOAuth2": { + "id": "QWNicjw1khRm5BVI", + "name": "Eidan - Gmail account" + } + }, + "disabled": true + }, + { + "parameters": { + "sendTo": "eidan.then14@gmail.com", + "subject": "#TheGLMWay Training", + "emailType": "text", + "message": "=Hello,\n\nThis is to inform you that, as of now, there are no people enrolled for today's activity.\n\nBest regards.\n\nThis is an automated message generated by the enrollment management system.", + "options": { + "appendAttribution": false + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 624, + 1856 + ], + "id": "0cd1d4fb-062f-42ca-bf9c-1424e17d50f0", + "name": "Send a message ingles6", + "webhookId": "a6043707-89d1-4c99-8de2-e60d9aaf7ed2", + "credentials": { + "gmailOAuth2": { + "id": "QWNicjw1khRm5BVI", + "name": "Eidan - Gmail account" + } + }, + "disabled": true + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "df25aa3e-d48f-4e52-ae1f-47678e954d52", + "leftValue": "={{ $json.session.idioma }}", + "rightValue": "EN", + "operator": { + "type": "string", + "operation": "equals", + "name": "filter.operator.equals" + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + 416, + 1936 + ], + "id": "dd5cce8e-ef5a-4bd4-967c-aec464f30fc4", + "name": "Es espanol o ingles1" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2480, + -400 + ], + "id": "740373c9-0145-41f4-a4b6-9dbb84ffbdaa", + "name": "Cambiar zona horaria" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana1')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2480, + -224 + ], + "id": "77da2dc7-546b-4ab7-a7c7-fa621196b2d0", + "name": "Cambiar zona horaria1" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana2')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2464, + 272 + ], + "id": "bbd8c7de-64eb-4262-a77b-b7307ab9c0c6", + "name": "Cambiar zona horaria2" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana3')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2464, + 448 + ], + "id": "677acc91-b856-482e-aa2c-9b329221918a", + "name": "Cambiar zona horaria3" } ], "connections": { @@ -5018,14 +5140,14 @@ "main": [ [ { - "node": "Send a message ingles2", + "node": "Send a message espanol2", "type": "main", "index": 0 } ], [ { - "node": "Send a message espanol2", + "node": "Send a message ingles2", "type": "main", "index": 0 } @@ -5830,7 +5952,13 @@ }, "Hay inscritos?": { "main": [ - [], + [ + { + "node": "Es espanol o ingles1", + "type": "main", + "index": 0 + } + ], [ { "node": "Es espanol o ingles", @@ -5888,7 +6016,7 @@ "main": [ [ { - "node": "Send a message espanol", + "node": "Cambiar zona horaria", "type": "main", "index": 0 } @@ -5899,7 +6027,7 @@ "main": [ [ { - "node": "Send a message ingles", + "node": "Cambiar zona horaria1", "type": "main", "index": 0 } @@ -5910,7 +6038,7 @@ "main": [ [ { - "node": "Send a message espanol1", + "node": "Cambiar zona horaria2", "type": "main", "index": 0 } @@ -5918,6 +6046,68 @@ ] }, "Convertir fecha a ingles1": { + "main": [ + [ + { + "node": "Cambiar zona horaria3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Es espanol o ingles1": { + "main": [ + [ + { + "node": "Send a message ingles6", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Send a message espanol6", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria": { + "main": [ + [ + { + "node": "Send a message espanol", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria1": { + "main": [ + [ + { + "node": "Send a message ingles", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria2": { + "main": [ + [ + { + "node": "Send a message espanol1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria3": { "main": [ [ { @@ -5951,19 +6141,19 @@ "recurrenceRules": [] }, "node:Comenzo evento ingles": { - "lastTimeChecked": "2026-07-01T21:16:27Z" + "lastTimeChecked": "2026-07-02T16:33:14Z" }, "node:Comenzo evento español": { - "lastTimeChecked": "2026-07-01T21:16:28Z" + "lastTimeChecked": "2026-07-02T16:33:14Z" }, "node:Termino evento español": { - "lastTimeChecked": "2026-07-01T21:16:28Z" + "lastTimeChecked": "2026-07-02T16:33:15Z" }, "node:Termino evento ingles": { - "lastTimeChecked": "2026-07-01T21:16:28Z" + "lastTimeChecked": "2026-07-02T16:33:15Z" }, "node:Prueba": { - "lastTimeChecked": "2026-07-01T21:16:28Z" + "lastTimeChecked": "2026-07-02T16:33:15Z" }, "node:Schedule Trigger2": { "recurrenceRules": [] @@ -6244,14 +6434,14 @@ "Schedule Trigger1": [ { "json": { - "timestamp": "2026-07-01T14:00:24.006-04:00", - "Readable date": "July 1st 2026, 2:00:24 pm", - "Readable time": "2:00:24 pm", + "timestamp": "2026-07-01T18:00:24.010-04:00", + "Readable date": "July 1st 2026, 6:00:24 pm", + "Readable time": "6:00:24 pm", "Day of week": "Wednesday", "Year": "2026", "Month": "July", "Day of month": "01", - "Hour": "14", + "Hour": "18", "Minute": "00", "Second": "24", "Timezone": "America/Santo_Domingo (UTC-04:00)" @@ -6270,20 +6460,20 @@ "content-length": "120", "accept": "*/*", "content-type": "application/json", - "traceparent": "00-6a451caf00000000fd3abfaf6b2acb63-c45af2dcb6cc8c70-01", + "traceparent": "00-6a466325000000008538f08cea35b99d-cc5e1f3f68342673-01", "tracestate": "dd=t.dm:-0", - "x-bamboohr-signature": "834446230a1b8076a2279412cb7cacdc655f9301bffdb380aee842ff295bc168", - "x-bamboohr-timestamp": "1782914223", - "x-datadog-parent-id": "14148888209111420016", + "x-bamboohr-signature": "84400ec7bf11cb911e8f74b304fe65e9637d84e74f52ba9a3475705a736d971a", + "x-bamboohr-timestamp": "1782997797", + "x-datadog-parent-id": "14726242188739749491", "x-datadog-sampling-priority": "1", - "x-datadog-tags": "_dd.p.tid=6a451caf00000000,_dd.p.dm=-0", - "x-datadog-trace-id": "18247107600383134563", - "x-forwarded-for": "18.119.117.53", + "x-datadog-tags": "_dd.p.tid=6a46632500000000,_dd.p.dm=-0", + "x-datadog-trace-id": "9599687093755689373", + "x-forwarded-for": "3.133.139.186", "x-forwarded-host": "agenteit.digitalcompass.agency", "x-forwarded-port": "443", "x-forwarded-proto": "https", "x-forwarded-server": "07b4f09d2c65", - "x-real-ip": "18.119.117.53", + "x-real-ip": "3.133.139.186", "accept-encoding": "gzip" }, "params": {}, @@ -6293,9 +6483,9 @@ { "changedFields": [], "fields": {}, - "id": "50816", + "id": "50821", "action": "Created", - "timestamp": "2026-07-01T07:56:59-06:00" + "timestamp": "2026-07-02T07:09:53-06:00" } ] }, @@ -6308,9 +6498,9 @@ } ] }, - "versionId": "4bef6f10-7d40-43dd-bfa7-824b04d847d7", - "activeVersionId": "4bef6f10-7d40-43dd-bfa7-824b04d847d7", - "versionCounter": 3605, + "versionId": "b081672b-1753-46b8-8436-25841f941414", + "activeVersionId": "b081672b-1753-46b8-8436-25841f941414", + "versionCounter": 3895, "triggerCount": 13, "shared": [ { @@ -6333,9 +6523,9 @@ ], "tags": [], "activeVersion": { - "updatedAt": "2026-07-01T21:16:28.000Z", - "createdAt": "2026-07-01T21:16:20.284Z", - "versionId": "4bef6f10-7d40-43dd-bfa7-824b04d847d7", + "updatedAt": "2026-07-02T16:33:15.000Z", + "createdAt": "2026-07-02T16:33:12.947Z", + "versionId": "b081672b-1753-46b8-8436-25841f941414", "workflowId": "F1iPg8hktNAebZua", "nodes": [ { @@ -6906,8 +7096,8 @@ "type": "n8n-nodes-base.googleCalendarTrigger", "typeVersion": 1, "position": [ - -320, - 2848 + -256, + 3168 ], "id": "e0c4da60-bbbe-4cc1-b340-1749e204b6b2", "name": "Termino evento español", @@ -6941,8 +7131,8 @@ "type": "n8n-nodes-base.googleCalendarTrigger", "typeVersion": 1, "position": [ - -320, - 2672 + -256, + 2992 ], "id": "cd1a89d4-21e5-475e-93ed-4f00bbfd0190", "name": "Termino evento ingles", @@ -7024,8 +7214,8 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 1440, - 928 + -336, + 2144 ], "id": "7f7d71b2-6c53-49a2-92d8-e66b2d0bff54", "name": "Schedule Trigger" @@ -7367,8 +7557,7 @@ -144 ], "id": "09ce1f03-3dfb-4108-9d25-6c05ac622a6f", - "name": "Alerta no hay coordinador RRHH", - "disabled": true + "name": "Alerta no hay coordinador RRHH" }, { "parameters": { @@ -7439,10 +7628,10 @@ }, { "parameters": { - "sendTo": "=eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee').item.json.workEmail }}", "subject": "Entrenamiento #TheGLMWay", "emailType": "text", - "message": "=Hola, GLMer!\n\nEste {{ $json.fechaFormateada }} a las {{ $('Obtener sesiones español').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español').item.json.enlace_reunion }}", + "message": "=Hola, GLMer!\n\nEste {{ $json.hora_convertida }} a las {{ $('Obtener sesiones español').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -7450,7 +7639,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2480, + 2672, -400 ], "id": "e6c3acf5-d54b-4e00-b7a9-74038d27e387", @@ -7461,15 +7650,14 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { - "sendTo": "=eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee').item.json.workEmail }}", "subject": "#TheGLMWay Training", "emailType": "text", - "message": "=Hello, GLMer!\n\nThis {{ $json.fechaFormateada }} at {{ $('Obtener sesiones ingles').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles').item.json.enlace_reunion }}", + "message": "=Hello, GLMer!\n\nThis {{ $json.hora_convertida }} at {{ $('Obtener sesiones ingles').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -7477,7 +7665,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2480, + 2672, -224 ], "id": "f173c8d1-9311-405c-a7ee-164726df32f1", @@ -7488,8 +7676,7 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { @@ -7532,8 +7719,8 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - -384, - 2464 + -320, + 2784 ], "typeVersion": 1, "id": "6d5c0fe5-5ab1-4cdc-bfd2-197dc2144274", @@ -7606,8 +7793,8 @@ "type": "n8n-nodes-base.googleCalendarTrigger", "typeVersion": 1, "position": [ - -656, - 2800 + -592, + 3120 ], "id": "35ec709a-30fd-4e3b-8492-cf6d8928e372", "name": "Prueba", @@ -7802,7 +7989,7 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 2816, + 3008, 352 ], "id": "30b3a4dd-6d50-405d-9c77-27febcc50aa4", @@ -7847,19 +8034,18 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ - 2992, + 3184, 432 ], "id": "67928478-7604-45a4-9ab0-851079e1470c", - "name": "Alerta no hay coordinador RRHH1", - "disabled": true + "name": "Alerta no hay coordinador RRHH1" }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee1').item.json.workEmail }}", "subject": "Entrenamiento #TheGLMWay", "emailType": "text", - "message": "=Hola, GLMer!\n\nEste {{ $json.fechaFormateada }} a las {{ $('Obtener sesiones español1').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español1').item.json.enlace_reunion }}", + "message": "=Hola, GLMer!\n\nEste {{ $json.hora_convertida }} a las {{ $('Obtener sesiones español1').item.json.hora_inicio }} tenemos entrenamiento, y queremos transmitirte nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\nTe esperamos puntual y con todas las ganas de aprender.\n\nNos vemos en el enlace!\n\n{{ $('Obtener sesiones español1').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -7867,7 +8053,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2464, + 2656, 272 ], "id": "32302b24-aa4c-4f9a-afc8-ef7e86cad56b", @@ -7878,15 +8064,14 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Get an employee1').item.json.workEmail }}", "subject": "#TheGLMWay Training", "emailType": "text", - "message": "=Hello, GLMer!\n\nThis {{ $json.fechaFormateada }} at {{ $('Obtener sesiones ingles1').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles1').item.json.enlace_reunion }}", + "message": "=Hello, GLMer!\n\nThis {{ $json.hora_convertida }} at {{ $('Obtener sesiones ingles1').item.json.hora_inicio }} we have training, and we want to share our culture, our DNA, and what makes us different from the rest.\n\nWe expect you to be punctual and eager to learn.\n\nSee you at the link!\n\n{{ $('Obtener sesiones ingles1').item.json.enlace_reunion }}", "options": { "appendAttribution": false } @@ -7894,7 +8079,7 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2464, + 2656, 448 ], "id": "db2cb6a3-1da8-472a-95eb-1a02de2a5fa4", @@ -7905,8 +8090,7 @@ "id": "QWNicjw1khRm5BVI", "name": "Eidan - Gmail account" } - }, - "disabled": true + } }, { "parameters": { @@ -8060,7 +8244,7 @@ "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ - 3168, + 3360, 352 ], "id": "98185de1-e81a-440f-8951-f98bd78c707b", @@ -8069,10 +8253,10 @@ }, { "parameters": { - "content": "## Enviar listado de inscritos al facilitador antes de iniciar el evento", - "height": 544, + "content": "## Notificacion a facilitador\n\n- Manda un listado de las personas inscritas en la sesion del miercoles al facilitador.\n- Si no hay personas inscritas envia un alerta al facilitador.", + "height": 768, "width": 1392, - "color": 6 + "color": 3 }, "type": "n8n-nodes-base.stickyNote", "position": [ @@ -8085,7 +8269,7 @@ }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Loop Over Items1').item.json.email }}", "subject": "Entrenamiento #TheGLMWay", "emailType": "text", "message": "=¡Hola, GLMer!\n\nEste es un recordatorio de tu entrenamiento, donde te transmitiremos nuestra cultura, nuestro ADN y por qué somos diferentes al resto.\n\n¡Te esperamos puntual y con toda la energía para aprender!\n\nÚnete aquí:\n{{ $json.conferenceData.entryPoints[0].uri }}", @@ -8097,7 +8281,7 @@ "typeVersion": 2.2, "position": [ 800, - 928 + 736 ], "id": "10b2cb4e-274d-4913-9345-2c024c071f4c", "name": "Send a message espanol2", @@ -8112,7 +8296,7 @@ }, { "parameters": { - "sendTo": "eidan.then14@gmail.com", + "sendTo": "={{ $('Loop Over Items1').item.json.email }}", "subject": "#TheGLMWay Training", "emailType": "text", "message": "=Hello, GLMer!\n\nThis is a reminder about your training session, where we'll share our culture, our DNA, and what makes us different from the rest.\n\nWe're looking forward to seeing you on time and ready to learn!\n\nJoin here:\n{{ $json.conferenceData.entryPoints[0].uri }}", @@ -8124,7 +8308,7 @@ "typeVersion": 2.2, "position": [ 800, - 752 + 944 ], "id": "1dc76989-8c58-4ffe-9e1c-5113a59d346c", "name": "Send a message ingles2", @@ -8142,7 +8326,11 @@ "rule": { "interval": [ { - "field": "hours" + "field": "weeks", + "triggerAtDay": [ + 2 + ], + "triggerAtHour": 17 } ] } @@ -8255,7 +8443,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 2656, + 2848, 352 ], "id": "3a6f198b-e136-49fb-a171-78164e10b600", @@ -8307,7 +8495,7 @@ "conditions": [ { "id": "9674fd7e-dd62-42d4-8931-6c67e59e3dd7", - "leftValue": "={{ !['Jamaica', 'Trinidad and Tobago', 'Barbados', 'Saint Thomas', 'St. Croix', 'Tobago', 'Guyana', 'Aruba'].includes($('Obtener notificados').item.json.location) }}", + "leftValue": "={{ !['Jamaica', 'Trinidad and Tobago', 'Barbados', 'Saint Thomas', 'St. Croix', 'Tobago', 'Guyana', 'Aruba'].includes($('Obtener notificados').item.json.pais) }}", "rightValue": "Jamaica", "operator": { "type": "boolean", @@ -9498,7 +9686,7 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 2704, + 2976, -352 ], "id": "ca136518-b776-45ba-b1a2-ad5327dd4cca", @@ -9549,7 +9737,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 3600, + 3872, -352 ], "id": "7c321f7d-bce9-447c-b94c-35f6967a48e3", @@ -9567,7 +9755,7 @@ "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ - 3424, + 3696, -352 ], "id": "27531838-5dc7-4169-a08a-f4da93899580", @@ -9597,7 +9785,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 3248, + 3520, -352 ], "id": "bd8cb535-6aa3-46df-9ca0-6efc0555f2e4", @@ -9618,7 +9806,7 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 2672, + 2944, -480 ], "typeVersion": 1, @@ -9659,8 +9847,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 576, - 2816 + 640, + 3136 ], "id": "3c22bc65-799f-4432-86fe-10ae92e036de", "name": "Send a message espanol4", @@ -9686,8 +9874,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 576, - 2640 + 640, + 2960 ], "id": "4dfc0135-6886-4d33-9b2a-a26efbe72832", "name": "Send a message ingles4", @@ -9739,8 +9927,8 @@ "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ - 832, - 2736 + 896, + 3056 ], "id": "b7c02e2f-1382-4086-9059-cf75fee9d278", "name": "Actualizar estado a recordado2", @@ -9761,8 +9949,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 448, - 3088 + 512, + 3408 ], "id": "ef89ad57-bdc8-4d9d-a250-41706ec900c1", "name": "HTTP Request", @@ -9785,8 +9973,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ - 272, - 3248 + 336, + 3568 ], "credentials": { "googleOAuth2Api": { @@ -9821,8 +10009,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ - 96, - 2656 + 160, + 2976 ], "credentials": { "googleOAuth2Api": { @@ -9843,8 +10031,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ - 256, - 2976 + 320, + 3296 ], "credentials": { "googleOAuth2Api": { @@ -9872,8 +10060,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - -144, - 2848 + -80, + 3168 ], "id": "955614d8-60a7-4762-8c02-86ac33e54cb3", "name": "HTTP Request2", @@ -9898,8 +10086,8 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 64, - 2848 + 128, + 3168 ], "id": "a84173c0-db9f-4251-af6b-6e475541fc52", "name": "HTTP Request3", @@ -9910,22 +10098,6 @@ } } }, - { - "parameters": { - "content": "## Aviso a instructor si no hay nadie inscrito a la sesion del dia\n", - "height": 464, - "width": 1392, - "color": 6 - }, - "type": "n8n-nodes-base.stickyNote", - "position": [ - 1392, - 736 - ], - "typeVersion": 1, - "id": "de09868e-4a06-4c64-9e66-aae9e14b9896", - "name": "Sticky Note11" - }, { "parameters": { "operation": "getAll", @@ -9946,7 +10118,7 @@ "type": "n8n-nodes-base.googleCalendar", "typeVersion": 1.3, "position": [ - 2896, + 3168, -352 ], "id": "b2b03ce6-6010-4e4f-be7a-d9040286e295", @@ -9965,7 +10137,7 @@ "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 3072, + 3344, -352 ], "id": "73c39af9-bc01-4d97-81ee-846aff70b3a6", @@ -9984,7 +10156,7 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 2704, + 2976, -48 ], "id": "55a755d8-8b74-440f-93c6-8a1ecba4c666", @@ -9998,7 +10170,7 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 2672, + 2944, -160 ], "typeVersion": 1, @@ -10025,7 +10197,7 @@ "type": "n8n-nodes-base.googleCalendar", "typeVersion": 1.3, "position": [ - 4144, + 4320, -208 ], "id": "3c32f96e-cd34-4be4-8c24-b4be7a8f29d9", @@ -10058,7 +10230,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 2880, + 3152, -48 ], "id": "73089147-ba1f-4fb4-ac17-d5670b263fd8", @@ -10096,7 +10268,7 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 3216, + 3488, -48 ], "id": "231f396b-855a-4c50-8d6d-096018e6d2ac", @@ -10111,7 +10283,7 @@ "type": "n8n-nodes-base.aggregate", "typeVersion": 1, "position": [ - 3040, + 3312, -48 ], "id": "e51bd2d5-22b5-46cf-b346-c1ba16bb5c34", @@ -10142,7 +10314,7 @@ "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ - 4944, + 5120, -304 ], "id": "3f4a0807-5f29-49cb-a851-41100e04f5bf", @@ -10157,7 +10329,7 @@ "type": "n8n-nodes-base.aggregate", "typeVersion": 1, "position": [ - 4800, + 4976, -304 ], "id": "3ecbb0c7-f5f5-4644-9f42-8be9502e814d", @@ -10172,7 +10344,7 @@ "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ - 4656, + 4832, -304 ], "id": "a099259d-36e3-48b6-aa77-d8132f33f85e", @@ -10195,7 +10367,7 @@ "type": "n8n-nodes-base.merge", "typeVersion": 3.2, "position": [ - 4512, + 4688, -304 ], "id": "456bcbe4-cc98-48fc-bcd2-40f8961934ae", @@ -10209,7 +10381,7 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 3936, + 4112, -480 ], "typeVersion": 1, @@ -10233,7 +10405,7 @@ "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [ - 3984, + 4160, -288 ], "id": "6ef829bf-1d52-4093-bcc5-45d922393611", @@ -10260,7 +10432,7 @@ "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ - 4144, + 4320, -384 ], "id": "f1bf8c51-43cf-41d4-93c4-1a1d0a0dac7a", @@ -10285,8 +10457,8 @@ "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ - 1600, - 928 + -176, + 2144 ], "id": "d34e225b-25bb-4a0a-845c-b9e274529000", "name": "Get many rows", @@ -10304,7 +10476,7 @@ "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 4320, + 4496, -208 ], "id": "8f80d9ca-1e4b-4753-b482-df7b0fd24f61", @@ -10343,8 +10515,8 @@ "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 1776, - 928 + 0, + 2144 ], "id": "0db9e6a4-2e5e-4513-97fd-71d22aa90822", "name": "Calcular sesion mas cercana4" @@ -10362,8 +10534,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2640, - 1008 + 624, + 2432 ], "id": "0cf2219c-db12-4231-bac6-8e5f2816530f", "name": "Send a message espanol5", @@ -10389,8 +10561,8 @@ "type": "n8n-nodes-base.gmail", "typeVersion": 2.2, "position": [ - 2640, - 832 + 624, + 2256 ], "id": "96bac1b9-d921-418d-947d-f07980bb76aa", "name": "Send a message ingles5", @@ -10431,8 +10603,8 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 2416, - 928 + 416, + 2352 ], "id": "748f3ae7-7e6d-41f5-be1d-861a9566ec00", "name": "Es espanol o ingles" @@ -10443,8 +10615,8 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 2784, - 896 + 912, + 1904 ], "typeVersion": 1, "id": "2d9a27b5-9ca6-4518-bd06-3a91389c4736", @@ -10491,8 +10663,8 @@ }, "type": "n8n-nodes-base.stickyNote", "position": [ - 992, - 2656 + 1056, + 2976 ], "typeVersion": 1, "id": "a9266141-49ab-41f7-b8ab-dc45e746ed1c", @@ -10544,8 +10716,8 @@ "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ - 3344, - 176 + 3744, + 272 ], "id": "df865311-bf25-4b35-ac8a-da71a3d93e05", "name": "Añadir a listado de participantes1", @@ -10743,8 +10915,8 @@ "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ - 1968, - 928 + 192, + 2144 ], "id": "d1472787-4c2f-4e1d-8673-5c2368d469ab", "name": "Hay inscritos?" @@ -10875,7 +11047,7 @@ }, { "parameters": { - "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana1').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" + "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -10901,7 +11073,7 @@ }, { "parameters": { - "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana1').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" + "jsCode": "// Diccionario de meses inglés -> español\nconst meses = {\n january: 'Enero',\n february: 'Febrero',\n march: 'Marzo',\n april: 'Abril',\n may: 'Mayo',\n june: 'Junio',\n july: 'Julio',\n august: 'Agosto',\n september: 'Septiembre',\n october: 'Octubre',\n november: 'Noviembre',\n december: 'Diciembre'\n};\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana2').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mesIngles = partes[1].toLowerCase();\n const mesEspanol = meses[mesIngles] || partes[1];\n\n item.json.fechaFormateada = `${mesEspanol} ${dia}`;\n}\n\nreturn $input.all();" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -10914,7 +11086,7 @@ }, { "parameters": { - "jsCode": "function getOrdinalSuffix(dia) {\n const d = parseInt(dia, 10);\n if (d > 3 && d < 21) return `${d}th`; // 11th-13th excepción cubierta aquí\n switch (d % 10) {\n case 1: return `${d}st`;\n case 2: return `${d}nd`;\n case 3: return `${d}rd`;\n default: return `${d}th`;\n }\n}\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana1').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mes = partes[1];\n const diaConSufijo = getOrdinalSuffix(dia);\n\n item.json.fechaFormateada = `${mes} ${diaConSufijo}`;\n}\n\nreturn $input.all();" + "jsCode": "function getOrdinalSuffix(dia) {\n const d = parseInt(dia, 10);\n if (d > 3 && d < 21) return `${d}th`; // 11th-13th excepción cubierta aquí\n switch (d % 10) {\n case 1: return `${d}st`;\n case 2: return `${d}nd`;\n case 3: return `${d}rd`;\n default: return `${d}th`;\n }\n}\n\nfor (const item of $input.all()) {\n const fecha = $('Calcular sesion mas cercana3').first().json.session.fecha_sesion; // Cambia \"fecha\" por el nombre real de tu campo\n const partes = fecha.trim().split(' '); // [\"8\", \"July\"]\n\n const dia = partes[0];\n const mes = partes[1];\n const diaConSufijo = getOrdinalSuffix(dia);\n\n item.json.fechaFormateada = `${mes} ${diaConSufijo}`;\n}\n\nreturn $input.all();" }, "type": "n8n-nodes-base.code", "typeVersion": 2, @@ -10924,6 +11096,146 @@ ], "id": "4abc2638-3ae3-4aba-b45f-0b60e73e3267", "name": "Convertir fecha a ingles1" + }, + { + "parameters": { + "sendTo": "eidan.then14@gmail.com", + "subject": "Entrenamiento #TheGLMWay", + "emailType": "text", + "message": "=
\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

Sesión de entrenamiento

\n

{{ $json.session.titulo }}

\n
\n

📅 {{ $json.session.fecha_sesion }}  |  🕒 {{ $json.session.hora_inicio }} - {{ $json.session.hora_fin }}

\n

🎤 Instructor: {{ $json.session.instructor }}

\n

🔗 Unirse a la reunión

\n
\n

Hola,

\n

Aquí tienes el listado de participantes para la sesión de hoy:

\n
\n {{ (function(){\n const data = $json.session.participantes || {};\n const colores = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#db2777'];\n let i = 0;\n let html = '';\n\n for (const pais in data) {\n const nombrePais = pais && pais.trim() !== '' ? pais : 'Sin país asignado';\n const color = colores[i % colores.length];\n i++;\n const lista = data[pais];\n\n html += `\n \n \n \n `;\n\n lista.forEach((p, idx) => {\n const bg = idx % 2 === 0 ? '#ffffff' : '#fafafa';\n html += `\n \n \n `;\n });\n\n html += `
\n ${nombrePais}\n (${lista.length} participante${lista.length !== 1 ? 's' : ''})\n
\n

${p.nombre}

\n

${p.correo}${p.division ? '  •  ' + p.division : ''}${p.departamento ? '  •  ' + p.departamento : ''}

\n
`;\n }\n\n return html || '

No hay participantes registrados.

';\n })() }}\n
\n

Saludos,
#TheGLMWay

\n
\n
", + "options": { + "appendAttribution": false + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 624, + 2032 + ], + "id": "3167fd45-4eae-4db4-92fb-9c80223cb4fa", + "name": "Send a message espanol6", + "webhookId": "a6043707-89d1-4c99-8de2-e60d9aaf7ed2", + "credentials": { + "gmailOAuth2": { + "id": "QWNicjw1khRm5BVI", + "name": "Eidan - Gmail account" + } + }, + "disabled": true + }, + { + "parameters": { + "sendTo": "eidan.then14@gmail.com", + "subject": "#TheGLMWay Training", + "emailType": "text", + "message": "=Hello,\n\nThis is to inform you that, as of now, there are no people enrolled for today's activity.\n\nBest regards.\n\nThis is an automated message generated by the enrollment management system.", + "options": { + "appendAttribution": false + } + }, + "type": "n8n-nodes-base.gmail", + "typeVersion": 2.2, + "position": [ + 624, + 1856 + ], + "id": "0cd1d4fb-062f-42ca-bf9c-1424e17d50f0", + "name": "Send a message ingles6", + "webhookId": "a6043707-89d1-4c99-8de2-e60d9aaf7ed2", + "credentials": { + "gmailOAuth2": { + "id": "QWNicjw1khRm5BVI", + "name": "Eidan - Gmail account" + } + }, + "disabled": true + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "strict", + "version": 3 + }, + "conditions": [ + { + "id": "df25aa3e-d48f-4e52-ae1f-47678e954d52", + "leftValue": "={{ $json.session.idioma }}", + "rightValue": "EN", + "operator": { + "type": "string", + "operation": "equals", + "name": "filter.operator.equals" + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.3, + "position": [ + 416, + 1936 + ], + "id": "dd5cce8e-ef5a-4bd4-967c-aec464f30fc4", + "name": "Es espanol o ingles1" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2480, + -400 + ], + "id": "740373c9-0145-41f4-a4b6-9dbb84ffbdaa", + "name": "Cambiar zona horaria" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana1')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2480, + -224 + ], + "id": "77da2dc7-546b-4ab7-a7c7-fa621196b2d0", + "name": "Cambiar zona horaria1" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana2')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2464, + 272 + ], + "id": "bbd8c7de-64eb-4262-a77b-b7307ab9c0c6", + "name": "Cambiar zona horaria2" + }, + { + "parameters": { + "jsCode": "const TIMEZONES = {\n \"Argentina\": \"America/Argentina/Buenos_Aires\",\n \"Bolivia\": \"America/La_Paz\",\n \"Brasil\": \"America/Sao_Paulo\",\n \"Chile\": \"America/Santiago\",\n \"Colombia\": \"America/Bogota\",\n \"Costa Rica\": \"America/Costa_Rica\",\n \"Cuba\": \"America/Havana\",\n \"Ecuador\": \"America/Guayaquil\",\n \"El Salvador\": \"America/El_Salvador\",\n \"Guatemala\": \"America/Guatemala\",\n \"Honduras\": \"America/Tegucigalpa\",\n \"Mexico\": \"America/Mexico_City\",\n \"Nicaragua\": \"America/Managua\",\n \"Panama\": \"America/Panama\",\n \"Paraguay\": \"America/Asuncion\",\n \"Peru\": \"America/Lima\",\n \"Puerto Rico\": \"America/Puerto_Rico\",\n \"Republica Dominicana\": \"America/Santo_Domingo\",\n \"Uruguay\": \"America/Montevideo\",\n \"Venezuela\": \"America/Caracas\",\n \"Jamaica\": \"America/Jamaica\",\n \"Trinidad and Tobago\": \"America/Port_of_Spain\",\n \"Guyana\": \"America/Guyana\",\n \"Surinam\": \"America/Paramaribo\",\n \"Belice\": \"America/Belize\"\n};\n\n// ==============================\n// FUNCIONES\n// ==============================\n\nfunction normalizarPais(texto) {\n return texto\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .trim()\n .toLowerCase();\n}\n\nfunction obtenerOffsetMinutos(fecha, timeZone) {\n const dtf = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\"\n });\n\n const partes = dtf.formatToParts(fecha).reduce((acc, p) => {\n acc[p.type] = p.value;\n return acc;\n }, {});\n\n const utcEquivalente = Date.UTC(\n Number(partes.year),\n Number(partes.month) - 1,\n Number(partes.day),\n Number(partes.hour),\n Number(partes.minute),\n Number(partes.second)\n );\n\n return (utcEquivalente - fecha.getTime()) / 60000;\n}\n\nfunction obtenerUTCDesdeHoraLocal(horaTexto, timeZone) {\n const [hora, minuto] = horaTexto.split(\":\").map(Number);\n\n const ahora = new Date();\n\n const y = ahora.getUTCFullYear();\n const m = ahora.getUTCMonth();\n const d = ahora.getUTCDate();\n\n let fechaUTC = new Date(Date.UTC(y, m, d, hora, minuto, 0));\n\n const offset = obtenerOffsetMinutos(fechaUTC, timeZone);\n\n fechaUTC = new Date(fechaUTC.getTime() - offset * 60000);\n\n return fechaUTC;\n}\n\nfunction formatearHora(fechaUTC, timeZone) {\n return new Intl.DateTimeFormat(\"es-DO\", {\n timeZone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false\n }).format(fechaUTC);\n}\n\nfunction convertirHoraDesdeSantoDomingo(horaTexto) {\n\n const fechaUTC = obtenerUTCDesdeHoraLocal(\n horaTexto,\n TIMEZONES[\"Republica Dominicana\"]\n );\n\n const resultado = {};\n\n for (const [pais, tz] of Object.entries(TIMEZONES)) {\n resultado[pais] = formatearHora(fechaUTC, tz);\n }\n\n return resultado;\n}\n\n// ==============================\n// EJECUCIÓN\n// ==============================\n\nconst horaEntrada = $('Calcular sesion mas cercana3')\n .first()\n .json\n .session\n .hora_inicio;\n\nif (!horaEntrada || !/^\\d{1,2}:\\d{2}$/.test(horaEntrada)) {\n throw new Error(\n `Formato de hora inválido: \"${horaEntrada}\". Usa HH:mm, por ejemplo 19:00`\n );\n}\n\nconst conversiones = convertirHoraDesdeSantoDomingo(horaEntrada);\n\nreturn items.map(item => {\n\n const paisEntrada = item.json.pais;\n\n if (!paisEntrada) {\n throw new Error('Cada item debe contener el campo \"pais\".');\n }\n\n // Buscar el país ignorando mayúsculas y acentos\n const paisEncontrado = Object.keys(TIMEZONES).find(\n pais => normalizarPais(pais) === normalizarPais(paisEntrada)\n );\n\n if (!paisEncontrado) {\n throw new Error(\n `País no reconocido: \"${paisEntrada}\". Países válidos: ${Object.keys(TIMEZONES).join(\", \")}`\n );\n }\n\n return {\n json: {\n hora_base: horaEntrada,\n pais_base: \"Republica Dominicana\",\n hora_convertida: conversiones[paisEncontrado],\n conversiones\n }\n };\n});" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2464, + 448 + ], + "id": "677acc91-b856-482e-aa2c-9b329221918a", + "name": "Cambiar zona horaria3" } ], "connections": { @@ -11348,14 +11660,14 @@ "main": [ [ { - "node": "Send a message ingles2", + "node": "Send a message espanol2", "type": "main", "index": 0 } ], [ { - "node": "Send a message espanol2", + "node": "Send a message ingles2", "type": "main", "index": 0 } @@ -12160,7 +12472,13 @@ }, "Hay inscritos?": { "main": [ - [], + [ + { + "node": "Es espanol o ingles1", + "type": "main", + "index": 0 + } + ], [ { "node": "Es espanol o ingles", @@ -12218,7 +12536,7 @@ "main": [ [ { - "node": "Send a message espanol", + "node": "Cambiar zona horaria", "type": "main", "index": 0 } @@ -12229,7 +12547,7 @@ "main": [ [ { - "node": "Send a message ingles", + "node": "Cambiar zona horaria1", "type": "main", "index": 0 } @@ -12240,7 +12558,7 @@ "main": [ [ { - "node": "Send a message espanol1", + "node": "Cambiar zona horaria2", "type": "main", "index": 0 } @@ -12248,6 +12566,68 @@ ] }, "Convertir fecha a ingles1": { + "main": [ + [ + { + "node": "Cambiar zona horaria3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Es espanol o ingles1": { + "main": [ + [ + { + "node": "Send a message ingles6", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Send a message espanol6", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria": { + "main": [ + [ + { + "node": "Send a message espanol", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria1": { + "main": [ + [ + { + "node": "Send a message ingles", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria2": { + "main": [ + [ + { + "node": "Send a message espanol1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Cambiar zona horaria3": { "main": [ [ { @@ -12260,15 +12640,15 @@ } }, "authors": "Eidan Then", - "name": "Version 4bef6f10", + "name": "Version b081672b", "description": "", - "autosaved": true, + "autosaved": false, "workflowPublishHistory": [ { - "createdAt": "2026-07-01T21:16:28.782Z", - "id": 2213, + "createdAt": "2026-07-02T16:33:15.612Z", + "id": 2278, "workflowId": "F1iPg8hktNAebZua", - "versionId": "4bef6f10-7d40-43dd-bfa7-824b04d847d7", + "versionId": "b081672b-1753-46b8-8436-25841f941414", "event": "activated", "userId": "de14e7e5-491b-4983-abb2-f88af42e92fd" }