From 4c67a79be00e76a93adca2fa36f08b47bfb9a2cb Mon Sep 17 00:00:00 2001 From: Md Afiq Iskandar Date: Fri, 25 Jul 2025 11:12:58 +0800 Subject: [PATCH] Implement Email Notification System and Update Notification API - Introduced a new API endpoint for sending email notifications using Nodemailer, allowing for customizable email content and recipient management. - Enhanced the notification configuration to support various recipient types (email, user, role, variable) and added error handling for recipient resolution. - Updated the process builder to include HTML message formatting and improved logging for notification processing. - Modified the Nuxt configuration to include Nodemailer as a dependency and adjusted security settings for the notifications API. - Refactored process definition JSON to accommodate changes in notification handling and updated UI components accordingly. --- .../process-builder/processDefinition.json | 56 ++- nuxt.config.js | 6 + package.json | 1 + pages/process-builder/manage.vue | 4 +- pages/workflow/[id].vue | 90 +++- server/api/notifications/send.post.js | 452 ++++++++++++++++++ yarn.lock | 5 + 7 files changed, 576 insertions(+), 38 deletions(-) create mode 100644 server/api/notifications/send.post.js diff --git a/docs/json/process-builder/processDefinition.json b/docs/json/process-builder/processDefinition.json index 14816bb..6ebd608 100644 --- a/docs/json/process-builder/processDefinition.json +++ b/docs/json/process-builder/processDefinition.json @@ -89,15 +89,15 @@ "targetHandle": "html-1752550500000-left" }, { - "id": "gateway-1752550505000-notification-1752621850786", - "data": { "condition": "todoStatus === false" }, + "id": "gateway-1752550505000-form-1752546702226-1753409198979", + "data": {}, "type": "custom", "label": "Not Completed", "source": "gateway-1752550505000", - "target": "notification-1752621850786", + "target": "form-1752546702226", "animated": true, "sourceHandle": "gateway-1752550505000-bottom", - "targetHandle": "notification-1752621850786-left" + "targetHandle": "form-1752546702226-top" } ], "nodes": [ @@ -106,14 +106,14 @@ "data": { "label": "Start", "description": "Process start point" }, "type": "start", "label": "Start", - "position": { "x": 300, "y": 135 } + "position": { "x": 300, "y": 300 } }, { "id": "form-1752546702226", "data": { "label": "Pilihan Kategori Asnaf", "shape": "rectangle", - "formId": 7, + "formId": 1, "formName": "Pilihan Kategori Asnaf", "formUuid": "d3612e05-b31a-46dc-b5e5-67e6c5bd3e78", "textColor": "#6b21a8", @@ -136,7 +136,7 @@ }, "type": "form", "label": "Pilihan Kategori Asnaf", - "position": { "x": 510, "y": 105 } + "position": { "x": 555, "y": 270 } }, { "id": "end-1752546716111", @@ -170,16 +170,20 @@ }, "type": "api", "label": "API Call", - "position": { "x": 795, "y": 105 } + "position": { "x": 795, "y": -60 } }, { "id": "script-1752550430989", "data": { "label": "Script Task", + "shape": "rectangle", + "textColor": "#374151", "scriptCode": "// Map API response to process variables\nconst api = processVariables.apiResponse || {};\nprocessVariables.todoTitle = api.kategori_asnaf || '';\nprocessVariables.namaAsnaf = api.nama_asnaf || '';\nprocessVariables.todoStatus = api.id > 100; // true if id > 100, otherwise false\n\n// New logic: Calculate a score\nconst katLen = (api.kategori_asnaf || '').length;\nconst namaLen = (api.nama_asnaf || '').length;\nprocessVariables.asnafScore = katLen * 10 + namaLen * 5;\n\n// New logic: Add a timestamp\nprocessVariables.resultTimestamp = new Date().toISOString();\n\n// New logic: Create a summary string\nprocessVariables.resultSummary = `Asnaf: ${processVariables.todoTitle}, Nama: ${processVariables.namaAsnaf}, Score: ${processVariables.asnafScore}, Time: ${processVariables.resultTimestamp}`;\n", + "borderColor": "#6b7280", "description": "Execute JavaScript code", "inputVariables": ["apiResponse"], "scriptLanguage": "javascript", + "backgroundColor": "#f9fafb", "outputVariables": [ { "name": "todoTitle", @@ -215,7 +219,7 @@ }, "type": "script", "label": "Script Task", - "position": { "x": 1050, "y": 120 } + "position": { "x": 1110, "y": -60 } }, { "id": "gateway-1752550505000", @@ -228,14 +232,6 @@ "id": "condition-group-1", "output": "Completed", "conditions": [ - { - "id": "condition-1", - "value": true, - "operator": "eq", - "variable": "todoStatus", - "valueType": "boolean", - "logicalOperator": "and" - }, { "id": "condition-1753408402567", "value": "afiq", @@ -254,10 +250,12 @@ "conditions": [ { "id": "condition-2", - "value": false, - "operator": "eq", - "variable": "todoStatus", - "valueType": "boolean", + "value": "afiq", + "maxValue": "", + "minValue": "", + "operator": "not_contains", + "variable": "namaAsnaf", + "valueType": "string", "logicalOperator": "and" } ] @@ -291,7 +289,7 @@ }, "type": "html", "label": "Show Result", - "position": { "x": 1590, "y": 150 } + "position": { "x": 1635, "y": 150 } }, { "id": "notification-1752621850786", @@ -302,26 +300,26 @@ "priority": "medium", "expiration": { "unit": "hours", "value": 24, "enabled": false }, "description": "Send notification to users", - "htmlMessage": "", - "messageFormat": "text", + "htmlMessage": "
\n

Notification Title

\n

Hello {namaAsnaf},

\n

This is a basic notification message.

\n

Thank you,
Process Maker

\n
", + "messageFormat": "html", "recipientRole": "", "recipientType": "email", "recipientUser": "", "recipientEmail": "mdafiqiskandar@gmail.com", "recipientGroup": "", - "deliveryOptions": { "sms": false, "email": false, "inApp": true }, + "deliveryOptions": { "sms": false, "email": true, "inApp": true }, "richTextMessage": "", "notificationType": "info", "recipientVariable": "" }, "type": "notification", "label": "Notification", - "position": { "x": 1590, "y": 360 } + "position": { "x": 1815, "y": 405 } } ], "viewport": { - "x": -271.3433493533669, - "y": 163.8456958483416, - "zoom": 0.6965142034495484 + "x": -145.563928050559, + "y": 328.559309674283, + "zoom": 0.7049100631988332 } } diff --git a/nuxt.config.js b/nuxt.config.js index a90f876..5a01081 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -559,5 +559,11 @@ export default defineNuxtConfig({ requestSizeLimiter: false, }, }, + "/api/notifications/**": { + security: { + xssValidator: false, + requestSizeLimiter: false, + }, + }, }, }); diff --git a/package.json b/package.json index 31f1cfe..b16cfc5 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "luxon": "^3.1.0", "marked": "^16.1.1", "maska": "^1.5.0", + "nodemailer": "^7.0.5", "pinia": "^2.1.6", "prettier": "^3.3.3", "prettier-plugin-vue": "^1.1.6", diff --git a/pages/process-builder/manage.vue b/pages/process-builder/manage.vue index ef9f909..2019534 100644 --- a/pages/process-builder/manage.vue +++ b/pages/process-builder/manage.vue @@ -676,14 +676,14 @@ const copyWorkflowLink = async (processId) => { - + -->