Questions et réponses mensuelles sur les ressources humaines avec Google Meet

Avancé

Ceci est unHR, AI Summarizationworkflow d'automatisation du domainecontenant 26 nœuds.Utilise principalement des nœuds comme Set, Form, Merge, MySql, Aggregate. Automatiser les séances de questions-réponses RH avec le clustering de questions par IA et l'intégration Google Calendar

Prérequis
  • Informations de connexion à la base de données MySQL
  • Clé API OpenAI
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "id": "1VM3ArDydbQVPrdN",
  "meta": {
    "instanceId": "46f10f8f90bd424d08d7ec9e92bdd735f336873f2ca9f21d76cc7ac132eeaabd",
    "templateCredsSetupCompleted": true
  },
  "name": "Monthly HR Q&A in Google Meet",
  "tags": [],
  "nodes": [
    {
      "id": "fedb0008-1164-4c8c-b699-a4576ccb3f14",
      "name": "Agent IA",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1536,
        288
      ],
      "parameters": {
        "text": "=Questions:\n\n{{ $json.questions.toJsonString() }}",
        "options": {
          "systemMessage": "=# HR Monthly Q&A Agent\n\nYou are an assistant specialized in organizing Monthly HR Q&A meetings.  \nYou will (1) process employees’ submitted questions, (2) deduplicate and cluster them, (3) prioritize by frequency, (4) create a Google Meet using the tool **Create an event in Google Calendar**, and (5) return a JSON object containing the meeting info and a **script text** (a written guide with all the questions for the HR staff to follow).\n\n---\n\n## Input\nYou will always receive a JSON array of questions in this format:\n\n```json\n[\n  {\n    \"id\": 1,\n    \"name\": \"Gabriel\",\n    \"department\": \"Inovação\",\n    \"email\": \"gabriel.hmedeirossantos@gmail.com\",\n    \"question\": \"Como é o VR? e qual a data de pagamento?\",\n    \"created_at\": \"2025-09-16 13:52:54\"\n  }\n]\n````\n\n---\n\n## Workflow\n\n### 1) Validate\n\n* Keep only entries with a valid `question` and `email`.\n* Deduplicate by **email** (for attendance), but keep all questions.\n\n### 2) Normalize\n\n* Simplify each question into a **short, clear version**.\n* Preserve the original language.\n\n### 3) Cluster & Deduplicate\n\n* Group semantically similar questions.\n* Select a **canonical phrasing** for each cluster.\n\n### 4) Prioritize\n\n* Order clusters by `count` (how many employees asked them).\n* Break ties using earliest `created_at`.\n\n### 5) Schedule Google Meet\n\nMeeting date\n\n## Calendar Tool\n- You have access to the tool **Create an event in Google Calendar**.\n- You MUST call this tool to schedule the meeting (last Friday of the current month, 16:00–17:00, America/Sao_Paulo) before returning the final JSON.\n- The tool must be called with:\n  - `title`: \"Monthly HR Q&A – {Month YYYY}\"\n  - `start`: {{ $json.start_date }}\n  - `end`: {{ $json.end_date }}\n  - `timezone`: \"America/Sao_Paulo\"\n  - `attendees`: array of unique emails\n  - **Conference/Meet enabled**\n- After the tool returns, extract `event_id` and `meet_link` (hangoutLink) from the tool response and use them in the output.\n\n---\n\n## Output\n\nAlways return a JSON with two top-level keys: `meeting` and `script`.\n\n### JSON shape\n\n```json\n{\n  \"meeting\": {\n    \"title\": \"Monthly HR Q&A – September 2025\",\n    \"start\": \"2025-09-26T16:00:00-03:00\",\n    \"end\": \"2025-09-26T17:00:00-03:00\",\n    \"timezone\": \"America/Sao_Paulo\",\n    \"meet_link\": \"https://meet.google.com/xxx-xxxx-xxx\",\n    \"event_id\": \"abcdef123456\",\n    \"attendees_count\": 12\n  },\n  \"script\": \"## Script for HR Q&A – September 2025\\n\\n### 1) Meal voucher and payment date (7 employees)\\nHow does the meal voucher work and what is the payment date?\\n\\n### 2) Health plan coverage for spouse (5 employees)\\nCan I include my spouse in the health plan and what is the cost?\\n\\n### 3) Dental plan discount (3 employees)\\nDoes the company offer a dental plan and what is the discount?\\n\\n---\\nTotal clusters: 3 | Total submissions: 15\"\n}\n```\n\n---\n\n## Notes\n\n* `script` must be a **single text field**, formatted as Markdown, not an array of objects.\n* It should read like a **meeting guide**: ordered list of questions, with counts and canonical phrasing.\n* If no questions are valid, still schedule the meeting and set `script` to: `\"No questions were submitted this month.\"`."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "5f7a1fd8-4935-4447-9a78-ddee0be57542",
      "name": "Créer un événement dans Google Calendar",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        1632,
        512
      ],
      "parameters": {
        "end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End', ``, 'string') }}",
        "start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start', ``, 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "gabriel.hmedeirossantos@gmail.com",
          "cachedResultName": "gabriel.hmedeirossantos@gmail.com"
        },
        "additionalFields": {
          "summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Summary', ``, 'string') }}",
          "attendees": [
            "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('attendees0_Attendees', `The attendees of the event. Multiple ones can be separated by comma.`, 'string') }}"
          ],
          "description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}"
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "id": "0VoHlbfiHi81dc7p",
          "name": "Google Calendar account"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "c64f155a-bdcc-46c2-a47e-6a14998a0752",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        1472,
        512
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "zwivGqRORUgpjepY",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "8b59e674-859c-462f-b8b2-dea794351f28",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1792,
        512
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"meeting\": {\n    \"title\": \"Monthly HR Q&A – September 2025\",\n    \"start\": \"2025-09-26T16:00:00-03:00\",\n    \"end\": \"2025-09-26T17:00:00-03:00\",\n    \"timezone\": \"America/Sao_Paulo\",\n    \"meet_link\": \"https://meet.google.com/xxx-xxxx-xxx\",\n    \"event_id\": \"abcdef123456\",\n    \"attendees_count\": 12\n  },\n  \"script\": \"## Script for HR Q&A – September 2025\\n\\n### 1) Meal voucher and payment date (7 employees)\\nHow does the meal voucher work and what is the payment date?\\n\\n### 2) Health plan coverage for spouse (5 employees)\\nCan I include my spouse in the health plan and what is the cost?\\n\\n### 3) Dental plan discount (3 employees)\\nDoes the company offer a dental plan and what is the discount?\\n\\n---\\nTotal clusters: 3 | Total submissions: 15\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "65d92503-fad1-4816-88d4-9f28a03afa77",
      "name": "Convert to File",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        2208,
        288
      ],
      "parameters": {
        "options": {
          "fileName": "MeetingItinerary.txt"
        },
        "operation": "toText",
        "sourceProperty": "output.script"
      },
      "typeVersion": 1.1
    },
    {
      "id": "3eb9a6d5-3e1a-4761-aafe-4ec1c7fcf028",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        2592,
        304
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "d0af1ffb-9b14-47d6-b0fa-74828c16398b",
      "name": "Formulaire de questions",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -208,
        -144
      ],
      "webhookId": "abc351d4-2aca-4228-b86d-49873b150b3e",
      "parameters": {
        "options": {
          "customCss": "/* ===== Base layout ===== */\nhtml, body {\n  height: 100%;\n  font-family: var(--font-family);\n  font-weight: var(--font-weight-normal);\n  font-size: var(--font-size-body);\n  background:\n    radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n    radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n    var(--color-background);\n  color: var(--color-html-text);\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n  max-width: var(--container-width);\n  margin: 32px auto;\n  padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin: 0 auto 12px;\n  max-width: var(--container-width);\n  background: var(--color-test-notice-bg);\n  border: 1px solid var(--color-test-notice-border);\n  color: var(--color-test-notice-text);\n  padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n  border-radius: var(--border-radius-input);\n  font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n  background: var(--color-card-bg);\n  border: 1px solid var(--color-card-border);\n  border-radius: var(--border-radius-card);\n  box-shadow: var(--box-shadow-card);\n  padding: var(--padding-card);\n  margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n  font-size: var(--font-size-header);\n  color: var(--color-header);\n  margin: 4px 0 6px;\n  font-weight: var(--font-weight-bold);\n  letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n  font-size: var(--font-size-paragraph);\n  color: var(--color-header-subtext);\n  margin: 0 0 16px;\n  line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n  display: grid;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n\n.form-field label {\n  font-size: var(--font-size-label);\n  color: var(--color-label);\n  font-weight: 600;\n}\n\n.form-field label .required {\n  color: var(--color-required);\n  margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n  width: 100%;\n  appearance: none;\n  border: 1px solid var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  font-size: var(--font-size-input);\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n  opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n  border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n  outline: none;\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n  border-color: var(--color-error);\n  box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n  font-size: 12px;\n  color: var(--color-link);\n}\n\n.error-text {\n  font-size: var(--font-size-error);\n  color: var(--color-error);\n  margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n  position: relative;\n}\n\n.input-file input[type=\"file\"] {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n\n.input-file .file-ui {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 10px;\n  border: 1px dashed var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n  border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n  accent-color: var(--color-focus-border);\n  width: var(--checkbox-size);\n  height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n  margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n  width: 100%;\n  height: var(--submit-btn-height);\n  background: var(--color-submit-btn-bg);\n  color: var(--color-submit-btn-text);\n  border: 1px solid transparent;\n  border-radius: var(--border-radius-input);\n  font-size: 15px;\n  font-weight: 700;\n  letter-spacing: .2px;\n  cursor: pointer;\n  transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n  filter: brightness(0.98);\n  transform: translateY(-1px);\n  box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n  transform: translateY(0);\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n  opacity: .6;\n  cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., “Clear form”) */\n.clear-link {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  margin-top: 10px;\n  font-size: var(--font-size-link);\n  color: var(--color-link);\n  text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / “Automated with n8n” ===== */\n.form-footer {\n  margin: 10px auto 24px;\n  max-width: var(--container-width);\n  text-align: center;\n  color: var(--color-link);\n  font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n  .container, .form-wrapper, .n8n-form {\n    margin: 20px auto;\n    padding: 0 10px;\n  }\n  .form-card { padding: 20px; }\n  .form-card h1, h1.form-title { font-size: 18px; }\n  .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n  body {\n    background:\n      radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n      radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n      #16181d;\n    color: #d8dbe3;\n  }\n\n  .test-notice {\n    background: #2a2115;\n    border-color: #3a2b18;\n    color: #ffce74;\n  }\n\n  .form-card {\n    background: #1c1f27;\n    border-color: #2a2f3a;\n    box-shadow: 0 8px 24px rgba(0,0,0,.35);\n  }\n\n  .form-card h1, h1.form-title { color: #e9ebf3; }\n  .form-subtitle { color: #a6adbb; }\n\n  input, textarea, select {\n    background: #11151c;\n    border-color: #2a2f3a;\n    color: #d8dbe3;\n  }\n  input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n  input:focus, textarea:focus, select:focus {\n    border-color: #b7a8ff;\n    box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n  }\n\n  .help-text { color: #9aa3b2; }\n\n  button[type=\"submit\"],\n  .button-submit {\n    box-shadow: 0 10px 26px rgba(255,109,90,.34);\n  }\n\n  .form-footer { color: #9aa3b2; }\n}\n",
          "buttonLabel": "Send question"
        },
        "formTitle": "Monthly HR meeting",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Your name",
              "requiredField": true
            },
            {
              "fieldLabel": "Department"
            },
            {
              "fieldType": "email",
              "fieldLabel": "Internal email"
            },
            {
              "fieldLabel": "Your question",
              "requiredField": true
            }
          ]
        },
        "responseMode": "lastNode",
        "formDescription": "Do you have any questions? Let us know below and we'll answer them soon at our monthly meeting."
      },
      "typeVersion": 2.3
    },
    {
      "id": "eeff26db-3267-4126-81e1-df6996106aaf",
      "name": "Ajouter une question à la base de données",
      "type": "n8n-nodes-base.mySql",
      "position": [
        208,
        -144
      ],
      "parameters": {
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "hr_questions",
          "cachedResultName": "hr_questions"
        },
        "options": {},
        "dataMode": "defineBelow",
        "valuesToSend": {
          "values": [
            {
              "value": "={{ $json['Your name'] }}",
              "column": "name"
            },
            {
              "value": "={{ $json.Department }}",
              "column": "department"
            },
            {
              "value": "={{ $json['Internal email'] }}",
              "column": "email"
            },
            {
              "value": "={{ $json['Your question'] }}",
              "column": "question"
            },
            {
              "value": "={{ $json.submittedAt.toDateTime() }}",
              "column": "created_at"
            }
          ]
        }
      },
      "credentials": {
        "mySql": {
          "id": "sQeUBEQkBvNWbM2y",
          "name": "MySQL - Estudos"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "4ad67f5f-8a7d-4d70-9c3e-a3d250c2562d",
      "name": "Retourner le formulaire",
      "type": "n8n-nodes-base.form",
      "position": [
        624,
        -144
      ],
      "webhookId": "c2e31794-9f17-43a6-806c-05fca8b2c01d",
      "parameters": {
        "options": {
          "customCss": "/* ===== Base layout ===== */\nhtml, body {\n  height: 100%;\n  font-family: var(--font-family);\n  font-weight: var(--font-weight-normal);\n  font-size: var(--font-size-body);\n  background:\n    radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n    radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n    var(--color-background);\n  color: var(--color-html-text);\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n  max-width: var(--container-width);\n  margin: 32px auto;\n  padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin: 0 auto 12px;\n  max-width: var(--container-width);\n  background: var(--color-test-notice-bg);\n  border: 1px solid var(--color-test-notice-border);\n  color: var(--color-test-notice-text);\n  padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n  border-radius: var(--border-radius-input);\n  font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n  background: var(--color-card-bg);\n  border: 1px solid var(--color-card-border);\n  border-radius: var(--border-radius-card);\n  box-shadow: var(--box-shadow-card);\n  padding: var(--padding-card);\n  margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n  font-size: var(--font-size-header);\n  color: var(--color-header);\n  margin: 4px 0 6px;\n  font-weight: var(--font-weight-bold);\n  letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n  font-size: var(--font-size-paragraph);\n  color: var(--color-header-subtext);\n  margin: 0 0 16px;\n  line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n  display: grid;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n\n.form-field label {\n  font-size: var(--font-size-label);\n  color: var(--color-label);\n  font-weight: 600;\n}\n\n.form-field label .required {\n  color: var(--color-required);\n  margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n  width: 100%;\n  appearance: none;\n  border: 1px solid var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  font-size: var(--font-size-input);\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n  opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n  border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n  outline: none;\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n  border-color: var(--color-error);\n  box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n  font-size: 12px;\n  color: var(--color-link);\n}\n\n.error-text {\n  font-size: var(--font-size-error);\n  color: var(--color-error);\n  margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n  position: relative;\n}\n\n.input-file input[type=\"file\"] {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n\n.input-file .file-ui {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 10px;\n  border: 1px dashed var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n  border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n  accent-color: var(--color-focus-border);\n  width: var(--checkbox-size);\n  height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n  margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n  width: 100%;\n  height: var(--submit-btn-height);\n  background: var(--color-submit-btn-bg);\n  color: var(--color-submit-btn-text);\n  border: 1px solid transparent;\n  border-radius: var(--border-radius-input);\n  font-size: 15px;\n  font-weight: 700;\n  letter-spacing: .2px;\n  cursor: pointer;\n  transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n  filter: brightness(0.98);\n  transform: translateY(-1px);\n  box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n  transform: translateY(0);\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n  opacity: .6;\n  cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., “Clear form”) */\n.clear-link {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  margin-top: 10px;\n  font-size: var(--font-size-link);\n  color: var(--color-link);\n  text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / “Automated with n8n” ===== */\n.form-footer {\n  margin: 10px auto 24px;\n  max-width: var(--container-width);\n  text-align: center;\n  color: var(--color-link);\n  font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n  .container, .form-wrapper, .n8n-form {\n    margin: 20px auto;\n    padding: 0 10px;\n  }\n  .form-card { padding: 20px; }\n  .form-card h1, h1.form-title { font-size: 18px; }\n  .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n  body {\n    background:\n      radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n      radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n      #16181d;\n    color: #d8dbe3;\n  }\n\n  .test-notice {\n    background: #2a2115;\n    border-color: #3a2b18;\n    color: #ffce74;\n  }\n\n  .form-card {\n    background: #1c1f27;\n    border-color: #2a2f3a;\n    box-shadow: 0 8px 24px rgba(0,0,0,.35);\n  }\n\n  .form-card h1, h1.form-title { color: #e9ebf3; }\n  .form-subtitle { color: #a6adbb; }\n\n  input, textarea, select {\n    background: #11151c;\n    border-color: #2a2f3a;\n    color: #d8dbe3;\n  }\n  input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n  input:focus, textarea:focus, select:focus {\n    border-color: #b7a8ff;\n    box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n  }\n\n  .help-text { color: #9aa3b2; }\n\n  button[type=\"submit\"],\n  .button-submit {\n    box-shadow: 0 10px 26px rgba(255,109,90,.34);\n  }\n\n  .form-footer { color: #9aa3b2; }\n}\n"
        },
        "operation": "completion",
        "completionTitle": "Your question has been sent",
        "completionMessage": "It will be answered soon at our monthly meeting."
      },
      "typeVersion": 2.3
    },
    {
      "id": "0bf75f31-39e1-4011-837d-6da8bd431c04",
      "name": "Form to get questions",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -208,
        288
      ],
      "webhookId": "e941986f-9a36-40a5-a0fa-ac522cea9a28",
      "parameters": {
        "options": {
          "customCss": "/* ===== Base layout ===== */\nhtml, body {\n  height: 100%;\n  font-family: var(--font-family);\n  font-weight: var(--font-weight-normal);\n  font-size: var(--font-size-body);\n  background:\n    radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n    radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n    var(--color-background);\n  color: var(--color-html-text);\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n  max-width: var(--container-width);\n  margin: 32px auto;\n  padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin: 0 auto 12px;\n  max-width: var(--container-width);\n  background: var(--color-test-notice-bg);\n  border: 1px solid var(--color-test-notice-border);\n  color: var(--color-test-notice-text);\n  padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n  border-radius: var(--border-radius-input);\n  font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n  background: var(--color-card-bg);\n  border: 1px solid var(--color-card-border);\n  border-radius: var(--border-radius-card);\n  box-shadow: var(--box-shadow-card);\n  padding: var(--padding-card);\n  margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n  font-size: var(--font-size-header);\n  color: var(--color-header);\n  margin: 4px 0 6px;\n  font-weight: var(--font-weight-bold);\n  letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n  font-size: var(--font-size-paragraph);\n  color: var(--color-header-subtext);\n  margin: 0 0 16px;\n  line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n  display: grid;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n\n.form-field label {\n  font-size: var(--font-size-label);\n  color: var(--color-label);\n  font-weight: 600;\n}\n\n.form-field label .required {\n  color: var(--color-required);\n  margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n  width: 100%;\n  appearance: none;\n  border: 1px solid var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  font-size: var(--font-size-input);\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n  opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n  border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n  outline: none;\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n  border-color: var(--color-error);\n  box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n  font-size: 12px;\n  color: var(--color-link);\n}\n\n.error-text {\n  font-size: var(--font-size-error);\n  color: var(--color-error);\n  margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n  position: relative;\n}\n\n.input-file input[type=\"file\"] {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n\n.input-file .file-ui {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 10px;\n  border: 1px dashed var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n  border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n  accent-color: var(--color-focus-border);\n  width: var(--checkbox-size);\n  height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n  margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n  width: 100%;\n  height: var(--submit-btn-height);\n  background: var(--color-submit-btn-bg);\n  color: var(--color-submit-btn-text);\n  border: 1px solid transparent;\n  border-radius: var(--border-radius-input);\n  font-size: 15px;\n  font-weight: 700;\n  letter-spacing: .2px;\n  cursor: pointer;\n  transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n  filter: brightness(0.98);\n  transform: translateY(-1px);\n  box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n  transform: translateY(0);\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n  opacity: .6;\n  cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., “Clear form”) */\n.clear-link {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  margin-top: 10px;\n  font-size: var(--font-size-link);\n  color: var(--color-link);\n  text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / “Automated with n8n” ===== */\n.form-footer {\n  margin: 10px auto 24px;\n  max-width: var(--container-width);\n  text-align: center;\n  color: var(--color-link);\n  font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n  .container, .form-wrapper, .n8n-form {\n    margin: 20px auto;\n    padding: 0 10px;\n  }\n  .form-card { padding: 20px; }\n  .form-card h1, h1.form-title { font-size: 18px; }\n  .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n  body {\n    background:\n      radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n      radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n      #16181d;\n    color: #d8dbe3;\n  }\n\n  .test-notice {\n    background: #2a2115;\n    border-color: #3a2b18;\n    color: #ffce74;\n  }\n\n  .form-card {\n    background: #1c1f27;\n    border-color: #2a2f3a;\n    box-shadow: 0 8px 24px rgba(0,0,0,.35);\n  }\n\n  .form-card h1, h1.form-title { color: #e9ebf3; }\n  .form-subtitle { color: #a6adbb; }\n\n  input, textarea, select {\n    background: #11151c;\n    border-color: #2a2f3a;\n    color: #d8dbe3;\n  }\n  input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n  input:focus, textarea:focus, select:focus {\n    border-color: #b7a8ff;\n    box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n  }\n\n  .help-text { color: #9aa3b2; }\n\n  button[type=\"submit\"],\n  .button-submit {\n    box-shadow: 0 10px 26px rgba(255,109,90,.34);\n  }\n\n  .form-footer { color: #9aa3b2; }\n}",
          "buttonLabel": "Get Questions"
        },
        "formTitle": "Monthly HR meeting",
        "formFields": {
          "values": [
            {
              "fieldType": "date",
              "fieldLabel": "Of the day"
            },
            {
              "fieldType": "date",
              "fieldLabel": "Until the day"
            }
          ]
        },
        "responseMode": "lastNode",
        "formDescription": "Get top questions received"
      },
      "typeVersion": 2.3
    },
    {
      "id": "e34867e7-8e27-4a2a-93be-8a67ed646e22",
      "name": "Get queries from the database",
      "type": "n8n-nodes-base.mySql",
      "position": [
        192,
        288
      ],
      "parameters": {
        "query": "SELECT *\nfrom hr_questions\nWHERE created_at BETWEEN '{{ $json['Of the day'] }}'\nAND '{{ $json['Until the day'] }}';",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "mySql": {
          "id": "sQeUBEQkBvNWbM2y",
          "name": "MySQL - Estudos"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "28a3be94-52e4-47d1-a37d-b43edb42a0c0",
      "name": "Return meeting script",
      "type": "n8n-nodes-base.form",
      "position": [
        2992,
        304
      ],
      "webhookId": "c2e31794-9f17-43a6-806c-05fca8b2c01d",
      "parameters": {
        "options": {
          "customCss": "/* ===== Base layout ===== */\nhtml, body {\n  height: 100%;\n  font-family: var(--font-family);\n  font-weight: var(--font-weight-normal);\n  font-size: var(--font-size-body);\n  background:\n    radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n    radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n    var(--color-background);\n  color: var(--color-html-text);\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n  max-width: var(--container-width);\n  margin: 32px auto;\n  padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  margin: 0 auto 12px;\n  max-width: var(--container-width);\n  background: var(--color-test-notice-bg);\n  border: 1px solid var(--color-test-notice-border);\n  color: var(--color-test-notice-text);\n  padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n  border-radius: var(--border-radius-input);\n  font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n  background: var(--color-card-bg);\n  border: 1px solid var(--color-card-border);\n  border-radius: var(--border-radius-card);\n  box-shadow: var(--box-shadow-card);\n  padding: var(--padding-card);\n  margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n  font-size: var(--font-size-header);\n  color: var(--color-header);\n  margin: 4px 0 6px;\n  font-weight: var(--font-weight-bold);\n  letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n  font-size: var(--font-size-paragraph);\n  color: var(--color-header-subtext);\n  margin: 0 0 16px;\n  line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n  display: grid;\n  gap: 6px;\n  margin-bottom: 14px;\n}\n\n.form-field label {\n  font-size: var(--font-size-label);\n  color: var(--color-label);\n  font-weight: 600;\n}\n\n.form-field label .required {\n  color: var(--color-required);\n  margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n  width: 100%;\n  appearance: none;\n  border: 1px solid var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  font-size: var(--font-size-input);\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n  opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n  border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n  outline: none;\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n  border-color: var(--color-error);\n  box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n  font-size: 12px;\n  color: var(--color-link);\n}\n\n.error-text {\n  font-size: var(--font-size-error);\n  color: var(--color-error);\n  margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n  position: relative;\n}\n\n.input-file input[type=\"file\"] {\n  position: absolute;\n  inset: 0;\n  opacity: 0;\n  cursor: pointer;\n}\n\n.input-file .file-ui {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 10px;\n  border: 1px dashed var(--color-input-border);\n  border-radius: var(--border-radius-input);\n  padding: 12px 14px;\n  color: var(--color-input-text);\n  background: #fff;\n  transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n  border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n  border-color: var(--color-focus-border);\n  box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n  accent-color: var(--color-focus-border);\n  width: var(--checkbox-size);\n  height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n  margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n  width: 100%;\n  height: var(--submit-btn-height);\n  background: var(--color-submit-btn-bg);\n  color: var(--color-submit-btn-text);\n  border: 1px solid transparent;\n  border-radius: var(--border-radius-input);\n  font-size: 15px;\n  font-weight: 700;\n  letter-spacing: .2px;\n  cursor: pointer;\n  transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n  filter: brightness(0.98);\n  transform: translateY(-1px);\n  box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n  transform: translateY(0);\n  box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n  opacity: .6;\n  cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., “Clear form”) */\n.clear-link {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  margin-top: 10px;\n  font-size: var(--font-size-link);\n  color: var(--color-link);\n  text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / “Automated with n8n” ===== */\n.form-footer {\n  margin: 10px auto 24px;\n  max-width: var(--container-width);\n  text-align: center;\n  color: var(--color-link);\n  font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n  .container, .form-wrapper, .n8n-form {\n    margin: 20px auto;\n    padding: 0 10px;\n  }\n  .form-card { padding: 20px; }\n  .form-card h1, h1.form-title { font-size: 18px; }\n  .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n  body {\n    background:\n      radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n      radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n      #16181d;\n    color: #d8dbe3;\n  }\n\n  .test-notice {\n    background: #2a2115;\n    border-color: #3a2b18;\n    color: #ffce74;\n  }\n\n  .form-card {\n    background: #1c1f27;\n    border-color: #2a2f3a;\n    box-shadow: 0 8px 24px rgba(0,0,0,.35);\n  }\n\n  .form-card h1, h1.form-title { color: #e9ebf3; }\n  .form-subtitle { color: #a6adbb; }\n\n  input, textarea, select {\n    background: #11151c;\n    border-color: #2a2f3a;\n    color: #d8dbe3;\n  }\n  input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n  input:focus, textarea:focus, select:focus {\n    border-color: #b7a8ff;\n    box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n  }\n\n  .help-text { color: #9aa3b2; }\n\n  button[type=\"submit\"],\n  .button-submit {\n    box-shadow: 0 10px 26px rgba(255,109,90,.34);\n  }\n\n  .form-footer { color: #9aa3b2; }\n}\n"
        },
        "operation": "completion",
        "respondWith": "returnBinary",
        "completionTitle": "Questions collected and meeting scheduled with employees who responded to the form",
        "completionMessage": "=Meeting scheduled:<br>\n{{ $json.output.meeting.title }}<br>\nAttendees: {{ $json.output.meeting.attendees_count }}<br>\nStart: {{ $json.output.meeting.start }}<br>\nEnd: {{ $json.output.meeting.end }}<br><br>\nThe .txt file with the employees' main questions will be automatically downloaded immediately."
      },
      "typeVersion": 2.3
    },
    {
      "id": "487e1170-ed55-4273-93b0-38df2dc73452",
      "name": "Get start_date and end_date Meeting",
      "type": "n8n-nodes-base.set",
      "position": [
        1008,
        288
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "bce3c461-e509-4728-8546-22aae16b6d84",
              "name": "start_date",
              "type": "string",
              "value": "={{ \n  (() => {\n    const now = new Date();\n    const year = now.getFullYear();\n    const month = now.getMonth(); // 0 = janeiro\n\n    // último dia do mês\n    const lastDay = new Date(year, month + 1, 0);\n\n    // voltar até encontrar sexta (5)\n    while (lastDay.getDay() !== 5) {\n      lastDay.setDate(lastDay.getDate() - 1);\n    }\n\n    // definir hora da reunião (16h00)\n    lastDay.setHours(16, 0, 0, 0);\n\n    return lastDay.toISOString();\n  })()\n}}\n"
            },
            {
              "id": "4200c0fd-8dd9-492f-87dd-f8dd0ff92858",
              "name": "end_date",
              "type": "string",
              "value": "={{ \n  (() => {\n    const now = new Date();\n    const year = now.getFullYear();\n    const month = now.getMonth();\n\n    const lastDay = new Date(year, month + 1, 0);\n    while (lastDay.getDay() !== 5) {\n      lastDay.setDate(lastDay.getDate() - 1);\n    }\n\n    // horário fim = 17h00\n    lastDay.setHours(17, 0, 0, 0);\n\n    return lastDay.toISOString();\n  })()\n}}\n"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "c038c2ed-74c0-4ab7-8377-564c4bb7d6db",
      "name": "Aggregate Questions",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        624,
        288
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "questions"
      },
      "typeVersion": 1
    },
    {
      "id": "ecacc9e8-a321-496e-b103-f66e3e48a0f8",
      "name": "Note adhésive",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -832
      ],
      "parameters": {
        "color": 4,
        "width": 848,
        "height": 448,
        "content": "## Monthly HR Q&A — End-to-end flow\n\nThis template collects employees’ questions via a form, stores them, and later lets HR:\n1) Fetch questions by date range,\n2) Deduplicate/cluster similar questions,\n3) Generate a clear script (text) for the meeting,\n4) Create a Google Calendar event with a Meet link for the last Friday of the current month,\n5) Return a confirmation page and auto-download the script (.txt).\n\n**Security**\n- No API keys hardcoded. Use credentials in nodes.\n- Avoid PII in public artifacts; keep emails internal only.\n\n**Success criteria**\n- Form submits to DB without errors,\n- AI Agent returns JSON with { meeting, script },\n- Calendar event is actually created (has event_id + meet_link),\n- Final page shows meeting info and downloads the script file.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "96cbd737-4482-4476-9f3b-c53c4810f7e5",
      "name": "Note adhésive1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -368
      ],
      "parameters": {
        "color": 4,
        "width": 368,
        "height": 400,
        "content": "Collects employee inputs:\n- Your name (required)\n- Department\n- Internal email (required for follow-up)\n- Your question (required)\n\nStyled with custom CSS for better UX.\nOn submit → forwards to DB insert node.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d15210c2-8094-4220-a097-5621639df4bd",
      "name": "Note adhésive2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        -368
      ],
      "parameters": {
        "width": 368,
        "height": 400,
        "content": "Writes to table: hr_questions\nColumns:\n- name, department, email, question\n- created_at = submittedAt.toDateTime()\n\nTip:\n- Validate email format on the Form if possible.\n- Keep questions in original language (used later by the Agent).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1892a011-bb06-4eb5-a033-1106fdd89b6e",
      "name": "Note adhésive3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        -368
      ],
      "parameters": {
        "color": 3,
        "width": 368,
        "height": 400,
        "content": "Thank-you/confirmation page for employees.\n\nMessaging:\n- Confirms the question has been received,\n- Explains questions will be addressed in the monthly HR Q&A meeting.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7d6c8f68-ece5-4496-8a29-e8f2c894679d",
      "name": "Note adhésive4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        64
      ],
      "parameters": {
        "color": 4,
        "width": 368,
        "height": 400,
        "content": "HR utility form to select a date window:\n- \"Of the day\" → start date\n- \"Until the day\" → end date\n\nThese dates feed the SQL query to pull relevant questions from DB.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "62914b4b-2b0c-422f-976e-4a3ee4131045",
      "name": "Note adhésive5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        64
      ],
      "parameters": {
        "width": 368,
        "height": 400,
        "content": "SQL range query on hr_questions by created_at.\n\nCheck:\n- Date format matches DB column type (TIMESTAMP/DATE).\n- Consider timezone normalization in DB.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "1be5a326-490b-41a9-9b15-219ccd8c3b7e",
      "name": "Note adhésive6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        64
      ],
      "parameters": {
        "width": 368,
        "height": 400,
        "content": "Aggregates all rows into a single array: `questions`.\n\nThis array is passed to the AI Agent as the payload (JSON).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f6c84649-3757-4ecc-b16b-b246c04cc34e",
      "name": "Note adhésive7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        64
      ],
      "parameters": {
        "width": 368,
        "height": 400,
        "content": "Computes meeting start/end:\n- Last Friday of the current month,\n- 16:00–17:00 (local time).\n\nExpressions return ISO strings. Update logic if you want:\n- If the last Friday has already passed, move to next month.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "567689c8-2ca4-4e6e-b0d4-85b6fcdc1848",
      "name": "Note adhésive8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        -144
      ],
      "parameters": {
        "color": 5,
        "width": 752,
        "height": 864,
        "content": "Roles:\n1) Validate questions (non-empty, valid emails),\n2) Deduplicate/cluster by semantic similarity,\n3) Prioritize by frequency (ties: earliest created_at),\n4) CALL the tool \"Create an event in Google Calendar\" with:\n   - title: \"Monthly HR Q&A – {Month YYYY}\"\n   - start / end (from previous node)\n   - timezone: America/Sao_Paulo\n   - attendees: unique valid emails\n   - Meet enabled (conference)\n5) Return JSON:\n{\n  \"meeting\": { title, start, end, timezone, meet_link, event_id, attendees_count },\n  \"script\": \"TEXT (Markdown) — list of deduped questions with counts\"\n}\n\nImportant:\n- Only return the final JSON AFTER the calendar tool is called.\n- `script` is ONE text field (not an array).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ddf03f40-13c8-4e98-ab50-81ccb5394e1f",
      "name": "Note adhésive9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2064,
        64
      ],
      "parameters": {
        "width": 368,
        "height": 400,
        "content": "Converts `output.script` (text) → MeetingItinerary.txt\n\nThis file is automatically downloaded in the final step.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "72b7e6ce-3b32-46a6-ada6-bcfc7a0f8b08",
      "name": "Note adhésive10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2464,
        64
      ],
      "parameters": {
        "width": 368,
        "height": 400,
        "content": "Merges JSON output from the AI Agent with the generated file,\nso the final page can show event info and trigger the download.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b04406c0-5577-4f2e-b15f-f1e7cce5a109",
      "name": "Note adhésive11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2864,
        64
      ],
      "parameters": {
        "color": 3,
        "width": 368,
        "height": 400,
        "content": "HR confirmation page:\n- Shows title, attendees_count, start/end,\n- Triggers download of MeetingItinerary.txt.\n\nTip:\n- Keep copy clear and actionable for HR.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "7e3a6fd2-a3ee-435f-b4fe-d74dc6ee2203",
  "connections": {
    "3eb9a6d5-3e1a-4761-aafe-4ec1c7fcf028": {
      "main": [
        [
          {
            "node": "28a3be94-52e4-47d1-a37d-b43edb42a0c0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fedb0008-1164-4c8c-b699-a4576ccb3f14": {
      "main": [
        [
          {
            "node": "65d92503-fad1-4816-88d4-9f28a03afa77",
            "type": "main",
            "index": 0
          },
          {
            "node": "3eb9a6d5-3e1a-4761-aafe-4ec1c7fcf028",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d0af1ffb-9b14-47d6-b0fa-74828c16398b": {
      "main": [
        [
          {
            "node": "eeff26db-3267-4126-81e1-df6996106aaf",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "65d92503-fad1-4816-88d4-9f28a03afa77": {
      "main": [
        [
          {
            "node": "3eb9a6d5-3e1a-4761-aafe-4ec1c7fcf028",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "c64f155a-bdcc-46c2-a47e-6a14998a0752": {
      "ai_languageModel": [
        [
          {
            "node": "fedb0008-1164-4c8c-b699-a4576ccb3f14",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "c038c2ed-74c0-4ab7-8377-564c4bb7d6db": {
      "main": [
        [
          {
            "node": "487e1170-ed55-4273-93b0-38df2dc73452",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0bf75f31-39e1-4011-837d-6da8bd431c04": {
      "main": [
        [
          {
            "node": "e34867e7-8e27-4a2a-93be-8a67ed646e22",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "eeff26db-3267-4126-81e1-df6996106aaf": {
      "main": [
        [
          {
            "node": "4ad67f5f-8a7d-4d70-9c3e-a3d250c2562d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "8b59e674-859c-462f-b8b2-dea794351f28": {
      "ai_outputParser": [
        [
          {
            "node": "fedb0008-1164-4c8c-b699-a4576ccb3f14",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "e34867e7-8e27-4a2a-93be-8a67ed646e22": {
      "main": [
        [
          {
            "node": "c038c2ed-74c0-4ab7-8377-564c4bb7d6db",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "5f7a1fd8-4935-4447-9a78-ddee0be57542": {
      "ai_tool": [
        [
          {
            "node": "fedb0008-1164-4c8c-b699-a4576ccb3f14",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "487e1170-ed55-4273-93b0-38df2dc73452": {
      "main": [
        [
          {
            "node": "fedb0008-1164-4c8c-b699-a4576ccb3f14",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Foire aux questions

Comment utiliser ce workflow ?

Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.

Dans quelles scénarios ce workflow est-il adapté ?

Avancé - Ressources Humaines, Résumé IA

Est-ce payant ?

Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.

Workflows recommandés

Informations sur le workflow
Niveau de difficulté
Avancé
Nombre de nœuds26
Catégorie2
Types de nœuds12
Description de la difficulté

Adapté aux utilisateurs avancés, avec des workflows complexes contenant 16+ nœuds

Auteur
Gabriel Santos

Gabriel Santos

@gabrielhmsantos

Enthusiastic developer passionate about automation and system integration. I work mainly with Python, RPA, and N8N, building workflows and custom solutions to optimize processes and connect platforms. Always learning, sharing, and exploring new ways to automate smarter.

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34