@eztrak/shared 0.8.1 → 0.11.0

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
@@ -72,6 +72,31 @@ truncateText("A very long description here", 10); // "A very ..."
72
72
  truncateFileName("annual-financial-report-2026.pdf", 20); // "annual-financ....pdf"
73
73
  ```
74
74
 
75
+ Browser local storage helpers (JSON serialize/deserialize, safe fallbacks):
76
+
77
+ ```js
78
+ import {
79
+ getItem,
80
+ setItem,
81
+ loadUserState,
82
+ saveUserState,
83
+ USER_STATE_STORAGE_KEY,
84
+ } from "@eztrak/shared/utils";
85
+
86
+ // User auth state (default key: "authState")
87
+ saveUserState({ access_token: "...", name: "Ali" });
88
+ const user = loadUserState();
89
+
90
+ // Generic key/value storage
91
+ setItem("selectedProject", { id: 1, name: "Project A" });
92
+ const project = getItem("selectedProject");
93
+
94
+ // Explicit fallback when a key is missing
95
+ const permissions = getItem("userPermissionsNew", []);
96
+ ```
97
+
98
+ Backward-compatible aliases are also exported: `get` / `set`, `getItemFromLocalStorage`, `storeItemInLocalStorage`, and `saveItemLocalStorage`.
99
+
75
100
  RTK Query errors (toast or SweetAlert — no extra wiring):
76
101
 
77
102
  ```js
@@ -84,11 +109,64 @@ handleApiError(error, { fallbackMessage: "Save failed" });
84
109
  handleApiError(error, { showAlert: true });
85
110
  ```
86
111
 
87
- Confirmation dialog (SweetAlert2):
112
+ ### confirmationAlert
113
+
114
+ Standard SweetAlert2 confirmation dialog for all Eztrak apps (Main, Vault, Pilot, PBF). Use **one function** with an options object — no per-module copies in `utils.js`.
115
+
116
+ **Import:**
88
117
 
89
118
  ```js
119
+ import { confirmationAlert } from "@eztrak/shared";
120
+ // or
90
121
  import { confirmationAlert } from "@eztrak/shared/utils";
122
+ ```
123
+
124
+ **Signature:**
125
+
126
+ ```ts
127
+ confirmationAlert(
128
+ onConfirm: () => void | Promise<void>,
129
+ options?: ConfirmationAlertOptions
130
+ ): void
131
+ ```
132
+
133
+ `onConfirm` may be sync or `async`. When a loader is active, the modal stays open until `onConfirm` resolves; on failure it shows an error message inside the dialog.
134
+
135
+ #### Options
136
+
137
+ | Prop | Type | Default | Description |
138
+ | --- | --- | --- | --- |
139
+ | `title` | `string` | `"Are you sure?"` (or `"Confirmation Required"` when `inputExpectedValue` is set) | Dialog heading |
140
+ | `text` | `string` | — | Plain-text message (ignored when `html` is used without input) |
141
+ | `html` | `string` | — | Custom HTML body (e.g. project delete instructions) |
142
+ | `icon` | `"warning" \| "info" \| "question" \| "error" \| "success"` | `"warning"` | SweetAlert2 icon |
143
+ | `confirmButtonText` | `string` | `"Yes"` | Confirm button label |
144
+ | `cancelButtonText` | `string` | `"Cancel"` | Cancel button label |
145
+ | `confirmButtonColor` | `string` | `"#FF7335"` | Confirm button color (brand orange) |
146
+ | `cancelButtonColor` | `string` | `"#ff0000"` | Cancel button color |
147
+ | `showLoaderOnConfirm` | `boolean` | `false` | Show loader while `onConfirm` runs (API deletes/updates) |
148
+ | `inputExpectedValue` | `string` | — | User must type this value to confirm (trimmed, **case-sensitive**) |
149
+ | `inputPlaceholder` | `string` | `"Type confirmation text to proceed"` | Placeholder for the confirmation input |
150
+ | `reverseButtons` | `boolean` | — | Swap Cancel / Confirm order (e.g. logout) |
151
+ | `focusCancel` | `boolean` | — | Focus Cancel on open (e.g. logout) |
152
+ | `zIndex` | `number` | — | Stack the modal above tables/overlays |
91
153
 
154
+ #### Behavior modes
155
+
156
+ 1. **Simple confirm** — no `showLoaderOnConfirm`, no `inputExpectedValue`
157
+ User clicks Yes → `onConfirm()` runs → modal closes.
158
+
159
+ 2. **Loader confirm** — `showLoaderOnConfirm: true`
160
+ User clicks confirm → loader → `onConfirm()` awaited → success closes modal; error shows validation message and keeps modal open.
161
+
162
+ 3. **Typed confirm** — `inputExpectedValue` set
163
+ Input field appears; user must type the exact value (`typed.trim() === expected.trim()`). Loader is enabled automatically.
164
+
165
+ #### Examples
166
+
167
+ **Simple Yes/No (column reset, generic confirm):**
168
+
169
+ ```js
92
170
  confirmationAlert(() => resetLayout(), {
93
171
  title: "Reset column layout to default",
94
172
  text: "Reset column order, widths, and visibility to defaults?",
@@ -96,6 +174,108 @@ confirmationAlert(() => resetLayout(), {
96
174
  });
97
175
  ```
98
176
 
177
+ **Custom button colors:**
178
+
179
+ ```js
180
+ confirmationAlert(() => handleDelete(), {
181
+ title: "Delete this item?",
182
+ confirmButtonText: "Yes, delete it!",
183
+ confirmButtonColor: "#B91C1C",
184
+ cancelButtonColor: "#6b7280",
185
+ showLoaderOnConfirm: true,
186
+ });
187
+ ```
188
+
189
+ **Bulk delete with API loader:**
190
+
191
+ ```js
192
+ confirmationAlert(async () => {
193
+ await dispatch(deleteItems(ids)).unwrap();
194
+ }, {
195
+ text: "Are you sure you want to delete selected items?",
196
+ confirmButtonText: "OK",
197
+ showLoaderOnConfirm: true,
198
+ });
199
+ ```
200
+
201
+ **Type `DELETE` to confirm:**
202
+
203
+ ```js
204
+ confirmationAlert(() => bulkDelete(), {
205
+ text: "Are you sure you want to delete selected items?",
206
+ inputExpectedValue: "DELETE",
207
+ confirmButtonText: "Confirm",
208
+ });
209
+ ```
210
+
211
+ **Delete project — type project name:**
212
+
213
+ ```js
214
+ confirmationAlert(async () => {
215
+ await deleteProject(projectId);
216
+ }, {
217
+ title: "Delete Project",
218
+ html: `
219
+ <p style="font-size:14px">To confirm, please type the project name below:</p>
220
+ <p style="font-weight:bold;color:#d9534f">${projectName}</p>
221
+ `,
222
+ inputExpectedValue: projectName,
223
+ inputPlaceholder: "Enter project name",
224
+ confirmButtonText: "Delete",
225
+ showLoaderOnConfirm: true,
226
+ });
227
+ ```
228
+
229
+ **Logout:**
230
+
231
+ ```js
232
+ confirmationAlert(() => authService.logout(), {
233
+ title: "Logout Confirmation",
234
+ text: "Are you sure you want to logout?",
235
+ icon: "question",
236
+ confirmButtonText: "Yes, Logout",
237
+ cancelButtonColor: "#6b7280",
238
+ reverseButtons: true,
239
+ focusCancel: true,
240
+ });
241
+ ```
242
+
243
+ **Modal above AG Grid / overlays:**
244
+
245
+ ```js
246
+ confirmationAlert(() => deleteRow(), {
247
+ text: "Are you sure?",
248
+ showLoaderOnConfirm: true,
249
+ zIndex: 9999,
250
+ });
251
+ ```
252
+
253
+ #### Replacing local helpers
254
+
255
+ When migrating, remove duplicate functions from app `utils.js` and use `confirmationAlert` from `@eztrak/shared` instead:
256
+
257
+ | Old local function | Replacement |
258
+ | --- | --- |
259
+ | `confirmationAlert(onConfirm, text, icon)` (main-app 3-arg) | `confirmationAlert(onConfirm, { text, icon, confirmButtonText: "OK" })` |
260
+ | `confirmationAlert(onConfirm, { ... })` (vault/pilot/pbf) | Same options; change import to `@eztrak/shared` |
261
+ | `showDeleteConfirmation(onConfirm)` | `confirmationAlert(onConfirm, { text: "...", confirmButtonText: "OK", showLoaderOnConfirm: true })` |
262
+ | `showDeletePrompt(onConfirm, projectName)` | `confirmationAlert(onConfirm, { title: "Delete Project", html: "...", inputExpectedValue: projectName, showLoaderOnConfirm: true, confirmButtonText: "Delete" })` |
263
+ | `showLogoutConfirmation(onConfirm)` | `confirmationAlert(onConfirm, { title: "Logout Confirmation", text: "...", icon: "question", reverseButtons: true, focusCancel: true, cancelButtonColor: "#6b7280" })` |
264
+ | `confirmationAlertWithInput(onConfirm, "DELETE", text)` | `confirmationAlert(onConfirm, { text, inputExpectedValue: "DELETE", confirmButtonText: "Confirm" })` |
265
+
266
+ **Not replaced by this helper:** `showDeleteConfirmationAlert` (red button + inline “Deleting…” spinner on the button) — keep that component or pass equivalent colors + `showLoaderOnConfirm` if the default center loader is acceptable.
267
+
268
+ **Direct `Swal.fire({ ... })` calls** in budget/RFQ/forecast flows are separate one-off UIs; migrate only when those screens are touched.
269
+
270
+ #### TypeScript
271
+
272
+ ```ts
273
+ import {
274
+ confirmationAlert,
275
+ type ConfirmationAlertOptions,
276
+ } from "@eztrak/shared";
277
+ ```
278
+
99
279
  ### Hooks
100
280
 
101
281
  ```js
@@ -401,6 +581,43 @@ import { ToolTip } from "@eztrak/shared/components";
401
581
  | `showTooltip` | `boolean` | `true` | Disable tooltip while still rendering truncated text |
402
582
  | `tooltipWhenTruncatedOnly` | `boolean` | `false` | Only show tooltip when truncation occurs |
403
583
 
584
+ ### Components — CardSkeleton
585
+
586
+ Card-shaped loading placeholder for project cards, folders, user cards, and similar content. The default output matches the existing app `CardSkeleton` layout: avatar placeholder plus two text bars.
587
+
588
+ ```tsx
589
+ import { CardSkeleton } from "@eztrak/shared/components";
590
+
591
+ // Existing app behavior
592
+ <CardSkeleton />
593
+
594
+ // Render multiple placeholders
595
+ <CardSkeleton count={4} className="rounded-xl bg-white shadow-sm" />
596
+
597
+ // Image/card content variant
598
+ <CardSkeleton
599
+ variant="image"
600
+ lines={3}
601
+ lineWidths={["w-48", "w-64", "w-40"]}
602
+ />
603
+ ```
604
+
605
+ | Prop | Type | Default | Description |
606
+ | --- | --- | --- | --- |
607
+ | `variant` | `"profile" \| "image" \| "content"` | `"profile"` | Preset layout. `profile` shows avatar, `image` shows image block, `content` shows only text lines |
608
+ | `count` | `number` | `1` | Number of skeleton cards to render |
609
+ | `lines` | `number` | `2` | Number of text placeholder lines |
610
+ | `lineWidths` | `string[]` | `["w-32", "w-48"]` | Tailwind width classes for each line |
611
+ | `showAvatar` | `boolean` | variant-based | Override avatar visibility |
612
+ | `showImage` | `boolean` | variant-based | Override image placeholder visibility |
613
+ | `animationDuration` | `CSSProperties["animationDuration"]` | `"0.7s"` | Pulse animation duration |
614
+ | `className` | `string` | — | Extra classes for each skeleton card |
615
+ | `avatarClassName` | `string` | — | Extra classes for the avatar placeholder |
616
+ | `imageClassName` | `string` | — | Extra classes for the image placeholder |
617
+ | `contentClassName` | `string` | — | Extra classes for the content row |
618
+ | `lineClassName` | `string` | — | Extra classes for each text line |
619
+ | `srLabel` | `string` | `"Loading..."` | Screen-reader loading label |
620
+
404
621
  ### Components — Loader
405
622
 
406
623
  Spinner for full-page or inline loading states. Requires `loader.css`:
@@ -547,6 +764,8 @@ Pass classes per slot — they merge with the default `eztrak-tabs-*` styles via
547
764
  ```js
548
765
  import {
549
766
  cn,
767
+ confirmationAlert,
768
+ handleApiError,
550
769
  useGridHeight,
551
770
  EztrakTabs,
552
771
  CustomPagination,
@@ -576,9 +795,9 @@ Opens on [http://localhost:6006](http://localhost:6006) with stories for `Eztrak
576
795
  | Subpath | Description |
577
796
  | --- | --- |
578
797
  | `@eztrak/shared` | Main entry — re-exports utils, hooks, and components |
579
- | `@eztrak/shared/utils` | `cn`, `handleApiError`, `confirmationAlert`, date/format helpers, API error helpers, form field helpers |
798
+ | `@eztrak/shared/utils` | `cn`, `handleApiError`, `confirmationAlert`, `ConfirmationAlertOptions`, `getItem`, `setItem`, `loadUserState`, `saveUserState`, date/format helpers, API error helpers, form field helpers |
580
799
  | `@eztrak/shared/hooks` | `usePaginationUrlSync`, `useGridHeight` |
581
- | `@eztrak/shared/components` | `EztrakTabs`, `CustomPagination`, `CustomCellEditor`, `SearchInput`, `ResetFiltersButton`, `TooltipText`, `ToolTip`, `Modal`, `Loader`, `TableLayoutToolbarControls`, `ResetColumnsButton`, and related types |
800
+ | `@eztrak/shared/components` | `EztrakTabs`, `CustomPagination`, `CustomCellEditor`, `SearchInput`, `ResetFiltersButton`, `TooltipText`, `ToolTip`, `CardSkeleton`, `Modal`, `Loader`, `TableLayoutToolbarControls`, `ResetColumnsButton`, and related types |
582
801
  | `@eztrak/shared/components/tabs.css` | Default tab styles (CSS variables) |
583
802
  | `@eztrak/shared/components/loader.css` | Loader spinner styles |
584
803
 
@@ -0,0 +1,56 @@
1
+ // src/utils/localStorage.ts
2
+ var USER_STATE_STORAGE_KEY = "authState";
3
+ function getStorage() {
4
+ if (typeof window === "undefined") return null;
5
+ return window.localStorage;
6
+ }
7
+ function getItem(key, fallback = null) {
8
+ try {
9
+ const raw = getStorage()?.getItem(key);
10
+ if (raw === null || raw === void 0) {
11
+ return fallback;
12
+ }
13
+ return JSON.parse(raw);
14
+ } catch {
15
+ return fallback;
16
+ }
17
+ }
18
+ function setItem(key, value) {
19
+ try {
20
+ getStorage()?.setItem(key, JSON.stringify(value));
21
+ } catch {
22
+ }
23
+ }
24
+ function removeItem(key) {
25
+ try {
26
+ getStorage()?.removeItem(key);
27
+ } catch {
28
+ }
29
+ }
30
+ function loadUserState(key = USER_STATE_STORAGE_KEY) {
31
+ const value = getItem(key, void 0);
32
+ return value === null ? void 0 : value;
33
+ }
34
+ function saveUserState(state, key = USER_STATE_STORAGE_KEY) {
35
+ setItem(key, state);
36
+ }
37
+ var get = getItem;
38
+ var set = setItem;
39
+ var getItemFromLocalStorage = getItem;
40
+ var storeItemInLocalStorage = setItem;
41
+ var saveItemLocalStorage = setItem;
42
+
43
+ export {
44
+ USER_STATE_STORAGE_KEY,
45
+ getItem,
46
+ setItem,
47
+ removeItem,
48
+ loadUserState,
49
+ saveUserState,
50
+ get,
51
+ set,
52
+ getItemFromLocalStorage,
53
+ storeItemInLocalStorage,
54
+ saveItemLocalStorage
55
+ };
56
+ //# sourceMappingURL=chunk-7PPCJV2M.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/localStorage.ts"],"sourcesContent":["export const USER_STATE_STORAGE_KEY = \"authState\";\r\n\r\nfunction getStorage(): Storage | null {\r\n if (typeof window === \"undefined\") return null;\r\n return window.localStorage;\r\n}\r\n\r\nexport function getItem<T>(key: string, fallback: T | null = null): T | null {\r\n try {\r\n const raw = getStorage()?.getItem(key);\r\n if (raw === null || raw === undefined) {\r\n return fallback;\r\n }\r\n return JSON.parse(raw) as T;\r\n } catch {\r\n return fallback;\r\n }\r\n}\r\n\r\nexport function setItem<T>(key: string, value: T): void {\r\n try {\r\n getStorage()?.setItem(key, JSON.stringify(value));\r\n } catch {\r\n // Ignore write errors (quota exceeded, private mode, etc.)\r\n }\r\n}\r\n\r\nexport function removeItem(key: string): void {\r\n try {\r\n getStorage()?.removeItem(key);\r\n } catch {\r\n // Ignore remove errors\r\n }\r\n}\r\n\r\nexport function loadUserState<T = unknown>(\r\n key: string = USER_STATE_STORAGE_KEY\r\n): T | undefined {\r\n const value = getItem<T | undefined>(key, undefined);\r\n return value === null ? undefined : value;\r\n}\r\n\r\nexport function saveUserState<T = unknown>(\r\n state: T,\r\n key: string = USER_STATE_STORAGE_KEY\r\n): void {\r\n setItem(key, state);\r\n}\r\n\r\n/** Task-aligned aliases */\r\nexport const get = getItem;\r\nexport const set = setItem;\r\n\r\n/** Backward-compatible aliases used in federation apps */\r\nexport const getItemFromLocalStorage = getItem;\r\nexport const storeItemInLocalStorage = setItem;\r\nexport const saveItemLocalStorage = setItem;\r\n"],"mappings":";AAAO,IAAM,yBAAyB;AAEtC,SAAS,aAA6B;AACpC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO;AAChB;AAEO,SAAS,QAAW,KAAa,WAAqB,MAAgB;AAC3E,MAAI;AACF,UAAM,MAAM,WAAW,GAAG,QAAQ,GAAG;AACrC,QAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,aAAO;AAAA,IACT;AACA,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,QAAW,KAAa,OAAgB;AACtD,MAAI;AACF,eAAW,GAAG,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EAClD,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,WAAW,KAAmB;AAC5C,MAAI;AACF,eAAW,GAAG,WAAW,GAAG;AAAA,EAC9B,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,cACd,MAAc,wBACC;AACf,QAAM,QAAQ,QAAuB,KAAK,MAAS;AACnD,SAAO,UAAU,OAAO,SAAY;AACtC;AAEO,SAAS,cACd,OACA,MAAc,wBACR;AACN,UAAQ,KAAK,KAAK;AACpB;AAGO,IAAM,MAAM;AACZ,IAAM,MAAM;AAGZ,IAAM,0BAA0B;AAChC,IAAM,0BAA0B;AAChC,IAAM,uBAAuB;","names":[]}
@@ -255,18 +255,92 @@ function handleApiError(error, { showAlert = false, fallbackMessage } = {}) {
255
255
  const message = fallbackMessage || data?.message || data?.title || error.error || "An error occurred. Please try again.";
256
256
  toast.error(message);
257
257
  }
258
+ var BRAND_ORANGE = "#FF7335";
259
+ var CANCEL_RED = "#ff0000";
260
+ function getConfirmErrorMessage(error) {
261
+ if (error && typeof error === "object") {
262
+ const err = error;
263
+ return err.data?.message || err.data?.title || err.message || "Something went wrong. Please try again.";
264
+ }
265
+ return "Something went wrong. Please try again.";
266
+ }
258
267
  function confirmationAlert(onConfirm, options = {}) {
259
- void Swal.fire({
260
- title: options.title ?? "Are you sure?",
261
- text: options.text,
262
- icon: options.icon ?? "warning",
268
+ const {
269
+ title = options.inputExpectedValue ? "Confirmation Required" : "Are you sure?",
270
+ text,
271
+ html,
272
+ icon = "warning",
273
+ confirmButtonText = "Yes",
274
+ cancelButtonText = "Cancel",
275
+ confirmButtonColor = BRAND_ORANGE,
276
+ cancelButtonColor = CANCEL_RED,
277
+ showLoaderOnConfirm = false,
278
+ inputExpectedValue,
279
+ inputPlaceholder = "Type confirmation text to proceed",
280
+ reverseButtons,
281
+ focusCancel,
282
+ zIndex
283
+ } = options;
284
+ const hasInput = Boolean(inputExpectedValue);
285
+ const useLoader = showLoaderOnConfirm || hasInput;
286
+ const inputHtml = html ?? (hasInput ? `
287
+ ${text ? `<p style="margin-bottom: 15px;">${text}</p>` : ""}
288
+ <p style="margin-bottom: 10px; font-weight: bold; color: #666;">
289
+ Please type "<span style="color: ${BRAND_ORANGE}; font-weight: bold;">${inputExpectedValue}</span>" to confirm:
290
+ </p>
291
+ ` : void 0);
292
+ const swalOptions = {
293
+ title,
294
+ icon,
263
295
  showCancelButton: true,
264
- confirmButtonText: options.confirmButtonText ?? "Yes",
265
- cancelButtonText: options.cancelButtonText ?? "Cancel",
266
- confirmButtonColor: "#FF7335"
267
- }).then((result) => {
268
- if (result.isConfirmed) {
269
- onConfirm();
296
+ confirmButtonText,
297
+ cancelButtonText,
298
+ confirmButtonColor,
299
+ cancelButtonColor,
300
+ reverseButtons,
301
+ focusCancel,
302
+ allowOutsideClick: () => !Swal.isLoading(),
303
+ allowEscapeKey: () => !Swal.isLoading(),
304
+ didOpen: () => {
305
+ if (zIndex == null) return;
306
+ const container = Swal.getContainer();
307
+ if (container) container.style.zIndex = String(zIndex);
308
+ }
309
+ };
310
+ if (hasInput) {
311
+ swalOptions.html = inputHtml;
312
+ swalOptions.input = "text";
313
+ swalOptions.inputPlaceholder = inputPlaceholder;
314
+ swalOptions.inputValidator = (value) => {
315
+ if (!value?.trim()) return "You need to enter the confirmation text!";
316
+ if (value.trim() !== inputExpectedValue.trim()) {
317
+ return `Please type "${inputExpectedValue}" exactly as shown!`;
318
+ }
319
+ return null;
320
+ };
321
+ } else if (html) {
322
+ swalOptions.html = html;
323
+ } else {
324
+ swalOptions.text = text;
325
+ }
326
+ if (useLoader) {
327
+ swalOptions.showLoaderOnConfirm = true;
328
+ swalOptions.preConfirm = (inputValue) => {
329
+ if (hasInput && inputValue?.trim() !== inputExpectedValue.trim()) {
330
+ Swal.showValidationMessage(
331
+ `Please type "${inputExpectedValue}" exactly as shown!`
332
+ );
333
+ return false;
334
+ }
335
+ return Promise.resolve().then(() => onConfirm()).catch((error) => {
336
+ Swal.showValidationMessage(getConfirmErrorMessage(error));
337
+ throw error;
338
+ });
339
+ };
340
+ }
341
+ void Swal.fire(swalOptions).then((result) => {
342
+ if (result.isConfirmed && !useLoader) {
343
+ void Promise.resolve(onConfirm());
270
344
  }
271
345
  });
272
346
  }
@@ -299,4 +373,4 @@ export {
299
373
  handleApiError,
300
374
  confirmationAlert
301
375
  };
302
- //# sourceMappingURL=chunk-GVIHLI67.mjs.map
376
+ //# sourceMappingURL=chunk-BMX2RODZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/cn.ts","../src/utils/helpers.ts","../src/utils/handleApiError.ts"],"sourcesContent":["import clsx from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: Parameters<typeof clsx>) {\r\n return twMerge(clsx(inputs));\r\n}\r\n","export const truncateText = (\r\n text: string | number | null | undefined,\r\n maxLength = 20\r\n): string => {\r\n if (text === null || text === undefined) return \"\";\r\n const value = String(text);\r\n if (value.length <= maxLength) {\r\n return value;\r\n }\r\n return value.slice(0, maxLength - 3) + \"...\";\r\n};\r\n\r\nexport const truncateFileName = (\r\n fileName: string | null | undefined,\r\n maxLength = 20\r\n): string => {\r\n if (!fileName) return \"\";\r\n if (fileName.length <= maxLength) {\r\n return fileName;\r\n }\r\n const extension = fileName.slice(fileName.lastIndexOf(\".\"));\r\n const truncatedName =\r\n fileName.slice(0, maxLength - extension.length - 3) + \"...\";\r\n return truncatedName + extension;\r\n};\r\n\r\nexport function getApiError(apiError: unknown): string {\r\n if (!apiError) return \"An unknown error occurred\";\r\n\r\n if (typeof apiError === \"string\") {\r\n return apiError;\r\n }\r\n\r\n const error = apiError as {\r\n response?: { data?: Record<string, unknown> };\r\n message?: string;\r\n };\r\n\r\n if (error.response?.data) {\r\n const responseData = error.response.data;\r\n\r\n if (typeof responseData === \"string\") {\r\n return responseData;\r\n }\r\n\r\n const errors = responseData.errors;\r\n if (errors && typeof errors === \"object\") {\r\n for (const key in errors) {\r\n const messages = (errors as Record<string, unknown>)[key];\r\n if (Array.isArray(messages) && messages.length > 0) {\r\n return String(messages[0]);\r\n }\r\n }\r\n }\r\n\r\n if (responseData.message) {\r\n return String(responseData.message);\r\n }\r\n\r\n if (responseData.title) {\r\n return String(responseData.title);\r\n }\r\n }\r\n\r\n if (error.message) {\r\n return error.message;\r\n }\r\n\r\n return \"An unknown error occurred\";\r\n}\r\n\r\nexport const normalizeApiListItems = (responseData: unknown): unknown[] => {\r\n if (responseData == null) return [];\r\n if (Array.isArray(responseData)) return responseData;\r\n\r\n const record = responseData as Record<string, unknown>;\r\n const data = record.data;\r\n\r\n if (\r\n data &&\r\n typeof data === \"object\" &&\r\n !Array.isArray(data) &&\r\n Array.isArray((data as { items?: unknown[] }).items)\r\n ) {\r\n return (data as { items: unknown[] }).items;\r\n }\r\n\r\n const returnModel = record.returnModel as { items?: unknown[] } | undefined;\r\n if (Array.isArray(returnModel?.items)) return returnModel.items;\r\n if (Array.isArray(data)) return data;\r\n if (typeof data === \"object\" && data !== null) {\r\n return [data];\r\n }\r\n return [];\r\n};\r\n\r\nexport function capitalizeText(name: string | null | undefined): string {\r\n if (!name) return \"\";\r\n return name\r\n .split(\" \")\r\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\r\n .join(\" \");\r\n}\r\n\r\nexport function toLowerCaseText(text: string | null | undefined): string {\r\n if (!text) return \"\";\r\n return text.toLowerCase();\r\n}\r\n\r\nexport const formatDate = (dateString: string | null | undefined): string => {\r\n if (dateString === null || dateString === undefined) return \"-\";\r\n const date = new Date(dateString);\r\n\r\n if (isNaN(date.getTime())) {\r\n return dateString;\r\n }\r\n\r\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\r\n const day = date.getDate().toString().padStart(2, \"0\");\r\n const year = date.getFullYear().toString().slice(-2);\r\n return `${month}/${day}/${year}`;\r\n};\r\n\r\nexport const isEmpty = (obj: object): boolean => {\r\n return Object.keys(obj).length === 0;\r\n};\r\n\r\nexport const toDateInputValue = (date: string | Date | null | undefined): string => {\r\n if (!date) return \"\";\r\n\r\n let d: Date;\r\n\r\n if (typeof date === \"string\") {\r\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(date)) {\r\n return date;\r\n }\r\n d = new Date(date);\r\n } else {\r\n d = new Date(date);\r\n }\r\n\r\n if (isNaN(d.getTime())) return \"\";\r\n\r\n const localDate = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\r\n const year = localDate.getFullYear();\r\n const month = (localDate.getMonth() + 1).toString().padStart(2, \"0\");\r\n const day = localDate.getDate().toString().padStart(2, \"0\");\r\n\r\n return `${year}-${month}-${day}`;\r\n};\r\n\r\nexport function convertCommaDataToArray(data: string | null | undefined): string[] {\r\n if (!data || data.trim() === \"\") {\r\n return [];\r\n }\r\n return data.split(\",\").map((item) => item.trim());\r\n}\r\n\r\nexport function checkIfExistsInArray<T>(array: T[], searchValue: T): boolean {\r\n return array.includes(searchValue);\r\n}\r\n\r\nexport const currencyFormatter = (value: number | null | undefined): string => {\r\n return value?.toLocaleString(\"en-US\", {\r\n style: \"currency\",\r\n currency: \"USD\",\r\n }) ?? \"\";\r\n};\r\nexport const formatNumber = (number: number | string | null | undefined): string => {\r\n if (isNaN(Number(number)) || number === null || number === undefined) {\r\n return \"0\";\r\n }\r\n\r\n const formattedNumber = Number(number)\r\n .toString()\r\n .replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\r\n\r\n return formattedNumber;\r\n};\r\n\r\nexport const parseCurrencyValueToNumber = (value: unknown): number => {\r\n if (value == null || value === \"\") return 0;\r\n\r\n const raw =\r\n typeof value === \"object\" && value !== null && \"value\" in value\r\n ? (value as { value: unknown }).value\r\n : value;\r\n\r\n const stringValue = raw == null || raw === \"\" ? \"\" : String(raw);\r\n const numericValue = stringValue.replace(/[$,]/g, \"\");\r\n return parseFloat(numericValue) || 0;\r\n};\r\n\r\nexport function flattenTableData<T extends Record<string, unknown>>(\r\n data: T[] | null | undefined,\r\n columns: { field?: string }[] | null | undefined\r\n): T[] {\r\n if (!Array.isArray(data)) return [];\r\n return data.map((row) => {\r\n const flatRow = { ...row };\r\n (columns ?? []).forEach((col) => {\r\n const key = col.field;\r\n if (!key) return;\r\n if (key.includes(\".\")) {\r\n const value = key.split(\".\").reduce<unknown>(\r\n (acc, part) => (acc as Record<string, unknown> | undefined)?.[part],\r\n row\r\n );\r\n (flatRow as Record<string, unknown>)[key] = value ?? \"\";\r\n }\r\n });\r\n return flatRow;\r\n });\r\n}\r\n\r\nexport const getErrorMessages = (error: {\r\n data?: {\r\n errors?: Record<string, string[]>;\r\n title?: string;\r\n error?: { name?: string[] };\r\n };\r\n} | null | undefined): string => {\r\n if (error?.data?.errors) {\r\n return Object.values(error.data.errors).flat().join(\"\\n\");\r\n }\r\n return (\r\n error?.data?.title ??\r\n error?.data?.error?.name?.[0] ??\r\n \"Failed to save\"\r\n );\r\n};\r\n\r\nexport const getFieldsByCategory = <T extends { category?: string }>(\r\n fields: T[] = [],\r\n key: string\r\n): T[] => {\r\n return fields?.filter(({ category = \"\" }) => category === key);\r\n};\r\n\r\nexport const getFieldsByName = <T extends { name?: string }>(\r\n fields: T[] = [],\r\n key: string\r\n): T[] => {\r\n return fields.filter(({ name = \"\" }) => name === key);\r\n};\r\n\r\nexport const deserializeJson = <T = unknown>(jsonString: string): T | null => {\r\n try {\r\n return JSON.parse(jsonString) as T;\r\n } catch (error) {\r\n console.error(\"Error parsing JSON:\", error);\r\n return null;\r\n }\r\n};\r\n\r\nexport const formatDateTime = (dateString: string | null | undefined): string => {\r\n if (!dateString) return \"Invalid Date\";\r\n const date = new Date(dateString);\r\n if (isNaN(date.getTime())) return \"Invalid Date\";\r\n const options: Intl.DateTimeFormatOptions = {\r\n year: \"numeric\",\r\n month: \"numeric\",\r\n day: \"numeric\",\r\n hour: \"numeric\",\r\n minute: \"numeric\",\r\n hour12: true,\r\n };\r\n return date.toLocaleString(\"en-US\", options);\r\n};\r\n\r\nexport const removeByPropertyValues = <T extends Record<string, unknown>>(\r\n items: T[] = [],\r\n property: keyof T & string,\r\n values: unknown[] = [],\r\n { caseInsensitive = false }: { caseInsensitive?: boolean } = {}\r\n): T[] => {\r\n if (!Array.isArray(items) || !property) return Array.isArray(items) ? items : [];\r\n const normalize = (v: unknown) =>\r\n caseInsensitive && typeof v === \"string\" ? v.toLowerCase() : v;\r\n const valuesArr = Array.isArray(values) ? values : [values];\r\n const toRemove = new Set(valuesArr.map(normalize));\r\n return items.filter((item) => !toRemove.has(normalize(item?.[property])));\r\n};\r\n\r\nexport const getFieldsByInputType = <T extends { inputType?: string }>(\r\n fields: T[] = [],\r\n key: string\r\n): T[] => {\r\n return fields.filter(({ inputType = \"\" }) => inputType === key);\r\n};\r\n","import toast from \"react-hot-toast\";\r\nimport Swal, { type SweetAlertOptions, type SweetAlertResult } from \"sweetalert2\";\r\n\r\nexport type RtkQueryError = {\r\n status?: number | string;\r\n originalStatus?: number;\r\n data?: {\r\n errors?: Record<string, string[]>;\r\n message?: string;\r\n title?: string;\r\n };\r\n error?: string;\r\n};\r\n\r\nexport interface HandleApiErrorOptions {\r\n showAlert?: boolean;\r\n fallbackMessage?: string;\r\n}\r\n\r\nexport function formatValidationErrorsHtml(\r\n errors: Record<string, string[]>\r\n): string {\r\n const items = Object.keys(errors).flatMap((field) =>\r\n (errors[field] || []).map((msg) => `<li>${msg}</li>`)\r\n );\r\n return `\r\n <div style=\"text-align:left\">\r\n <p>Please review and correct the following:</p>\r\n <ul style=\"margin:8px 0 0 16px; list-style:disc;\">\r\n ${items.join(\"\")}\r\n </ul>\r\n </div>\r\n `;\r\n}\r\n\r\nfunction showValidationAlert(html: string): void {\r\n void Swal.fire({\r\n title: \"Validation errors\",\r\n html,\r\n icon: \"error\",\r\n confirmButtonText: \"OK\",\r\n showCancelButton: false,\r\n confirmButtonColor: \"#FF7335\",\r\n });\r\n}\r\n\r\n/** Handles RTK Query errors with built-in toast.error or SweetAlert (when showAlert is true). */\r\nexport function handleApiError(\r\n error: RtkQueryError | null | undefined,\r\n { showAlert = false, fallbackMessage }: HandleApiErrorOptions = {}\r\n): void {\r\n if (!error) return;\r\n\r\n const status = error.status ?? error.originalStatus;\r\n const data = error.data;\r\n\r\n if (status === 200) {\r\n return;\r\n }\r\n\r\n if (data?.errors && typeof data.errors === \"object\") {\r\n const errors = data.errors;\r\n\r\n if (showAlert) {\r\n showValidationAlert(formatValidationErrorsHtml(errors));\r\n } else {\r\n Object.keys(errors).forEach((field) => {\r\n (errors[field] || []).forEach((errorMessage) => {\r\n toast.error(errorMessage);\r\n });\r\n });\r\n }\r\n return;\r\n }\r\n\r\n if (status === 404) {\r\n return;\r\n }\r\n\r\n const message =\r\n fallbackMessage ||\r\n data?.message ||\r\n data?.title ||\r\n error.error ||\r\n \"An error occurred. Please try again.\";\r\n\r\n toast.error(message);\r\n}\r\n\r\nconst BRAND_ORANGE = \"#FF7335\";\r\nconst CANCEL_RED = \"#ff0000\";\r\n\r\nexport interface ConfirmationAlertOptions {\r\n title?: string;\r\n text?: string;\r\n html?: string;\r\n icon?: \"warning\" | \"info\" | \"question\" | \"error\" | \"success\";\r\n confirmButtonText?: string;\r\n cancelButtonText?: string;\r\n confirmButtonColor?: string;\r\n cancelButtonColor?: string;\r\n showLoaderOnConfirm?: boolean;\r\n /** Type exactly — trimmed, case-sensitive. */\r\n inputExpectedValue?: string;\r\n inputPlaceholder?: string;\r\n reverseButtons?: boolean;\r\n focusCancel?: boolean;\r\n zIndex?: number;\r\n}\r\n\r\nfunction getConfirmErrorMessage(error: unknown): string {\r\n if (error && typeof error === \"object\") {\r\n const err = error as {\r\n message?: string;\r\n data?: { message?: string; title?: string };\r\n };\r\n return (\r\n err.data?.message ||\r\n err.data?.title ||\r\n err.message ||\r\n \"Something went wrong. Please try again.\"\r\n );\r\n }\r\n return \"Something went wrong. Please try again.\";\r\n}\r\n\r\nexport function confirmationAlert(\r\n onConfirm: () => void | Promise<void>,\r\n options: ConfirmationAlertOptions = {}\r\n): void {\r\n const {\r\n title = options.inputExpectedValue ? \"Confirmation Required\" : \"Are you sure?\",\r\n text,\r\n html,\r\n icon = \"warning\",\r\n confirmButtonText = \"Yes\",\r\n cancelButtonText = \"Cancel\",\r\n confirmButtonColor = BRAND_ORANGE,\r\n cancelButtonColor = CANCEL_RED,\r\n showLoaderOnConfirm = false,\r\n inputExpectedValue,\r\n inputPlaceholder = \"Type confirmation text to proceed\",\r\n reverseButtons,\r\n focusCancel,\r\n zIndex,\r\n } = options;\r\n\r\n const hasInput = Boolean(inputExpectedValue);\r\n const useLoader = showLoaderOnConfirm || hasInput;\r\n\r\n const inputHtml =\r\n html ??\r\n (hasInput\r\n ? `\r\n ${text ? `<p style=\"margin-bottom: 15px;\">${text}</p>` : \"\"}\r\n <p style=\"margin-bottom: 10px; font-weight: bold; color: #666;\">\r\n Please type \"<span style=\"color: ${BRAND_ORANGE}; font-weight: bold;\">${inputExpectedValue}</span>\" to confirm:\r\n </p>\r\n `\r\n : undefined);\r\n\r\n const swalOptions: SweetAlertOptions = {\r\n title,\r\n icon,\r\n showCancelButton: true,\r\n confirmButtonText,\r\n cancelButtonText,\r\n confirmButtonColor,\r\n cancelButtonColor,\r\n reverseButtons,\r\n focusCancel,\r\n allowOutsideClick: () => !Swal.isLoading(),\r\n allowEscapeKey: () => !Swal.isLoading(),\r\n didOpen: () => {\r\n if (zIndex == null) return;\r\n const container = Swal.getContainer();\r\n if (container) container.style.zIndex = String(zIndex);\r\n },\r\n };\r\n\r\n if (hasInput) {\r\n swalOptions.html = inputHtml;\r\n swalOptions.input = \"text\";\r\n swalOptions.inputPlaceholder = inputPlaceholder;\r\n swalOptions.inputValidator = (value: string) => {\r\n if (!value?.trim()) return \"You need to enter the confirmation text!\";\r\n if (value.trim() !== inputExpectedValue!.trim()) {\r\n return `Please type \"${inputExpectedValue}\" exactly as shown!`;\r\n }\r\n return null;\r\n };\r\n } else if (html) {\r\n swalOptions.html = html;\r\n } else {\r\n swalOptions.text = text;\r\n }\r\n\r\n if (useLoader) {\r\n swalOptions.showLoaderOnConfirm = true;\r\n swalOptions.preConfirm = (inputValue: string) => {\r\n if (hasInput && inputValue?.trim() !== inputExpectedValue!.trim()) {\r\n Swal.showValidationMessage(\r\n `Please type \"${inputExpectedValue}\" exactly as shown!`\r\n );\r\n return false;\r\n }\r\n return Promise.resolve()\r\n .then(() => onConfirm())\r\n .catch((error) => {\r\n Swal.showValidationMessage(getConfirmErrorMessage(error));\r\n throw error;\r\n });\r\n };\r\n }\r\n\r\n void Swal.fire(swalOptions).then((result: SweetAlertResult) => {\r\n if (result.isConfirmed && !useLoader) {\r\n void Promise.resolve(onConfirm());\r\n }\r\n });\r\n}\r\n"],"mappings":"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","names":[]}