This workflow follows the Emailsend → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"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.getUTC
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
redis
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
debug. Uses httpRequest, slack, redis, mailgun. Scheduled trigger; 60 nodes.
Source: https://github.com/cymmGithub/n8n-sync-calendars/blob/959c227c958937a15c6d7508c590cf134a208f56/workflows/debug.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Simplify financial oversight with this automated n8n workflow. Triggered daily, it fetches cash flow and expense data from a Google Sheet, analyzes inflows and outflows, validates records, and generat
This workflow automatically monitors competitor affiliate programs twice daily using Bright Data's web scraping API to extract commission rates, cookie durations, average order values, and payout term
Real Estate Market Scanning. Uses scheduleTrigger, httpRequest, splitOut, emailSend. Scheduled trigger; 15 nodes.
Automate your payroll process with this efficient workflow. Triggered monthly on the 28th, it fetches employee data from a Google Sheet, uses AI to calculate net salaries with tax and deductions, stru