{
  "name": "debug",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "* 22-23 * * *"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -336,
        1040
      ],
      "id": "6edd981d-414b-4205-89f0-38880a68810a",
      "name": "Schedule Trigger1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://sync-service:3001/oponeo/scraper",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -64,
        1040
      ],
      "id": "8a2d84ea-18c5-4548-b851-d376eeb54dbf",
      "name": "Scrape Oponeo1",
      "retryOnFail": true
    },
    {
      "parameters": {
        "jsCode": "function transform_to_wo_format(reservation) {\n    const reservation_details = reservation.details\n\tconst [start, end] = reservation_details.time.split(' - ');\n\n\tconst [day, month, year] = reservation_details.date.split('-').map(Number);\n\tconst [startHour, startMinute] = start.split(':').map(Number);\n\tconst [endHour, endMinute] = end.split(':').map(Number);\n\n\tconst local_start_date = new Date(year, month - 1, day, startHour, startMinute);\n\tconst local_end_date = new Date(year, month - 1, day, endHour, endMinute);\n    const start_date_time = new Date(\n      local_start_date.getTime() - local_start_date.getTimezoneOffset() * 60000);\n    const end_date_time = new Date(\n      local_end_date.getTime()   - local_end_date.getTimezoneOffset() * 60000);\n  \n\treturn {\n        type: '1',\n\t\tworkspace: 13096,\n        time: 30,\n\t\tlicencePlate: reservation_details.registration_number || 'OPONEO',\n\t\tfirstname: reservation_details.client_name.split(\" \")[0] || 'Brak informacji',\n\t\tlastname: reservation_details.client_name.split(\" \")[1] || 'Brak informacji',\n\t\tphone: reservation_details.phone.replace(/\\D/g, '') || 'Brak informacji',\n\t\temail: reservation_details.email || '',\n        category: 1,\n        notes: {\n          oponeo_url: reservation.reservation_url || 'Brak informacji',\n          description: reservation.details.description || 'Brak informacji',\n        },\n\t\tfromDate: start_date_time,\n\t\ttoDate: end_date_time,\n        reservationType: \"1\",\n\t};\n}\nconst inputData = $input.all().map(i => i.json);\n\nif(inputData[0].data.length === 0){\n  return [];\n}\n\nconst transformed_data = [];\n\nfor (const reservation of inputData[0].data) {\n  transformed_data.push(transform_to_wo_format(reservation));\n}\n\nreturn transformed_data.map(item => ({ json: item }));\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        160,
        1040
      ],
      "id": "b06d7c9e-c130-42ea-aea5-0307e05b4b1c",
      "name": "transform to WO format1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.wymianaopon.pl/api/events",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.WO_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        544,
        1184
      ],
      "id": "e5587958-ca54-45c1-967b-adfbd3514932",
      "name": "POST to WO calendar1",
      "retryOnFail": true,
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "mode": "mergeByIndex"
      },
      "name": "Merge results1",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        704,
        1072
      ],
      "id": "9faf6de7-9739-47c5-98bf-7ab1f418694b"
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "C08UVQMGYN9",
          "mode": "id"
        },
        "messageType": "block",
        "blocksUi": "={\n  \"blocks\": [\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"*\u26a0\ufe0f Konflikt rezerwacji na Oponeo!*\"\n      }\n    },\n    {\n      \"type\": \"section\",\n      \"fields\": [\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Klient:*\\n{{ $json.firstname }} {{ $json.lastname }}\\n{{ $json.email }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Termin:*\\n\u2022 Od: {{ DateTime.fromISO($json.fromDate).setZone('utc').setLocale('pl').toFormat('d LLLL yyyy, HH:mm') }}\\n\u2022 Do: {{ DateTime.fromISO($json.toDate).setZone('utc').setLocale('pl').toFormat('d LLLL yyyy, HH:mm') }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Nr rejestracyjny:*\\n{{ $json.licencePlate }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Nr telefonu:*\\n{{ $json.phone }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Oponeo link:*\\n<{{ $json.notes.oponeo_url }}|Kliknij tutaj>\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*WO wolne terminy:*\\n<https://serwis.wymianaopon.pl/dostepne|Kliknij tutaj>\"\n        }\n      ]\n    },\n    {\n      \"type\": \"divider\"\n    },\n    {\n      \"type\": \"actions\",\n      \"elements\": [\n       {\n        \"type\": \"button\",\n        \"action_id\": \"delete_reservation\",\n        \"text\": {\n          \"type\": \"plain_text\",\n          \"text\": \"\ud83d\udcdd Odhaczona?\"\n        },\n        \"value\": \"{{ $json.notes.oponeo_url.split('/').pop() }}\",\n        \"confirm\": {\n          \"title\": {\n            \"type\": \"plain_text\",\n            \"text\": \"Potwierdzenie\"\n          },\n          \"text\": {\n            \"type\": \"mrkdwn\",\n            \"text\": \"Czy na pewno chcesz oznaczy\u0107 t\u0119 rezerwacj\u0119 jako odhaczon\u0105?\"\n          },\n          \"confirm\": {\n            \"type\": \"plain_text\",\n            \"text\": \"Odhacz\"\n          },\n          \"deny\": {\n            \"type\": \"plain_text\",\n            \"text\": \"Anuluj\"\n          }\n  }\n}\n      ]\n    }\n  ]\n}\n",
        "text": "={{ $json }}",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "link_names": false
        }
      },
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        2480,
        848
      ],
      "id": "28cfd11e-5192-4a28-ba8d-90d42e1c3817",
      "name": "Conflict alert1"
    },
    {
      "parameters": {
        "content": "DEBUG\n",
        "height": 420,
        "width": 640,
        "color": 3
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        144
      ],
      "id": "95b38a08-4ea2-41e3-a45e-d6fad49052a1",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "mode": "mergeByIndex"
      },
      "name": "Merge results",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        1360,
        672
      ],
      "id": "7aecc935-6b31-49a9-a2d0-55c281be5908"
    },
    {
      "parameters": {
        "operation": "get",
        "propertyName": "=reservation",
        "key": "={{ $json.reservationId }}",
        "options": {
          "dotNotation": true
        }
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        1104,
        640
      ],
      "id": "d3f0b31c-8f8a-43ef-956c-723d51654921",
      "name": "Check if exists"
    },
    {
      "parameters": {
        "mode": "mergeByKey",
        "propertyName1": "reservationId",
        "propertyName2": "reservationId"
      },
      "name": "Merge results2",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        2032,
        1040
      ],
      "id": "44186c50-dd9a-4336-b365-5313233abd24"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cee47e86-9b51-40d4-bcd9-62d3f859df49",
              "name": "reservationId",
              "value": "={{ $json.notes.oponeo_url.split('/').pop() }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        992,
        1072
      ],
      "id": "0f7d8842-10c7-4f1e-b1e7-4622aeac39dd",
      "name": "Add reservation id"
    },
    {
      "parameters": {
        "operation": "set",
        "key": "={{ $json.reservationId }}",
        "value": "={{ \n  JSON.stringify({\n    licencePlate: $json.licencePlate,\n    firstname: $json.firstname,\n    lastname: $json.lastname,\n    phone: $json.phone,\n    email: $json.email,\n    notes: $json.notes,\n    fromDate: $json.fromDate,\n    toDate: $json.toDate,\n    notification_status: \"pending\",\n  }) \n}}"
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        2288,
        1040
      ],
      "id": "acf9794b-bd93-4808-8213-478d3ca9a904",
      "name": "Redis1"
    },
    {
      "parameters": {
        "jsCode": "const reservation = JSON.parse($json.reservation);\n\n// Return the fields directly so they are available in the next node\nreturn [{ json: reservation }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2272,
        640
      ],
      "id": "6e097197-1eb9-4c14-8098-1a185c7e3d30",
      "name": "Code"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "3bec3317-fe9a-4b3a-8f4a-1474e386e10d",
                    "leftValue": "={{(() => {\n  const reservation = JSON.parse($json.reservation);\n  return reservation.notification_status === 'resolved';\n})()}}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "31f8f4a2-aa0e-4cf1-8896-bdda238bac5b",
                    "leftValue": "={{(() => {\n  const reservation = JSON.parse($json.reservation);\n  return reservation.notification_status === 'pending';\n})()}}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "054abd6e-015a-48ae-b826-09c10cce0179",
                    "leftValue": "={{(() => {\n  const reservation = JSON.parse($json.reservation);\n  return !reservation && !!$json.error;\n})()}}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "7d59c735-e164-4c65-9477-d95e757ad45e",
                    "leftValue": "={{(() => {\n  const reservation = JSON.parse($json.reservation);\n  return !reservation && !$json.error;\n})()}}",
                    "rightValue": "",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "looseTypeValidation": true,
        "options": {
          "allMatchingOutputs": false
        }
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        1584,
        640
      ],
      "id": "dcd7e1ae-f2d7-4c8a-882f-c7b742d84bc7",
      "name": "Notification Determinator"
    },
    {
      "parameters": {
        "mode": "mergeByKey",
        "propertyName1": "reservationId",
        "propertyName2": "reservationId"
      },
      "name": "Merge results3",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        1408,
        928
      ],
      "id": "2aa10340-e54b-4b3c-80eb-590904248eb2"
    },
    {
      "parameters": {
        "operation": "set",
        "key": "={{ $json.reservationId }}",
        "value": "={{ \n  JSON.stringify({\n    licencePlate: $json.licencePlate,\n    firstname: $json.firstname,\n    lastname: $json.lastname,\n    phone: $json.phone,\n    email: $json.email,\n    notes: $json.notes,\n    fromDate: $json.fromDate,\n    toDate: $json.toDate,\n    notification_status: \"resolved\",\n  }) \n}}"
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        1600,
        912
      ],
      "id": "755920c7-f328-4f34-a2e0-cb9fa06896d1",
      "name": "Redis"
    },
    {
      "parameters": {
        "content": "## 0 - reservation acknowledged\n## 1 - reservation waiting for acknowledge (loop)\n## 2 - reservation waiting for acknowledge\n## 3 - reservation without conflict\n",
        "height": 300,
        "width": 360,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1584,
        320
      ],
      "id": "afa5ed26-b2a6-4fa1-9e86-e7624866c5bc",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "4bf10bb5-782f-40ce-b82d-90051f41a45f",
              "name": "notes",
              "value": "={{ $json.notes.description }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": "={{ true }}",
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        352,
        1184
      ],
      "id": "08278236-3d03-44e0-9341-7ad493d5f803",
      "name": "Take only desc from notes"
    },
    {
      "parameters": {
        "fromEmail": "calendar-syncer@digito.pl",
        "toEmail": "przemswiercz@gmail.com",
        "subject": "\u274c Synchronizacja kalendarzy nie powiod\u0142a si\u0119",
        "html": "{{(() => {\n  const data = $json;\n  const from = DateTime.fromISO(data.startDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const to = DateTime.fromISO(data.endDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const reservationId = (data.notes || '').split('/').pop();\n\n  return `\n    <div style=\"max-width:600px; margin:auto; font-family:Arial,sans-serif; background:#ffffff; border-radius:10px; padding:24px; border:1px solid #e0e0e0; color:#333333; box-shadow:0 2px 6px rgba(0,0,0,0.05);\">\n      <p style=\"text-align:center; font-size:15px; margin-bottom:30px;\">Godzina <strong>Do</strong> wykracza poza normalne godziny pracy:</p>\n      \n      <table cellpadding=\"10\" cellspacing=\"0\" style=\"width:100%; font-size:15px; border-collapse:collapse;\">\n        <tr>\n          <td style=\"font-weight:bold; width:40%;\">Termin:</td>\n          <td>\n            \u2022 Od: ${from}<br/>\n            \u2022 Do: ${to}\n          </td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Numer rejestracyjny:</td>\n          <td>${data.licencePlate || 'Brak'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Telefon:</td>\n          <td>${data.prefix || ''} ${data.phoneNumber || 'Brak'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Edytuj rezerwacj\u0119:</td>\n          <td><a href=\"https://serwis.wymianaopon.pl/zaplanowane\" style=\"color:#0d6efd;\" target=\"_blank\">Kliknij tutaj</a></td>\n        </tr>\n      </table>\n  `;\n})()}}\n"
      },
      "type": "n8n-nodes-base.mailgun",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "id": "9398fc8d-390e-43a5-8f63-8a2dcfc7d029",
      "name": "Mailgun"
    },
    {
      "parameters": {
        "fromEmail": "calendar-syncer@digito.pl",
        "toEmail": "przemswiercz@gmail.com",
        "subject": "Konflikt rezerwacji na Oponeo!",
        "text": "=",
        "html": "={{(() => {\n  const data = $json;\n\n  // Format Polish date range\n  const from = DateTime.fromISO(data.fromDate).setZone('utc').setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const to = DateTime.fromISO(data.toDate).setZone('utc').setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n\n  // Extract reservation ID from Oponeo link\n  const reservationId = data.notes.oponeo_url.split('/').pop();\n\n  return `\n    <div style=\"max-width:600px; margin:auto; font-family:Arial,sans-serif; background:#ffffff; border-radius:10px; padding:24px; border:1px solid #e0e0e0; color:#333333; box-shadow:0 2px 6px rgba(0,0,0,0.05);\">\n      \n      <div style=\"text-align:center; margin-bottom:24px;\">\n        <img src=\"https://blinksoft.pl/assets/opony.png\" alt=\"Logo\" style=\"max-width:50px; height:auto;\" />\n      </div>\n\n      <h2 style=\"text-align:center; color: #000000;\">\u26a0\ufe0f Konflikt rezerwacji Oponeo!</h2>\n      \n      <table cellpadding=\"10\" cellspacing=\"0\" style=\"width:100%; font-size:15px; border-collapse:collapse;\">\n        <tr>\n          <td style=\"font-weight:bold; width:40%;\">Imi\u0119:</td>\n          <td>${data.firstname}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Nazwisko:</td>\n          <td>${data.lastname}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Numer Rejestracyjny:</td>\n          <td>${data.licencePlate}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Telefon:</td>\n          <td>${data.phone}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Email:</td>\n          <td>${data.email || 'Brak informacji'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Data:</td>\n          <td>${from} \u2013 ${to}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Oponeo link:</td>\n          <td><a href=\"${data.notes.oponeo_url}\" style=\"color:#0d6efd;\" target=\"_blank\">Kliknij tutaj</a></td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">WO wolne terminy:</td>\n          <td><a href=\"https://serwis.wymianaopon.pl/dostepne\" style=\"color:#0d6efd;\" target=\"_blank\">Kliknij tutaj</a></td>\n        </tr>\n      </table>\n\n      <div style=\"text-align:center; margin-top:30px;\">\n        <a href=\"https://own8n.bieda.it/webhook/74730634-b4fb-4598-ab88-7d37e98ab843?reservationId=${reservationId}\" \n           target=\"_blank\"\n           style=\"background-color:#198754; color:#ffffff; padding:14px 28px; text-decoration:none; font-weight:bold; border-radius:6px; display:inline-block; font-size:16px;\">\n          \u2705 Odhacz\n        </a>\n      </div>\n\n      <p style=\"font-size:12px; color:#777; margin-top:20px; text-align:center;\">\n         Aby wi\u0119cej nie dostawa\u0107 notyfikacji na temat tej rezerwacji.\n      </p>\n    </div>\n  `;\n})()}}\n"
      },
      "type": "n8n-nodes-base.mailgun",
      "typeVersion": 1,
      "position": [
        2784,
        848
      ],
      "id": "a6108d1a-dd64-481b-a142-08b5a3407ca9",
      "name": "Mailgun1"
    },
    {
      "parameters": {
        "fromEmail": "przemswiercz@gmail.com",
        "toEmail": "info@wladcysieci.pl",
        "subject": "Zg\u0142oszenie - Dzie\u0144 Admina 2025",
        "html": "<!DOCTYPE html>\n<html lang=\"pl\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\t\t<title>LEGO Remote Power System - Konkurs Dzie\u0144 Admina</title>\n\t</head>\n\t<body\n\t\tstyle=\"\n\t\t\tmargin: 0;\n\t\t\tpadding: 0;\n\t\t\tfont-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n\t\t\t\tsans-serif;\n\t\t\tbackground-color: #ffffff;\n\t\t\tcolor: #1a1a1a;\n\t\t\tline-height: 1.6;\n\t\t\"\n\t>\n\t\t<table\n\t\t\tcellpadding=\"0\"\n\t\t\tcellspacing=\"0\"\n\t\t\tborder=\"0\"\n\t\t\twidth=\"100%\"\n\t\t\tstyle=\"background-color: #ffffff\"\n\t\t>\n\t\t\t<tr>\n\t\t\t\t<td align=\"center\" style=\"padding: 40px 20px\">\n\t\t\t\t\t<table\n\t\t\t\t\t\tcellpadding=\"0\"\n\t\t\t\t\t\tcellspacing=\"0\"\n\t\t\t\t\t\tborder=\"0\"\n\t\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\t\tstyle=\"max-width: 680px\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<!-- Main Title with LEGO Logo -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 48px; text-align: center\">\n\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_logo.png\"\n\t\t\t\t\t\t\t\t\talt=\"LEGO\"\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\theight: 60px;\n\t\t\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\t\t\tmargin-bottom: 24px;\n\t\t\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<h3\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tfont-weight: 700;\n\t\t\t\t\t\t\t\t\t\tmargin: 0 0 16px 0;\n\t\t\t\t\t\t\t\t\t\tcolor: #1a1a1a;\n\t\t\t\t\t\t\t\t\t\tletter-spacing: -0.02em;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\ud83e\uddd1\u200d\ud83c\udf3e LEGO Remote Power Btn\n\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- Intro Card -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 32px\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #f9fafb;\n\t\t\t\t\t\t\t\t\t\tborder: 1px solid #e5e7eb;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\t\t\tpadding: 24px;\n\t\t\t\t\t\t\t\t\t\tbox-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\t\t\t\t\t\tcolor: #374151;\n\t\t\t\t\t\t\t\t\t\t\tline-height: 1.7;\n\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tCze\u015b\u0107! Nazywam si\u0119 Przemek i po godzinach lubi\u0119 sobie\n\t\t\t\t\t\t\t\t\t\t\"potinkerowa\u0107\" :). Stworzy\u0142em wi\u0119c proste, ale przydatne\n\t\t\t\t\t\t\t\t\t\trozwi\u0105zanie, dzi\u0119ki kt\u00f3remu mog\u0119 fizycznie w\u0142\u0105czy\u0107 zdalnie laptopa.\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- Main Image Section -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 0px; text-align: center;\">\n\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\tcolor: #94a3b8;\n\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 8px 0;\n\t\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\t\t\t\t\t\t\tletter-spacing: 0.05em;\n\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_main.gif\"\n\t\t\t\t\t\t\t\t\t\t\talt=\"LEGO\"\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- Problem Section -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 48px\">\n\t\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tfont-size: 28px;\n\t\t\t\t\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\t\t\t\t\tmargin: 0 0 24px 0;\n\t\t\t\t\t\t\t\t\t\tcolor: #1a1a1a;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tWyzwanie:\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #fef3c7;\n\t\t\t\t\t\t\t\t\t\tborder: 1px solid #fde68a;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\t\t\t\t\tpadding: 20px;\n\t\t\t\t\t\t\t\t\t\tmargin-bottom: 16px;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<p style=\"margin: 0; color: #92400e; font-size: 16px\">\n\t\t\t\t\t\t\t\t\t\tCodziennie uruchamianie\tlaptopa, kt\u00f3ry zarz\u0105dza 13 x WYSE do testowania\n\t\t\t\t\t\t\t\t\t\tnaszej aplikacji. Ca\u0142y setup zlokalizowany jest daleko od naszych biurek (w samym rogu biura).\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<p style=\"font-size: 16px; color: #4b5563; margin: 0\">\n\t\t\t\t\t\t\t\t\tDlatego zamiast tradycyjnego Wake-on-LAN, postanowi\u0142em zbudowa\u0107 co\u015b\n\t\t\t\t\t\t\t\t\tbardziej kreatywnego co pozwoli\u0142o mi si\u0119 zapozna\u0107 z lutownic\u0105 :).\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- How It Works -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 48px\">\n\t\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tfont-size: 28px;\n\t\t\t\t\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\t\t\t\t\tmargin: 0 0 24px 0;\n\t\t\t\t\t\t\t\t\t\tcolor: #1a1a1a;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tJak to dzia\u0142a?\n\t\t\t\t\t\t\t\t</h2>\n\n\t\t\t\t\t\t\t\t<!-- Step 1 -->\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #ffffff;\n\t\t\t\t\t\t\t\t\t\tborder: 1px solid #e5e7eb;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\t\t\t\t\tpadding: 20px;\n\t\t\t\t\t\t\t\t\t\tmargin-bottom: 16px;\n\t\t\t\t\t\t\t\t\t\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div style=\"display: flex; align-items: start\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\tcolor: white;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-weight: 700;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\t\t\tpadding: 4px 10px;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 100px;\n\t\t\t\t\t\t\t\t\t\t\t\tmargin-right: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>1\ufe0f\u20e3</span\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<h3\n\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 8px 0;\n\t\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: #111827;\n\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tHTTP Request\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t\t<p style=\"margin: 0; color: #6b7280; font-size: 15px\">\n\t\t\t\t\t\t\t\t\t\t\t\tProste \u017c\u0105danie HTTP do ESP32 z dowolnego miejsca\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #1e293b;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\t\t\t\t\tpadding: 24px;\n\t\t\t\t\t\t\t\t\t\tfont-family: 'Menlo', 'Monaco', 'Courier New', monospace;\n\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\tline-height: 1.5;\n\t\t\t\t\t\t\t\t\t\toverflow-x: auto;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span style=\"color: #c084fc;\">// \u2514\u2500$ alias togglemf</span><br>\n\t\t\t\t\t\t\t\t<span style=\"color: #86efac;\">togglemf='curl http://192.168.1.128/toggle_power'</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<!-- Step 2 -->\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #ffffff;\n\t\t\t\t\t\t\t\t\t\tborder: 1px solid #e5e7eb;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\t\t\t\t\tpadding: 20px;\n\t\t\t\t\t\t\t\t\t\tmargin-bottom: 16px;\n\t\t\t\t\t\t\t\t\t\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div style=\"display: flex; align-items: start\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\tcolor: white;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-weight: 700;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\t\t\tpadding: 4px 10px;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 100px;\n\t\t\t\t\t\t\t\t\t\t\t\tmargin-right: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>2\ufe0f\u20e3</span\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<h3\n\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 8px 0;\n\t\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: #111827;\n\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tESP32 + WiFi\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t\t<p style=\"margin: 0; color: #6b7280; font-size: 15px\">\n\t\t\t\t\t\t\t\t\t\t\t\tMikrokontroler odbiera sygna\u0142 i uruchamia serwomechanizm\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<!-- Step 3 -->\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #ffffff;\n\t\t\t\t\t\t\t\t\t\tborder: 1px solid #e5e7eb;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\t\t\t\t\tpadding: 20px;\n\t\t\t\t\t\t\t\t\t\tmargin-bottom: 16px;\n\t\t\t\t\t\t\t\t\t\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div style=\"display: flex; align-items: start\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\tcolor: white;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-weight: 700;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\t\t\tpadding: 4px 10px;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 100px;\n\t\t\t\t\t\t\t\t\t\t\t\tmargin-right: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>3\ufe0f\u20e3</span\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<h3\n\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 8px 0;\n\t\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: #111827;\n\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tLudzik rusza wid\u0142ami\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t\t<p style=\"margin: 0; color: #6b7280; font-size: 15px\">\n\t\t\t\t\t\t\t\t\t\t\t\tLudzik LEGO fizycznie wciska przycisk power na laptopie\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<!-- Step 4 -->\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground-color: #ffffff;\n\t\t\t\t\t\t\t\t\t\tborder: 1px solid #e5e7eb;\n\t\t\t\t\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\t\t\t\t\tpadding: 20px;\n\t\t\t\t\t\t\t\t\t\tbox-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div style=\"display: flex; align-items: start\">\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\tbackground-color: #10b981;\n\t\t\t\t\t\t\t\t\t\t\t\tcolor: white;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-weight: 700;\n\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\t\t\tpadding: 4px 10px;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 100px;\n\t\t\t\t\t\t\t\t\t\t\t\tmargin-right: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\u2713</span\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<h3\n\t\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 8px 0;\n\t\t\t\t\t\t\t\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: #111827;\n\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tLaptop uruchamia si\u0119 i automatycznie startuje 13\n\t\t\t\t\t\t\t\t\t\t\t\tkomputer\u00f3w testowych!\n\t\t\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 0px; text-align: center;\">\n\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\tcolor: #94a3b8;\n\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 8px 0;\n\t\t\t\t\t\t\t\t\t\t\tfont-size: 14px;\n\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_second.gif\"\n\t\t\t\t\t\t\t\t\t\t\talt=\"LEGO\"\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_full.jpg\"\n\t\t\t\t\t\t\t\t\t\t\talt=\"LEGO\"\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- Photo Gallery -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 24px\">\n\t\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tfont-size: 28px;\n\t\t\t\t\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\t\t\t\t\tmargin: 24px 0 8px 0;\n\t\t\t\t\t\t\t\t\t\tcolor: #1a1a1a;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\u2699\ufe0f Galeria\n\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t\t\t<p style=\"\n\t\t\t\t\t\t\t\t\t\t\tfont-size: 16px;\n\t\t\t\t\t\t\t\t\t\t\tcolor: #6b7280;\n\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 0 0;\n\t\t\t\t\t\t\t\t\t\t\ttext-align: center;\n\t\t\t\t\t\t\t\t\t\t\tline-height: 1.6;\n\t\t\t\t\t\t\t\t\t\t\">\n\t\t\t\t\t\t\t\t\t\t\tNa poni\u017cszych zdj\u0119ciach przedstawiam kr\u00f3tki proces tworzenia mojego LEGO power button.\n\t\t\t\t\t\t\t\t\t\t</p>\n<tr>\n\t<td style=\"padding-bottom: 24px\">\n\t\t<tr>\n\t\t\t<td style=\"text-align: center;\">\n\t\t\t\t<div\n\t\t\t\t\tstyle=\"\n\t\t\t\t\t\tbackground-color: #f3f4f6;\n\t\t\t\t\t\tborder-radius: 8px;\n\t\t\t\t\t\tpadding: 20px;\n\t\t\t\t\t\ttext-align: center;\n\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\twidth: 100%;\n\t\t\t\t\t\tbox-sizing: border-box;\n\t\t\t\t\t\"\n\t\t\t\t>\n\t\t\t\t\t<div style=\"text-align: center; line-height: 0;\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_esp.jpg\"\n\t\t\t\t\t\t\talt=\"ESP32 z komponentami\"\n\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\tmargin: 8px;\n\t\t\t\t\t\t\t\tvertical-align: top;\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_bold.jpg\"\n\t\t\t\t\t\t\talt=\"Lutowanie komponent\u00f3w\"\n\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\tmargin: 8px;\n\t\t\t\t\t\t\t\tvertical-align: top;\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_random.jpg\"\n\t\t\t\t\t\t\talt=\"Monta\u017c mechanizmu\"\n\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\tmargin: 8px;\n\t\t\t\t\t\t\t\tvertical-align: top;\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_glue.jpg\"\n\t\t\t\t\t\t\talt=\"Finalne po\u0142\u0105czenia\"\n\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\tmargin: 8px;\n\t\t\t\t\t\t\t\tvertical-align: top;\n\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t</td>\n</tr>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- Why Section -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding-bottom: 48px\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\tbackground: linear-gradient(\n\t\t\t\t\t\t\t\t\t\t\t135deg,\n\t\t\t\t\t\t\t\t\t\t\t#6366f1 0%,\n\t\t\t\t\t\t\t\t\t\t\t#8b5cf6 100%\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\t\t\tpadding: 32px;\n\t\t\t\t\t\t\t\t\t\tmargin-top: 24px;\n\t\t\t\t\t\t\t\t\t\ttext-align: center;\n\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<h2\n\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\tcolor: #ffffff;\n\t\t\t\t\t\t\t\t\t\t\tfont-size: 24px;\n\t\t\t\t\t\t\t\t\t\t\tfont-weight: 600;\n\t\t\t\t\t\t\t\t\t\t\tmargin: 0 0 0 0;\n\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tCiekawostka?\n\t\t\t\t\t\t\t\t\t</h2>\n\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\tcolor: #e0e7ff;\n\t\t\t\t\t\t\t\t\t\t\tfont-size: 17px;\n\t\t\t\t\t\t\t\t\t\t\tline-height: 1.7;\n\t\t\t\t\t\t\t\t\t\t\tmargin: 0;\n\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tPierwotnie ca\u0142o\u015b\u0107 by\u0142a...kopark\u0105 :)\n\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc=\"https://blinksoft.pl/assets/LEGO_excavator.jpg\"\n\t\t\t\t\t\t\t\t\t\t\talt=\"LEGO\"\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\t\t\t\theight: 200px;\n\t\t\t\t\t\t\t\t\t\t\t\twidth: auto;\n\t\t\t\t\t\t\t\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\t\t\t\t\t\t\t\tborder-radius: 12px;\n\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\n\t\t\t\t\t\t<!-- Footer -->\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\tstyle=\"\n\t\t\t\t\t\t\t\t\tpadding: 32px 0;\n\t\t\t\t\t\t\t\t\ttext-align: center;\n\t\t\t\t\t\t\t\t\tborder-top: 1px solid #e5e7eb;\n\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<p style=\"color: #6b7280; margin: 0; font-size: 14px\">\n\t\t\t\t\t\t\t\t\tProjekt zg\u0142oszony do konkursu z okazji\n\n\t\t\t\t\t\t\t\t\t<a href=\"https://wladcysieci.pl/2025/07/25/dzien-admina-2025-pokaz-kim-jestes-po-godzinach-bo-admin-tez-czlowiek/\"><strong>Dnia Admina 2025</strong></a>\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td style=\"padding: 32px 0; border-top: 1px solid #e5e7eb;\">\n\t\t\t\t\t\t\t\t<div style=\"color: #6b7280; font-size: 14px; line-height: 1.8;\">\n\t\t\t\t\t\t\t\t\t<div><strong>Imi\u0119 i nazwisko:</strong> Przemys\u0142aw \u015awiercz</div>\n\t\t\t\t\t\t\t\t\t<div><strong>Adres:</strong> \u015awi\u0119tego Micha\u0142a 100/310</div>\n\t\t\t\t\t\t\t\t\t<div><strong>Kod pocztowy i miasto:</strong> 61-005 Pozna\u0144</div>\n\t\t\t\t\t\t\t\t\t<div><strong>Telefon:</strong> +48 667 966 255</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</table>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</table>\n\t</body>\n</html>\n",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        2800,
        1456
      ],
      "id": "a471b921-cd72-4caa-9f87-f13cacb2439f",
      "name": "Send email"
    },
    {
      "parameters": {
        "mode": "mergeByIndex"
      },
      "name": "Merge results4",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        128,
        2368
      ],
      "id": "500bb05e-bc1d-47fe-9266-cde9876f6a1e"
    },
    {
      "parameters": {
        "jsCode": "// n8n Custom Code Node\n// This code compares redisValue times with item dates\n// Get input items\nconst input = $input.all()[0].json;\n\nif (input.withinHoursReservations.length < 0) {\n  return [];\n}\n\nconst {withinHoursReservations} = input;\n\nconst convertToZulu = (timestamp) => {\n  return new Date(timestamp.replace(/(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})\\+\\d{2}:\\d{2}$/, '$1.000Z'));\n};\n\n// Process each item\nconst outputItems = withinHoursReservations.map((item) => {\n    if(!item.redisValue) return null;\n  \n    // Parse the redisValue JSON string\n    const redisData = JSON.parse(item.redisValue);\n    \n    // Extract Redis times\n    const redisStartTime = new Date(redisData.startTime);\n    const redisEndTime = new Date(redisData.endTime);\n    \n    // Get item dates\n    const itemStartDate = item.startDate ? convertToZulu(item.startDate) : null;\n    const itemEndDate = item.endDate ? convertToZulu(item.endDate) : null;\n    \n    // Perform comparisons\n    const startTimeMatch = itemStartDate ? redisStartTime.getTime() === itemStartDate.getTime() : false;\n    const endTimeMatch = itemEndDate ? redisEndTime.getTime() === itemEndDate.getTime() : false;\n    const timesMatch = startTimeMatch && endTimeMatch;\n    \n    // Only return item if times DON'T match\n    if (!timesMatch) {\n      return {\n        json: {\n            oponeoReservationId: redisData.oponeoReservationId,\n        }\n      };\n    }\n    \n    // Return null for matching times (will be filtered out)\n    return null;\n \n}).filter(item => item !== null); // Remove null items\n\nreturn outputItems;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        592,
        2112
      ],
      "id": "8977c96f-2ec7-46cf-a16b-e6b2652ea4af",
      "name": "Code2"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "af150d69-a5ea-487c-b80a-4f55ac939e3b",
              "name": "=id",
              "value": "={{ $json.id.toString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -320,
        2448
      ],
      "id": "7fd8dc3e-5691-45a0-8c1a-db1b99fa68da",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "operation": "get",
        "propertyName": "=redisValue",
        "key": "=WO:{{ $json.id }}",
        "options": {}
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        -96,
        2448
      ],
      "id": "ae04c0cf-77ea-4bea-b0cb-f069523cddc1",
      "name": "Redis3",
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fieldToSplitOut": "data.items",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        -544,
        2352
      ],
      "id": "e4200201-662d-4322-bb0c-2d6f6758d759",
      "name": "Split Out"
    },
    {
      "parameters": {
        "jsCode": "// This code prepares Redis-ready key-value pairs from nested reservation objects\nconst results = $input.first().json.results || [];\n\nreturn results.map(item => {\n  return {\n    json: {\n      key: String(item.reservation.woReservationId),\n      value: JSON.stringify({\n        oponeoReservationId: item.reservationId,\n        startTime: item.startTime,\n        endTime: item.endTime,\n        lastSeen: new Date().toISOString(),\n      }),\n    }\n  };\n});\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1664,
        2368
      ],
      "id": "2ac3fbf6-1e6f-4738-b6d4-cf0f5bbd445d",
      "name": "Code1"
    },
    {
      "parameters": {
        "operation": "set",
        "key": "=WO:{{ $json.key }}",
        "value": "={{ $json.value }}"
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        1920,
        2368
      ],
      "id": "770c62ec-204a-4a7b-b526-68320547e203",
      "name": "Redis2",
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "if ($input.first().json.outOfHoursReservations.length > 0) {\n  return $input.first().json.outOfHoursReservations\n} else {\n  return [];\n}"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        608,
        2608
      ],
      "id": "e7b90b41-581d-432f-af10-27f9a82654a9",
      "name": "Check for out of hours reservations"
    },
    {
      "parameters": {
        "jsCode": "const inputData = $input.all().map(i => i.json);\nreturn [\n  {\n    json: (() => {\n      const outOfHoursReservations = [];\n      const withinHoursReservations = [];\n      let hour;\n      \n      for (const reservation of inputData) {\n\n        // fresh resrvations comes with UTC while edited are in Europe/Warsaw Tz ]:\n        const isWarsawWinterTz = reservation.endDate.match(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+01:00$/);\n        const endDate = new Date(reservation.endDate);\n        \n        if(isWarsawWinterTz) {\n          hour = endDate.getUTCHours() + 1;\n        } else {\n          hour = endDate.getUTCHours() + 2;\n        }\n        \n        const redisValue = JSON.parse(reservation.redisValue);\n        const minutes = endDate.getUTCMinutes();\n        const isOutsideWorkingHours = hour > 17 || (hour === 17 && minutes > 0);\n\n        if (isOutsideWorkingHours) {\n          outOfHoursReservations.push(reservation);\n        } else if (!redisValue) {\n          withinHoursReservations.push(reservation);\n        } else if (redisValue && redisValue.lastSeen < reservation.updatedAt) {\n          console.log(redisValue.lastSeen, reservation)\n          withinHoursReservations.push(reservation);\n        }\n      }\n      \n      return {\n        outOfHoursReservations,\n        withinHoursReservations,\n      };\n    })()\n  }\n];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        368,
        2368
      ],
      "id": "d06da6a6-c052-499a-b504-13b27179651b",
      "name": "Reservation Splitter"
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "C08UVQMGYN9",
          "mode": "id"
        },
        "messageType": "block",
        "blocksUi": "={\n  \"blocks\": [\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"*\u274c Synchronizacja dla poni\u017cszej rezerwacji nie powiod\u0142a si\u0119, godzina 'Do' wykracza poza normalne godziny pracy:*\"\n      }\n    },\n    {\n      \"type\": \"section\",\n      \"fields\": [\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Termin:*\\n\u2022 Od: {{ DateTime.fromISO($json.startDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm') }}\\n\u2022 Do: {{ DateTime.fromISO($json.endDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm') }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Nr rejestracyjny:*\\n{{ $json.licencePlate || 'Brak' }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Telefon:*\\n{{ $json.prefix || '' }} {{ $json.phoneNumber || 'Brak' }}\"\n        }\n      ]\n    },\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"\\n<https://serwis.wymianaopon.pl/zaplanowane|Edytuj rezerwacj\u0119 bezpo\u015brednio w WO>\"\n      }\n    },\n    {\n      \"type\": \"divider\"\n    }\n  ]\n}\n",
        "text": "={{ $json }}",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "link_names": false
        }
      },
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        928,
        2736
      ],
      "id": "a2bf5171-96bc-4f45-89e7-b3add7dab9a4",
      "name": "Conflict alert"
    },
    {
      "parameters": {
        "jsCode": "// n8n JavaScript Code Node\nconst input = $input.first().json\n\nif (input.withinHoursReservations.length < 0) {\n  return [];\n}\n\nconst {withinHoursReservations} = input;\nconst TICKS_PER_MILLISECOND = 10000;\nconst EPOCH_TICKS_AT_UNIX_EPOCH = 621355968000000000;\n\nfunction convertToUnixEpochWarsaw(isoDateString) {\n\tif (!isoDateString) return null;\n\ttry {\n\t\tconst date = new Date(isoDateString);\n\t\tif (isNaN(date.getTime())) return null;\n\n\t\t// Convert to Warsaw time\n\t\tconst formatter = new Intl.DateTimeFormat('pl-PL', {\n\t\t\ttimeZone: 'Europe/Warsaw',\n\t\t\tyear: 'numeric',\n\t\t\tmonth: '2-digit',\n\t\t\tday: '2-digit',\n\t\t\thour: '2-digit',\n\t\t\tminute: '2-digit',\n\t\t\tsecond: '2-digit',\n\t\t});\n\n\t\tconst parts = formatter.formatToParts(date).reduce((acc, part) => {\n\t\t\tacc[part.type] = part.value;\n\t\t\treturn acc;\n\t\t}, {});\n\n\t\t// Construct local datetime string in Warsaw time\n\t\tconst warsawDateString = `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}`;\n\t\tconst warsawDate = new Date(warsawDateString);\n\n\t\treturn warsawDate.getTime() * TICKS_PER_MILLISECOND + EPOCH_TICKS_AT_UNIX_EPOCH;\n\t} catch (error) {\n\t\tconsole.warn(`Failed Warsaw conversion for ${isoDateString}`, error);\n\t\treturn null;\n\t}\n}\n\nfunction parseEvent(event) {\n\treturn {\n\t\twoReservationId: event.id || null,\n\t\tphoneNumber: event.phoneNumber || null,\n\t\tlicencePlate: event.licencePlate || null,\n\t\tstartDate: convertToUnixEpochWarsaw(event.startDate),\n\t\tendDate: convertToUnixEpochWarsaw(event.endDate),\n        redisValue: event.redisValue,\n\t};\n}\n\nconst output = withinHoursReservations.map((item) => parseEvent(item)) || [];\n\nreturn [{\n  json: {\n    debug_mode: false,\n    reservations: output.map(item => item)\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1152,
        2368
      ],
      "id": "25090489-f82a-47f8-b9a9-4390c5312b1c",
      "name": "Prepare data for Oponeo"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 15 * 1 sun"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -1264,
        2352
      ],
      "id": "7357cb69-b5f6-4f90-b8ea-32b2b6fd1468",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "fromEmail": "przemswiercz@gmail.com",
        "toEmail": "przemek.swiercz@mindyra.com",
        "subject": "\u274c Synchronizacja kalendarzy nie powiod\u0142a si\u0119",
        "html": "={{(() => {\n  const data = $json;\n  const from = DateTime.fromISO(data.startDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const to = DateTime.fromISO(data.endDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const reservationId = (data.notes || '').split('/').pop();\n\n  return `\n    <div style=\"max-width:600px; margin:auto; font-family:Arial,sans-serif; background:#ffffff; border-radius:10px; padding:24px; border:1px solid #e0e0e0; color:#333333; box-shadow:0 2px 6px rgba(0,0,0,0.05);\">\n      <p style=\"text-align:center; font-size:15px; margin-bottom:30px;\">Godzina <strong>Do</strong> wykracza poza normalne godziny pracy:</p>\n      \n      <table cellpadding=\"10\" cellspacing=\"0\" style=\"width:100%; font-size:15px; border-collapse:collapse;\">\n        <tr>\n          <td style=\"font-weight:bold; width:40%;\">Termin:</td>\n          <td>\n            \u2022 Od: ${from}<br/>\n            \u2022 Do: ${to}\n          </td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Numer rejestracyjny:</td>\n          <td>${data.licencePlate || 'Brak'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Telefon:</td>\n          <td>${data.prefix || ''} ${data.phoneNumber || 'Brak'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Edytuj rezerwacj\u0119:</td>\n          <td><a href=\"https://serwis.wymianaopon.pl/zaplanowane\" style=\"color:#0d6efd;\" target=\"_blank\">Kliknij tutaj</a></td>\n        </tr>\n      </table>\n  `;\n})()}}\n",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        928,
        2480
      ],
      "id": "a65972ba-1086-4210-9637-2069c48ae057",
      "name": "Send Email1"
    },
    {
      "parameters": {
        "functionCode": "const [woEvents] = $items(\"Get WO Events (date_from)\", 0).map(item => item.json);\nconst redisRecordsArray = $items(\"Current Redis Records\", 0).map(item => item.json)[0];\n\n// Convert Redis object to array\nconst redisRecords = Object.entries(redisRecordsArray || {}).map(([key, value]) => {\n  try {\n    const data = JSON.parse(value);\n    data.wo_id = +key.split(\":\")[1];\n    return data;\n  } catch (e) {\n    return null;\n  }\n}).filter(Boolean);\n\nconst deletes = [];\n\nfor (const rec of redisRecords) {\n  const foundWO = woEvents.data.items.find(e => e.id === rec.wo_id);\n  \n  if (!foundWO) {\n      deletes.push({ \n        woId: rec.wo_id,\n        oponeoReservationId: rec.oponeoReservationId, \n      });\n  }\n}\n\nreturn deletes;"
      },
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        -336,
        2016
      ],
      "name": "Compare & Split",
      "id": "706fc16d-d13c-4099-88a1-4807a7af5eb0"
    },
    {
      "parameters": {
        "operation": "keys",
        "keyPattern": "WO:*"
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        -544,
        2016
      ],
      "id": "b101716d-a61a-4f0a-83e3-b7a765a16778",
      "name": "Current Redis Records",
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://sync-service:3001/oponeo/obliterator",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -128,
        2128
      ],
      "id": "82b65cff-d358-4ef2-851f-e243e7e97e6d",
      "name": "Obliterator"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://sync-service:3001/oponeo/obliterator",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        2112
      ],
      "id": "34f57a36-1d65-4b85-aa57-2b4ac1e27ff6",
      "name": "Obliterator1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://sync-service:3001/oponeo/mutator",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1424,
        2368
      ],
      "id": "296cfaea-b677-459e-9d39-b1ced4c8a815",
      "name": "Mutator"
    },
    {
      "parameters": {
        "operation": "delete",
        "key": "=WO:{{ $json.woId }}"
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        -128,
        1920
      ],
      "id": "5e1cf7b1-4c76-4593-bb5c-8e3976ea24ae",
      "name": "Redis4",
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "mergeByIndex"
      },
      "name": "Merge results5",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        -16,
        4160
      ],
      "id": "46897345-e156-4581-8f0f-86b1e13418cc"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http:/sync-service:3001/oponeo/obliterator",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -304,
        4320
      ],
      "id": "c5bd8ca3-dc61-48ee-b63f-a3de7ed464f3",
      "name": "HTTP Request2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http:/sync-service:3001/oponeo/mutator",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        512,
        4160
      ],
      "id": "ea7b715a-5214-4f27-b09e-2ba49fcee1b7",
      "name": "HTTP Request1"
    },
    {
      "parameters": {
        "url": "http:/sync-service:3001/wo/events",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1952,
        4128
      ],
      "id": "33298978-3638-4689-9897-90ce7d50df04",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "jsCode": "if ($input.first().json.outOfHoursReservations.length > 0) {\n  return $input.first().json.outOfHoursReservations\n} else {\n  return [];\n}"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -560,
        3936
      ],
      "id": "a5f867a9-ea6a-4304-bdc0-d4e33ec8dedf",
      "name": "Check for out of hours reservations1"
    },
    {
      "parameters": {
        "jsCode": "const inputData = $input.all().map(i => i.json);\nreturn [\n  {\n    json: (() => {\n      const outOfHoursReservations = [];\n      const withinHoursReservations = [];\n      let hour;\n      \n      for (const reservation of inputData) {\n\n        // fresh resrvations comes with UTC while edited are in Europe/Warsaw Tz ]:\n        const isWarsawWinterTz = reservation.endDate.match(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+01:00$/);\n        const endDate = new Date(reservation.endDate);\n        \n        \n        if(isWarsawWinterTz) {\n          hour = endDate.getUTCHours() + 1;\n        } else {\n          hour = endDate.getUTCHours() + 2;\n        }\n        \n        const minutes = endDate.getUTCMinutes();\n\n        const isOutsideWorkingHours = hour > 17 || (hour === 17 && minutes > 0);\n\n        if (isOutsideWorkingHours) {\n          outOfHoursReservations.push(reservation);\n        } else {\n          withinHoursReservations.push(reservation);\n        }\n      }\n\n      return {\n        outOfHoursReservations,\n        withinHoursReservations,\n      };\n    })()\n  }\n];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -816,
        4144
      ],
      "id": "c7b30b69-632c-47a7-bd8f-a852344ece0d",
      "name": "Reservation Splitter1"
    },
    {
      "parameters": {
        "jsCode": "// n8n JavaScript Code Node\nconst input = $input.all()[0].json;\n\nif (input.withinHoursReservations.length < 0) {\n  return [];\n}\n\nconst {withinHoursReservations} = input;\nconst TICKS_PER_MILLISECOND = 10000;\nconst EPOCH_TICKS_AT_UNIX_EPOCH = 621355968000000000;\n\nfunction convertToUnixEpochWarsaw(isoDateString) {\n\tif (!isoDateString) return null;\n\ttry {\n\t\tconst date = new Date(isoDateString);\n\t\tif (isNaN(date.getTime())) return null;\n\n\t\t// Convert to Warsaw time\n\t\tconst formatter = new Intl.DateTimeFormat('pl-PL', {\n\t\t\ttimeZone: 'Europe/Warsaw',\n\t\t\tyear: 'numeric',\n\t\t\tmonth: '2-digit',\n\t\t\tday: '2-digit',\n\t\t\thour: '2-digit',\n\t\t\tminute: '2-digit',\n\t\t\tsecond: '2-digit',\n\t\t});\n\n\t\tconst parts = formatter.formatToParts(date).reduce((acc, part) => {\n\t\t\tacc[part.type] = part.value;\n\t\t\treturn acc;\n\t\t}, {});\n\n\t\t// Construct local datetime string in Warsaw time\n\t\tconst warsawDateString = `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}`;\n\t\tconst warsawDate = new Date(warsawDateString);\n\n\t\treturn warsawDate.getTime() * TICKS_PER_MILLISECOND + EPOCH_TICKS_AT_UNIX_EPOCH;\n\t} catch (error) {\n\t\tconsole.warn(`Failed Warsaw conversion for ${isoDateString}`, error);\n\t\treturn null;\n\t}\n}\n\nfunction parseEvent(event) {\n\treturn {\n\t\twoReservationId: event.id || null,\n\t\tphoneNumber: event.phoneNumber || null,\n\t\tlicencePlate: event.licencePlate || null,\n\t\tstartDate: convertToUnixEpochWarsaw(event.startDate),\n\t\tendDate: convertToUnixEpochWarsaw(event.endDate),\n\t};\n}\n\nconst output = withinHoursReservations.map((item) => parseEvent(item)) || [];\n\nreturn [{\n  json: {\n    debug_mode: false,\n    reservations: output.map(item => item)\n  }\n}];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        288,
        4160
      ],
      "id": "500c426b-0298-4dc8-8460-fd2394fe8fce",
      "name": "Prepare data for Oponeo1"
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "X6Yzdzg8neppbMAW",
          "mode": "list",
          "cachedResultName": "syncer: OPONEO => WO"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {},
          "matchingColumns": [],
          "schema": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {
          "waitForSubWorkflow": true
        }
      },
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.2,
      "position": [
        752,
        3952
      ],
      "id": "68d66792-2f74-4728-8d3f-c74271858a3f",
      "name": "OPONEO => WO1"
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "C090JGLJ4VC",
          "mode": "id"
        },
        "messageType": "block",
        "blocksUi": "={\n  \"blocks\": [\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"*\u274c Synchronizacja dla poni\u017cszej rezerwacji nie powiod\u0142a si\u0119, godzina 'Do' wykracza poza normalne godziny pracy:*\"\n      }\n    },\n    {\n      \"type\": \"section\",\n      \"fields\": [\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Termin:*\\n\u2022 Od: {{ DateTime.fromISO($json.startDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm') }}\\n\u2022 Do: {{ DateTime.fromISO($json.endDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm') }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Nr rejestracyjny:*\\n{{ $json.licencePlate || 'Brak' }}\"\n        },\n        {\n          \"type\": \"mrkdwn\",\n          \"text\": \"*Telefon:*\\n{{ $json.prefix || '' }} {{ $json.phoneNumber || 'Brak' }}\"\n        }\n      ]\n    },\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"\\n<https://serwis.wymianaopon.pl/zaplanowane|Edytuj rezerwacj\u0119 bezpo\u015brednio w WO>\"\n      }\n    },\n    {\n      \"type\": \"divider\"\n    }\n  ]\n}\n",
        "text": "={{ $json }}",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "link_names": false
        }
      },
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        -320,
        4032
      ],
      "id": "864c4294-b777-4b98-af95-5f9faf22c764",
      "name": "OutOfHours Alert"
    },
    {
      "parameters": {
        "fromEmail": "calendar-syncer@digito.pl",
        "toEmail": "hurt@digito.com.pl",
        "subject": "\u274c Synchronizacja kalendarzy nie powiod\u0142a si\u0119",
        "html": "={{(() => {\n  const data = $json;\n  const from = DateTime.fromISO(data.startDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const to = DateTime.fromISO(data.endDate).setLocale('pl').toFormat('d LLLL yyyy, HH:mm');\n  const reservationId = (data.notes || '').split('/').pop();\n\n  return `\n    <div style=\"max-width:600px; margin:auto; font-family:Arial,sans-serif; background:#ffffff; border-radius:10px; padding:24px; border:1px solid #e0e0e0; color:#333333; box-shadow:0 2px 6px rgba(0,0,0,0.05);\">\n      <div style=\"text-align:center; margin-bottom:24px;\">\n        <img src=\"https://blinksoft.pl/assets/opony.png\" alt=\"Logo\" style=\"max-width:50px; height:auto;\" />\n      </div>\n\n      <p style=\"text-align:center; font-size:15px; margin-bottom:30px;\">Godzina <strong>Do</strong> wykracza poza normalne godziny pracy:</p>\n\n      <table cellpadding=\"10\" cellspacing=\"0\" style=\"width:100%; font-size:15px; border-collapse:collapse;\">\n        <tr>\n          <td style=\"font-weight:bold; width:40%;\">Termin:</td>\n          <td>\n            \u2022 Od: ${from}<br/>\n            \u2022 Do: ${to}\n          </td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Numer rejestracyjny:</td>\n          <td>${data.licencePlate || 'Brak'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Telefon:</td>\n          <td>${data.prefix || ''} ${data.phoneNumber || 'Brak'}</td>\n        </tr>\n        <tr>\n          <td style=\"font-weight:bold;\">Edytuj rezerwacj\u0119:</td>\n          <td><a href=\"https://serwis.wymianaopon.pl/zaplanowane\" style=\"color:#0d6efd;\" target=\"_blank\">Kliknij tutaj</a></td>\n        </tr>\n      </table>\n    </div>\n  `;\n})()}}\n"
      },
      "type": "n8n-nodes-base.mailgun",
      "typeVersion": 1,
      "position": [
        -320,
        3856
      ],
      "id": "eddf234b-8acd-4dc4-ae8a-0a060bd902b5",
      "name": "Mailgun2"
    },
    {
      "parameters": {
        "mode": "mergeByIndex"
      },
      "name": "Merge results6",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 1,
      "position": [
        -1056,
        4144
      ],
      "id": "23c942d3-667e-4e86-8831-09c6b5123649"
    },
    {
      "parameters": {
        "jsCode": "// n8n Custom Code Node\n// This code compares redisValue times with item dates\n// Get input items\nconst input = $input.all()[0].json;\n\nif (input.withinHoursReservations.length < 0) {\n  return [];\n}\n\nconst {withinHoursReservations} = input;\n\nconst convertToZulu = (timestamp) => {\n  return new Date(timestamp.replace(/(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})\\+\\d{2}:\\d{2}$/, '$1.000Z'));\n};\n\n// Process each item\nconst outputItems = withinHoursReservations.map((item) => {\n    if(!item.redisValue) return null;\n  \n    // Parse the redisValue JSON string\n    const redisData = JSON.parse(item.redisValue);\n    \n    // Extract Redis times\n    const redisStartTime = new Date(redisData.startTime);\n    const redisEndTime = new Date(redisData.endTime);\n    \n    // Get item dates\n    const itemStartDate = item.startDate ? convertToZulu(item.startDate) : null;\n    const itemEndDate = item.endDate ? convertToZulu(item.endDate) : null;\n    \n    // Perform comparisons\n    const startTimeMatch = itemStartDate ? redisStartTime.getTime() === itemStartDate.getTime() : false;\n    const endTimeMatch = itemEndDate ? redisEndTime.getTime() === itemEndDate.getTime() : false;\n    const timesMatch = startTimeMatch && endTimeMatch;\n    \n    // Only return item if times DON'T match\n    if (!timesMatch) {\n      return {\n        json: {\n            oponeoReservationId: redisData.oponeoReservationId,\n        }\n      };\n    }\n    \n    // Return null for matching times (will be filtered out)\n    return null;\n \n}).filter(item => item !== null); // Remove null items\n\nreturn outputItems;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -544,
        4320
      ],
      "id": "c14f2ed2-a653-4985-9e35-0f8b7e6edb64",
      "name": "Code3"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "af150d69-a5ea-487c-b80a-4f55ac939e3b",
              "name": "=id",
              "value": "={{ $json.id.toString() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -1504,
        4224
      ],
      "id": "fc46dcd4-e4c8-45be-b6dc-ecdff4f86d87",
      "name": "Edit Fields1"
    },
    {
      "parameters": {
        "operation": "get",
        "propertyName": "=redisValue",
        "key": "={{ $json.id }}",
        "options": {}
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        -1280,
        4224
      ],
      "id": "bb17ff2a-0807-4bc5-a452-22990af769d1",
      "name": "Redis5"
    },
    {
      "parameters": {
        "fieldToSplitOut": "data.items",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        -1728,
        4128
      ],
      "id": "c9b7d260-d029-4757-8e70-976937a96332",
      "name": "Split Out1"
    },
    {
      "parameters": {
        "jsCode": "// This code prepares Redis-ready key-value pairs from nested reservation objects\nconst results = $json.results || [];\n\nreturn results.map(item => {\n  return {\n    json: {\n      key: String(item.reservation.woReservationId),\n      value: JSON.stringify({\n        oponeoReservationId: item.reservationId,\n        startTime: item.startTime,\n        endTime: item.endTime,\n      }),\n    }\n  };\n});\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        736,
        4336
      ],
      "id": "dcc2e026-0f40-460d-9149-f011ed784bc3",
      "name": "Code4"
    },
    {
      "parameters": {
        "operation": "set",
        "key": "={{ $json.key }}",
        "value": "={{ $json.value }}"
      },
      "type": "n8n-nodes-base.redis",
      "typeVersion": 1,
      "position": [
        960,
        4336
      ],
      "id": "6fdc49e2-88b1-4d73-85de-b551f5a380c5",
      "name": "Redis6"
    },
    {
      "parameters": {
        "content": "# WO OLD\n\n",
        "height": 1488,
        "width": 3392
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2112,
        3472
      ],
      "id": "3aba06ac-2f14-46b9-ae1f-0b5d608f86a4",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "url": "http://sync-service:3001/wo/events?filter_by=date_from",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1040,
        2352
      ],
      "id": "5e2c86e2-a234-441a-9148-3629d73591f1",
      "name": "Get WO Events (date_from)"
    },
    {
      "parameters": {
        "url": "http://sync-service:3001/wo/events?filter_by=updated_at_from",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -768,
        2352
      ],
      "id": "b24188ab-a401-4f9e-ba93-00c750e889df",
      "name": "Get WO Events (updated_at_from)"
    }
  ],
  "connections": {
    "Schedule Trigger1": {
      "main": [
        []
      ]
    },
    "Scrape Oponeo1": {
      "main": [
        [
          {
            "node": "transform to WO format1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "transform to WO format1": {
      "main": [
        [
          {
            "node": "Merge results1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Take only desc from notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST to WO calendar1": {
      "main": [
        [
          {
            "node": "Merge results1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge results1": {
      "main": [
        [
          {
            "node": "Add reservation id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge results": {
      "main": [
        [
          {
            "node": "Notification Determinator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if exists": {
      "main": [
        [
          {
            "node": "Merge results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge results2": {
      "main": [
        [
          {
            "node": "Redis1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add reservation id": {
      "main": [
        [
          {
            "node": "Check if exists",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge results2",
            "type": "main",
            "index": 1
          },
          {
            "node": "Merge results",
            "type": "main",
            "index": 1
          },
          {
            "node": "Merge results3",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Notification Determinator": {
      "main": [
        [],
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge results2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge results3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge results3": {
      "main": [
        [
          {
            "node": "Redis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Take only desc from notes": {
      "main": [
        [
          {
            "node": "POST to WO calendar1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge results4": {
      "main": [
        [
          {
            "node": "Reservation Splitter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code2": {
      "main": [
        []
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Redis3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Redis3": {
      "main": [
        [
          {
            "node": "Merge results4",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Merge results4",
            "type": "main",
            "index": 0
          },
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code1": {
      "main": [
        [
          {
            "node": "Redis2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reservation Splitter": {
      "main": [
        [
          {
            "node": "Check for out of hours reservations",
            "type": "main",
            "index": 0
          },
          {
            "node": "Prepare data for Oponeo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare data for Oponeo": {
      "main": [
        [
          {
            "node": "Mutator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get WO Events (date_from)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare & Split": {
      "main": [
        []
      ]
    },
    "Current Redis Records": {
      "main": [
        [
          {
            "node": "Compare & Split",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mutator": {
      "main": [
        [
          {
            "node": "Code1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge results5": {
      "main": [
        [
          {
            "node": "Prepare data for Oponeo1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request2": {
      "main": [
        [
          {
            "node": "Merge results5",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "HTTP Request1": {
      "main": [
        [
          {
            "node": "Code4",
            "type": "main",
            "index": 0
          },
          {
            "node": "OPONEO => WO1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Split Out1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for out of hours reservations1": {
      "main": [
        [
          {
            "node": "Mailgun2",
            "type": "main",
            "index": 0
          },
          {
            "node": "OutOfHours Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reservation Splitter1": {
      "main": [
        [
          {
            "node": "Check for out of hours reservations1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code3",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge results5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare data for Oponeo1": {
      "main": [
        [
          {
            "node": "HTTP Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge results6": {
      "main": [
        [
          {
            "node": "Reservation Splitter1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code3": {
      "main": [
        [
          {
            "node": "HTTP Request2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "Redis5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Redis5": {
      "main": [
        [
          {
            "node": "Merge results6",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Split Out1": {
      "main": [
        [
          {
            "node": "Merge results6",
            "type": "main",
            "index": 0
          },
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code4": {
      "main": [
        [
          {
            "node": "Redis6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get WO Events (date_from)": {
      "main": [
        [
          {
            "node": "Current Redis Records",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get WO Events (updated_at_from)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get WO Events (updated_at_from)": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "27ff4cda-abf8-456f-b914-13e072c9a7a6",
  "id": "jiI3TSDcfwoVgibW",
  "tags": []
}