@lodashventure/medusa-notification-webhook 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,5 +10,6 @@ exports.customerFields = [
10
10
  "customer.first_name",
11
11
  "customer.last_name",
12
12
  "customer.company_name",
13
+ "customer.metadata",
13
14
  ];
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItZmllbGRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbnN0YW50cy9jdXN0b21lci1maWVsZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxjQUFjLEdBQUc7SUFDNUIsV0FBVztJQUNYLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsZ0JBQWdCO0lBQ2hCLHNCQUFzQjtJQUN0QixxQkFBcUI7SUFDckIsb0JBQW9CO0lBQ3BCLHVCQUF1QjtDQUN4QixDQUFDIn0=
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItZmllbGRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbnN0YW50cy9jdXN0b21lci1maWVsZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxjQUFjLEdBQUc7SUFDNUIsV0FBVztJQUNYLGFBQWE7SUFDYixnQkFBZ0I7SUFDaEIsZ0JBQWdCO0lBQ2hCLHNCQUFzQjtJQUN0QixxQkFBcUI7SUFDckIsb0JBQW9CO0lBQ3BCLHVCQUF1QjtJQUN2QixtQkFBbUI7Q0FDcEIsQ0FBQyJ9
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.quoteFields = void 0;
4
4
  exports.quoteFields = [
5
5
  "id",
6
+ "doc_no",
6
7
  "status",
8
+ "created_at",
9
+ "updated_at",
7
10
  "cart.id",
8
11
  "draft_order.id",
9
12
  "draft_order.currency_code",
@@ -42,4 +45,4 @@ exports.quoteFields = [
42
45
  "draft_order.items.detail.*",
43
46
  "draft_order.payment_collections.*",
44
47
  ];
45
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGUtZmllbGRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbnN0YW50cy9xdW90ZS1maWVsZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxXQUFXLEdBQUc7SUFDekIsSUFBSTtJQUNKLFFBQVE7SUFDUixTQUFTO0lBQ1QsZ0JBQWdCO0lBQ2hCLDJCQUEyQjtJQUMzQix3QkFBd0I7SUFDeEIsdUJBQXVCO0lBQ3ZCLG9CQUFvQjtJQUNwQixxQkFBcUI7SUFDckIscUJBQXFCO0lBQ3JCLG1CQUFtQjtJQUNuQixzQkFBc0I7SUFDdEIsdUJBQXVCO0lBQ3ZCLDBCQUEwQjtJQUMxQiw0QkFBNEI7SUFDNUIsZ0NBQWdDO0lBQ2hDLDRCQUE0QjtJQUM1QixnQ0FBZ0M7SUFDaEMsd0JBQXdCO0lBQ3hCLDJCQUEyQjtJQUMzQiw0QkFBNEI7SUFDNUIsaUNBQWlDO0lBQ2pDLG9DQUFvQztJQUNwQyxxQ0FBcUM7SUFDckMsNEJBQTRCO0lBQzVCLCtCQUErQjtJQUMvQixnQ0FBZ0M7SUFDaEMseUNBQXlDO0lBQ3pDLHdDQUF3QztJQUN4QyxxQ0FBcUM7SUFDckMsd0JBQXdCO0lBQ3hCLHdCQUF3QjtJQUN4QixxQkFBcUI7SUFDckIsK0JBQStCO0lBQy9CLGlDQUFpQztJQUNqQyw2QkFBNkI7SUFDN0IscUNBQXFDO0lBQ3JDLDRCQUE0QjtJQUM1QixtQ0FBbUM7Q0FDcEMsQ0FBQyJ9
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGUtZmllbGRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbnN0YW50cy9xdW90ZS1maWVsZHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxXQUFXLEdBQUc7SUFDekIsSUFBSTtJQUNKLFFBQVE7SUFDUixRQUFRO0lBQ1IsWUFBWTtJQUNaLFlBQVk7SUFDWixTQUFTO0lBQ1QsZ0JBQWdCO0lBQ2hCLDJCQUEyQjtJQUMzQix3QkFBd0I7SUFDeEIsdUJBQXVCO0lBQ3ZCLG9CQUFvQjtJQUNwQixxQkFBcUI7SUFDckIscUJBQXFCO0lBQ3JCLG1CQUFtQjtJQUNuQixzQkFBc0I7SUFDdEIsdUJBQXVCO0lBQ3ZCLDBCQUEwQjtJQUMxQiw0QkFBNEI7SUFDNUIsZ0NBQWdDO0lBQ2hDLDRCQUE0QjtJQUM1QixnQ0FBZ0M7SUFDaEMsd0JBQXdCO0lBQ3hCLDJCQUEyQjtJQUMzQiw0QkFBNEI7SUFDNUIsaUNBQWlDO0lBQ2pDLG9DQUFvQztJQUNwQyxxQ0FBcUM7SUFDckMsNEJBQTRCO0lBQzVCLCtCQUErQjtJQUMvQixnQ0FBZ0M7SUFDaEMseUNBQXlDO0lBQ3pDLHdDQUF3QztJQUN4QyxxQ0FBcUM7SUFDckMsd0JBQXdCO0lBQ3hCLHdCQUF3QjtJQUN4QixxQkFBcUI7SUFDckIsK0JBQStCO0lBQy9CLGlDQUFpQztJQUNqQyw2QkFBNkI7SUFDN0IscUNBQXFDO0lBQ3JDLDRCQUE0QjtJQUM1QixtQ0FBbUM7Q0FDcEMsQ0FBQyJ9
@@ -8,9 +8,24 @@ class EventHookModuleService extends (0, utils_1.MedusaService)({}) {
8
8
  webHookConfig: {},
9
9
  };
10
10
  }
11
+ normalizeConfig(config) {
12
+ const normalized = { ...config };
13
+ if (!normalized.requestQuotationHook &&
14
+ normalized.requestQuotationHookUrl) {
15
+ normalized.requestQuotationHook = normalized.requestQuotationHookUrl;
16
+ }
17
+ if (!normalized.deliveryStartedHook &&
18
+ normalized.deliveryStartedHookUrl) {
19
+ normalized.deliveryStartedHook = normalized.deliveryStartedHookUrl;
20
+ }
21
+ return normalized;
22
+ }
11
23
  async getWebHookOptions() {
12
- return this._options.webHookConfig;
24
+ if (!this.normalizedConfig) {
25
+ this.normalizedConfig = this.normalizeConfig(this._options.webHookConfig);
26
+ }
27
+ return this.normalizedConfig;
13
28
  }
14
29
  }
15
30
  exports.default = EventHookModuleService;
16
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2V2ZW50LWhvb2svc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUEwRDtBQUcxRCxNQUFxQixzQkFBdUIsU0FBUSxJQUFBLHFCQUFhLEVBQUMsRUFBRSxDQUFDO0lBR25FLFlBQVksRUFBRSxFQUFFLE9BQXVCO1FBQ3JDLEtBQUssQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDO1FBRXBCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxJQUFJO1lBQ3pCLGFBQWEsRUFBRSxFQUFFO1NBQ2xCLENBQUM7SUFDSixDQUFDO0lBRUQsS0FBSyxDQUFDLGlCQUFpQjtRQUNyQixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDO0lBQ3JDLENBQUM7Q0FDRjtBQWRELHlDQWNDIn0=
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2V2ZW50LWhvb2svc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFEQUEwRDtBQUcxRCxNQUFxQixzQkFBdUIsU0FBUSxJQUFBLHFCQUFhLEVBQUMsRUFBRSxDQUFDO0lBSW5FLFlBQVksRUFBRSxFQUFFLE9BQXVCO1FBQ3JDLEtBQUssQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDO1FBRXBCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxJQUFJO1lBQ3pCLGFBQWEsRUFBRSxFQUFFO1NBQ2xCLENBQUM7SUFDSixDQUFDO0lBRVMsZUFBZSxDQUN2QixNQUFzQztRQUV0QyxNQUFNLFVBQVUsR0FBRyxFQUFFLEdBQUcsTUFBTSxFQUFFLENBQUM7UUFFakMsSUFDRSxDQUFDLFVBQVUsQ0FBQyxvQkFBb0I7WUFDaEMsVUFBVSxDQUFDLHVCQUF1QixFQUNsQyxDQUFDO1lBQ0QsVUFBVSxDQUFDLG9CQUFvQixHQUFHLFVBQVUsQ0FBQyx1QkFBdUIsQ0FBQztRQUN2RSxDQUFDO1FBRUQsSUFDRSxDQUFDLFVBQVUsQ0FBQyxtQkFBbUI7WUFDL0IsVUFBVSxDQUFDLHNCQUFzQixFQUNqQyxDQUFDO1lBQ0QsVUFBVSxDQUFDLG1CQUFtQixHQUFHLFVBQVUsQ0FBQyxzQkFBc0IsQ0FBQztRQUNyRSxDQUFDO1FBRUQsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUVELEtBQUssQ0FBQyxpQkFBaUI7UUFDckIsSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzNCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDO0lBQy9CLENBQUM7Q0FDRjtBQXpDRCx5Q0F5Q0MifQ==
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.config = void 0;
7
+ exports.default = customerCreatedNovuSubscriber;
8
+ const utils_1 = require("@medusajs/framework/utils");
9
+ const event_hook_1 = require("../modules/event-hook");
10
+ const create_novu_subscriber_1 = __importDefault(require("../workflows/create-novu-subscriber"));
11
+ const mapChannels = (customer) => {
12
+ const channels = [];
13
+ if (customer.email) {
14
+ channels.push({
15
+ type: "email",
16
+ identity: customer.email,
17
+ metadata: {
18
+ has_account: customer.has_account ?? false,
19
+ },
20
+ });
21
+ }
22
+ const metadata = (customer.metadata ?? {});
23
+ const lineDisplayName = typeof metadata["line_display_name"] === "string"
24
+ ? metadata["line_display_name"]
25
+ : undefined;
26
+ const linePictureUrl = typeof metadata["line_picture_url"] === "string"
27
+ ? metadata["line_picture_url"]
28
+ : undefined;
29
+ const lineLinkedAt = typeof metadata["line_linked_at"] === "string"
30
+ ? metadata["line_linked_at"]
31
+ : undefined;
32
+ const lineUserId = typeof metadata["line_user_id"] === "string"
33
+ ? metadata["line_user_id"]
34
+ : undefined;
35
+ if (lineUserId) {
36
+ channels.push({
37
+ type: "line",
38
+ identity: lineUserId,
39
+ metadata: {
40
+ display_name: lineDisplayName,
41
+ picture_url: linePictureUrl,
42
+ linked_at: lineLinkedAt,
43
+ },
44
+ });
45
+ }
46
+ return channels;
47
+ };
48
+ async function customerCreatedNovuSubscriber({ event: { data }, container, }) {
49
+ const logger = container.resolve("logger");
50
+ const eventHookService = container.resolve(event_hook_1.EVENT_HOOK_MODULE);
51
+ const customerService = container.resolve(utils_1.Modules.CUSTOMER);
52
+ const webHookOptions = await eventHookService.getWebHookOptions();
53
+ const subscriberHook = webHookOptions.customerCreatedSubscriberHook;
54
+ if (!subscriberHook?.url) {
55
+ return;
56
+ }
57
+ if (!subscriberHook.authHeader?.headerAuthKey ||
58
+ !subscriberHook.authHeader?.headerAuthValue) {
59
+ logger.warn("[Novu] Customer subscriber hook configured without auth headers. Skipping subscriber creation.");
60
+ return;
61
+ }
62
+ let customer = null;
63
+ try {
64
+ customer = await customerService.retrieveCustomer(data.id);
65
+ }
66
+ catch (error) {
67
+ logger.warn(`[Novu] Unable to retrieve customer ${data.id} for Novu sync. ${error instanceof Error ? error.message : ""}`);
68
+ return;
69
+ }
70
+ const channels = mapChannels(customer);
71
+ if (channels.length === 0) {
72
+ logger.info(`[Novu] Customer ${customer.id} does not have email or LINE channels. Skipping Novu subscriber creation.`);
73
+ return;
74
+ }
75
+ const metadata = (customer.metadata ?? {});
76
+ const subscriberPayload = {
77
+ subscriberId: customer.id,
78
+ email: customer.email ?? undefined,
79
+ firstName: customer.first_name ?? undefined,
80
+ lastName: customer.last_name ?? undefined,
81
+ phone: customer.phone ?? undefined,
82
+ data: {
83
+ has_account: customer.has_account ?? false,
84
+ company_name: customer.company_name,
85
+ metadata,
86
+ channels: channels.reduce((acc, channel) => {
87
+ acc[channel.type] = {
88
+ identity: channel.identity,
89
+ ...channel.metadata,
90
+ };
91
+ return acc;
92
+ }, {}),
93
+ },
94
+ channels,
95
+ };
96
+ try {
97
+ await create_novu_subscriber_1.default.run({
98
+ input: {
99
+ name: subscriberHook.name ?? "customer-created",
100
+ url: subscriberHook.url,
101
+ authKey: subscriberHook.authHeader.headerAuthKey,
102
+ authValue: subscriberHook.authHeader.headerAuthValue,
103
+ subscriber: subscriberPayload,
104
+ },
105
+ });
106
+ logger.info(`[Novu] Synced customer ${customer.id} as subscriber with ${channels.length} channel(s).`);
107
+ }
108
+ catch (error) {
109
+ logger.error(`[Novu] Failed to sync customer ${customer.id} with Novu subscriber API:`, error);
110
+ }
111
+ }
112
+ exports.config = {
113
+ event: "customer.created",
114
+ };
115
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItY3JlYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zdWJzY3JpYmVycy9jdXN0b21lci1jcmVhdGVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQTJEQSxnREF1RkM7QUFoSkQscURBQW9EO0FBRXBELHNEQUEwRDtBQUMxRCxpR0FFNkM7QUFNN0MsTUFBTSxXQUFXLEdBQUcsQ0FBQyxRQUFxQixFQUF3QixFQUFFO0lBQ2xFLE1BQU0sUUFBUSxHQUF5QixFQUFFLENBQUM7SUFFMUMsSUFBSSxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDbkIsUUFBUSxDQUFDLElBQUksQ0FBQztZQUNaLElBQUksRUFBRSxPQUFPO1lBQ2IsUUFBUSxFQUFFLFFBQVEsQ0FBQyxLQUFLO1lBQ3hCLFFBQVEsRUFBRTtnQkFDUixXQUFXLEVBQUUsUUFBUSxDQUFDLFdBQVcsSUFBSSxLQUFLO2FBQzNDO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELE1BQU0sUUFBUSxHQUFHLENBQUMsUUFBUSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQTRCLENBQUM7SUFDdEUsTUFBTSxlQUFlLEdBQ25CLE9BQU8sUUFBUSxDQUFDLG1CQUFtQixDQUFDLEtBQUssUUFBUTtRQUMvQyxDQUFDLENBQUUsUUFBUSxDQUFDLG1CQUFtQixDQUFZO1FBQzNDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDaEIsTUFBTSxjQUFjLEdBQ2xCLE9BQU8sUUFBUSxDQUFDLGtCQUFrQixDQUFDLEtBQUssUUFBUTtRQUM5QyxDQUFDLENBQUUsUUFBUSxDQUFDLGtCQUFrQixDQUFZO1FBQzFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDaEIsTUFBTSxZQUFZLEdBQ2hCLE9BQU8sUUFBUSxDQUFDLGdCQUFnQixDQUFDLEtBQUssUUFBUTtRQUM1QyxDQUFDLENBQUUsUUFBUSxDQUFDLGdCQUFnQixDQUFZO1FBQ3hDLENBQUMsQ0FBQyxTQUFT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
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.config = void 0;
7
+ exports.default = quoteSentSubscriber;
8
+ const utils_1 = require("@medusajs/framework/utils");
9
+ const path_1 = __importDefault(require("path"));
10
+ const promises_1 = require("fs/promises");
11
+ const event_hook_1 = require("../modules/event-hook");
12
+ const quote_fields_1 = require("../constants/quote-fields");
13
+ const customer_fields_1 = require("../constants/customer-fields");
14
+ const call_external_api_1 = __importDefault(require("../workflows/call-external-api"));
15
+ const FLEX_TEMPLATE_PATH = path_1.default.join(process.cwd(), "flex/qoute.json");
16
+ const LINE_PUSH_ENDPOINT = "https://api.line.me/v2/bot/message/push";
17
+ const STATUS_LABELS = {
18
+ draft: "ร่าง",
19
+ pending_merchant: "รอตรวจสอบ",
20
+ pending_customer: "รอการยืนยัน",
21
+ approved: "อนุมัติแล้ว",
22
+ accepted: "ลูกค้ายืนยันแล้ว",
23
+ customer_rejected: "ลูกค้าปฏิเสธ",
24
+ merchant_rejected: "ยกเลิกโดยร้านค้า",
25
+ voided: "ยกเลิกแล้ว",
26
+ expired: "หมดอายุ",
27
+ converted: "แปลงเป็นออเดอร์แล้ว",
28
+ };
29
+ let cachedFlexTemplate = null;
30
+ const loadFlexTemplate = async () => {
31
+ if (cachedFlexTemplate) {
32
+ return cachedFlexTemplate;
33
+ }
34
+ try {
35
+ const file = await (0, promises_1.readFile)(FLEX_TEMPLATE_PATH, "utf-8");
36
+ cachedFlexTemplate = JSON.parse(file);
37
+ return cachedFlexTemplate;
38
+ }
39
+ catch (error) {
40
+ console.warn(`[Quote] Unable to load LINE flex template at ${FLEX_TEMPLATE_PATH}`, error);
41
+ return null;
42
+ }
43
+ };
44
+ const cloneTemplate = async () => {
45
+ const template = await loadFlexTemplate();
46
+ if (!template) {
47
+ return null;
48
+ }
49
+ return JSON.parse(JSON.stringify(template));
50
+ };
51
+ const formatCurrency = (amount, currency) => {
52
+ const safeCurrency = currency || "THB";
53
+ const safeAmount = typeof amount === "number" ? amount : 0;
54
+ try {
55
+ return new Intl.NumberFormat("th-TH", {
56
+ style: "currency",
57
+ currency: safeCurrency,
58
+ }).format(safeAmount);
59
+ }
60
+ catch {
61
+ return `${safeAmount.toFixed(2)} ${safeCurrency}`;
62
+ }
63
+ };
64
+ const formatDateTime = (value) => {
65
+ if (!value) {
66
+ return "";
67
+ }
68
+ try {
69
+ return new Intl.DateTimeFormat("th-TH", {
70
+ dateStyle: "medium",
71
+ timeStyle: "short",
72
+ }).format(new Date(value));
73
+ }
74
+ catch {
75
+ return value;
76
+ }
77
+ };
78
+ const itemQuantityText = (item) => {
79
+ const quantity = typeof item.detail?.quantity === "number"
80
+ ? item.detail?.quantity
81
+ : item.quantity;
82
+ if (typeof quantity === "number" && !Number.isNaN(quantity)) {
83
+ return `${quantity} ชิ้น`;
84
+ }
85
+ return "1 ชิ้น";
86
+ };
87
+ const buildItemBox = (index, item, currency) => {
88
+ return {
89
+ type: "box",
90
+ layout: "horizontal",
91
+ contents: [
92
+ {
93
+ type: "box",
94
+ layout: "vertical",
95
+ contents: [
96
+ {
97
+ type: "text",
98
+ text: `${index + 1}.`,
99
+ size: "sm",
100
+ color: "#666666",
101
+ },
102
+ ],
103
+ width: "25px",
104
+ },
105
+ {
106
+ type: "box",
107
+ layout: "vertical",
108
+ contents: [
109
+ {
110
+ type: "text",
111
+ text: item.title || "สินค้า",
112
+ size: "sm",
113
+ wrap: true,
114
+ color: "#333333",
115
+ },
116
+ {
117
+ type: "box",
118
+ layout: "horizontal",
119
+ contents: [
120
+ {
121
+ type: "text",
122
+ text: itemQuantityText(item),
123
+ size: "xs",
124
+ color: "#666666",
125
+ },
126
+ {
127
+ type: "text",
128
+ text: formatCurrency(item.detail?.unit_price, currency),
129
+ size: "xs",
130
+ color: "#1DB446",
131
+ weight: "bold",
132
+ align: "end",
133
+ },
134
+ ],
135
+ },
136
+ ],
137
+ flex: 1,
138
+ },
139
+ ],
140
+ margin: "md",
141
+ };
142
+ };
143
+ const getQuoteLabel = (quote) => {
144
+ if (quote.doc_no) {
145
+ return quote.doc_no;
146
+ }
147
+ if (quote.id?.length > 6) {
148
+ return `#${quote.id.slice(-6)}`;
149
+ }
150
+ return `#${quote.id}`;
151
+ };
152
+ const buildFlexBubble = async (quote) => {
153
+ const bubble = await cloneTemplate();
154
+ if (!bubble) {
155
+ return null;
156
+ }
157
+ const quoteLabel = getQuoteLabel(quote);
158
+ const statusLabel = STATUS_LABELS[quote.status] ?? quote.status ?? "สถานะไม่ระบุ";
159
+ const customerName = [quote.customer?.first_name, quote.customer?.last_name]
160
+ .filter(Boolean)
161
+ .join(" ")
162
+ .trim() ||
163
+ quote.customer?.email ||
164
+ "ลูกค้า";
165
+ const currency = quote.draft_order?.currency_code || "THB";
166
+ const items = quote.draft_order?.items || [];
167
+ const header = bubble.header;
168
+ if (header?.contents?.[0]?.contents?.[0]) {
169
+ header.contents[0].contents[0].text = `ใบเสนอราคา ${quoteLabel}`;
170
+ }
171
+ if (header?.contents?.[0]?.contents?.[1]) {
172
+ header.contents[0].contents[1].text = statusLabel;
173
+ }
174
+ const body = bubble.body;
175
+ if (body?.contents?.[0]?.contents?.[1]) {
176
+ body.contents[0].contents[1].text = items.length
177
+ ? `${items.length} รายการ`
178
+ : "ไม่มีสินค้า";
179
+ }
180
+ const lineItemsBox = body?.contents?.[2];
181
+ if (lineItemsBox) {
182
+ const maxItems = 4;
183
+ const renderedItems = items
184
+ .slice(0, maxItems)
185
+ .map((item, index) => buildItemBox(index, item, currency));
186
+ if (items.length > maxItems) {
187
+ renderedItems.push({
188
+ type: "text",
189
+ text: `... และอีก ${items.length - maxItems} รายการ`,
190
+ size: "xs",
191
+ color: "#999999",
192
+ align: "center",
193
+ margin: "md",
194
+ });
195
+ }
196
+ if (renderedItems.length === 0) {
197
+ renderedItems.push({
198
+ type: "text",
199
+ text: "ยังไม่มีสินค้าในใบเสนอราคา",
200
+ size: "xs",
201
+ color: "#999999",
202
+ align: "center",
203
+ margin: "md",
204
+ });
205
+ }
206
+ lineItemsBox.contents = renderedItems;
207
+ delete lineItemsBox.height;
208
+ }
209
+ const totalsBox = body?.contents?.[4];
210
+ if (totalsBox?.contents?.[0]?.contents?.[1]) {
211
+ totalsBox.contents[0].contents[1].text = formatCurrency(quote.draft_order?.subtotal, currency);
212
+ }
213
+ if (totalsBox?.contents?.[1]?.contents?.[0]) {
214
+ totalsBox.contents[1].contents[0].text = "ภาษีทั้งหมด";
215
+ }
216
+ if (totalsBox?.contents?.[1]?.contents?.[1]) {
217
+ totalsBox.contents[1].contents[1].text = formatCurrency(quote.draft_order?.tax_total, currency);
218
+ }
219
+ const grandTotalBox = body?.contents?.[6];
220
+ if (grandTotalBox?.contents?.[1]) {
221
+ grandTotalBox.contents[1].text = formatCurrency(quote.draft_order?.total, currency);
222
+ }
223
+ const footer = bubble.footer;
224
+ const footerInfoBox = footer?.contents?.[0];
225
+ if (footerInfoBox?.contents?.[0]) {
226
+ footerInfoBox.contents[0].text = `ลูกค้า: ${customerName}`;
227
+ }
228
+ if (footerInfoBox?.contents?.[1]) {
229
+ footerInfoBox.contents[1].text = `วันที่: ${formatDateTime(quote.updated_at || quote.created_at)}`;
230
+ }
231
+ const buttonsBox = footer?.contents?.[2];
232
+ const baseUrl = process.env.STOREFRONT_URL || "https://localhost:3000";
233
+ const detailUrl = `${baseUrl.replace(/\/$/, "")}/quotation/${quote.id}`;
234
+ const confirmUrl = `${detailUrl}?action=accept`;
235
+ if (buttonsBox?.contents?.[0]?.action) {
236
+ buttonsBox.contents[0].action.uri = confirmUrl;
237
+ }
238
+ if (buttonsBox?.contents?.[1]?.action) {
239
+ buttonsBox.contents[1].action.uri = detailUrl;
240
+ }
241
+ return bubble;
242
+ };
243
+ const sendLineFlexMessage = async (logger, lineUserId, bubble, quote) => {
244
+ const accessToken = process.env.LINE_MESSAGING_CHANNEL_ACCESS_TOKEN;
245
+ if (!accessToken) {
246
+ logger.warn("[Quote] LINE_MESSAGING_CHANNEL_ACCESS_TOKEN is not configured. Skipping LINE notification.");
247
+ return false;
248
+ }
249
+ const payload = {
250
+ to: lineUserId,
251
+ messages: [
252
+ {
253
+ type: "flex",
254
+ altText: `ใบเสนอราคา ${getQuoteLabel(quote)}`,
255
+ contents: bubble,
256
+ },
257
+ ],
258
+ };
259
+ const response = await fetch(LINE_PUSH_ENDPOINT, {
260
+ method: "POST",
261
+ headers: {
262
+ "Content-Type": "application/json",
263
+ Authorization: `Bearer ${accessToken}`,
264
+ },
265
+ body: JSON.stringify(payload),
266
+ });
267
+ if (!response.ok) {
268
+ const body = await response.text();
269
+ throw new Error(`LINE push message failed: ${response.status} ${response.statusText} - ${body}`);
270
+ }
271
+ return true;
272
+ };
273
+ const triggerNovu = async (logger, quote, hook) => {
274
+ if (!hook?.url ||
275
+ !hook.authHeader?.headerAuthKey ||
276
+ !hook.authHeader.headerAuthValue) {
277
+ return false;
278
+ }
279
+ const customerMetadata = (quote.customer?.metadata ?? {});
280
+ const subscriberId = (typeof customerMetadata.novu_subscriber_id === "string"
281
+ ? customerMetadata.novu_subscriber_id
282
+ : undefined) ??
283
+ quote.customer?.id ??
284
+ quote.customer_id ??
285
+ quote.customer?.email;
286
+ if (!subscriberId) {
287
+ logger.warn(`[Quote] Unable to resolve Novu subscriber for quote ${quote.id}, skipping email trigger.`);
288
+ return false;
289
+ }
290
+ const novuPayload = {
291
+ name: hook.name ?? "quote-sent",
292
+ to: subscriberId,
293
+ payload: { quote },
294
+ };
295
+ await call_external_api_1.default.run({
296
+ input: {
297
+ name: hook.name ?? "quote-sent",
298
+ url: hook.url,
299
+ authKey: hook.authHeader.headerAuthKey,
300
+ authValue: hook.authHeader.headerAuthValue,
301
+ type: "QUOTE_SENT",
302
+ payload: novuPayload,
303
+ },
304
+ });
305
+ return true;
306
+ };
307
+ async function quoteSentSubscriber({ event: { data }, container, }) {
308
+ const logger = container.resolve("logger");
309
+ const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
310
+ const eventHookService = container.resolve(event_hook_1.EVENT_HOOK_MODULE);
311
+ const { data: [quote], } = await query.graph({
312
+ entity: "quote",
313
+ fields: [...quote_fields_1.quoteFields, ...customer_fields_1.customerFields],
314
+ filters: { id: data.id },
315
+ }, { throwIfKeyNotFound: true });
316
+ if (!quote) {
317
+ logger.warn(`[Quote] Unable to load quote ${data.id} for notification dispatch.`);
318
+ return;
319
+ }
320
+ const customerMetadata = (quote.customer?.metadata ?? {});
321
+ const lineUserId = typeof customerMetadata.line_user_id === "string"
322
+ ? customerMetadata.line_user_id
323
+ : undefined;
324
+ if (lineUserId) {
325
+ try {
326
+ const bubble = await buildFlexBubble(quote);
327
+ if (bubble) {
328
+ await sendLineFlexMessage(logger, lineUserId, bubble, quote);
329
+ logger.info(`[Quote] Sent LINE flex message for quote ${quote.id}`);
330
+ return;
331
+ }
332
+ }
333
+ catch (error) {
334
+ logger.error(`[Quote] Failed to send LINE flex message for quote ${quote.id}`, error);
335
+ }
336
+ }
337
+ const webHookOptions = await eventHookService.getWebHookOptions();
338
+ const quoteSentHook = webHookOptions.quoteSentHook || webHookOptions.requestQuotationHook;
339
+ if (!quoteSentHook?.url) {
340
+ logger.warn(`[Quote] No Novu webhook configured for quote sent event. Quote ${quote.id} was not notified via email channel.`);
341
+ return;
342
+ }
343
+ try {
344
+ await triggerNovu(logger, quote, quoteSentHook);
345
+ logger.info(`[Quote] Triggered Novu quote-sent hook for ${quote.id}`);
346
+ }
347
+ catch (error) {
348
+ logger.error(`[Quote] Failed to trigger Novu quote-sent hook for ${quote.id}`, error);
349
+ }
350
+ }
351
+ exports.config = {
352
+ event: "quote.sent",
353
+ };
354
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGUtc2VudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zdWJzY3JpYmVycy9xdW90ZS1zZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQXdiQSxzQ0F3RUM7QUEvZkQscURBQXNFO0FBRXRFLGdEQUF3QjtBQUN4QiwwQ0FBdUM7QUFFdkMsc0RBQTBEO0FBQzFELDREQUF3RDtBQUN4RCxrRUFBOEQ7QUFDOUQsdUZBQXFFO0FBK0NyRSxNQUFNLGtCQUFrQixHQUFHLGNBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFLGlCQUFpQixDQUFDLENBQUM7QUFDdkUsTUFBTSxrQkFBa0IsR0FBRyx5Q0FBeUMsQ0FBQztBQUNyRSxNQUFNLGFBQWEsR0FBMkI7SUFDNUMsS0FBSyxFQUFFLE1BQU07SUFDYixnQkFBZ0IsRUFBRSxXQUFXO0lBQzdCLGdCQUFnQixFQUFFLGFBQWE7SUFDL0IsUUFBUSxFQUFFLGFBQWE7SUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtJQUM1QixpQkFBaUIsRUFBRSxjQUFjO0lBQ2pDLGlCQUFpQixFQUFFLGtCQUFrQjtJQUNyQyxNQUFNLEVBQUUsWUFBWTtJQUNwQixPQUFPLEVBQUUsU0FBUztJQUNsQixTQUFTLEVBQUUscUJBQXFCO0NBQ2pDLENBQUM7QUFFRixJQUFJLGtCQUFrQixHQUFtQyxJQUFJLENBQUM7QUFFOUQsTUFBTSxnQkFBZ0IsR0FBRyxLQUFLLElBQUksRUFBRTtJQUNsQyxJQUFJLGtCQUFrQixFQUFFLENBQUM7UUFDdkIsT0FBTyxrQkFBa0IsQ0FBQztJQUM1QixDQUFDO0lBRUQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFBLG1CQUFRLEVBQUMsa0JBQWtCLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDekQsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN0QyxPQUFPLGtC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
@@ -20,4 +20,4 @@ const callExternalApiWorkflow = (0, workflows_sdk_1.createWorkflow)("call-extern
20
20
  return new workflows_sdk_1.WorkflowResponse(input);
21
21
  });
22
22
  exports.default = callExternalApiWorkflow;
23
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsbC1leHRlcm5hbC1hcGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL2NhbGwtZXh0ZXJuYWwtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscUVBSzJDO0FBb0IzQyxNQUFNLG1CQUFtQixHQUFHLElBQUEsMEJBQVUsRUFDcEMsbUJBQW1CLEVBQ25CLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFpQixFQUFFLEVBQUU7SUFDeEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFOUMsTUFBTSxLQUFLLENBQUMsR0FBRyxFQUFFO1FBQ2YsTUFBTSxFQUFFLE1BQU07UUFDZCxPQUFPLEVBQUU7WUFDUCxjQUFjLEVBQUUsa0JBQWtCO1lBQ2xDLENBQUMsT0FBTyxDQUFDLEVBQUUsU0FBUztTQUNyQjtRQUNELElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQztLQUM5QixDQUFDLENBQUM7SUFFSCxPQUFPLElBQUksNEJBQVksQ0FBQztRQUN0QixPQUFPLEVBQUUsV0FBVyxJQUFJLElBQUksSUFBSSxhQUFhLEdBQUcsZUFBZTtLQUNoRSxDQUFDLENBQUM7QUFDTCxDQUFDLENBQ0YsQ0FBQztBQUVGLE1BQU0sdUJBQXVCLEdBQUcsSUFBQSw4QkFBYyxFQUM1QyxtQkFBbUIsRUFDbkIsQ0FBQyxLQUFvQixFQUFFLEVBQUU7SUFDdkIsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFM0IsT0FBTyxJQUFJLGdDQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3JDLENBQUMsQ0FDRixDQUFDO0FBRUYsa0JBQWUsdUJBQXVCLENBQUMifQ==
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsbC1leHRlcm5hbC1hcGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL2NhbGwtZXh0ZXJuYWwtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscUVBSzJDO0FBcUIzQyxNQUFNLG1CQUFtQixHQUFHLElBQUEsMEJBQVUsRUFDcEMsbUJBQW1CLEVBQ25CLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFpQixFQUFFLEVBQUU7SUFDeEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFOUMsTUFBTSxLQUFLLENBQUMsR0FBRyxFQUFFO1FBQ2YsTUFBTSxFQUFFLE1BQU07UUFDZCxPQUFPLEVBQUU7WUFDUCxjQUFjLEVBQUUsa0JBQWtCO1lBQ2xDLENBQUMsT0FBTyxDQUFDLEVBQUUsU0FBUztTQUNyQjtRQUNELElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQztLQUM5QixDQUFDLENBQUM7SUFFSCxPQUFPLElBQUksNEJBQVksQ0FBQztRQUN0QixPQUFPLEVBQUUsV0FBVyxJQUFJLElBQUksSUFBSSxhQUFhLEdBQUcsZUFBZTtLQUNoRSxDQUFDLENBQUM7QUFDTCxDQUFDLENBQ0YsQ0FBQztBQUVGLE1BQU0sdUJBQXVCLEdBQUcsSUFBQSw4QkFBYyxFQUM1QyxtQkFBbUIsRUFDbkIsQ0FBQyxLQUFvQixFQUFFLEVBQUU7SUFDdkIsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFM0IsT0FBTyxJQUFJLGdDQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3JDLENBQUMsQ0FDRixDQUFDO0FBRUYsa0JBQWUsdUJBQXVCLENBQUMifQ==
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
4
+ const normalizeUrl = (url) => url.replace(/\/$/, "");
5
+ const requestNovu = async (url, method, headers, body) => {
6
+ const response = await fetch(url, {
7
+ method,
8
+ headers: {
9
+ "Content-Type": "application/json",
10
+ ...headers,
11
+ },
12
+ body: JSON.stringify(body),
13
+ });
14
+ const text = await response.text();
15
+ let data = text;
16
+ if (text) {
17
+ try {
18
+ data = JSON.parse(text);
19
+ }
20
+ catch {
21
+ // Swallow JSON parse errors, keep raw text for debugging
22
+ }
23
+ }
24
+ return { response, data };
25
+ };
26
+ const createNovuSubscriberStep = (0, workflows_sdk_1.createStep)("create-novu-subscriber", async ({ url, authKey, authValue, subscriber }) => {
27
+ const baseUrl = normalizeUrl(url);
28
+ const headers = {
29
+ [authKey]: authValue,
30
+ };
31
+ const { response, data } = await requestNovu(baseUrl, "POST", headers, {
32
+ ...subscriber,
33
+ // Novu expects subscriberId field on create
34
+ subscriberId: subscriber.subscriberId,
35
+ });
36
+ if (response.ok) {
37
+ return new workflows_sdk_1.StepResponse({
38
+ message: `Created Novu subscriber ${subscriber.subscriberId}`,
39
+ wasUpdated: false,
40
+ });
41
+ }
42
+ const conflict = response.status === 409 ||
43
+ (typeof data === "object" &&
44
+ data !== null &&
45
+ typeof data.message === "string" &&
46
+ data.message.toLowerCase().includes("exists"));
47
+ if (!conflict) {
48
+ throw new Error(`Failed to create Novu subscriber ${subscriber.subscriberId}: ${data?.message ?? response.statusText}`);
49
+ }
50
+ const updateUrl = `${baseUrl}/${encodeURIComponent(subscriber.subscriberId)}`;
51
+ const { response: updateResponse, data: updateData } = await requestNovu(updateUrl, "PUT", headers, subscriber);
52
+ if (!updateResponse.ok) {
53
+ throw new Error(`Failed to update Novu subscriber ${subscriber.subscriberId}: ${updateData?.message ??
54
+ updateResponse.statusText}`);
55
+ }
56
+ return new workflows_sdk_1.StepResponse({
57
+ message: `Updated existing Novu subscriber ${subscriber.subscriberId}`,
58
+ wasUpdated: true,
59
+ });
60
+ });
61
+ const createNovuSubscriberWorkflow = (0, workflows_sdk_1.createWorkflow)("create-novu-subscriber", (input) => {
62
+ createNovuSubscriberStep(input);
63
+ return new workflows_sdk_1.WorkflowResponse({
64
+ subscriberId: input.subscriber.subscriberId,
65
+ });
66
+ });
67
+ exports.default = createNovuSubscriberWorkflow;
68
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLW5vdnUtc3Vic2NyaWJlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvY3JlYXRlLW5vdnUtc3Vic2NyaWJlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHFFQUsyQztBQTJCM0MsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFXLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRTdELE1BQU0sV0FBVyxHQUFHLEtBQUssRUFDdkIsR0FBVyxFQUNYLE1BQWMsRUFDZCxPQUErQixFQUMvQixJQUFhLEVBQ2IsRUFBRTtJQUNGLE1BQU0sUUFBUSxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRTtRQUNoQyxNQUFNO1FBQ04sT0FBTyxFQUFFO1lBQ1AsY0FBYyxFQUFFLGtCQUFrQjtZQUNsQyxHQUFHLE9BQU87U0FDWDtRQUNELElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQztLQUMzQixDQUFDLENBQUM7SUFFSCxNQUFNLElBQUksR0FBRyxNQUFNLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNuQyxJQUFJLElBQUksR0FBWSxJQUFJLENBQUM7SUFFekIsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUNULElBQUksQ0FBQztZQUNILElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzFCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCx5REFBeUQ7UUFDM0QsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDO0FBQzVCLENBQUMsQ0FBQztBQUVGLE1BQU0sd0JBQXdCLEdBQUcsSUFBQSwwQkFBVSxFQUN6Qyx3QkFBd0IsRUFDeEIsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFB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
package/README.md CHANGED
@@ -20,6 +20,14 @@ module.exports = defineConfig({
20
20
  headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
21
21
  },
22
22
  },
23
+ quoteSentHook: {
24
+ name: "quote-sent", // triggered when merchant sends a quote
25
+ url: process.env.NOTI_WEBHOOK_URL_QUOTE_SENT,
26
+ authHeader: {
27
+ headerAuthKey: "Authorization",
28
+ headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
29
+ },
30
+ },
23
31
  placedOrderHook: {
24
32
  name: "placed-order", // for novu workflow name
25
33
  url: process.env.NOTI_WEBHOOK_URL_PLACED_ORDER,
@@ -60,6 +68,16 @@ module.exports = defineConfig({
60
68
  headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
61
69
  },
62
70
  },
71
+ customerCreatedSubscriberHook: {
72
+ name: "customer-created", // creates/updates Novu subscriber
73
+ url:
74
+ process.env.NOTI_WEBHOOK_URL_CUSTOMER_SUBSCRIBER ||
75
+ "https://api.novu.co/v1/subscribers",
76
+ authHeader: {
77
+ headerAuthKey: "Authorization",
78
+ headerAuthValue: `ApiKey ${process.env.NOTI_WEBHOOK_API_KEY_NOVU}`,
79
+ },
80
+ },
63
81
  },
64
82
  },
65
83
  },
@@ -75,7 +93,23 @@ NOTI_WEBHOOK_URL_DELIVERY_STARTED=<endpoint ex: "https://api.novu.co/v1/events/t
75
93
  NOTI_WEBHOOK_URL_PICKUP_CREATED=<endpoint ex: "https://api.novu.co/v1/events/trigger">
76
94
  NOTI_WEBHOOK_URL_PICKUP_UPDATED=<endpoint ex: "https://api.novu.co/v1/events/trigger">
77
95
  NOTI_WEBHOOK_URL_PICKUP_CANCELLED=<endpoint ex: "https://api.novu.co/v1/events/trigger">
96
+ NOTI_WEBHOOK_URL_QUOTE_SENT=<endpoint ex: "https://api.novu.co/v1/events/trigger">
97
+ NOTI_WEBHOOK_URL_CUSTOMER_SUBSCRIBER=https://api.novu.co/v1/subscribers
98
+ LINE_MESSAGING_CHANNEL_ACCESS_TOKEN=<line-messaging-channel-access-token>
78
99
  ```
79
100
 
101
+ ## Customer subscriber sync
102
+
103
+ When `customerCreatedSubscriberHook` is configured the plugin listens to the `customer.created` event and automatically creates (or updates) a subscriber in Novu using the configured endpoint. Email identities are stored separately from LINE identities, so Novu workflows can target each channel independently. LINE metadata is pulled from the customer's profile (`metadata.line_user_id`, `metadata.line_display_name`, etc.).
104
+
105
+ ## Quote sent notifications
106
+
107
+ When an admin clicks **Send quote** the system emits a `quote.sent` event. This plugin listens for that event and:
108
+
109
+ - Pushes a LINE Flex message (based on `flex/qoute.json`) via `https://api.line.me/v2/bot/message/push` if the customer has a `metadata.line_user_id`. Set `LINE_MESSAGING_CHANNEL_ACCESS_TOKEN` with your bot channel token.
110
+ - Falls back to triggering the configured Novu webhook (`quoteSentHook`) for customers that only supplied an email address (or when LINE notifications are not configured). The payload includes the quote, draft order, and customer context so you can fan out to email/other channels from Novu.
111
+
112
+ Update the `flex/qoute.json` bubble template to adjust colors/text/layout without touching the code.
113
+
80
114
  4. all done now just start your medusa backend
81
115
  `yarn dev` `yarn start` or whatever you use to start your backend
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodashventure/medusa-notification-webhook",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "A starter for Medusa plugins.",
5
5
  "author": "Medusa (https://medusajs.com)",
6
6
  "license": "MIT",
@@ -28,26 +28,26 @@
28
28
  "prepublishOnly": "medusa plugin:build"
29
29
  },
30
30
  "devDependencies": {
31
- "@medusajs/admin-sdk": "2.11.2",
32
- "@medusajs/admin-shared": "2.11.2",
33
- "@medusajs/cli": "2.11.2",
34
- "@medusajs/framework": "2.11.2",
35
- "@medusajs/icons": "^2.11.2",
36
- "@medusajs/medusa": "2.11.2",
37
- "@medusajs/test-utils": "2.11.2",
38
- "@medusajs/ui": "4.0.4",
39
- "@swc/core": "1.5.7",
40
- "@types/jsonwebtoken": "^9.0.7",
41
- "@types/node": "^20.0.0",
42
- "@types/react": "^18.3.2",
43
- "@types/react-dom": "^18.2.25",
31
+ "@medusajs/admin-sdk": "2.11.3",
32
+ "@medusajs/admin-shared": "2.11.3",
33
+ "@medusajs/cli": "2.11.3",
34
+ "@medusajs/framework": "2.11.3",
35
+ "@medusajs/icons": "^2.11.3",
36
+ "@medusajs/medusa": "2.11.3",
37
+ "@medusajs/test-utils": "2.11.3",
38
+ "@medusajs/ui": "4.0.27",
39
+ "@swc/core": "1.15.0",
40
+ "@types/jsonwebtoken": "^9.0.10",
41
+ "@types/node": "^24.10.0",
42
+ "@types/react": "^19.2.2",
43
+ "@types/react-dom": "^19.2.2",
44
44
  "prop-types": "^15.8.1",
45
- "react": "^18.2.0",
46
- "react-dom": "^18.2.0",
45
+ "react": "^19.2.0",
46
+ "react-dom": "^19.2.0",
47
47
  "ts-node": "^10.9.2",
48
- "typescript": "^5.6.2",
49
- "vite": "^5.2.11",
50
- "yalc": "^1.0.0-pre.53"
48
+ "typescript": "^5.9.3",
49
+ "vite": "^7.2.2",
50
+ "yalc": "1.0.0-pre.53"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@medusajs/admin-sdk": "2.11.2",