@namphuongtechnologi/np-hub 0.2.0 → 0.2.1

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.
@@ -167,7 +167,8 @@ var SupportWidgetElement = class extends HTMLElement {
167
167
  this.closeAfterSuccessTimer = null;
168
168
  this.toastAutoCloseTimer = null;
169
169
  this.toastCountdownInterval = null;
170
- this.toastDuration = 4e3;
170
+ this.toastSuccessDuration = 4e3;
171
+ this.toastErrorDuration = 4e3;
171
172
  this.toastRemainingMs = 0;
172
173
  this.toastTotalMs = 0;
173
174
  this.isToastPaused = false;
@@ -233,7 +234,23 @@ var SupportWidgetElement = class extends HTMLElement {
233
234
  this.emailContacts = config.emailContacts;
234
235
  }
235
236
  if (config.toastDuration !== void 0) {
236
- this.toastDuration = this.normalizeToastDuration(config.toastDuration);
237
+ const duration = config.toastDuration;
238
+ if (typeof duration === "number") {
239
+ const val = this.normalizeToastDuration(duration);
240
+ this.toastSuccessDuration = val;
241
+ this.toastErrorDuration = val;
242
+ } else if (duration && typeof duration === "object") {
243
+ if (duration.success !== void 0) {
244
+ this.toastSuccessDuration = this.normalizeToastDuration(duration.success);
245
+ } else {
246
+ this.toastSuccessDuration = 4e3;
247
+ }
248
+ if (duration.error !== void 0) {
249
+ this.toastErrorDuration = this.normalizeToastDuration(duration.error);
250
+ } else {
251
+ this.toastErrorDuration = 4e3;
252
+ }
253
+ }
237
254
  }
238
255
  }
239
256
  setFormPrefill(data) {
@@ -251,6 +268,7 @@ var SupportWidgetElement = class extends HTMLElement {
251
268
  var _a, _b;
252
269
  this.clearCloseAfterSuccessTimer();
253
270
  this.resetFormData();
271
+ this.hideToast(false);
254
272
  (_b = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("modal")) == null ? void 0 : _b.classList.remove("show");
255
273
  this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));
256
274
  }
@@ -280,9 +298,27 @@ var SupportWidgetElement = class extends HTMLElement {
280
298
  };
281
299
  const toastDurationAttr = this.getAttribute("toast-duration");
282
300
  if (toastDurationAttr !== null) {
283
- const parsed = parseInt(toastDurationAttr, 10);
284
- if (!isNaN(parsed)) {
285
- this.toastDuration = this.normalizeToastDuration(parsed);
301
+ const trimmed = toastDurationAttr.trim();
302
+ if (trimmed.startsWith("{")) {
303
+ try {
304
+ const parsed = JSON.parse(trimmed);
305
+ if (parsed && typeof parsed === "object") {
306
+ if (parsed.success !== void 0) {
307
+ this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));
308
+ }
309
+ if (parsed.error !== void 0) {
310
+ this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));
311
+ }
312
+ }
313
+ } catch (e) {
314
+ }
315
+ } else {
316
+ const parsed = parseInt(trimmed, 10);
317
+ if (!isNaN(parsed)) {
318
+ const val = this.normalizeToastDuration(parsed);
319
+ this.toastSuccessDuration = val;
320
+ this.toastErrorDuration = val;
321
+ }
286
322
  }
287
323
  }
288
324
  }
@@ -723,7 +759,7 @@ var SupportWidgetElement = class extends HTMLElement {
723
759
  this.clearFieldError("email");
724
760
  this.clearFieldError("content");
725
761
  }
726
- showToast(payload, autoCloseMs = this.toastDuration) {
762
+ showToast(payload, autoCloseMs) {
727
763
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
728
764
  const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
729
765
  const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
@@ -773,8 +809,9 @@ var SupportWidgetElement = class extends HTMLElement {
773
809
  linkRowEl.classList.add("is-hidden");
774
810
  detailsEl.classList.add("is-hidden");
775
811
  }
776
- this.toastRemainingMs = autoCloseMs;
777
- this.toastTotalMs = autoCloseMs;
812
+ const duration = autoCloseMs !== void 0 ? autoCloseMs : payload.type === "success" ? this.toastSuccessDuration : this.toastErrorDuration;
813
+ this.toastRemainingMs = duration;
814
+ this.toastTotalMs = duration;
778
815
  const toastCardEl = (_l = this.shadowRoot) == null ? void 0 : _l.querySelector(".toast-card");
779
816
  this.isToastPaused = Boolean(toastCardEl == null ? void 0 : toastCardEl.matches(":hover"));
780
817
  toastEl.classList.remove("success", "error");
@@ -785,7 +822,7 @@ var SupportWidgetElement = class extends HTMLElement {
785
822
  countdownBarEl
786
823
  );
787
824
  }
788
- hideToast() {
825
+ hideToast(shouldCloseModal = true) {
789
826
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
790
827
  const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
791
828
  const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
@@ -806,6 +843,7 @@ var SupportWidgetElement = class extends HTMLElement {
806
843
  if (!toastEl || !titleEl || !messageEl || !detailsEl || !requestCodeEl || !linkRowEl || !linkEl || !countdownBarEl) {
807
844
  return;
808
845
  }
846
+ const isSuccess = toastEl.classList.contains("success");
809
847
  this.clearToastAutoCloseTimer();
810
848
  this.clearToastCountdown();
811
849
  this.toastRemainingMs = 0;
@@ -827,6 +865,9 @@ var SupportWidgetElement = class extends HTMLElement {
827
865
  countdownEl.classList.add("is-hidden");
828
866
  }
829
867
  toastEl.classList.remove("success", "error", "show");
868
+ if (isSuccess && shouldCloseModal) {
869
+ this.close();
870
+ }
830
871
  }
831
872
  startToastCountdown(countdownEl, secondsEl, barEl) {
832
873
  const updateCountdown = () => {
@@ -926,7 +967,7 @@ var SupportWidgetElement = class extends HTMLElement {
926
967
  async submit() {
927
968
  var _a;
928
969
  this.clearCloseAfterSuccessTimer();
929
- this.hideToast();
970
+ this.hideToast(false);
930
971
  if (!this.validateRequiredFields()) {
931
972
  this.dispatchEvent(
932
973
  new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {
@@ -1016,8 +1057,8 @@ var SupportWidgetElement = class extends HTMLElement {
1016
1057
  })
1017
1058
  );
1018
1059
  this.closeAfterSuccessTimer = window.setTimeout(() => {
1019
- this.close();
1020
- }, 1200);
1060
+ this.hideToast();
1061
+ }, this.toastSuccessDuration);
1021
1062
  } catch (error) {
1022
1063
  const errorContent = mapHttpClientError(error);
1023
1064
  this.showToast(this.toToastPayload(errorContent));
@@ -1135,4 +1176,4 @@ export {
1135
1176
  SupportWidgetElement,
1136
1177
  registerSupportWidget
1137
1178
  };
1138
- //# sourceMappingURL=chunk-C5WUIBKX.js.map
1179
+ //# sourceMappingURL=chunk-CIUOZK3Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config/endpoints.ts","../src/core/validation/request-validator.ts","../src/services/api/create-request-response.ts","../src/services/api/http-client.ts","../src/services/api/support-api.ts","../src/component/events/widget-events.ts","../src/component/support-widget.styles.css","../src/component/support-widget.template.html","../src/component/support-widget.element.ts","../src/register.ts"],"sourcesContent":["export const PROD_API_URL = \"https://namphuong-api.azurewebsites.net\";\nexport const DEV_API_URL = \"https://namphuong-api-dev.azurewebsites.net\";\n\nexport function resolveBaseUrl(input: { isDev?: boolean }): string {\n return input.isDev ? DEV_API_URL : PROD_API_URL;\n}\n","import type {\n SupportSubmissionInput,\n SupportUser,\n SupportWidgetConfig,\n} from \"../../types/public\";\n\nexport function validateWidgetConfig(config: SupportWidgetConfig): void {\n if (config.projectId !== undefined && config.projectId.trim().length === 0) {\n throw new Error(\"projectId cannot be empty.\");\n }\n}\n\nexport function validateUser(user: SupportUser): void {\n if (!user.name?.trim()) {\n throw new Error(\"user.name is required.\");\n }\n if (!user.email?.trim()) {\n throw new Error(\"user.email is required.\");\n }\n if (!user.phoneNumber?.trim()) {\n throw new Error(\"user.phoneNumber is required.\");\n }\n}\n\nexport function validateSubmission(input: SupportSubmissionInput): void {\n if (!input.content?.trim()) {\n throw new Error(\"Content is required.\");\n }\n}\n","export interface CreateRequestResponse {\n statusCode?: number;\n message?: string;\n totalRecord?: number;\n data?: {\n url?: string;\n requestCode?: string;\n } | null;\n}\n\nexport interface SubmitToastContent {\n type: \"success\" | \"error\";\n message: string;\n statusCode?: string;\n requestCode?: string;\n url?: string;\n}\n\nconst SUCCESS_FALLBACK_MESSAGE = \"Yêu cầu đã được tiếp nhận thành công.\";\nconst ERROR_FALLBACK_MESSAGE = \"Có lỗi xảy ra khi gửi yêu cầu.\";\n\nexport function isCreateRequestSuccess(\n result: CreateRequestResponse,\n): boolean {\n if (typeof result.statusCode === \"number\") {\n return result.statusCode >= 200 && result.statusCode < 300;\n }\n return true;\n}\n\nexport function mapCreateRequestSuccess(\n result: CreateRequestResponse,\n): SubmitToastContent {\n const statusCode =\n typeof result.statusCode === \"number\"\n ? String(result.statusCode)\n : undefined;\n const requestCode = result.data?.requestCode?.trim() ?? \"\";\n const url = result.data?.url?.trim() ?? \"\";\n\n return {\n type: \"success\",\n message: result.message?.trim() || SUCCESS_FALLBACK_MESSAGE,\n statusCode,\n requestCode: requestCode || undefined,\n url: url || undefined,\n };\n}\n\nexport function mapCreateRequestFailure(\n result: CreateRequestResponse,\n): SubmitToastContent {\n const statusCode =\n typeof result.statusCode === \"number\"\n ? String(result.statusCode)\n : undefined;\n\n return {\n type: \"error\",\n message: result.message?.trim() || ERROR_FALLBACK_MESSAGE,\n statusCode,\n };\n}\n\nexport function mapHttpClientError(error: unknown): SubmitToastContent {\n const fallbackMessage = ERROR_FALLBACK_MESSAGE;\n const rawMessage =\n error instanceof Error ? error.message : fallbackMessage;\n const matchedStatus = rawMessage.match(/Request failed \\((\\d+)\\):/);\n const httpStatus = matchedStatus ? matchedStatus[1] : \"\";\n const afterColon = rawMessage.match(/Request failed \\(\\d+\\):\\s*(.*)$/s);\n const payloadText = afterColon?.[1]?.trim() ?? \"\";\n\n let parsedMessage = \"\";\n let bodyStatus = \"\";\n if (payloadText.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(payloadText) as CreateRequestResponse;\n if (typeof parsed.statusCode === \"number\") {\n bodyStatus = String(parsed.statusCode);\n }\n if (typeof parsed.message === \"string\") {\n parsedMessage = parsed.message.trim();\n }\n } catch {\n // Ignore JSON parse error, fallback to message extraction below.\n }\n }\n\n if (!parsedMessage && payloadText) {\n parsedMessage = payloadText;\n }\n if (!parsedMessage) {\n parsedMessage = rawMessage || fallbackMessage;\n }\n\n return {\n type: \"error\",\n message: parsedMessage,\n statusCode: bodyStatus || httpStatus || undefined,\n };\n}\n","export async function postFormData<TResponse>(\n url: string,\n body: FormData,\n): Promise<TResponse> {\n const response = await fetch(url, {\n method: \"POST\",\n body,\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(`Request failed (${response.status}): ${message}`);\n }\n\n return (await response.json()) as TResponse;\n}\n","import type { CreateRequestResponse } from \"./create-request-response\";\nimport { postFormData } from \"./http-client\";\n\nexport type { CreateRequestResponse };\n\nexport async function createSupportRequest(\n baseUrl: string,\n formData: FormData,\n): Promise<CreateRequestResponse> {\n const endpoint = `${baseUrl.replace(/\\/$/, \"\")}/api/supportcenter/create-request-anonymous`;\n return postFormData<CreateRequestResponse>(endpoint, formData);\n}\n\n","export const WIDGET_EVENTS = {\n OPEN: \"np-hub-open\",\n CLOSE: \"np-hub-close\",\n SUBMIT_SUCCESS: \"np-hub-submit-success\",\n SUBMIT_ERROR: \"np-hub-submit-error\",\n} as const;\n","@import url(\"https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap\");\n\n:host {\n --np-primary: #003c82;\n --np-primary-dark: #002a5c;\n --np-primary-soft: rgba(0, 60, 130, 0.08);\n --np-border: #e2e8f0;\n --np-text: #0f172a;\n --np-text-muted: #64748b;\n font-family: \"Outfit\", sans-serif;\n display: block;\n position: fixed;\n right: var(--np-hub-right, 20px);\n bottom: var(--np-hub-bottom, 20px);\n left: var(--np-hub-left, auto);\n top: var(--np-hub-top, auto);\n z-index: 10000;\n}\n\n.launcher {\n width: var(--np-hub-width, 65px);\n height: var(--np-hub-height, 65px);\n border: none;\n border-radius: 50%;\n cursor: grab;\n box-shadow: 0 8px 28px rgba(6, 6, 7, 0.2);\n background-color: #ffffff;\n background-image: var(--np-hub-icon);\n background-position: center;\n background-repeat: no-repeat;\n background-size: 75%;\n transition:\n transform 0.2s ease,\n box-shadow 0.2s ease;\n}\n\n.launcher:hover {\n transform: scale(1.05);\n box-shadow: 0 12px 32px rgba(0, 60, 130, 0.3);\n}\n\n.launcher:active {\n cursor: grabbing;\n}\n\n.modal {\n position: fixed;\n inset: 0;\n background: rgba(15, 23, 42, 0.55);\n backdrop-filter: blur(6px);\n display: none;\n align-items: center;\n justify-content: center;\n padding: 16px;\n opacity: 0;\n transition: opacity 0.25s ease-out;\n}\n\n.modal.show {\n display: flex;\n opacity: 1;\n}\n\n.dialog {\n width: min(540px, 100%);\n max-height: calc(100vh - 32px);\n overflow-y: auto;\n background: #ffffff;\n border-radius: 20px;\n box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);\n padding: 28px 28px 26px;\n box-sizing: border-box;\n position: relative;\n transform: translateY(24px) scale(0.98);\n transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.modal.show .dialog {\n transform: translateY(0) scale(1);\n}\n\n.dialog::-webkit-scrollbar {\n width: 8px;\n}\n\n.dialog::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 8px;\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: center;\n padding-bottom: 12px;\n margin-bottom: 16px;\n border-bottom: 1px solid var(--np-border);\n position: relative;\n}\n\n.title {\n margin: 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--np-primary);\n text-align: center;\n letter-spacing: -0.01em;\n}\n\n.close-btn {\n border: none;\n background: #f1f5f9;\n color: #64748b;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n position: absolute;\n right: 0;\n top: 0;\n bottom: 18px;\n margin: auto 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n\n.close-btn:hover {\n background: #e2e8f0;\n color: #0f172a;\n transform: rotate(90deg);\n}\n\n.form-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 20px;\n}\n\n.field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.field-error {\n display: none;\n}\n\n.field.full {\n grid-column: 1 / -1;\n}\n\nlabel {\n font-size: 13px;\n font-weight: 600;\n color: #334155;\n}\n\n.required {\n color: #ef4444;\n margin-right: 3px;\n}\n\ninput,\ntextarea {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid #d5dde7;\n border-radius: 10px;\n font-size: 14px;\n padding: 11px 13px;\n color: var(--np-text);\n outline: none;\n font-family: inherit;\n background: #fbfcfe;\n transition:\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n background 0.15s ease;\n}\n\ntextarea {\n min-height: 100px;\n resize: vertical;\n line-height: 1.5;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n color: #94a3b8;\n}\n\ninput:hover,\ntextarea:hover {\n border-color: #b6c2d1;\n}\n\ninput:focus,\ntextarea:focus {\n border-color: var(--np-primary);\n background: #ffffff;\n box-shadow: 0 0 0 4px var(--np-primary-soft);\n}\n\n.input-wrap.has-error input,\n.input-wrap.has-error textarea {\n border-color: #e45b5b;\n background: #ffffff;\n}\n\n.input-wrap.has-error input:focus,\n.input-wrap.has-error textarea:focus {\n border-color: #e45b5b;\n box-shadow: 0 0 0 3px rgba(228, 91, 91, 0.14);\n}\n\n.input-wrap {\n position: relative;\n}\n\n.input-wrap input,\n.input-wrap textarea {\n padding-right: 40px;\n}\n\n.btn-clear {\n position: absolute;\n right: 10px;\n top: 50%;\n transform: translateY(-50%);\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background: #e2e8f0;\n color: #64748b;\n font-size: 16px;\n line-height: 1;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0;\n opacity: 0;\n pointer-events: none;\n transition:\n opacity 0.15s ease,\n background 0.15s ease,\n color 0.15s ease;\n}\n\n.input-wrap.has-value:hover .btn-clear,\n.input-wrap.has-value:focus-within .btn-clear {\n opacity: 1;\n pointer-events: auto;\n}\n\n.btn-clear:hover {\n background: #cbd5e1;\n color: #0f172a;\n}\n\n.input-wrap--textarea .btn-clear {\n top: 12px;\n transform: none;\n}\n\n/* File upload styling */\n.upload-zone {\n display: flex;\n align-items: center;\n gap: 16px;\n background: #f8fafc;\n border: 1.5px dashed #cbd5e1;\n padding: 16px 16px;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n user-select: none;\n outline: none;\n}\n\n.upload-zone:hover {\n background: var(--np-primary-soft);\n border-color: var(--np-primary);\n}\n\n.upload-zone:focus-visible {\n border-color: var(--np-primary);\n box-shadow: 0 0 0 4px var(--np-primary-soft);\n}\n\n.upload-zone.dragover {\n background: rgba(0, 60, 130, 0.08);\n border-color: var(--np-primary);\n transform: scale(1.005);\n}\n\n.upload-icon-wrapper {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 10px;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n color: #64748b;\n box-shadow: 0 2px 4px rgba(15, 23, 42, 0.04);\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.upload-zone:hover .upload-icon-wrapper,\n.upload-zone.dragover .upload-icon-wrapper {\n color: var(--np-primary);\n border-color: rgba(0, 60, 130, 0.15);\n box-shadow: 0 4px 8px rgba(0, 60, 130, 0.08);\n}\n\n.upload-text-container {\n display: flex;\n flex-direction: column;\n gap: 3px;\n flex: 1;\n}\n\n.upload-main-text {\n font-size: 13px;\n font-weight: 600;\n color: #334155;\n transition: color 0.2s ease;\n}\n\n.upload-zone:hover .upload-main-text {\n color: var(--np-primary);\n}\n\n.upload-sub-text {\n font-size: 11px;\n color: #64748b;\n line-height: 1.4;\n}\n\n.file-list {\n margin-top: 8px;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 12px;\n}\n\n.file-item {\n position: relative;\n display: flex;\n flex-direction: column;\n background: #ffffff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n font-size: 11px;\n color: #475569;\n box-shadow: 0 2px 5px rgba(15, 23, 42, 0.03);\n animation: file-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n transition: all 0.2s ease;\n overflow: hidden;\n}\n\n.file-item:hover {\n border-color: #cbd5e1;\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);\n transform: translateY(-2px);\n}\n\n.file-preview-trigger {\n border: none;\n background: none;\n padding: 0;\n margin: 0;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n width: 100%;\n color: inherit;\n font: inherit;\n}\n\n.file-thumb {\n width: 100%;\n aspect-ratio: 1 / 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f8fafc;\n border-bottom: 1px solid #f1f5f9;\n position: relative;\n overflow: hidden;\n transition: background 0.2s ease;\n}\n\n.file-thumb img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n transition: transform 0.25s ease;\n}\n\n.file-item:hover .file-thumb img {\n transform: scale(1.06);\n}\n\n.file-thumb--icon {\n color: #64748b;\n background: #f1f5f9;\n}\n\n.file-item:hover .file-thumb--icon {\n background: #e2e8f0;\n color: var(--np-primary);\n}\n\n@keyframes file-in {\n from {\n opacity: 0;\n transform: translateY(6px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.file-info-container {\n padding: 8px 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n box-sizing: border-box;\n}\n\n.file-item .file-name {\n width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n line-height: 1.2;\n color: #334155;\n font-size: 11px;\n font-weight: 500;\n text-align: center;\n}\n\n.file-badge {\n position: absolute;\n top: 6px;\n left: 6px;\n z-index: 2;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 9px;\n font-weight: 600;\n line-height: 1;\n color: #0369a1;\n background: rgba(224, 242, 254, 0.9);\n border: 1px solid rgba(186, 230, 253, 0.8);\n backdrop-filter: blur(4px);\n box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);\n}\n\n.file-item .btn-remove {\n position: absolute;\n top: 6px;\n right: 6px;\n z-index: 2;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 255, 255, 0.9);\n border: 1px solid #e2e8f0;\n border-radius: 50%;\n color: #64748b;\n cursor: pointer;\n padding: 0;\n box-shadow: 0 2px 4px rgba(15, 23, 42, 0.06);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n backdrop-filter: blur(4px);\n}\n\n.file-item .btn-remove svg {\n width: 8px;\n height: 8px;\n stroke-width: 3px;\n transition: transform 0.2s ease;\n}\n\n.file-item .btn-remove:hover {\n color: #ffffff;\n background: #ef4444;\n border-color: #ef4444;\n transform: scale(1.1);\n box-shadow: 0 3px 8px rgba(239, 68, 68, 0.25);\n}\n\n.file-item .btn-remove:hover svg {\n transform: rotate(90deg);\n}\n\n/* File preview popup */\n.preview-overlay {\n position: fixed;\n inset: 0;\n z-index: 20;\n display: none;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(15, 23, 42, 0.72);\n backdrop-filter: blur(4px);\n opacity: 0;\n transition: opacity 0.2s ease-out;\n}\n\n.preview-overlay.show {\n display: flex;\n opacity: 1;\n}\n\n.preview-box {\n position: relative;\n max-width: min(720px, 100%);\n max-height: calc(100vh - 96px);\n background: #ffffff;\n border-radius: 16px;\n padding: 16px;\n box-shadow: 0 24px 60px rgba(15, 23, 42, 0.35);\n display: flex;\n flex-direction: column;\n}\n\n.preview-close {\n position: absolute;\n top: -14px;\n right: -14px;\n width: 34px;\n height: 34px;\n border-radius: 50%;\n border: none;\n background: #ffffff;\n color: #334155;\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.25);\n transition:\n color 0.15s ease,\n transform 0.15s ease;\n}\n\n.preview-close:hover {\n color: #ef4444;\n transform: scale(1.05);\n}\n\n.preview-body {\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.preview-body img {\n max-width: 100%;\n max-height: calc(100vh - 140px);\n object-fit: contain;\n border-radius: 8px;\n display: block;\n}\n\n.preview-file {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 32px 40px;\n text-align: center;\n color: var(--np-text);\n}\n\n.preview-file svg {\n color: var(--np-primary);\n}\n\n.preview-file .preview-file-name {\n font-size: 14px;\n font-weight: 600;\n word-break: break-all;\n}\n\n.preview-file a {\n color: var(--np-primary);\n font-size: 13px;\n font-weight: 600;\n text-decoration: none;\n}\n\n.preview-file a:hover {\n text-decoration: underline;\n}\n\n.actions {\n margin-top: 20px;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n}\n\n.toast {\n position: fixed;\n inset: 0;\n z-index: 10002;\n display: none;\n align-items: flex-start;\n justify-content: center;\n background: transparent;\n pointer-events: auto; /* disable interaction underneath */\n}\n\n.toast.show {\n display: flex;\n animation: toast-fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n}\n\n.toast.success .toast-card {\n --toast-status-color: #10b981;\n --toast-status-bg: rgba(16, 185, 129, 0.12);\n --toast-badge-text: #047857;\n --toast-badge-bg: #ecfdf5;\n --toast-badge-border: #d1fae5;\n}\n\n.toast.error .toast-card {\n --toast-status-color: #ef4444;\n --toast-status-bg: rgba(239, 68, 68, 0.12);\n --toast-badge-text: #b91c1c;\n --toast-badge-bg: #fef2f2;\n --toast-badge-border: #fee2e2;\n}\n\n.toast-card {\n position: relative;\n width: min(90vw, 420px);\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 20px 24px 22px 24px;\n border-radius: 20px; /* matched to main dialog border-radius */\n background: #ffffff;\n border: 1px solid var(--np-border);\n box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22); /* matched to main dialog shadow */\n pointer-events: auto;\n overflow: hidden;\n margin-top: 24px; /* spacing from top of viewport */\n animation: toast-in 0.35s cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.toast-main {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n\n.toast-icon {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: var(--toast-status-bg);\n margin-top: 2px;\n}\n\n.toast-icon svg {\n display: none;\n width: 18px;\n height: 18px;\n margin: 0;\n}\n\n.toast.success .toast-icon svg.icon-success {\n display: block;\n color: var(--toast-status-color);\n}\n\n.toast.error .toast-icon svg.icon-error {\n display: block;\n color: var(--toast-status-color);\n}\n\n.toast-body {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n padding-top: 4px;\n}\n\n.toast-title {\n margin: 0;\n font-size: 16px;\n font-weight: 700;\n color: #111827; /* dark charcoal black */\n letter-spacing: -0.01em;\n line-height: 1.3;\n}\n\n.toast-message {\n margin: 0;\n font-size: 13.5px;\n line-height: 1.45;\n color: var(--np-text-muted);\n}\n\n.toast.success .toast-message {\n text-transform: uppercase;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.05em;\n color: var(--np-text-muted);\n margin-top: 2px;\n}\n\n.toast-close-wrapper {\n position: relative;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.toast-countdown-ring {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n}\n\n.ring-track {\n stroke: #f1f5f9; /* soft gray track */\n}\n\n.ring-bar {\n stroke-linecap: round;\n transition: stroke-dashoffset 0.1s linear;\n}\n\n.toast-close-btn {\n position: relative;\n z-index: 1;\n border: none;\n background: transparent;\n color: #94a3b8; /* subtle gray close icon */\n width: 28px;\n height: 28px;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n\n.toast-close-btn svg {\n width: 12px;\n height: 12px;\n display: block;\n}\n\n.toast-close-btn:hover {\n background: #f1f5f9;\n color: #0f172a;\n transform: rotate(90deg);\n}\n\n.toast-details {\n margin-left: 44px;\n margin-top: 4px;\n padding: 12px 0 0 0;\n border-top: 1px solid var(--np-border); /* separator line */\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.toast-detail-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.toast-detail-label {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--np-text-muted);\n}\n\n.toast-detail-value {\n min-width: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--np-text);\n display: flex;\n justify-content: flex-end;\n}\n\n.toast-detail-code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n color: var(--np-primary);\n background: var(--np-primary-soft);\n border: 1px solid rgba(0, 60, 130, 0.15);\n padding: 2px 8px;\n border-radius: 8px;\n font-size: 12px;\n display: inline-block;\n text-align: right;\n}\n\n.toast-detail-link {\n color: var(--np-primary);\n text-decoration: none;\n font-weight: 600;\n display: inline-block;\n max-width: 220px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border-bottom: 1px dashed rgba(0, 60, 130, 0.3);\n transition: all 0.15s ease;\n font-size: 12.5px;\n}\n\n.toast-detail-link:hover {\n color: var(--np-primary-dark);\n border-bottom-style: solid;\n border-bottom-color: var(--np-primary-dark);\n}\n\n.toast-detail-value-wrap {\n display: flex;\n align-items: center;\n gap: 6px;\n justify-content: flex-end;\n min-width: 0;\n}\n\n.toast-copy-btn {\n background: transparent;\n border: none;\n padding: 4px;\n margin: 0;\n cursor: pointer;\n color: var(--np-text-muted);\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n}\n\n.toast-copy-btn:hover {\n background: #f1f5f9;\n color: var(--np-primary);\n}\n\n.toast-copy-btn svg {\n width: 14px;\n height: 14px;\n display: block;\n}\n\n.toast-copy-btn .icon-copied {\n color: var(--np-primary);\n}\n\n.toast-copy-btn.copied {\n background: var(--np-primary-soft);\n}\n\n.toast-countdown {\n margin-left: 44px;\n margin-top: 14px;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.toast-countdown::before {\n content: \"\";\n flex: 1;\n height: 1px;\n background: #f1f5f9; /* soft divider line */\n}\n\n.toast-countdown-row {\n font-size: 11px;\n color: var(--np-text-muted);\n font-weight: 500;\n white-space: nowrap;\n display: inline-block;\n}\n\n.toast.success .toast-countdown-row {\n text-transform: none;\n font-size: 12px;\n}\n\n.toast.error .toast-countdown-row {\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.toast .is-hidden {\n display: none !important;\n}\n\n@keyframes toast-in {\n from {\n opacity: 0;\n transform: translateY(-20px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes toast-fade-in {\n from {\n background: rgba(15, 23, 42, 0);\n backdrop-filter: blur(0px);\n -webkit-backdrop-filter: blur(0px);\n }\n to {\n background: rgba(15, 23, 42, 0.35);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n }\n}\n\n.btn {\n border-radius: 10px;\n padding: 10px 22px;\n font-size: 15px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.18s ease;\n min-width: 120px;\n}\n\n.btn:active {\n transform: translateY(1px);\n}\n\n.btn-secondary {\n background: #ffffff;\n border: 1px solid #d5dde7;\n color: #334155;\n}\n\n.btn-secondary:hover {\n background: #f1f5f9;\n border-color: #b6c2d1;\n}\n\n.btn-primary {\n background: linear-gradient(\n 135deg,\n var(--np-primary),\n var(--np-primary-dark)\n );\n border: 1px solid var(--np-primary-dark);\n color: #ffffff;\n box-shadow: 0 6px 16px rgba(0, 60, 130, 0.28);\n}\n\n.btn-primary:hover {\n box-shadow: 0 8px 22px rgba(0, 60, 130, 0.38);\n filter: brightness(1.06);\n}\n\n@media (max-width: 480px) {\n .dialog {\n padding: 24px 20px 24px;\n border-radius: 16px;\n }\n\n .form-grid {\n grid-template-columns: 1fr;\n }\n\n .actions {\n justify-content: stretch;\n }\n\n .actions .btn {\n flex: 1;\n }\n}\n","<button\n id=\"open-btn\"\n class=\"launcher\"\n type=\"button\"\n aria-label=\"Open support form\"\n></button>\n\n<div id=\"modal\" class=\"modal\">\n <div\n class=\"dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Support request form\"\n >\n <div class=\"header\">\n <h3 class=\"title\">Tạo yêu cầu hỗ trợ</h3>\n <button id=\"close-btn\" class=\"close-btn\" type=\"button\" aria-label=\"Close\">\n ×\n </button>\n </div>\n\n <div class=\"form-grid\">\n <div class=\"field\">\n <label for=\"requester\"><span class=\"required\">*</span> Họ và tên</label>\n <div class=\"input-wrap\">\n <input id=\"requester\" type=\"text\" placeholder=\"Họ và tên\" />\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"requester-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field\">\n <label for=\"phone-number\"\n ><span class=\"required\">*</span> Số điện thoại</label\n >\n <div class=\"input-wrap\">\n <input id=\"phone-number\" type=\"text\" placeholder=\"Số điện thoại\" />\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"phone-number-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field full\">\n <label for=\"email\"><span class=\"required\">*</span> Email</label>\n <div class=\"input-wrap\">\n <input id=\"email\" type=\"email\" placeholder=\"Email\" />\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"email-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field full\">\n <label for=\"content\"><span class=\"required\">*</span> Nội dung</label>\n <div class=\"input-wrap input-wrap--textarea\">\n <textarea\n id=\"content\"\n placeholder=\"Anh/Chị vui lòng nhập thông tin cần hỗ trợ\"\n ></textarea>\n <button type=\"button\" class=\"btn-clear\" aria-label=\"Xóa\">×</button>\n </div>\n <p id=\"content-error\" class=\"field-error\" aria-live=\"polite\"></p>\n </div>\n\n <div class=\"field full\">\n <label>File đính kèm</label>\n <div\n id=\"upload-trigger\"\n class=\"upload-zone\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Tải lên tệp\"\n >\n <input id=\"file-input\" type=\"file\" multiple style=\"display: none\" />\n <div class=\"upload-icon-wrapper\">\n <svg\n class=\"upload-icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"></path>\n <polyline points=\"17 8 12 3 7 8\"></polyline>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"></line>\n </svg>\n </div>\n <div class=\"upload-text-container\">\n <span class=\"upload-main-text\"\n >Chọn tệp tải lên hoặc kéo thả vào đây</span\n >\n <span class=\"upload-sub-text\"\n >Hỗ trợ dán ảnh hoặc tệp (Ctrl+V / Cmd+V) để đính kèm nhanh</span\n >\n </div>\n </div>\n <div id=\"file-list\" class=\"file-list\"></div>\n </div>\n </div>\n\n <div id=\"preview-overlay\" class=\"preview-overlay\">\n <div\n class=\"preview-box\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Xem trước tệp\"\n >\n <button\n id=\"preview-close\"\n class=\"preview-close\"\n type=\"button\"\n aria-label=\"Đóng\"\n >\n ×\n </button>\n <div id=\"preview-body\" class=\"preview-body\"></div>\n </div>\n </div>\n\n <div class=\"actions\">\n <button id=\"cancel-btn\" class=\"btn btn-secondary\" type=\"button\">\n Hủy\n </button>\n <button id=\"submit-btn\" class=\"btn btn-primary\" type=\"button\">\n Tạo yêu cầu\n </button>\n </div>\n </div>\n</div>\n\n<div\n id=\"toast\"\n class=\"toast\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n>\n <div class=\"toast-card\">\n <div class=\"toast-main\">\n <div class=\"toast-icon\" aria-hidden=\"true\">\n <svg\n class=\"icon-success\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n <svg\n class=\"icon-error\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"></line>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"></line>\n </svg>\n </div>\n <div class=\"toast-body\">\n <h4 id=\"toast-title\" class=\"toast-title\"></h4>\n <p id=\"toast-message\" class=\"toast-message\"></p>\n </div>\n <div class=\"toast-close-wrapper\">\n <svg\n class=\"toast-countdown-ring\"\n width=\"36\"\n height=\"36\"\n viewBox=\"0 0 36 36\"\n >\n <circle\n class=\"ring-track\"\n cx=\"18\"\n cy=\"18\"\n r=\"15\"\n fill=\"none\"\n stroke=\"#e2e8f0\"\n stroke-width=\"2.5\"\n ></circle>\n <circle\n id=\"toast-countdown-bar\"\n class=\"ring-bar\"\n cx=\"18\"\n cy=\"18\"\n r=\"15\"\n fill=\"none\"\n stroke=\"var(--toast-status-color)\"\n stroke-width=\"2.5\"\n stroke-dasharray=\"94.25\"\n stroke-dashoffset=\"0\"\n transform=\"rotate(-90 18 18)\"\n ></circle>\n </svg>\n <button\n id=\"toast-close-btn\"\n class=\"toast-close-btn\"\n type=\"button\"\n aria-label=\"Đóng thông báo\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n </div>\n </div>\n <div id=\"toast-details\" class=\"toast-details is-hidden\">\n <div class=\"toast-detail-row\">\n <span class=\"toast-detail-label\">Mã hỗ trợ</span>\n <div class=\"toast-detail-value-wrap\">\n <span\n id=\"toast-request-code\"\n class=\"toast-detail-value toast-detail-code\"\n ></span>\n <button\n id=\"toast-copy-code-btn\"\n class=\"toast-copy-btn\"\n type=\"button\"\n title=\"Sao chép mã hỗ trợ\"\n >\n <svg\n class=\"icon-copy\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path\n d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\n ></path>\n </svg>\n <svg\n class=\"icon-copied is-hidden\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n </button>\n </div>\n </div>\n <div id=\"toast-link-row\" class=\"toast-detail-row is-hidden\">\n <span class=\"toast-detail-label\">Tra cứu</span>\n <div class=\"toast-detail-value-wrap\">\n <a\n id=\"toast-link\"\n class=\"toast-detail-value toast-detail-link\"\n href=\"#\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n ></a>\n <button\n id=\"toast-copy-link-btn\"\n class=\"toast-copy-btn\"\n type=\"button\"\n title=\"Sao chép liên kết\"\n >\n <svg\n class=\"icon-copy\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path\n d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"\n ></path>\n </svg>\n <svg\n class=\"icon-copied is-hidden\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n </button>\n </div>\n </div>\n </div>\n </div>\n</div>\n","import { resolveBaseUrl } from \"../core/config/endpoints\";\nimport {\n validateSubmission,\n validateUser,\n validateWidgetConfig,\n} from \"../core/validation/request-validator\";\nimport {\n isCreateRequestSuccess,\n mapCreateRequestFailure,\n mapCreateRequestSuccess,\n mapHttpClientError,\n type SubmitToastContent,\n} from \"../services/api/create-request-response\";\nimport { createSupportRequest } from \"../services/api/support-api\";\nimport type {\n SupportSubmissionInput,\n SupportUser,\n SupportUserPrefill,\n SupportWidgetConfig,\n} from \"../types/public\";\nimport { WIDGET_EVENTS } from \"./events/widget-events\";\nimport npSupportLogo from \"./assets/np-support-logo.png\";\nimport styles from \"./support-widget.styles.css\";\nimport markup from \"./support-widget.template.html\";\n\nlet sharedTemplate: HTMLTemplateElement | null = null;\n\nfunction getTemplate(): HTMLTemplateElement {\n if (!sharedTemplate) {\n sharedTemplate = document.createElement(\"template\");\n sharedTemplate.innerHTML = `<style>${styles}</style>${markup}`;\n }\n return sharedTemplate;\n}\n\nasync function urlToFile(url: string): Promise<File | null> {\n try {\n const response = await fetch(url);\n if (!response.ok) return null;\n const blob = await response.blob();\n const name = url.substring(url.lastIndexOf(\"/\") + 1) || \"attachment\";\n return new File([blob], name, { type: blob.type });\n } catch (e) {\n console.warn(\"Failed to fetch prefilled attachment\", url, e);\n return null;\n }\n}\n\ninterface FormPrefillInput {\n content?: string;\n attachments?: string[];\n}\n\ninterface ToastPayload extends SubmitToastContent {\n title: string;\n}\n\ntype RequiredFieldId = \"requester\" | \"phone-number\" | \"email\" | \"content\";\n\nexport class SupportWidgetElement extends HTMLElement {\n static observedAttributes = [\n \"project-id\",\n \"is-dev\",\n \"width\",\n \"height\",\n \"right\",\n \"bottom\",\n \"left\",\n \"top\",\n \"toast-duration\",\n ];\n\n private config: SupportWidgetConfig = {\n projectId: undefined,\n };\n\n private user?: SupportUserPrefill;\n private dragOccurred = false;\n\n // Internal states for fields mapped behind the scenes\n private selectedFiles: File[] = [];\n private prefilledAttachments: string[] = [];\n private previewUrls: string[] = [];\n private closeAfterSuccessTimer: number | null = null;\n private toastAutoCloseTimer: number | null = null;\n private toastCountdownInterval: number | null = null;\n private toastSuccessDuration = 4000;\n private toastErrorDuration = 4000;\n private toastRemainingMs = 0;\n private toastTotalMs = 0;\n private isToastPaused = false;\n public priority: number | undefined;\n public coordinators: string[] = [];\n public emailContacts: string[] = [];\n\n constructor() {\n super();\n const root = this.attachShadow({ mode: \"open\" });\n root.appendChild(getTemplate().content.cloneNode(true));\n this.bindActions();\n }\n\n connectedCallback(): void {\n this.syncConfigFromAttributes();\n this.prefillFromState();\n this.applyLauncherStyle();\n this.applyPositionStyle();\n this.restorePosition();\n window.addEventListener(\"resize\", this.handleResize);\n }\n\n disconnectedCallback(): void {\n window.removeEventListener(\"resize\", this.handleResize);\n this.clearCloseAfterSuccessTimer();\n this.clearToastAutoCloseTimer();\n this.clearToastCountdown();\n }\n\n attributeChangedCallback(): void {\n this.syncConfigFromAttributes();\n this.prefillFromState();\n this.applyLauncherStyle();\n this.applyPositionStyle();\n }\n\n setUser(user: SupportUserPrefill): void {\n this.user = user;\n this.prefillFromState();\n }\n\n setConfig(config: Partial<SupportWidgetConfig>): void {\n this.config = {\n ...this.config,\n ...config,\n };\n if (config.projectId !== undefined) {\n this.setAttribute(\"project-id\", config.projectId);\n }\n if (config.isDev !== undefined) {\n if (config.isDev) {\n this.setAttribute(\"is-dev\", \"\");\n } else {\n this.removeAttribute(\"is-dev\");\n }\n }\n if (config.priority !== undefined) {\n this.priority = config.priority;\n }\n if (config.coordinators !== undefined) {\n this.coordinators = config.coordinators;\n }\n if (config.emailContacts !== undefined) {\n this.emailContacts = config.emailContacts;\n }\n if (config.toastDuration !== undefined) {\n const duration = config.toastDuration;\n if (typeof duration === \"number\") {\n const val = this.normalizeToastDuration(duration);\n this.toastSuccessDuration = val;\n this.toastErrorDuration = val;\n } else if (duration && typeof duration === \"object\") {\n if (duration.success !== undefined) {\n this.toastSuccessDuration = this.normalizeToastDuration(duration.success);\n } else {\n this.toastSuccessDuration = 4000;\n }\n if (duration.error !== undefined) {\n this.toastErrorDuration = this.normalizeToastDuration(duration.error);\n } else {\n this.toastErrorDuration = 4000;\n }\n }\n }\n }\n\n setFormPrefill(data: FormPrefillInput): void {\n this.setInputValue(\"content\", data.content ?? \"\");\n this.prefilledAttachments = data.attachments ?? [];\n this.renderFileList();\n }\n\n open(): void {\n this.shadowRoot?.getElementById(\"modal\")?.classList.add(\"show\");\n this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.OPEN, { bubbles: true }));\n }\n\n close(): void {\n this.clearCloseAfterSuccessTimer();\n this.resetFormData();\n this.hideToast(false);\n this.shadowRoot?.getElementById(\"modal\")?.classList.remove(\"show\");\n this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));\n }\n\n public showDemoToast(type: \"success\" | \"error\"): void {\n if (type === \"success\") {\n this.showToast({\n type: \"success\",\n title: \"Gửi yêu cầu thành công\",\n message: \"Successful.\",\n requestCode: \"2607-230-1322\",\n url: \"https://hotro.azurewebsites.net/yeu-cau/2607-230-1322\",\n });\n } else {\n this.showToast({\n type: \"error\",\n title: \"Gửi yêu cầu thất bại\",\n message: \"Phone number is invalid\",\n });\n }\n }\n\n private syncConfigFromAttributes(): void {\n this.config = {\n ...this.config,\n projectId: this.getAttribute(\"project-id\") ?? \"\",\n isDev: this.hasAttribute(\"is-dev\"),\n };\n\n const toastDurationAttr = this.getAttribute(\"toast-duration\");\n if (toastDurationAttr !== null) {\n const trimmed = toastDurationAttr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(trimmed);\n if (parsed && typeof parsed === \"object\") {\n if (parsed.success !== undefined) {\n this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));\n }\n if (parsed.error !== undefined) {\n this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));\n }\n }\n } catch {\n // ignore parsing error\n }\n } else {\n const parsed = parseInt(trimmed, 10);\n if (!isNaN(parsed)) {\n const val = this.normalizeToastDuration(parsed);\n this.toastSuccessDuration = val;\n this.toastErrorDuration = val;\n }\n }\n }\n }\n\n private bindActions(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n const launcher = root.getElementById(\"open-btn\");\n if (launcher) {\n launcher.addEventListener(\"click\", () => {\n if (this.dragOccurred) {\n this.dragOccurred = false;\n return;\n }\n this.open();\n });\n this.setupDrag(launcher);\n }\n\n root\n .getElementById(\"close-btn\")\n ?.addEventListener(\"click\", () => this.close());\n root\n .getElementById(\"cancel-btn\")\n ?.addEventListener(\"click\", () => this.close());\n root\n .getElementById(\"submit-btn\")\n ?.addEventListener(\"click\", () => void this.submit());\n root.getElementById(\"toast-close-btn\")?.addEventListener(\"click\", () => {\n this.hideToast();\n });\n\n const toastCardEl = root.querySelector(\".toast-card\");\n if (toastCardEl) {\n toastCardEl.addEventListener(\"mouseenter\", () => {\n this.isToastPaused = true;\n });\n toastCardEl.addEventListener(\"mouseleave\", () => {\n this.isToastPaused = false;\n });\n }\n\n this.setupClearableInputs();\n\n // File selection UI handlers\n const uploadTrigger = root.getElementById(\"upload-trigger\");\n const fileInput = root.getElementById(\n \"file-input\",\n ) as HTMLInputElement | null;\n\n if (uploadTrigger && fileInput) {\n uploadTrigger.addEventListener(\"click\", () => {\n fileInput.click();\n });\n\n uploadTrigger.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n fileInput.click();\n }\n });\n\n uploadTrigger.addEventListener(\"dragover\", (e: DragEvent) => {\n e.preventDefault();\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = \"copy\";\n }\n uploadTrigger.classList.add(\"dragover\");\n });\n\n uploadTrigger.addEventListener(\"dragleave\", () => {\n uploadTrigger.classList.remove(\"dragover\");\n });\n\n uploadTrigger.addEventListener(\"drop\", (e: DragEvent) => {\n e.preventDefault();\n uploadTrigger.classList.remove(\"dragover\");\n if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n this.addSelectedFiles(Array.from(e.dataTransfer.files));\n }\n });\n\n fileInput.addEventListener(\"change\", () => {\n if (fileInput.files) {\n this.addSelectedFiles(Array.from(fileInput.files));\n // Reset file input value to allow selecting the same file again\n fileInput.value = \"\";\n }\n });\n }\n\n this.setupToastCopy();\n this.setupClipboardPaste();\n this.setupPreviewControls();\n }\n\n private setupPreviewControls(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n const overlay = root.getElementById(\"preview-overlay\");\n root\n .getElementById(\"preview-close\")\n ?.addEventListener(\"click\", () => this.closePreview());\n\n overlay?.addEventListener(\"click\", (event) => {\n if (event.target === overlay) this.closePreview();\n });\n\n root.addEventListener(\"keydown\", (event) => {\n if ((event as KeyboardEvent).key === \"Escape\") this.closePreview();\n });\n }\n\n private setupToastCopy(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n const copyCodeBtn = root.getElementById(\"toast-copy-code-btn\");\n const copyLinkBtn = root.getElementById(\"toast-copy-link-btn\");\n\n const copyText = async (text: string, btn: HTMLElement | null) => {\n if (!text || !btn) return;\n\n const doSuccessFeedback = () => {\n btn.classList.add(\"copied\");\n const copyIcon = btn.querySelector(\".icon-copy\");\n const copiedIcon = btn.querySelector(\".icon-copied\");\n if (copyIcon && copiedIcon) {\n copyIcon.classList.add(\"is-hidden\");\n copiedIcon.classList.remove(\"is-hidden\");\n }\n\n window.setTimeout(() => {\n btn.classList.remove(\"copied\");\n if (copyIcon && copiedIcon) {\n copyIcon.classList.remove(\"is-hidden\");\n copiedIcon.classList.add(\"is-hidden\");\n }\n }, 1500);\n };\n\n if (navigator.clipboard && window.isSecureContext) {\n try {\n await navigator.clipboard.writeText(text);\n doSuccessFeedback();\n return;\n } catch (err) {\n console.warn(\"navigator.clipboard.writeText failed, trying fallback: \", err);\n }\n }\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n document.execCommand(\"copy\");\n doSuccessFeedback();\n } catch (err) {\n console.error(\"Fallback copy failed: \", err);\n } finally {\n document.body.removeChild(textarea);\n }\n };\n\n copyCodeBtn?.addEventListener(\"click\", () => {\n const codeEl = root.getElementById(\"toast-request-code\");\n const text = codeEl?.textContent || \"\";\n void copyText(text, copyCodeBtn);\n });\n\n copyLinkBtn?.addEventListener(\"click\", () => {\n const linkEl = root.getElementById(\"toast-link\") as HTMLAnchorElement | null;\n const text = linkEl?.href || \"\";\n void copyText(text, copyLinkBtn);\n });\n }\n\n private setupClipboardPaste(): void {\n const dialog = this.shadowRoot?.querySelector(\".dialog\");\n if (!dialog) return;\n\n dialog.addEventListener(\"paste\", (event) => {\n const clipboard = (event as ClipboardEvent).clipboardData;\n if (!clipboard) return;\n\n const files = Array.from(clipboard.items)\n .filter((item: DataTransferItem) => item.kind === \"file\")\n .map((item: DataTransferItem) => item.getAsFile())\n .filter((file): file is File => file !== null);\n\n if (files.length === 0) return;\n\n event.preventDefault();\n this.addSelectedFiles(files);\n });\n }\n\n private addSelectedFiles(files: File[]): void {\n if (files.length === 0) return;\n this.selectedFiles.push(...files);\n this.renderFileList();\n }\n\n private renderFileList(): void {\n const fileListEl = this.shadowRoot?.getElementById(\"file-list\");\n if (!fileListEl) return;\n fileListEl.innerHTML = \"\";\n\n // Release object URLs created in the previous render to avoid leaks\n this.previewUrls.forEach((url) => URL.revokeObjectURL(url));\n this.previewUrls = [];\n\n // Prefilled URLs\n this.prefilledAttachments.forEach((url, index) => {\n const name = url.substring(url.lastIndexOf(\"/\") + 1) || url;\n const isImage = this.isImageName(name);\n const previewUrl = isImage ? url : null;\n fileListEl.appendChild(\n this.buildFileItem({\n name,\n title: url,\n previewUrl,\n type: \"prefilled\",\n index,\n isImage,\n openUrl: url,\n badge: \"Đã gán\",\n }),\n );\n });\n\n // Selected local files\n this.selectedFiles.forEach((file, index) => {\n let previewUrl: string | null = null;\n const isImage = file.type.startsWith(\"image/\");\n if (isImage) {\n previewUrl = URL.createObjectURL(file);\n this.previewUrls.push(previewUrl);\n }\n fileListEl.appendChild(\n this.buildFileItem({\n name: file.name,\n title: file.name,\n previewUrl,\n type: \"selected\",\n index,\n isImage,\n openUrl: null,\n badge: null,\n }),\n );\n });\n\n // Hook remove event\n fileListEl.querySelectorAll(\".btn-remove\").forEach((btn) => {\n btn.addEventListener(\"click\", (e) => {\n const target = e.currentTarget as HTMLElement;\n const type = target.getAttribute(\"data-type\");\n const index = parseInt(target.getAttribute(\"data-index\") || \"0\", 10);\n if (type === \"prefilled\") {\n this.prefilledAttachments.splice(index, 1);\n } else {\n this.selectedFiles.splice(index, 1);\n }\n this.renderFileList();\n });\n });\n }\n\n private buildFileItem(opts: {\n name: string;\n title: string;\n previewUrl: string | null;\n type: \"prefilled\" | \"selected\";\n index: number;\n isImage: boolean;\n openUrl: string | null;\n badge: string | null;\n }): HTMLDivElement {\n const item = document.createElement(\"div\");\n item.className = \"file-item\";\n\n const trigger = document.createElement(\"button\");\n trigger.type = \"button\";\n trigger.className = \"file-preview-trigger\";\n trigger.title = \"Nhấn để xem trước\";\n trigger.addEventListener(\"click\", () => {\n this.openPreview({\n name: opts.title,\n previewUrl: opts.previewUrl,\n isImage: opts.isImage,\n openUrl: opts.openUrl,\n });\n });\n\n const thumb = document.createElement(\"div\");\n thumb.className = \"file-thumb\";\n if (opts.previewUrl) {\n const img = document.createElement(\"img\");\n img.src = opts.previewUrl;\n img.alt = opts.name;\n // Fall back to a file icon if the image fails to load\n img.addEventListener(\"error\", () => this.fillWithFileIcon(thumb));\n thumb.appendChild(img);\n } else {\n this.fillWithFileIcon(thumb);\n }\n\n const infoContainer = document.createElement(\"div\");\n infoContainer.className = \"file-info-container\";\n\n const name = document.createElement(\"span\");\n name.className = \"file-name\";\n name.title = opts.title;\n name.textContent = opts.name;\n\n infoContainer.appendChild(name);\n trigger.append(thumb, infoContainer);\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = \"btn-remove\";\n removeBtn.setAttribute(\"aria-label\", \"Xóa tệp\");\n removeBtn.setAttribute(\"data-type\", opts.type);\n removeBtn.setAttribute(\"data-index\", String(opts.index));\n removeBtn.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n `;\n\n item.append(trigger, removeBtn);\n\n if (opts.badge) {\n const badge = document.createElement(\"span\");\n badge.className = \"file-badge\";\n badge.textContent = opts.badge;\n item.appendChild(badge);\n }\n\n return item;\n }\n\n private fillWithFileIcon(thumb: HTMLElement): void {\n thumb.innerHTML = \"\";\n thumb.classList.add(\"file-thumb--icon\");\n thumb.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"></path>\n <polyline points=\"14 2 14 8 20 8\"></polyline>\n </svg>`;\n }\n\n private openPreview(opts: {\n name: string;\n previewUrl: string | null;\n isImage: boolean;\n openUrl: string | null;\n }): void {\n const overlay = this.shadowRoot?.getElementById(\"preview-overlay\");\n const body = this.shadowRoot?.getElementById(\"preview-body\");\n if (!overlay || !body) return;\n\n body.innerHTML = \"\";\n\n if (opts.isImage && opts.previewUrl) {\n const img = document.createElement(\"img\");\n img.src = opts.previewUrl;\n img.alt = opts.name;\n body.appendChild(img);\n } else {\n const info = document.createElement(\"div\");\n info.className = \"preview-file\";\n const openLink = opts.openUrl\n ? `<a href=\"${opts.openUrl}\" target=\"_blank\" rel=\"noopener noreferrer\">Mở trong tab mới</a>`\n : `<span>Không có bản xem trước cho tệp này</span>`;\n info.innerHTML = `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"56\" height=\"56\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"></path>\n <polyline points=\"14 2 14 8 20 8\"></polyline>\n </svg>\n <span class=\"preview-file-name\"></span>\n ${openLink}`;\n const nameEl = info.querySelector(\".preview-file-name\");\n if (nameEl) nameEl.textContent = opts.name;\n body.appendChild(info);\n }\n\n overlay.classList.add(\"show\");\n }\n\n private closePreview(): void {\n const overlay = this.shadowRoot?.getElementById(\"preview-overlay\");\n if (!overlay) return;\n overlay.classList.remove(\"show\");\n const body = this.shadowRoot?.getElementById(\"preview-body\");\n if (body) body.innerHTML = \"\";\n }\n\n private isImageName(name: string): boolean {\n return /\\.(png|jpe?g|gif|webp|bmp|svg|avif)$/i.test(name);\n }\n\n private prefillFromState(): void {\n this.setInputValue(\"requester\", this.user?.name ?? \"\");\n this.setInputValue(\"email\", this.user?.email ?? \"\");\n this.setInputValue(\"phone-number\", this.user?.phoneNumber ?? \"\");\n }\n\n private applyLauncherStyle(): void {\n const launcher = this.shadowRoot?.getElementById(\"open-btn\");\n if (!launcher) return;\n\n const launcherStyles = [`--np-hub-icon: url(\"${npSupportLogo}\")`];\n const width = this.normalizeSize(this.getAttribute(\"width\"));\n const height = this.normalizeSize(this.getAttribute(\"height\"));\n if (width) launcherStyles.push(`--np-hub-width: ${width}`);\n if (height) launcherStyles.push(`--np-hub-height: ${height}`);\n\n launcher.setAttribute(\"style\", `${launcherStyles.join(\"; \")};`);\n }\n\n private applyPositionStyle(): void {\n // Skip when user already dragged the launcher (inline left/top set).\n if (this.style.left && this.style.top) return;\n\n const right = this.normalizeSize(this.getAttribute(\"right\"));\n const bottom = this.normalizeSize(this.getAttribute(\"bottom\"));\n const left = this.normalizeSize(this.getAttribute(\"left\"));\n const top = this.normalizeSize(this.getAttribute(\"top\"));\n\n this.setPositionVar(\"--np-hub-left\", left);\n this.setPositionVar(\"--np-hub-top\", top);\n this.setPositionVar(\"--np-hub-right\", right);\n this.setPositionVar(\"--np-hub-bottom\", bottom);\n\n // Avoid conflicting edges when only one side of a pair is given.\n if (left && !right) this.style.setProperty(\"--np-hub-right\", \"auto\");\n if (right && !left) this.style.setProperty(\"--np-hub-left\", \"auto\");\n if (top && !bottom) this.style.setProperty(\"--np-hub-bottom\", \"auto\");\n if (bottom && !top) this.style.setProperty(\"--np-hub-top\", \"auto\");\n }\n\n private setPositionVar(name: string, value: string | null): void {\n if (value) {\n this.style.setProperty(name, value);\n } else {\n this.style.removeProperty(name);\n }\n }\n\n private normalizeSize(value: string | null): string | null {\n const trimmed = value?.trim();\n if (!trimmed) return null;\n return /^\\d+(\\.\\d+)?$/.test(trimmed) ? `${trimmed}px` : trimmed;\n }\n\n private setupClearableInputs(): void {\n const root = this.shadowRoot;\n if (!root) return;\n\n root.querySelectorAll(\".input-wrap\").forEach((wrap) => {\n const input = wrap.querySelector(\"input, textarea\") as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n const clearBtn = wrap.querySelector(\n \".btn-clear\",\n ) as HTMLButtonElement | null;\n if (!input || !clearBtn) return;\n\n const updateVisibility = () => this.updateClearButton(input);\n\n input.addEventListener(\"input\", updateVisibility);\n input.addEventListener(\"input\", () => this.clearFieldError(input.id));\n clearBtn.addEventListener(\"click\", () => {\n input.value = \"\";\n input.focus();\n updateVisibility();\n this.clearFieldError(input.id);\n });\n\n updateVisibility();\n });\n }\n\n private updateClearButton(\n input: HTMLInputElement | HTMLTextAreaElement,\n ): void {\n const wrap = input.parentElement;\n if (wrap?.classList.contains(\"input-wrap\")) {\n wrap.classList.toggle(\"has-value\", input.value.length > 0);\n }\n }\n\n private setInputValue(id: string, value: string): void {\n const el = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n if (!el) return;\n el.value = value;\n this.updateClearButton(el);\n }\n\n private getInputValue(id: string): string {\n const el = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n return el?.value?.trim() ?? \"\";\n }\n\n private setFieldError(id: RequiredFieldId): void {\n const errorEl = this.shadowRoot?.getElementById(`${id}-error`);\n if (errorEl) {\n errorEl.textContent = \"\";\n }\n\n const input = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n const wrap = input?.parentElement;\n if (wrap?.classList.contains(\"input-wrap\")) {\n wrap.classList.add(\"has-error\");\n }\n }\n\n private clearFieldError(id: string): void {\n const errorEl = this.shadowRoot?.getElementById(`${id}-error`);\n if (errorEl) {\n errorEl.textContent = \"\";\n }\n\n const input = this.shadowRoot?.getElementById(id) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n const wrap = input?.parentElement;\n if (wrap?.classList.contains(\"input-wrap\")) {\n wrap.classList.remove(\"has-error\");\n }\n }\n\n private clearAllFieldErrors(): void {\n this.clearFieldError(\"requester\");\n this.clearFieldError(\"phone-number\");\n this.clearFieldError(\"email\");\n this.clearFieldError(\"content\");\n }\n\n private showToast(\n payload: ToastPayload,\n autoCloseMs?: number,\n ): void {\n const toastEl = this.shadowRoot?.getElementById(\"toast\");\n const titleEl = this.shadowRoot?.getElementById(\"toast-title\");\n const messageEl = this.shadowRoot?.getElementById(\"toast-message\");\n const detailsEl = this.shadowRoot?.getElementById(\"toast-details\");\n const requestCodeEl = this.shadowRoot?.getElementById(\"toast-request-code\");\n const linkRowEl = this.shadowRoot?.getElementById(\"toast-link-row\");\n const linkEl = this.shadowRoot?.getElementById(\n \"toast-link\",\n ) as HTMLAnchorElement | null;\n const countdownEl = this.shadowRoot?.getElementById(\"toast-countdown\");\n const countdownSecondsEl = this.shadowRoot?.getElementById(\n \"toast-countdown-seconds\",\n );\n const countdownBarEl = this.shadowRoot?.getElementById(\n \"toast-countdown-bar\",\n );\n if (\n !toastEl ||\n !titleEl ||\n !messageEl ||\n !detailsEl ||\n !requestCodeEl ||\n !linkRowEl ||\n !linkEl ||\n !countdownBarEl\n ) {\n return;\n }\n\n this.clearToastAutoCloseTimer();\n this.clearToastCountdown();\n\n titleEl.textContent = payload.title;\n messageEl.textContent = payload.message;\n\n const hasRequestCode = Boolean(payload.requestCode);\n const hasUrl = Boolean(payload.url);\n const showDetails =\n payload.type === \"success\" && (hasRequestCode || hasUrl);\n\n if (showDetails) {\n requestCodeEl.textContent = payload.requestCode ?? \"—\";\n if (hasUrl && payload.url) {\n linkEl.href = payload.url;\n linkEl.textContent = payload.url;\n linkEl.title = payload.url;\n linkRowEl.classList.remove(\"is-hidden\");\n } else {\n linkEl.removeAttribute(\"href\");\n linkEl.textContent = \"\";\n linkEl.removeAttribute(\"title\");\n linkRowEl.classList.add(\"is-hidden\");\n }\n detailsEl.classList.remove(\"is-hidden\");\n } else {\n requestCodeEl.textContent = \"\";\n linkEl.removeAttribute(\"href\");\n linkEl.textContent = \"\";\n linkEl.removeAttribute(\"title\");\n linkRowEl.classList.add(\"is-hidden\");\n detailsEl.classList.add(\"is-hidden\");\n }\n\n const duration = autoCloseMs !== undefined\n ? autoCloseMs\n : (payload.type === \"success\" ? this.toastSuccessDuration : this.toastErrorDuration);\n\n this.toastRemainingMs = duration;\n this.toastTotalMs = duration;\n const toastCardEl = this.shadowRoot?.querySelector(\".toast-card\");\n this.isToastPaused = Boolean(toastCardEl?.matches(\":hover\"));\n\n toastEl.classList.remove(\"success\", \"error\");\n toastEl.classList.add(payload.type, \"show\");\n this.startToastCountdown(\n countdownEl ?? null,\n countdownSecondsEl ?? null,\n countdownBarEl,\n );\n }\n\n private hideToast(shouldCloseModal = true): void {\n const toastEl = this.shadowRoot?.getElementById(\"toast\");\n const titleEl = this.shadowRoot?.getElementById(\"toast-title\");\n const messageEl = this.shadowRoot?.getElementById(\"toast-message\");\n const detailsEl = this.shadowRoot?.getElementById(\"toast-details\");\n const requestCodeEl = this.shadowRoot?.getElementById(\"toast-request-code\");\n const linkRowEl = this.shadowRoot?.getElementById(\"toast-link-row\");\n const linkEl = this.shadowRoot?.getElementById(\n \"toast-link\",\n ) as HTMLAnchorElement | null;\n const countdownEl = this.shadowRoot?.getElementById(\"toast-countdown\");\n const countdownSecondsEl = this.shadowRoot?.getElementById(\n \"toast-countdown-seconds\",\n );\n const countdownBarEl = this.shadowRoot?.getElementById(\n \"toast-countdown-bar\",\n );\n if (\n !toastEl ||\n !titleEl ||\n !messageEl ||\n !detailsEl ||\n !requestCodeEl ||\n !linkRowEl ||\n !linkEl ||\n !countdownBarEl\n ) {\n return;\n }\n\n const isSuccess = toastEl.classList.contains(\"success\");\n\n this.clearToastAutoCloseTimer();\n this.clearToastCountdown();\n this.toastRemainingMs = 0;\n this.toastTotalMs = 0;\n this.isToastPaused = false;\n titleEl.textContent = \"\";\n messageEl.textContent = \"\";\n requestCodeEl.textContent = \"\";\n linkEl.removeAttribute(\"href\");\n linkEl.textContent = \"\";\n linkEl.removeAttribute(\"title\");\n linkRowEl.classList.add(\"is-hidden\");\n detailsEl.classList.add(\"is-hidden\");\n if (countdownSecondsEl) {\n countdownSecondsEl.textContent = \"\";\n }\n countdownBarEl.style.strokeDashoffset = \"0\";\n if (countdownEl) {\n countdownEl.classList.add(\"is-hidden\");\n }\n toastEl.classList.remove(\"success\", \"error\", \"show\");\n\n if (isSuccess && shouldCloseModal) {\n this.close();\n }\n }\n\n private startToastCountdown(\n countdownEl: HTMLElement | null,\n secondsEl: HTMLElement | null,\n barEl: HTMLElement,\n ): void {\n const updateCountdown = (): void => {\n const seconds = Math.max(1, Math.ceil(this.toastRemainingMs / 1000));\n if (secondsEl) {\n secondsEl.textContent = `${seconds}s`;\n }\n \n const percent = Math.max(0, Math.min(1, this.toastRemainingMs / this.toastTotalMs));\n const offset = 94.25 * (1 - percent);\n barEl.style.strokeDashoffset = `${offset}`;\n };\n\n updateCountdown();\n if (countdownEl) {\n countdownEl.classList.remove(\"is-hidden\");\n }\n\n this.toastCountdownInterval = window.setInterval(() => {\n if (this.isToastPaused) return;\n\n this.toastRemainingMs -= 100;\n if (this.toastRemainingMs <= 0) {\n this.toastRemainingMs = 0;\n barEl.style.strokeDashoffset = \"94.25\";\n if (secondsEl) {\n secondsEl.textContent = \"0s\";\n }\n this.hideToast();\n return;\n }\n updateCountdown();\n }, 100);\n }\n\n private clearToastCountdown(): void {\n if (this.toastCountdownInterval !== null) {\n window.clearInterval(this.toastCountdownInterval);\n this.toastCountdownInterval = null;\n }\n }\n\n private toToastPayload(content: SubmitToastContent): ToastPayload {\n return {\n ...content,\n title:\n content.type === \"success\"\n ? \"Gửi yêu cầu thành công\"\n : \"Gửi yêu cầu thất bại\",\n };\n }\n\n private clearToastAutoCloseTimer(): void {\n if (this.toastAutoCloseTimer !== null) {\n window.clearTimeout(this.toastAutoCloseTimer);\n this.toastAutoCloseTimer = null;\n }\n }\n\n private normalizeToastDuration(value: number): number {\n if (!Number.isFinite(value) || value <= 0) {\n return 4000;\n }\n return Math.floor(value);\n }\n\n private clearCloseAfterSuccessTimer(): void {\n if (this.closeAfterSuccessTimer !== null) {\n window.clearTimeout(this.closeAfterSuccessTimer);\n this.closeAfterSuccessTimer = null;\n }\n }\n\n private validateRequiredFields(): boolean {\n this.clearAllFieldErrors();\n\n const requiredFields: RequiredFieldId[] = [\n \"requester\",\n \"phone-number\",\n \"email\",\n \"content\",\n ];\n\n let firstInvalidId: RequiredFieldId | null = null;\n\n requiredFields.forEach((id) => {\n if (!this.getInputValue(id)) {\n this.setFieldError(id);\n if (!firstInvalidId) {\n firstInvalidId = id;\n }\n }\n });\n\n if (firstInvalidId) {\n const firstInvalid = this.shadowRoot?.getElementById(firstInvalidId) as\n | HTMLInputElement\n | HTMLTextAreaElement\n | null;\n firstInvalid?.focus();\n return false;\n }\n\n return true;\n }\n\n private resetFormData(): void {\n this.setInputValue(\"requester\", \"\");\n this.setInputValue(\"email\", \"\");\n this.setInputValue(\"phone-number\", \"\");\n this.setInputValue(\"content\", \"\");\n this.clearAllFieldErrors();\n\n this.selectedFiles = [];\n this.prefilledAttachments = [];\n this.renderFileList();\n this.closePreview();\n }\n\n private async submit(): Promise<void> {\n this.clearCloseAfterSuccessTimer();\n this.hideToast(false);\n\n if (!this.validateRequiredFields()) {\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message: \"Please fill in all required fields.\" },\n }),\n );\n return;\n }\n\n const user: SupportUser = {\n name: this.getInputValue(\"requester\"),\n email: this.getInputValue(\"email\"),\n phoneNumber: this.getInputValue(\"phone-number\"),\n };\n\n this.user = user;\n\n try {\n validateWidgetConfig(this.config);\n validateUser(user);\n\n const content = this.getInputValue(\"content\");\n const submission: SupportSubmissionInput = {\n content,\n attachments: [], // dummy value for local validation of required content field\n };\n validateSubmission(submission);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Invalid input.\";\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message },\n }),\n );\n return;\n }\n\n const content = this.getInputValue(\"content\");\n\n // Create FormData to send request as multipart/form-data\n const formData = new FormData();\n formData.append(\"Requester\", user.name);\n formData.append(\"PhoneNumber\", user.phoneNumber);\n formData.append(\"Email\", user.email);\n formData.append(\"Content\", content);\n if (this.config.projectId?.trim()) {\n formData.append(\"ProjectId\", this.config.projectId.trim());\n }\n if (typeof this.priority === \"number\") {\n formData.append(\"Priority\", String(this.priority));\n }\n\n // Fetch prefilled attachments and convert them to Files\n for (const url of this.prefilledAttachments) {\n const file = await urlToFile(url);\n if (file) {\n formData.append(\"Attachments\", file);\n }\n }\n\n // Add newly selected files\n for (const file of this.selectedFiles) {\n formData.append(\"Attachments\", file);\n }\n\n // Add coordinators and emailContacts\n if (this.coordinators.length > 0) {\n this.coordinators.forEach((coord) =>\n formData.append(\"Coordinators\", coord),\n );\n }\n if (this.emailContacts.length > 0) {\n this.emailContacts.forEach((contact) =>\n formData.append(\"EmailContacts\", contact),\n );\n }\n\n try {\n const baseUrl = resolveBaseUrl(this.config);\n const result = await createSupportRequest(baseUrl, formData);\n\n if (!isCreateRequestSuccess(result)) {\n const failureContent = mapCreateRequestFailure(result);\n this.showToast(this.toToastPayload(failureContent));\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message: failureContent.message, response: result },\n }),\n );\n return;\n }\n\n const successContent = mapCreateRequestSuccess(result);\n this.showToast(this.toToastPayload(successContent));\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_SUCCESS, {\n bubbles: true,\n detail: result,\n }),\n );\n this.closeAfterSuccessTimer = window.setTimeout(() => {\n this.hideToast();\n }, this.toastSuccessDuration);\n } catch (error) {\n const errorContent = mapHttpClientError(error);\n this.showToast(this.toToastPayload(errorContent));\n this.dispatchEvent(\n new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {\n bubbles: true,\n detail: { message: errorContent.message },\n }),\n );\n }\n }\n\n private setupDrag(launcher: HTMLElement): void {\n launcher.addEventListener(\"pointerdown\", (e: PointerEvent) => {\n if (e.button !== 0) return;\n\n this.dragOccurred = false;\n const rect = this.getBoundingClientRect();\n const startX = e.clientX;\n const startY = e.clientY;\n const initialLeft = rect.left;\n const initialTop = rect.top;\n\n try {\n launcher.setPointerCapture(e.pointerId);\n } catch {\n // ignore if pointer capture fails\n }\n\n const onPointerMove = (moveEvent: PointerEvent) => {\n const dx = moveEvent.clientX - startX;\n const dy = moveEvent.clientY - startY;\n\n if (!this.dragOccurred && (Math.abs(dx) > 4 || Math.abs(dy) > 4)) {\n this.dragOccurred = true;\n launcher.style.cursor = \"grabbing\";\n }\n\n if (this.dragOccurred) {\n const leftVal = initialLeft + dx;\n const topVal = initialTop + dy;\n this.adjustPosition(leftVal, topVal);\n }\n };\n\n const onPointerUp = (upEvent: PointerEvent) => {\n try {\n launcher.releasePointerCapture(upEvent.pointerId);\n } catch {\n // ignore\n }\n launcher.removeEventListener(\"pointermove\", onPointerMove);\n launcher.removeEventListener(\"pointerup\", onPointerUp);\n launcher.removeEventListener(\"pointercancel\", onPointerUp);\n launcher.style.cursor = \"\";\n\n if (this.dragOccurred) {\n localStorage.setItem(\n \"np-hub-widget-position\",\n JSON.stringify({\n left: this.style.left,\n top: this.style.top,\n }),\n );\n }\n };\n\n launcher.addEventListener(\"pointermove\", onPointerMove);\n launcher.addEventListener(\"pointerup\", onPointerUp);\n launcher.addEventListener(\"pointercancel\", onPointerUp);\n });\n }\n\n private adjustPosition(leftVal: number, topVal: number): void {\n const rect = this.getBoundingClientRect();\n const width = rect.width || 72;\n const height = rect.height || 72;\n\n const minLeft = 0;\n const maxLeft = window.innerWidth - width;\n const minTop = 0;\n const maxTop = window.innerHeight - height;\n\n const clampedLeft = Math.max(minLeft, Math.min(maxLeft, leftVal));\n const clampedTop = Math.max(minTop, Math.min(maxTop, topVal));\n\n this.style.right = \"auto\";\n this.style.bottom = \"auto\";\n this.style.left = `${clampedLeft}px`;\n this.style.top = `${clampedTop}px`;\n }\n\n private restorePosition(): void {\n const savedPosition = localStorage.getItem(\"np-hub-widget-position\");\n if (savedPosition) {\n try {\n const { left, top } = JSON.parse(savedPosition);\n if (left && top) {\n const leftVal = parseFloat(left);\n const topVal = parseFloat(top);\n this.adjustPosition(leftVal, topVal);\n }\n } catch {\n // Ignore parsing errors\n }\n }\n }\n\n private handleResize = (): void => {\n if (this.style.left && this.style.top) {\n const leftVal = parseFloat(this.style.left);\n const topVal = parseFloat(this.style.top);\n this.adjustPosition(leftVal, topVal);\n }\n };\n}\n","import { SupportWidgetElement } from \"./component/support-widget.element\";\n\nconst TAG_NAME = \"np-hub\";\n\nexport function registerSupportWidget(): void {\n if (!customElements.get(TAG_NAME)) {\n customElements.define(TAG_NAME, SupportWidgetElement);\n }\n}\n\nregisterSupportWidget();\n"],"mappings":"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","names":["_a","content","e"]}
package/dist/index.cjs CHANGED
@@ -195,7 +195,8 @@ var SupportWidgetElement = class extends HTMLElement {
195
195
  this.closeAfterSuccessTimer = null;
196
196
  this.toastAutoCloseTimer = null;
197
197
  this.toastCountdownInterval = null;
198
- this.toastDuration = 4e3;
198
+ this.toastSuccessDuration = 4e3;
199
+ this.toastErrorDuration = 4e3;
199
200
  this.toastRemainingMs = 0;
200
201
  this.toastTotalMs = 0;
201
202
  this.isToastPaused = false;
@@ -261,7 +262,23 @@ var SupportWidgetElement = class extends HTMLElement {
261
262
  this.emailContacts = config.emailContacts;
262
263
  }
263
264
  if (config.toastDuration !== void 0) {
264
- this.toastDuration = this.normalizeToastDuration(config.toastDuration);
265
+ const duration = config.toastDuration;
266
+ if (typeof duration === "number") {
267
+ const val = this.normalizeToastDuration(duration);
268
+ this.toastSuccessDuration = val;
269
+ this.toastErrorDuration = val;
270
+ } else if (duration && typeof duration === "object") {
271
+ if (duration.success !== void 0) {
272
+ this.toastSuccessDuration = this.normalizeToastDuration(duration.success);
273
+ } else {
274
+ this.toastSuccessDuration = 4e3;
275
+ }
276
+ if (duration.error !== void 0) {
277
+ this.toastErrorDuration = this.normalizeToastDuration(duration.error);
278
+ } else {
279
+ this.toastErrorDuration = 4e3;
280
+ }
281
+ }
265
282
  }
266
283
  }
267
284
  setFormPrefill(data) {
@@ -279,6 +296,7 @@ var SupportWidgetElement = class extends HTMLElement {
279
296
  var _a, _b;
280
297
  this.clearCloseAfterSuccessTimer();
281
298
  this.resetFormData();
299
+ this.hideToast(false);
282
300
  (_b = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("modal")) == null ? void 0 : _b.classList.remove("show");
283
301
  this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));
284
302
  }
@@ -308,9 +326,27 @@ var SupportWidgetElement = class extends HTMLElement {
308
326
  };
309
327
  const toastDurationAttr = this.getAttribute("toast-duration");
310
328
  if (toastDurationAttr !== null) {
311
- const parsed = parseInt(toastDurationAttr, 10);
312
- if (!isNaN(parsed)) {
313
- this.toastDuration = this.normalizeToastDuration(parsed);
329
+ const trimmed = toastDurationAttr.trim();
330
+ if (trimmed.startsWith("{")) {
331
+ try {
332
+ const parsed = JSON.parse(trimmed);
333
+ if (parsed && typeof parsed === "object") {
334
+ if (parsed.success !== void 0) {
335
+ this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));
336
+ }
337
+ if (parsed.error !== void 0) {
338
+ this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));
339
+ }
340
+ }
341
+ } catch (e) {
342
+ }
343
+ } else {
344
+ const parsed = parseInt(trimmed, 10);
345
+ if (!isNaN(parsed)) {
346
+ const val = this.normalizeToastDuration(parsed);
347
+ this.toastSuccessDuration = val;
348
+ this.toastErrorDuration = val;
349
+ }
314
350
  }
315
351
  }
316
352
  }
@@ -751,7 +787,7 @@ var SupportWidgetElement = class extends HTMLElement {
751
787
  this.clearFieldError("email");
752
788
  this.clearFieldError("content");
753
789
  }
754
- showToast(payload, autoCloseMs = this.toastDuration) {
790
+ showToast(payload, autoCloseMs) {
755
791
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
756
792
  const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
757
793
  const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
@@ -801,8 +837,9 @@ var SupportWidgetElement = class extends HTMLElement {
801
837
  linkRowEl.classList.add("is-hidden");
802
838
  detailsEl.classList.add("is-hidden");
803
839
  }
804
- this.toastRemainingMs = autoCloseMs;
805
- this.toastTotalMs = autoCloseMs;
840
+ const duration = autoCloseMs !== void 0 ? autoCloseMs : payload.type === "success" ? this.toastSuccessDuration : this.toastErrorDuration;
841
+ this.toastRemainingMs = duration;
842
+ this.toastTotalMs = duration;
806
843
  const toastCardEl = (_l = this.shadowRoot) == null ? void 0 : _l.querySelector(".toast-card");
807
844
  this.isToastPaused = Boolean(toastCardEl == null ? void 0 : toastCardEl.matches(":hover"));
808
845
  toastEl.classList.remove("success", "error");
@@ -813,7 +850,7 @@ var SupportWidgetElement = class extends HTMLElement {
813
850
  countdownBarEl
814
851
  );
815
852
  }
816
- hideToast() {
853
+ hideToast(shouldCloseModal = true) {
817
854
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
818
855
  const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
819
856
  const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
@@ -834,6 +871,7 @@ var SupportWidgetElement = class extends HTMLElement {
834
871
  if (!toastEl || !titleEl || !messageEl || !detailsEl || !requestCodeEl || !linkRowEl || !linkEl || !countdownBarEl) {
835
872
  return;
836
873
  }
874
+ const isSuccess = toastEl.classList.contains("success");
837
875
  this.clearToastAutoCloseTimer();
838
876
  this.clearToastCountdown();
839
877
  this.toastRemainingMs = 0;
@@ -855,6 +893,9 @@ var SupportWidgetElement = class extends HTMLElement {
855
893
  countdownEl.classList.add("is-hidden");
856
894
  }
857
895
  toastEl.classList.remove("success", "error", "show");
896
+ if (isSuccess && shouldCloseModal) {
897
+ this.close();
898
+ }
858
899
  }
859
900
  startToastCountdown(countdownEl, secondsEl, barEl) {
860
901
  const updateCountdown = () => {
@@ -954,7 +995,7 @@ var SupportWidgetElement = class extends HTMLElement {
954
995
  async submit() {
955
996
  var _a;
956
997
  this.clearCloseAfterSuccessTimer();
957
- this.hideToast();
998
+ this.hideToast(false);
958
999
  if (!this.validateRequiredFields()) {
959
1000
  this.dispatchEvent(
960
1001
  new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {
@@ -1044,8 +1085,8 @@ var SupportWidgetElement = class extends HTMLElement {
1044
1085
  })
1045
1086
  );
1046
1087
  this.closeAfterSuccessTimer = window.setTimeout(() => {
1047
- this.close();
1048
- }, 1200);
1088
+ this.hideToast();
1089
+ }, this.toastSuccessDuration);
1049
1090
  } catch (error) {
1050
1091
  const errorContent = mapHttpClientError(error);
1051
1092
  this.showToast(this.toToastPayload(errorContent));