{
  "name": "FlujoDeReservasUpdated",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "documentId": {
          "__rl": true,
          "value": "1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw",
          "mode": "list",
          "cachedResultName": "Registro Solicitud",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 1989465276,
          "mode": "list",
          "cachedResultName": "Respuestas de formulario 1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit#gid=1989465276"
        },
        "event": "rowAdded",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        -2288,
        544
      ],
      "id": "ac63537c-c48e-4644-909c-5c6527f29e83",
      "name": "DatosFormulario",
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -2096,
        720
      ],
      "id": "00f5e0ac-41f0-4220-af35-7082024e3dd4",
      "name": "Merge"
    },
    {
      "parameters": {
        "jsCode": "const r = $json;\n\n// \u2014\u2014 helpers\nfunction pad(n){ return String(n).padStart(2,'0'); }\nfunction parseHora(str){\n  const parts = String(str || '').trim().split(':');\n  return { hh: pad(+parts[0] || 0), mm: pad(+parts[1] || 0) };\n}\nfunction parseFechaSheets(v){\n  if (v == null) return null;\n  if (v instanceof Date && !isNaN(v)) return v;\n  if (typeof v === 'number') {\n    const epoch = Date.UTC(1899,11,30);\n    return new Date(epoch + Math.round(v * 86400000));\n  }\n  const s = String(v).trim();\n  const asDate = new Date(s);\n  if (!isNaN(asDate)) return asDate;\n  const m = s.match(/^(\\d{1,2})[\\/\\-](\\d{1,2})[\\/\\-](\\d{4})$/);\n  if (m) {\n    const dd = +m[1], mm = +m[2] - 1, yyyy = +m[3];\n    return new Date(yyyy, mm, dd);\n  }\n  return null;\n}\n\n// \u2014\u2014 toma la fecha\nconst rawFecha = r['Fecha'] ?? r['Fecha de la reuni\u00f3n'] ?? r['fecha'] ?? r['Date'] ?? r['Marca temporal'] ?? null;\nconst fecha = parseFechaSheets(rawFecha) || new Date();\n\nconst hi = parseHora(r['Inicio'] ?? r['Hora de inicio']);\nconst hf = parseHora(r['Fin']    ?? r['Hora de Termino']);\n\nconst yyyy = fecha.getFullYear();\nconst mm   = pad(fecha.getMonth()+1);\nconst dd   = pad(fecha.getDate());\n\n// Nombres de meses en espa\u00f1ol\nconst meses = [\n  'enero','febrero','marzo','abril','mayo','junio',\n  'julio','agosto','septiembre','octubre','noviembre','diciembre'\n];\n\n// Nueva variable con formato \u201c20 de septiembre del 2025\u201d\nconst fechaLarga = `${dd} de ${meses[fecha.getMonth()]} del ${yyyy}`;\n\n// huso horario fijo\nconst tzOffset = '-03:00';\nconst startIso = `${yyyy}-${mm}-${dd}T${hi.hh}:${hi.mm}:00${tzOffset}`;\nconst endIso   = `${yyyy}-${mm}-${dd}T${hf.hh}:${hf.mm}:00${tzOffset}`;\n\nreturn [{\n  json: {\n    startIso,\n    endIso,\n    summary: `Reuni\u00f3n: ${r['Nombre'] || r['Nombre del solicitante'] || ''}`,\n    description: `\u00c1rea: ${r['\u00c1rea'] || r['\u00c1rea/Departamento'] || ''}`,\n    requesterEmail: r['Correo'] || r['Correo electr\u00f3nico'] || '',\n    participantes: Number(r['N\u00b0 Participantes'] ?? r['N\u00famero de participantes'] ?? 0),\n\n    // para la hoja Registro:\n    nombre: r['Nombre'] || r['Nombre del solicitante'] || '',\n    correo: r['Correo'] || r['Correo electr\u00f3nico'] || '',\n    area:   r['\u00c1rea'] || r['\u00c1rea/Departamento'] || '',\n    fechaStr: `${dd}-${mm}-${yyyy}`,\n    fechaLarga,  // \u2190 aqu\u00ed queda disponible\n    inicioStr: `${hi.hh}:${hi.mm}`,\n    finStr: `${hf.hh}:${hf.mm}`,\n\n    ReservaID: `res-${Date.now()}-${Math.random().toString(16).slice(2,8)}`\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2752,
        704
      ],
      "id": "45a1c71d-f825-4b4c-a693-03256c3d520f",
      "name": "DatosCode"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw",
          "mode": "list",
          "cachedResultName": "Registro Solicitud",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 1534669159,
          "mode": "list",
          "cachedResultName": "Registro",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit#gid=1534669159"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Nombre": "={{ $json.nombre }}",
            "Correo": "={{ $json.correo }}",
            "Area/Departamento": "={{ $json.area }}",
            "Fecha": "={{ $json.fecha }}",
            "horaInicio": "={{ $json.inicio }}",
            "horaTermino": "={{ $json.termino }}",
            "N\u00b0 Participantes": "={{ $json.participantes }}",
            "Estado": "En proceso",
            "ID_Reserva": "={{ $json.ReservaID }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Nombre",
              "displayName": "Nombre",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Correo",
              "displayName": "Correo",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Area/Departamento",
              "displayName": "Area/Departamento",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Fecha",
              "displayName": "Fecha",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "horaInicio",
              "displayName": "horaInicio",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "horaTermino",
              "displayName": "horaTermino",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "N\u00b0 Participantes",
              "displayName": "N\u00b0 Participantes",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "ID_Reserva",
              "displayName": "ID_Reserva",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Estado",
              "displayName": "Estado",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Sala asignada",
              "displayName": "Sala asignada",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "ID_Sala",
              "displayName": "ID_Sala",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Event_ID",
              "displayName": "Event_ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -2416,
        544
      ],
      "id": "bcaaae7e-ce5c-49e6-9f41-12bb33e51bba",
      "name": "guardarRegistro",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw",
          "mode": "list",
          "cachedResultName": "Registro Solicitud",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 727732446,
          "mode": "list",
          "cachedResultName": "Salas",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit#gid=727732446"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -2256,
        544
      ],
      "id": "215153c4-513b-42ef-9270-75e5eb29ce38",
      "name": "leerSalas",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const reserva = $items()[0].json;\nconst rooms   = $items().slice(1).map(i => i.json);\n\nconst capReq = Number(reserva.participantes || 0);\n\nconst roomsToUse = rooms\n  .map(r => ({\n    room: String(r.Sala || '').trim(),\n    calendarId: String(r.ID || '').trim(),\n    capacity: Number(r.Capacidad || 0),\n    torre: String(r.Ubicacion || '').trim()   // \u2190 a\u00f1adimos la torre\n  }))\n  .filter(r => r.calendarId)\n  .filter(r => r.capacity >= capReq);\n\nreturn roomsToUse.map(r => ({\n  json: {\n    ...reserva,\n    room: r.room,\n    calendarId: r.calendarId,\n    capacity: r.capacity,\n    torre: r.torre                       // \u2190 heredamos torre al flujo\n  }\n}));\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1952,
        720
      ],
      "id": "773c3c6d-4a23-49b9-9fe7-949d633be120",
      "name": "filtrarCapacidad"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        -1776,
        720
      ],
      "id": "79ae0442-1c52-4ada-b9cb-9fb95643686d",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "operation": "getAll",
        "calendar": {
          "__rl": true,
          "value": "={{$json.calendarId}}",
          "mode": "id"
        },
        "returnAll": true,
        "timeMin": "={{ $json.startIso }}",
        "timeMax": "={{ $json.endIso }}",
        "options": {}
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        -1552,
        736
      ],
      "id": "0dc44b5b-25b3-4416-abac-743413289424",
      "name": "Get many events",
      "alwaysOutputData": true,
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Items del Merge: [ base, ...eventos ]\nconst all = $items();\nconst base = all[0]?.json || {};\nconst events = all.slice(1).map(i => i.json).filter(e => e && (e.start || e.end));\n\nconst reqStart = Date.parse(base.startIso);\nconst reqEnd   = Date.parse(base.endIso);\n\nfunction toRange(e){\n  const s  = e.start?.dateTime || (e.start?.date && (e.start.date + 'T00:00:00Z'));\n  const en = e.end?.dateTime   || (e.end?.date   && (e.end.date   + 'T23:59:59Z'));\n  if (!s || !en) return null;\n  return { start: Date.parse(s), end: Date.parse(en) };\n}\n\nconst ranges = events.map(toRange).filter(Boolean);\nconst isFree = !ranges.some(b => Math.max(reqStart, b.start) < Math.min(reqEnd, b.end));\n\nreturn [{\n  json: {\n    ...base,                 // <- aqu\u00ed vuelven ReservaID, room, calendarId, startIso, endIso\n    checkedEvents: events.length,\n    isFree\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1264,
        832
      ],
      "id": "400a32f3-3691-471c-8983-a09313c92d34",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "e02fa576-dad4-469f-bb23-4fa38aaa6b60",
              "leftValue": "={{$json.isFree}}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1104,
        880
      ],
      "id": "0a6f3021-ebce-43d7-b399-b56171f756f5",
      "name": "If"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -1392,
        832
      ],
      "id": "cccb1c95-602e-4a68-8c95-c0e5c6fff36b",
      "name": "Merge1"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -720,
        800
      ],
      "id": "1a4f2cff-6cc3-4677-87fd-822c33673676",
      "name": "Merge2"
    },
    {
      "parameters": {
        "sendTo": "={{ $json.correo }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.htmlBody }}",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        -304,
        800
      ],
      "id": "0ad4f8e6-8b3a-44a6-a0f4-0b27ffad584a",
      "name": "Send a message",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw",
          "mode": "list",
          "cachedResultName": "Registro Solicitud",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 1534669159,
          "mode": "list",
          "cachedResultName": "Registro",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit#gid=1534669159"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Sala asignada": "={{ $('Code in JavaScript1').item.json.room }}",
            "Estado": "Asignada",
            "ID_Reserva": "={{ $('Code in JavaScript1').item.json.ReservaID }}",
            "ID_Sala": "={{ $('Code in JavaScript1').item.json.calendarID }}",
            "Event_ID": "={{ $('Code in JavaScript1').item.json.eventID }}"
          },
          "matchingColumns": [
            "ID_Reserva"
          ],
          "schema": [
            {
              "id": "Nombre",
              "displayName": "Nombre",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Correo",
              "displayName": "Correo",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Area/Departamento",
              "displayName": "Area/Departamento",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Fecha",
              "displayName": "Fecha",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "horaInicio",
              "displayName": "horaInicio",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "horaTermino",
              "displayName": "horaTermino",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "N\u00b0 Participantes",
              "displayName": "N\u00b0 Participantes",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "ID_Reserva",
              "displayName": "ID_Reserva",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Estado",
              "displayName": "Estado",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Sala asignada",
              "displayName": "Sala asignada",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "ID_Sala",
              "displayName": "ID_Sala",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Event_ID",
              "displayName": "Event_ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -96,
        800
      ],
      "id": "97730ec7-7b29-4197-98ef-57f54dea30f9",
      "name": "Update row in sheet",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "calendar",
        "calendar": {
          "__rl": true,
          "value": "={{ $json.calendarId }}",
          "mode": "id"
        },
        "timeMin": "={{$json.option.start}}",
        "timeMax": "={{$json.option.end}}",
        "options": {}
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        -1440,
        352
      ],
      "id": "bdcc173b-fdd1-417c-8c09-09e65358f3bc",
      "name": "Get availability in a calendar",
      "alwaysOutputData": true,
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "73e55d6b-dafc-41fa-9733-b0bdd656d3e4",
              "leftValue": "={{ $json.available }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -1040,
        448
      ],
      "id": "cf1000e5-91c9-450c-86f8-65cbd15d6ca0",
      "name": "If1",
      "alwaysOutputData": false
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.htmlBody }}",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        -640,
        160
      ],
      "id": "06c0992d-294a-45d8-8ce7-b336f158ee09",
      "name": "Send a message1",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        -1216,
        448
      ],
      "id": "4f30f8ba-d17b-4456-9f0d-0e3e67806026",
      "name": "Merge3"
    },
    {
      "parameters": {
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -1440,
        512
      ],
      "id": "fdb19875-5073-4313-b17d-c65dfbd110b8",
      "name": "SET1"
    },
    {
      "parameters": {
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -1552,
        912
      ],
      "id": "a84c132a-4e31-452e-a553-ddb9db14b5f1",
      "name": "SET"
    },
    {
      "parameters": {
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -896,
        848
      ],
      "id": "438bd56d-3036-4e8e-851c-81f613e051f1",
      "name": "SET2"
    },
    {
      "parameters": {
        "jsCode": "// n8n Code/Function: GenerarAlternativasDesdeLoopDone (parche flex + meta + correo)\n// Soporta: startIso/endIso, calendarId, room | roomName\n// Propaga meta para el HTML/Webhook: descripcion/description, correo, area, ReservID, nombre, participantes.\n\nconst DEFAULT_MIN_MINUTES = 30;\nconst DEFAULT_MAX_MINUTES = 120;\nconst OFFSETS_MINUTES = [-180, -120, -60, 60, 120, 180];\nconst MAX_OPTIONS_PER_ROOM = 8;\n\nconst STEP_LABEL = (m)=> (m<0? `${Math.abs(m)} min antes` : `${m} min despu\u00e9s`);\n\nfunction toDate(x) {\n  if (!x) return null;\n  if (typeof x === 'object' && (x.date || x.dateTime)) {\n    return new Date(x.dateTime || x.date);\n  }\n  return new Date(x);\n}\nfunction toISO(dt) { return new Date(dt).toISOString(); }\nfunction clampDuration(mins, minAllowed, maxAllowed) {\n  return Math.max(minAllowed, Math.min(maxAllowed, mins));\n}\nfunction overlaps(aStart, aEnd, bStart, bEnd) {\n  return (aStart < bEnd) && (bStart < aEnd);\n}\nfunction sameDay(d1, d2) {\n  return d1.getFullYear() === d2.getFullYear() &&\n         d1.getMonth() === d2.getMonth() &&\n         d1.getDate() === d2.getDate();\n}\nfunction shiftMinutes(date, mins) { const d = new Date(date); d.setMinutes(d.getMinutes()+mins); return d; }\nfunction addDays(date, days) { const d = new Date(date); d.setDate(d.getDate()+days); return d; }\n\nfunction normalizeBusyIntervals(j) {\n  if (Array.isArray(j.busyIntervals)) {\n    return j.busyIntervals\n      .map(b => ({ start: toDate(b.start), end: toDate(b.end) }))\n      .filter(b => b.start && b.end && b.start < b.end);\n  }\n  const events = Array.isArray(j.events) ? j.events : [];\n  return events\n    .map(ev => {\n      const s = toDate(ev.start?.dateTime || ev.start?.date);\n      const e = toDate(ev.end?.dateTime || ev.end?.date);\n      return (s && e && s < e) ? { start: s, end: e } : null;\n    })\n    .filter(Boolean);\n}\n\n// ---- helpers correo ----\nfunction getRequesterEmail(localJson) {\n  // 1) del propio \u00edtem\n  let mail = localJson.correo || localJson.email || localJson.requesterEmail || localJson.solicitanteEmail;\n  // 2) intenta leer del nodo de formulario si existe (ajusta el nombre si es distinto)\n  if (!mail) {\n    try {\n      const form = $items('DatosFormulario1', 0, 0);\n      if (form && form.json) {\n        mail = form.json.correo || form.json.email || form.json.mail;\n      }\n    } catch (e) { /* si no existe el nodo, no pasa nada */ }\n  }\n  // 3) limpieza simple\n  if (typeof mail === 'string') {\n    mail = mail.trim();\n    if (mail === '') mail = undefined;\n  }\n  return mail;\n}\n\nconst out = [];\n\nfor (const item of items) {\n  const j = item.json || {};\n\n  // ---- Adaptadores de campos (flex) ----\n  const roomId   = j.roomId || j.calendarId || j.calendar || j.calendario || j.calendarid;\n  const roomName = j.roomName || j.room || j.name || String(roomId || 'Sala');\n\n  // ---- Meta a propagar (tal como viene en tu flujo) ----\n  const descripcion  = j.descripcion || j.description || '';          // ej: \"\u00c1rea: Informatica\"\n  const area         = j.area || j.Area || j['\u00e1rea'] || j['\u00c1rea'] || j.departamento || j.Departamento || '';\n  const ReservID     = j.ReservID || j.reservationId || j.idReserva || j.IDReserva || j.id || j.ReservaID || '';\n  const nombre       = j.nombre || j.solicitanteNombre || j.Nombre || '';\n  const participantes= j.participantes || j.Participantes || j.asistentes || j.participants || '';\n\n  // correo del solicitante (se propagar\u00e1 en la salida)\n  const correo = getRequesterEmail(j) || j.correo || '';\n\n  let reqStart = null, reqEnd = null;\n\n  // 1) request.start/end (si existiera)\n  if (j.request && (j.request.start || j.request.end)) {\n    reqStart = toDate(j.request.start);\n    reqEnd   = toDate(j.request.end);\n  }\n  // 2) requestStart/requestEnd\n  if (!reqStart && j.requestStart) reqStart = toDate(j.requestStart);\n  if (!reqEnd   && j.requestEnd)   reqEnd   = toDate(j.requestEnd);\n  // 3) startIso/endIso\n  if (!reqStart && j.startIso) reqStart = toDate(j.startIso);\n  if (!reqEnd   && j.endIso)   reqEnd   = toDate(j.endIso);\n  // 4) start/end directos\n  if (!reqStart && j.start) reqStart = toDate(j.start);\n  if (!reqEnd   && j.end)   reqEnd   = toDate(j.end);\n\n  // Si no hay hora o no hay sala, saltamos\n  if (!reqStart || !reqEnd || !(reqStart < reqEnd) || !roomId) {\n    continue;\n  }\n\n  // Restricciones\n  const minMinutes = Number.isFinite(Number(j.constraints?.minMinutes))\n    ? Number(j.constraints.minMinutes) : DEFAULT_MIN_MINUTES;\n  const maxMinutes = Number.isFinite(Number(j.constraints?.maxMinutes))\n    ? Number(j.constraints.maxMinutes) : DEFAULT_MAX_MINUTES;\n\n  const requestedDurationMin = Math.round((reqEnd - reqStart) / 60000);\n  const slotDurationMin = clampDuration(requestedDurationMin, minMinutes, maxMinutes);\n\n  // Busy conocido (si no hay, lista vac\u00eda \u2192 luego verificas con GCal)\n  const busy = normalizeBusyIntervals(j);\n\n  // Candidatos mismo d\u00eda\n  const sameDayCandidates = [];\n  for (const off of OFFSETS_MINUTES) {\n    const s = shiftMinutes(reqStart, off);\n    if (!sameDay(s, reqStart)) continue;\n    const e = shiftMinutes(s, slotDurationMin);\n    sameDayCandidates.push({ start: s, end: e, offset: off, dayShift: 0 });\n  }\n\n  // Candidatos +1 d\u00eda\n  const nextDayBase = addDays(reqStart, 1);\n  const nextDayCandidates = [];\n  for (const off of OFFSETS_MINUTES) {\n    const s = shiftMinutes(nextDayBase, off);\n    const e = shiftMinutes(s, slotDurationMin);\n    nextDayCandidates.push({ start: s, end: e, offset: off, dayShift: 1 });\n  }\n\n  const allCandidates = [...sameDayCandidates, ...nextDayCandidates];\n\n  // Filtrar contra busy (si busy=[], pasan todos)\n  const freeOptions = [];\n  outer:\n  for (const c of allCandidates) {\n    for (const b of busy) {\n      if (overlaps(c.start, c.end, b.start, b.end)) continue outer;\n    }\n    freeOptions.push(c);\n    if (freeOptions.length >= MAX_OPTIONS_PER_ROOM) break;\n  }\n\n  for (const opt of freeOptions) {\n    const prettyLabel = (opt.dayShift === 0)\n      ? `Mismo d\u00eda, ${STEP_LABEL(opt.offset)}`\n      : `+1 d\u00eda, ${STEP_LABEL(opt.offset)}`;\n\n    out.push({\n      json: {\n        // ---- Identificaci\u00f3n de sala ----\n        calendarId: roomId,\n        roomId,\n        roomName,\n\n        // ---- Meta propagada (para HTML/Webhook) ----\n        descripcion,\n        description: descripcion,                 // alias en ingl\u00e9s\n        area,\n        ReservID,\n        reservationId: ReservID,                  // alias\n        nombre,\n        solicitanteNombre: nombre,                // alias\n        participantes,\n\n        // ---- Contacto ----\n        correo,\n        to: correo,                               // alias por comodidad\n\n        // ---- Opci\u00f3n propuesta ----\n        option: {\n          start: toISO(opt.start),\n          end: toISO(opt.end),\n          minutes: slotDurationMin,\n          label: prettyLabel,\n          offsetsMinutes: opt.offset,\n          dayShift: opt.dayShift,\n        },\n\n        // ---- Datos de la solicitud original ----\n        request: {\n          start: toISO(reqStart),\n          end: toISO(reqEnd),\n          requestedMinutes: requestedDurationMin,\n          appliedMinutes: slotDurationMin,\n          minMinutes,\n          maxMinutes,\n        },\n      },\n    });\n  }\n}\n\nreturn out;\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1664,
        352
      ],
      "id": "b66f76a0-9f5a-4b66-8dae-339deb232222",
      "name": "GenerarAlternativas",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// Code: Build HTML Alternativas (Run Once for All Items)\n// Con soporte robusto para extraer meta (\u00e1rea, participantes, solicitante, id)\n// Salida: { to, subject, htmlBody }\n\nconst bookingEmail = 'correoparapracticar1@gmail.com';\nconst TZ = 'America/Santiago';\nconst CONFIRM_PREFIX = 'CONFIRMAR:';\nconst WEBHOOK_URL = 'http://localhost:5678/webhook-test/confirmar-reserva';\n\n// ====== Helpers ======\nfunction toLocal(dtStr) {\n  const d = new Date(dtStr);\n  const fecha = d.toLocaleDateString('es-CL', { timeZone: TZ, weekday:'long', day:'2-digit', month:'long', year:'numeric' });\n  const hora  = d.toLocaleTimeString('es-CL', { timeZone: TZ, hour:'2-digit', minute:'2-digit' });\n  return { fecha, hora };\n}\nfunction groupBy(arr, keyFn) {\n  const m = new Map();\n  for (const x of arr) {\n    const k = keyFn(x);\n    if (!m.has(k)) m.set(k, []);\n    m.get(k).push(x);\n  }\n  return m;\n}\nfunction uniqEmails(list) {\n  const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n  return Array.from(new Set(list.filter(e => e && emailRegex.test(e.trim())).map(e => e.trim())));\n}\nfunction enc(v) { return encodeURIComponent(v == null ? '' : String(v)); }\nfunction fromNode(name){ try{ return $items(name,0,0)?.json || {}; }catch{ return {}; } }\nfunction firstNonEmpty(...vals){ for (const v of vals){ if (v!=null && String(v).trim()!=='') return String(v).trim(); } return ''; }\n\n// ====== Normalizaci\u00f3n de items ======\nconst libres = items.map(i => i.json).filter(j => j?.option?.start && j?.option?.end);\nlibres.sort((a,b) => new Date(a.option.start) - new Date(b.option.start));\n\n// ====== Destinatarios ======\nconst emailsFromItems = items.map(i => (i.json.to || i.json.correo || '')).filter(Boolean);\nlet ref; try { ref = $items('SET1', 0, 0); } catch { ref = null; }\nconst fallback = (ref?.json?.correo || ref?.json?.email || '').trim();\nconst toList = uniqEmails([...emailsFromItems, fallback]);\nconst to = toList.join(', ');\n\n// ===== ULTRA-ROBUSTO: leer meta desde ra\u00edz, request, option y nodos cercanos =====\nconst src   = (items?.[0]?.json) || {};\nconst roots = [\n  src,\n  src.request || {},\n  src.option  || {},\n  src.data    || {},\n  fromNode('DatosCode'),\n  fromNode('DatosFormulario'),\n  fromNode('SET1'),\n  fromNode('guardarRegistro'),\n];\n\n// helper para buscar en m\u00faltiples objetos y variantes de clave\nfunction pick(keys){\n  for (const r of roots){\n    for (const k of keys){\n      if (r?.[k] != null && String(r[k]).trim()!=='') return String(r[k]).trim();\n      if (k.includes('.')){\n        const parts = k.split('.');\n        let cur = r;\n        for (const p of parts){ cur = cur?.[p]; }\n        if (cur != null && String(cur).trim()!=='') return String(cur).trim();\n      }\n    }\n  }\n  return '';\n}\n\n// ==== AQU\u00cd SE DEFINEN TUS 4 CAMPOS ====\nconst area = pick(['area','Area','\u00e1rea','\u00c1rea','departamento','Departamento','request.area','request.departamento']);\nconst participantes = pick(['participantes','Participantes','asistentes','participants','request.participantes','request.asistentes']);\nconst solicitanteNombre = pick(['solicitanteNombre','nombre','Nombre','solicitante','request.solicitanteNombre','request.nombre']);\nconst reservationId = firstNonEmpty(\n  pick(['ReservID','reservationId','reservaId','idReserva','IDReserva','id','ID','request.reservationId','request.id']),\n  'RID'\n);\n\n// ====== Asunto ======\nconst reqStart = libres[0]?.request?.start || null;\nconst reqLocal = reqStart ? toLocal(reqStart) : null;\nconst subject = (libres.length > 0)\n  ? `Alternativas de sala \u2014 ${reqLocal ? `${reqLocal.hora}, ${reqLocal.fecha}` : `${libres.length} opciones`} \u2014 ${reservationId}`\n  : `Sin alternativas disponibles \u2014 ${reservationId}`;\n\n// ====== Construcci\u00f3n HTML ======\nconst byRoom = groupBy(libres, j => j.roomName || j.roomId || 'Sala');\nlet opIndex = 1;\nlet cardsHTML = '';\n\nfor (const [room, arr] of byRoom.entries()) {\n  cardsHTML += `\n    <h2 style=\"font-size:16px; margin:24px 0 8px; color:#222;\">\n      <span style=\"display:inline-block;width:8px;height:8px;border-radius:999px;background:#6c5ce7;margin-right:8px;vertical-align:middle\"></span>\n      ${room} <span style=\"color:#666; font-weight:400; font-size:12px;\">(${arr.length} opci\u00f3n${arr.length>1?'es':''})</span>\n    </h2>`;\n\n  for (const j of arr) {\n    const s = toLocal(j.option.start);\n    const e = toLocal(j.option.end);\n    const dur = j.option.minutes || Math.round((new Date(j.option.end) - new Date(j.option.start))/60000);\n    const roomId = j.roomId || '';\n    const calendarId = j.calendarId || j.roomCalendarId || '';\n    const code = `${reservationId}-OP${String(opIndex).padStart(2,'0')}`;\n    j.confirmCode = code;\n\n    const webhookLink =\n      `${WEBHOOK_URL}`\n      + `?code=${enc(code)}`\n      + `&reservationId=${enc(reservationId)}`\n      + `&room=${enc(room)}`\n      + `&roomId=${enc(roomId)}`\n      + `&calendarId=${enc(calendarId)}`\n      + `&start=${enc(j.option.start)}`\n      + `&end=${enc(j.option.end)}`\n      + `&minutes=${enc(dur)}`\n      + `&requestStart=${enc(reqStart || '')}`\n      + `&to=${enc(to)}`\n      + `&solicitante=${enc(solicitanteNombre)}`\n      + `&area=${enc(area)}`\n      + `&participantes=${enc(participantes)}`;\n\n    cardsHTML += `\n      <div class=\"option\">\n        <div class=\"option-title\">${j.option.label || 'Alternativa'} \u2014 <span style=\"font-weight:700;\">C\u00f3digo: ${code}</span></div>\n        <div class=\"option-details\">\n          <div><strong>Fecha:</strong> ${s.fecha}</div>\n          <div><strong>Horario:</strong> ${s.hora} \u2014 ${e.hora}</div>\n          <div><strong>Duraci\u00f3n:</strong> ${dur} min</div>\n          ${j.capacidad ? `<div><strong>Capacidad:</strong> ${j.capacidad}</div>` : ``}\n          ${j.ubicacion ? `<div><strong>Ubicaci\u00f3n:</strong> ${j.ubicacion}</div>` : ``}\n        </div>\n        <a href=\"${webhookLink}\" target=\"_blank\" class=\"btn-confirm\">Elegir esta sala (confirmar)</a>\n      </div>`;\n    opIndex++;\n  }\n}\n\n// ====== Resumen ======\nconst resumenHTML = `\n  <div class=\"info-grid\">\n    <div class=\"info-box\"><span>\u00c1rea / Departamento</span><strong>${area || '(sin \u00e1rea)'}</strong></div>\n    <div class=\"info-box\"><span>Participantes</span><strong>${participantes || '(sin participantes)'}</strong></div>\n    <div class=\"info-box\"><span>Solicitante</span><strong>${solicitanteNombre || '(sin nombre)'}</strong></div>\n    <div class=\"info-box\"><span>ID de Reserva</span><strong>${reservationId}</strong></div>\n  </div>\n`;\n\n// ====== HTML Final ======\nconst htmlBody = `<!doctype html>\n<html lang=\"es\">\n<head>\n<meta charset=\"utf-8\">\n<title>${subject}</title>\n<style>\n  body {\n    background-color: #F9F6EE;\n    font-family: 'Segoe UI', Roboto, Arial, sans-serif;\n    margin: 0;\n    padding: 40px 16px;\n    color: #111;\n  }\n  .card {\n    max-width: 720px;\n    margin: 0 auto;\n    background: #ffffff;\n    border-radius: 18px;\n    overflow: hidden;\n    box-shadow: 0 4px 20px rgba(0,0,0,0.25);\n  }\n  .header {\n    background: linear-gradient(90deg, #f4b400 0%, #fcd34d 100%);\n    color: #1f1f1f;\n    padding: 24px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n  }\n  .header h1 {\n    margin: 0;\n    font-size: 20px;\n    font-weight: 700;\n  }\n  .estado {\n    background: rgba(255,255,255,0.25);\n    color: #111;\n    padding: 6px 12px;\n    border-radius: 20px;\n    font-size: 13px;\n    font-weight: 600;\n    text-transform: uppercase;\n  }\n  .content {\n    padding: 28px;\n  }\n  .intro {\n    font-size: 15px;\n    color: #222;\n    margin-bottom: 20px;\n    line-height: 1.5;\n  }\n  .info-grid {\n    display: grid;\n    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n    gap: 14px;\n    margin-bottom: 24px;\n  }\n  .info-box {\n    border: 1px solid #e6e6eb;\n    border-radius: 10px;\n    padding: 12px 16px;\n    background: #fafafa;\n  }\n  .info-box span {\n    display: block;\n    font-size: 12px;\n    color: #666;\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n    margin-bottom: 4px;\n  }\n  .info-box strong {\n    font-size: 15px;\n    color: #111;\n  }\n  .option {\n    border: 1px solid #eee;\n    border-radius: 10px;\n    padding: 18px 20px;\n    margin-bottom: 16px;\n    background: #f9f9f9;\n  }\n  .option-title {\n    font-weight: 700;\n    margin-bottom: 8px;\n    color: #1a1a1a;\n  }\n  .option-details {\n    font-size: 14px;\n    color: #333;\n    line-height: 1.5;\n  }\n  .btn-confirm {\n    display: inline-block;\n    background-color: #111827;\n    color: #fff !important;\n    text-decoration: none;\n    font-weight: 600;\n    border-radius: 8px;\n    padding: 12px 18px;\n    margin-top: 14px;\n    transition: background 0.2s ease-in-out;\n  }\n  .btn-confirm:hover {\n    background-color: #1e293b;\n  }\n  .footer {\n    font-size: 12px;\n    color: #bbb;\n    text-align: center;\n    padding: 20px;\n  }\n</style>\n</head>\n<body>\n  <div class=\"card\">\n    <div class=\"header\">\n      <h1>Alternativas disponibles</h1>\n      <div class=\"estado\">En espera de respuesta</div>\n    </div>\n    <div class=\"content\">\n      <p class=\"intro\">Hola <strong>${solicitanteNombre || 'Usuario'}</strong>, tu solicitud no encontr\u00f3 disponibilidad exacta. \n      A continuaci\u00f3n se muestran las salas alternativas disponibles. Elige la que m\u00e1s te acomode para confirmar tu reserva.</p>\n      ${resumenHTML}\n      ${cardsHTML || `<p style=\"color:#888;\">No se encontraron opciones disponibles.</p>`}\n    </div>\n    <div class=\"footer\">\n      * Horarios expresados en <b>America/Santiago (GMT-3)</b>.<br/>\n      Si ninguna opci\u00f3n te acomoda, responde a este correo indicando un nuevo horario.\n    </div>\n  </div>\n</body>\n</html>`;\n\n// ====== Validaci\u00f3n ======\nif (!to) {\n  return [{ json: { to:'', subject:`[REVISAR] ${subject}`, htmlBody, warning:'No se encontr\u00f3 correo (to)' } }];\n}\n\n// ====== Salida ======\nreturn [{\n  json: {\n    to,\n    subject,\n    htmlBody,\n    totalOptions: libres.length,\n    area,\n    participantes,\n    solicitanteNombre,\n    reservationId\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -816,
        304
      ],
      "id": "3cce2011-97e4-45f6-8938-fc95fe5c5691",
      "name": "HTML"
    },
    {
      "parameters": {
        "calendar": {
          "__rl": true,
          "value": "={{ $json.calendarId }}",
          "mode": "id",
          "__regex": "(^[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)"
        },
        "start": "={{ $json.startIso }}",
        "end": "={{ $json.endIso }}",
        "additionalFields": {
          "attendees": [
            "={{ $json.correo }}"
          ],
          "description": "=ID de la reserva: [{{ $json.ReservaID }}]   Su sala fue confirmada exitosamente. \u00bfCuando? {{ $json.fechaLarga }} de {{ $json.inicioStr }} a {{ $json.finStr }} \u00bfDonde? {{ $json.room }} {{ $json.torre }} Capacidad total: {{ $json.participantes }}  ID de la reserva: [{{$json.ReservaID}}]",
          "summary": "={{ $json.summary }} || {{ $json.room }} || Reservada "
        }
      },
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        -896,
        704
      ],
      "id": "ca485eee-e879-4aad-8354-7b61abb9aa54",
      "name": "Create an event",
      "alwaysOutputData": false,
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "457d6c56-c575-4b50-ac28-eb01a01df333",
              "name": "ReservaID",
              "value": "={{ $json.ReservaID }}",
              "type": "string"
            },
            {
              "id": "6c13aaa1-84d4-47b2-9c52-b499d4976192",
              "name": "nombre",
              "value": "={{ $json.nombre }}",
              "type": "string"
            },
            {
              "id": "04520c70-36b9-493d-ad76-3c88764930c2",
              "name": "area",
              "value": "={{$json.area || $json.Area || $json[\"\u00e1rea\"] || $json[\"\u00c1rea\"] || $json.departamento || $json.Departamento}}",
              "type": "string"
            },
            {
              "id": "25919f02-40d2-4688-89d1-9ab729b32b97",
              "name": "participantes",
              "value": "={{$json.participantes || $json.Participantes || $json.asistentes || $json.participants}}",
              "type": "string"
            },
            {
              "id": "d231763d-a855-46ca-94f0-cc1c1774f093",
              "name": "correo",
              "value": "={{ $json.correo }}",
              "type": "string"
            },
            {
              "id": "b119c7a5-1a29-4f55-8cf3-c6a38a568e65",
              "name": "fecha",
              "value": "={{ $json.fechaStr }}",
              "type": "string"
            },
            {
              "id": "c0f88c80-52ac-4389-9546-cc6583964403",
              "name": "inicio",
              "value": "={{ $json.inicioStr }}",
              "type": "string"
            },
            {
              "id": "bda5dfe3-f395-45ce-b446-735a1db8e3f3",
              "name": "termino",
              "value": "={{ $json.finStr }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -2608,
        544
      ],
      "id": "f8aa6156-2a64-4fff-a112-8ad4e00cecc0",
      "name": "SET_META"
    },
    {
      "parameters": {
        "jsCode": "// === CONFIG (usa URL p\u00fablica y /webhook, no /webhook-test) ===\nconst CANCEL_WEBHOOK_URL = 'http://localhost:5678/webhook-test/cancelar-reserva';\n\nconst TZ = 'America/Santiago';\n\n// === INPUTS DESDE Merge/SET ===\nconst d = items[0].json;\nconst e  = items[1].json;\nconst correo       = d.correo;\nconst room         = d.room;\nconst fechaStr     = d.fechaStr;\nconst inicioStr    = d.inicioStr;\nconst finStr       = d.finStr;\nconst summary      = d.summary;\nconst participantes= d.participantes;\nconst nombre       = d.nombre;\nconst ReservaID    = d.ReservaID;   \nconst calendarID = d.calendarId;\nconst eventID = e.id;\n// <- en tu panel se llama as\u00ed\n\nfunction enc(v){ return encodeURIComponent(v == null ? '' : String(v)); }\n\n// Enlace de cancelaci\u00f3n (GET) \u2014 usa tus campos reales\nconst cancelLink =\n  `${CANCEL_WEBHOOK_URL}`\n  + `?accion=cancelar`\n  + `&reservationId=${enc(ReservaID)}`\n  + `&eventId=${enc(eventID)}`\n  + `&calendarId=${enc(calendarID)}`\n  + `&email=${enc(correo)}`\n  + `&room=${enc(room)}`\n  + `&fecha=${enc(fechaStr)}`\n  + `&inicio=${enc(inicioStr)}`\n  + `&fin=${enc(finStr)}`;\n\n\n// Asunto\nconst subject = `\u2705 Reserva confirmada \u2014 ${room || '-'} \u2014 ${ReservaID || ''}`;\n\n// === HTML con bot\u00f3n BULLETPROOF (tabla) ===\nconst htmlBody = `\n<div style=\"font-family: Arial, sans-serif; line-height: 1.6; color: #333333; max-width: 600px; margin: auto; border: 1px solid #dddddd; border-radius: 8px; overflow: hidden;\">\n  <div style=\"background-color: #4CAF50; color: #ffffff; padding: 20px; text-align: center;\">\n    <h1 style=\"margin: 0; font-size: 24px;\">\u2705 Reserva Confirmada Exitosamente</h1>\n  </div>\n\n  <div style=\"padding: 20px;\">\n    <p>Estimado(a) <strong>${nombre || ''}</strong>,</p>\n    <p>\u00a1Tu reserva fue registrada y agregada al calendario!</p>\n\n    <h2 style=\"color: #4CAF50; font-size: 18px; border-bottom: 1px solid #eeeeee; padding-bottom: 5px;\">Detalles de la Reserva</h2>\n\n    <table width=\"100%\" cellspacing=\"0\" cellpadding=\"8\" border=\"0\" style=\"border-collapse: collapse;\">\n      <tbody>\n        <tr style=\"background-color: #f4f4f4;\">\n          <td width=\"30%\" style=\"font-weight: bold; border: 1px solid #dddddd;\">Sala Reservada</td>\n          <td width=\"70%\" style=\"border: 1px solid #dddddd;\"><strong>${room || '-'}</strong></td>\n        </tr>\n        <tr>\n          <td style=\"font-weight: bold; border: 1px solid #dddddd;\">Fecha</td>\n          <td style=\"border: 1px solid #dddddd;\">${fechaStr || '-'}</td>\n        </tr>\n        <tr style=\"background-color: #f4f4f4;\">\n          <td style=\"font-weight: bold; border: 1px solid #dddddd;\">Hora de Inicio</td>\n          <td style=\"border: 1px solid #dddddd;\">${inicioStr || '-'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight: bold; border: 1px solid #dddddd;\">Hora de Fin</td>\n          <td style=\"border: 1px solid #dddddd;\">${finStr || '-'}</td>\n        </tr>\n        <tr style=\"background-color: #f4f4f4;\">\n          <td style=\"font-weight: bold; border: 1px solid #dddddd;\">Motivo</td>\n          <td style=\"border: 1px solid #dddddd;\">${summary || '-'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight: bold; border: 1px solid #dddddd;\">Participantes</td>\n          <td style=\"border: 1px solid #dddddd;\">${participantes || '-'}</td>\n        </tr>\n      </tbody>\n    </table>\n\n    <p style=\"margin-top: 12px; font-size: 12px; color: #666666;\">\n      <strong>Referencia:</strong> ID de Reserva <b>${ReservaID || '-'}</b>\n    </p>\n\n    <!-- Bot\u00f3n BULLETPROOF (tabla) -->\n    <table role=\"presentation\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\" align=\"center\" style=\"margin:16px auto;\">\n      <tr>\n        <td align=\"center\" bgcolor=\"#e53935\" style=\"\n          border-radius:6px;\n          mso-padding-alt:12px 18px;\n        \">\n          <a href=\"${cancelLink}\"\n             style=\"\n               display:block;\n               padding:12px 18px;\n               font-weight:bold;\n               text-decoration:none;\n               color:#ffffff;\n               font-family: Arial, sans-serif;\n             \">\n            \u274c Cancelar reserva\n          </a>\n        </td>\n      </tr>\n    </table>\n\n    <p style=\"font-size:12px; color:#666; margin-top:10px; text-align:center;\">\n      \u00bfEst\u00e1s viendo esto desde un correo? Si el bot\u00f3n no funciona, usa este enlace:<br/>\n      <a href=\"${cancelLink}\" style=\"color:#e53935; font-weight:bold; word-break:break-all;\">Cancelar mi reserva</a>\n    </p>\n  </div>\n\n  <div style=\"background-color: #f4f4f4; padding: 15px; text-align: center; border-top: 1px solid #dddddd;\">\n    <p style=\"margin: 0; font-size: 12px; color: #666666;\">Sistema de Reservas</p>\n  </div>\n</div>\n`;\n\nreturn [{\n  json: {\n    to: correo || d.emailDestino,\n    subject,\n    htmlBody,\n    ReservaID,\n    room,\n    nombre,\n    participantes,\n    summary,\n    inicioStr,\n    finStr,\n    fechaStr,\n    correo,\n    TZ,\n    calendarID,\n    eventID\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -512,
        800
      ],
      "id": "0173fc2b-e73e-410c-91fb-f37f66b073ad",
      "name": "Code in JavaScript1"
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "documentId": {
          "__rl": true,
          "value": "1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw",
          "mode": "list",
          "cachedResultName": "Registro Solicitud",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 1989465276,
          "mode": "list",
          "cachedResultName": "Respuestas de formulario 1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n66HEZRlQ2SeqWs0UVmgNoBYkN7gZvjlFvJYdpwwDlw/edit#gid=1989465276"
        },
        "event": "rowAdded",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        -2928,
        704
      ],
      "id": "b2072bb2-94eb-4dfb-a633-b7c970261df3",
      "name": "DatosFormulario1",
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "DatosFormulario": {
      "main": [
        []
      ]
    },
    "DatosCode": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          },
          {
            "node": "SET_META",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "guardarRegistro": {
      "main": [
        [
          {
            "node": "leerSalas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "leerSalas": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "filtrarCapacidad",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "filtrarCapacidad": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "GenerarAlternativas",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get many events",
            "type": "main",
            "index": 0
          },
          {
            "node": "SET",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get many events": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Create an event",
            "type": "main",
            "index": 0
          },
          {
            "node": "SET2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge2": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get availability in a calendar": {
      "main": [
        [
          {
            "node": "Merge3",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge3": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SET1": {
      "main": [
        [
          {
            "node": "Merge3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SET": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SET2": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GenerarAlternativas": {
      "main": [
        [
          {
            "node": "Get availability in a calendar",
            "type": "main",
            "index": 0
          },
          {
            "node": "SET1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML": {
      "main": [
        [
          {
            "node": "Send a message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create an event": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "SET_META": {
      "main": [
        [
          {
            "node": "guardarRegistro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DatosFormulario1": {
      "main": [
        [
          {
            "node": "DatosCode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "88195cd4-ed09-47a6-a8ea-64cdd7ae394f",
  "id": "Xoo4qSpn61iPp8AB",
  "tags": []
}