@namphuongtechnologi/np-hub 0.2.0 → 0.2.2

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.
package/README.md CHANGED
@@ -121,14 +121,14 @@ export default function App() {
121
121
 
122
122
  ### `setConfig` / React props
123
123
 
124
- | Name | Required | Default | Description |
125
- | --------------- | -------- | ------- | ---------------------------------------------- |
126
- | `projectId` | No | — | Project code forwarded to the API |
127
- | `isDev` | No | `false` | `true` → Development API; otherwise Production |
128
- | `priority` | No | `0` | Request priority |
129
- | `coordinators` | No | `[]` | Coordinator email list |
130
- | `emailContacts` | No | `[]` | Contact emails that receive the notification |
131
- | `toastDuration` | No | `4000` | Toast auto-close delay in milliseconds |
124
+ | Name | Required | Default | Description |
125
+ | --------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------- |
126
+ | `projectId` | No | — | Project code forwarded to the API |
127
+ | `isDev` | No | `false` | `true` → Development API; otherwise Production |
128
+ | `priority` | No | `0` | Request priority |
129
+ | `coordinators` | No | `[]` | Coordinator email list |
130
+ | `emailContacts` | No | `[]` | Contact emails that receive the notification |
131
+ | `toastDuration` | No | `4000` | Toast auto-close delay in ms. Can be a number or an object: `{ success?: number, error?: number }` |
132
132
 
133
133
  ### Prefill (optional)
134
134
 
@@ -192,23 +192,32 @@ widget.setAttribute("bottom", "24");
192
192
 
193
193
  After a successful API submit or an API/exception failure, the widget shows a **centered toast** with the result message.
194
194
 
195
- | Behavior | Detail |
196
- | ----------------- | -------------------------------------------------------- |
197
- | When shown | Submit success, or API / runtime error |
198
- | When hidden | Missing required fields (only inline field highlighting) |
199
- | Auto-close | After `toastDuration` ms (default `4000`) |
200
- | Manual close | User can dismiss via the `×` button |
201
- | Success follow-up | Modal closes automatically ~1.2s after success |
195
+ | Behavior | Detail |
196
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------- |
197
+ | When shown | Submit success, or API / runtime error |
198
+ | When hidden | Missing required fields (only inline field highlighting) |
199
+ | Auto-close | After `toastDuration` ms (default `4000`). Pauses countdown on cursor hover. |
200
+ | Copy Actions | Provides quick-copy buttons for the support request code and lookup URL directly on the success toast |
201
+ | Manual close | User can dismiss via the `×` button |
202
+ | Success follow-up | Modal closes automatically after success toast auto-closes (controlled by `toastSuccessDuration`) |
203
+
204
+ `toastDuration` can be a single number (applies to both success/error) or structured with separate values:
202
205
 
203
206
  ```js
204
207
  widget.setConfig({
205
208
  projectId: "NPP",
206
- toastDuration: 6000, // auto-close after 6 seconds
209
+ toastDuration: {
210
+ success: 3000, // closes success toast after 3s
211
+ error: 8000 // keeps error toast open for 8s
212
+ }
207
213
  });
208
214
  ```
209
215
 
210
216
  ```tsx
211
- <SupportWidget projectId="NPP" toastDuration={6000} />
217
+ <SupportWidget
218
+ projectId="NPP"
219
+ toastDuration={{ success: 3000, error: 8000 }}
220
+ />
212
221
  ```
213
222
 
214
223
  ---
@@ -228,12 +237,13 @@ React equivalent props: `onOpen`, `onClose`, `onSubmitSuccess`, `onSubmitError`.
228
237
 
229
238
  ### Methods (Web Component)
230
239
 
231
- | Method | Description |
232
- | ---------------------- | ----------------------------- |
233
- | `setConfig(config)` | Apply project / API settings |
234
- | `setUser(user)` | Prefill requester fields |
235
- | `setFormPrefill(data)` | Prefill content / attachments |
236
- | `open()` / `close()` | Open or close the modal |
240
+ | Method | Description |
241
+ | ----------------------------- | ------------------------------------------------------------------- |
242
+ | `setConfig(config)` | Apply project / API settings |
243
+ | `setUser(user)` | Prefill requester fields |
244
+ | `setFormPrefill(data)` | Prefill content / attachments |
245
+ | `open()` / `close()` | Open or close the modal |
246
+ | `showDemoToast(type)` | Show a dummy toast for testing/dev (accepts `"success"` or `"error"`) |
237
247
 
238
248
  ---
239
249
 
@@ -165,9 +165,9 @@ var SupportWidgetElement = class extends HTMLElement {
165
165
  this.prefilledAttachments = [];
166
166
  this.previewUrls = [];
167
167
  this.closeAfterSuccessTimer = null;
168
- this.toastAutoCloseTimer = null;
169
168
  this.toastCountdownInterval = null;
170
- this.toastDuration = 4e3;
169
+ this.toastSuccessDuration = 4e3;
170
+ this.toastErrorDuration = 4e3;
171
171
  this.toastRemainingMs = 0;
172
172
  this.toastTotalMs = 0;
173
173
  this.isToastPaused = false;
@@ -195,7 +195,6 @@ var SupportWidgetElement = class extends HTMLElement {
195
195
  disconnectedCallback() {
196
196
  window.removeEventListener("resize", this.handleResize);
197
197
  this.clearCloseAfterSuccessTimer();
198
- this.clearToastAutoCloseTimer();
199
198
  this.clearToastCountdown();
200
199
  }
201
200
  attributeChangedCallback() {
@@ -208,6 +207,10 @@ var SupportWidgetElement = class extends HTMLElement {
208
207
  this.user = user;
209
208
  this.prefillFromState();
210
209
  }
210
+ /**
211
+ * Updates the widget configuration.
212
+ * Supports custom toast auto-close durations via `toastDuration`.
213
+ */
211
214
  setConfig(config) {
212
215
  this.config = {
213
216
  ...this.config,
@@ -233,7 +236,23 @@ var SupportWidgetElement = class extends HTMLElement {
233
236
  this.emailContacts = config.emailContacts;
234
237
  }
235
238
  if (config.toastDuration !== void 0) {
236
- this.toastDuration = this.normalizeToastDuration(config.toastDuration);
239
+ const duration = config.toastDuration;
240
+ if (typeof duration === "number") {
241
+ const val = this.normalizeToastDuration(duration);
242
+ this.toastSuccessDuration = val;
243
+ this.toastErrorDuration = val;
244
+ } else if (duration && typeof duration === "object") {
245
+ if (duration.success !== void 0) {
246
+ this.toastSuccessDuration = this.normalizeToastDuration(duration.success);
247
+ } else {
248
+ this.toastSuccessDuration = 4e3;
249
+ }
250
+ if (duration.error !== void 0) {
251
+ this.toastErrorDuration = this.normalizeToastDuration(duration.error);
252
+ } else {
253
+ this.toastErrorDuration = 4e3;
254
+ }
255
+ }
237
256
  }
238
257
  }
239
258
  setFormPrefill(data) {
@@ -251,9 +270,14 @@ var SupportWidgetElement = class extends HTMLElement {
251
270
  var _a, _b;
252
271
  this.clearCloseAfterSuccessTimer();
253
272
  this.resetFormData();
273
+ this.hideToast(false);
254
274
  (_b = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("modal")) == null ? void 0 : _b.classList.remove("show");
255
275
  this.dispatchEvent(new CustomEvent(WIDGET_EVENTS.CLOSE, { bubbles: true }));
256
276
  }
277
+ /**
278
+ * Helper method to trigger a mock/demo toast popup for testing and development.
279
+ * @param type - Either "success" or "error"
280
+ */
257
281
  showDemoToast(type) {
258
282
  if (type === "success") {
259
283
  this.showToast({
@@ -280,9 +304,27 @@ var SupportWidgetElement = class extends HTMLElement {
280
304
  };
281
305
  const toastDurationAttr = this.getAttribute("toast-duration");
282
306
  if (toastDurationAttr !== null) {
283
- const parsed = parseInt(toastDurationAttr, 10);
284
- if (!isNaN(parsed)) {
285
- this.toastDuration = this.normalizeToastDuration(parsed);
307
+ const trimmed = toastDurationAttr.trim();
308
+ if (trimmed.startsWith("{")) {
309
+ try {
310
+ const parsed = JSON.parse(trimmed);
311
+ if (parsed && typeof parsed === "object") {
312
+ if (parsed.success !== void 0) {
313
+ this.toastSuccessDuration = this.normalizeToastDuration(Number(parsed.success));
314
+ }
315
+ if (parsed.error !== void 0) {
316
+ this.toastErrorDuration = this.normalizeToastDuration(Number(parsed.error));
317
+ }
318
+ }
319
+ } catch (e) {
320
+ }
321
+ } else {
322
+ const parsed = parseInt(trimmed, 10);
323
+ if (!isNaN(parsed)) {
324
+ const val = this.normalizeToastDuration(parsed);
325
+ this.toastSuccessDuration = val;
326
+ this.toastErrorDuration = val;
327
+ }
286
328
  }
287
329
  }
288
330
  }
@@ -723,7 +765,7 @@ var SupportWidgetElement = class extends HTMLElement {
723
765
  this.clearFieldError("email");
724
766
  this.clearFieldError("content");
725
767
  }
726
- showToast(payload, autoCloseMs = this.toastDuration) {
768
+ showToast(payload, autoCloseMs) {
727
769
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
728
770
  const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
729
771
  const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
@@ -744,7 +786,6 @@ var SupportWidgetElement = class extends HTMLElement {
744
786
  if (!toastEl || !titleEl || !messageEl || !detailsEl || !requestCodeEl || !linkRowEl || !linkEl || !countdownBarEl) {
745
787
  return;
746
788
  }
747
- this.clearToastAutoCloseTimer();
748
789
  this.clearToastCountdown();
749
790
  titleEl.textContent = payload.title;
750
791
  messageEl.textContent = payload.message;
@@ -773,8 +814,9 @@ var SupportWidgetElement = class extends HTMLElement {
773
814
  linkRowEl.classList.add("is-hidden");
774
815
  detailsEl.classList.add("is-hidden");
775
816
  }
776
- this.toastRemainingMs = autoCloseMs;
777
- this.toastTotalMs = autoCloseMs;
817
+ const duration = autoCloseMs !== void 0 ? autoCloseMs : payload.type === "success" ? this.toastSuccessDuration : this.toastErrorDuration;
818
+ this.toastRemainingMs = duration;
819
+ this.toastTotalMs = duration;
778
820
  const toastCardEl = (_l = this.shadowRoot) == null ? void 0 : _l.querySelector(".toast-card");
779
821
  this.isToastPaused = Boolean(toastCardEl == null ? void 0 : toastCardEl.matches(":hover"));
780
822
  toastEl.classList.remove("success", "error");
@@ -785,7 +827,7 @@ var SupportWidgetElement = class extends HTMLElement {
785
827
  countdownBarEl
786
828
  );
787
829
  }
788
- hideToast() {
830
+ hideToast(shouldCloseModal = true) {
789
831
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
790
832
  const toastEl = (_a = this.shadowRoot) == null ? void 0 : _a.getElementById("toast");
791
833
  const titleEl = (_b = this.shadowRoot) == null ? void 0 : _b.getElementById("toast-title");
@@ -806,7 +848,7 @@ var SupportWidgetElement = class extends HTMLElement {
806
848
  if (!toastEl || !titleEl || !messageEl || !detailsEl || !requestCodeEl || !linkRowEl || !linkEl || !countdownBarEl) {
807
849
  return;
808
850
  }
809
- this.clearToastAutoCloseTimer();
851
+ const isSuccess = toastEl.classList.contains("success");
810
852
  this.clearToastCountdown();
811
853
  this.toastRemainingMs = 0;
812
854
  this.toastTotalMs = 0;
@@ -827,6 +869,9 @@ var SupportWidgetElement = class extends HTMLElement {
827
869
  countdownEl.classList.add("is-hidden");
828
870
  }
829
871
  toastEl.classList.remove("success", "error", "show");
872
+ if (isSuccess && shouldCloseModal) {
873
+ this.close();
874
+ }
830
875
  }
831
876
  startToastCountdown(countdownEl, secondsEl, barEl) {
832
877
  const updateCountdown = () => {
@@ -869,12 +914,6 @@ var SupportWidgetElement = class extends HTMLElement {
869
914
  title: content.type === "success" ? "G\u1EEDi y\xEAu c\u1EA7u th\xE0nh c\xF4ng" : "G\u1EEDi y\xEAu c\u1EA7u th\u1EA5t b\u1EA1i"
870
915
  };
871
916
  }
872
- clearToastAutoCloseTimer() {
873
- if (this.toastAutoCloseTimer !== null) {
874
- window.clearTimeout(this.toastAutoCloseTimer);
875
- this.toastAutoCloseTimer = null;
876
- }
877
- }
878
917
  normalizeToastDuration(value) {
879
918
  if (!Number.isFinite(value) || value <= 0) {
880
919
  return 4e3;
@@ -926,7 +965,7 @@ var SupportWidgetElement = class extends HTMLElement {
926
965
  async submit() {
927
966
  var _a;
928
967
  this.clearCloseAfterSuccessTimer();
929
- this.hideToast();
968
+ this.hideToast(false);
930
969
  if (!this.validateRequiredFields()) {
931
970
  this.dispatchEvent(
932
971
  new CustomEvent(WIDGET_EVENTS.SUBMIT_ERROR, {
@@ -1016,8 +1055,8 @@ var SupportWidgetElement = class extends HTMLElement {
1016
1055
  })
1017
1056
  );
1018
1057
  this.closeAfterSuccessTimer = window.setTimeout(() => {
1019
- this.close();
1020
- }, 1200);
1058
+ this.hideToast();
1059
+ }, this.toastSuccessDuration);
1021
1060
  } catch (error) {
1022
1061
  const errorContent = mapHttpClientError(error);
1023
1062
  this.showToast(this.toToastPayload(errorContent));
@@ -1135,4 +1174,4 @@ export {
1135
1174
  SupportWidgetElement,
1136
1175
  registerSupportWidget
1137
1176
  };
1138
- //# sourceMappingURL=chunk-C5WUIBKX.js.map
1177
+ //# sourceMappingURL=chunk-BO2GWI5I.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 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.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 /**\n * Updates the widget configuration.\n * Supports custom toast auto-close durations via `toastDuration`.\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 /**\n * Helper method to trigger a mock/demo toast popup for testing and development.\n * @param type - Either \"success\" or \"error\"\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.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.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\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"]}