@eztrak/shared 0.4.1 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ // src/utils/cn.ts
2
+ import clsx from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ // src/utils/handleApiError.ts
9
+ import toast from "react-hot-toast";
10
+ import Swal from "sweetalert2";
11
+ function formatValidationErrorsHtml(errors) {
12
+ const items = Object.keys(errors).flatMap(
13
+ (field) => (errors[field] || []).map((msg) => `<li>${msg}</li>`)
14
+ );
15
+ return `
16
+ <div style="text-align:left">
17
+ <p>Please review and correct the following:</p>
18
+ <ul style="margin:8px 0 0 16px; list-style:disc;">
19
+ ${items.join("")}
20
+ </ul>
21
+ </div>
22
+ `;
23
+ }
24
+ function showValidationAlert(html) {
25
+ void Swal.fire({
26
+ title: "Validation errors",
27
+ html,
28
+ icon: "error",
29
+ confirmButtonText: "OK",
30
+ showCancelButton: false,
31
+ confirmButtonColor: "#FF7335"
32
+ });
33
+ }
34
+ function handleApiError(error, { showAlert = false, fallbackMessage } = {}) {
35
+ if (!error) return;
36
+ const status = error.status ?? error.originalStatus;
37
+ const data = error.data;
38
+ if (status === 200) {
39
+ return;
40
+ }
41
+ if (data?.errors && typeof data.errors === "object") {
42
+ const errors = data.errors;
43
+ if (showAlert) {
44
+ showValidationAlert(formatValidationErrorsHtml(errors));
45
+ } else {
46
+ Object.keys(errors).forEach((field) => {
47
+ (errors[field] || []).forEach((errorMessage) => {
48
+ toast.error(errorMessage);
49
+ });
50
+ });
51
+ }
52
+ return;
53
+ }
54
+ if (status === 404) {
55
+ return;
56
+ }
57
+ const message = fallbackMessage || data?.message || data?.title || error.error || "An error occurred. Please try again.";
58
+ toast.error(message);
59
+ }
60
+ function confirmationAlert(onConfirm, options = {}) {
61
+ void Swal.fire({
62
+ title: options.title ?? "Are you sure?",
63
+ text: options.text,
64
+ icon: options.icon ?? "warning",
65
+ showCancelButton: true,
66
+ confirmButtonText: options.confirmButtonText ?? "Yes",
67
+ cancelButtonText: options.cancelButtonText ?? "Cancel",
68
+ confirmButtonColor: "#FF7335"
69
+ }).then((result) => {
70
+ if (result.isConfirmed) {
71
+ onConfirm();
72
+ }
73
+ });
74
+ }
75
+
76
+ export {
77
+ cn,
78
+ formatValidationErrorsHtml,
79
+ handleApiError,
80
+ confirmationAlert
81
+ };
82
+ //# sourceMappingURL=chunk-5OG22DIX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/cn.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","import toast from \"react-hot-toast\";\r\nimport Swal 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\nexport interface ConfirmationAlertOptions {\r\n title?: string;\r\n text?: string;\r\n icon?: \"warning\" | \"info\" | \"question\" | \"error\" | \"success\";\r\n confirmButtonText?: string;\r\n cancelButtonText?: string;\r\n}\r\n\r\nexport function confirmationAlert(\r\n onConfirm: () => void,\r\n options: ConfirmationAlertOptions = {}\r\n): void {\r\n void Swal.fire({\r\n title: options.title ?? \"Are you sure?\",\r\n text: options.text,\r\n icon: options.icon ?? \"warning\",\r\n showCancelButton: true,\r\n confirmButtonText: options.confirmButtonText ?? \"Yes\",\r\n cancelButtonText: options.cancelButtonText ?? \"Cancel\",\r\n confirmButtonColor: \"#FF7335\",\r\n }).then((result) => {\r\n if (result.isConfirmed) {\r\n onConfirm();\r\n }\r\n });\r\n}\r\n"],"mappings":";AAAA,OAAO,UAAU;AACjB,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAiC;AACrD,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;ACLA,OAAO,WAAW;AAClB,OAAO,UAAU;AAkBV,SAAS,2BACd,QACQ;AACR,QAAM,QAAQ,OAAO,KAAK,MAAM,EAAE;AAAA,IAAQ,CAAC,WACxC,OAAO,KAAK,KAAK,CAAC,GAAG,IAAI,CAAC,QAAQ,OAAO,GAAG,OAAO;AAAA,EACtD;AACA,SAAO;AAAA;AAAA;AAAA;AAAA,cAIK,MAAM,KAAK,EAAE,CAAC;AAAA;AAAA;AAAA;AAI5B;AAEA,SAAS,oBAAoB,MAAoB;AAC/C,OAAK,KAAK,KAAK;AAAA,IACb,OAAO;AAAA,IACP;AAAA,IACA,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,EACtB,CAAC;AACH;AAGO,SAAS,eACd,OACA,EAAE,YAAY,OAAO,gBAAgB,IAA2B,CAAC,GAC3D;AACN,MAAI,CAAC,MAAO;AAEZ,QAAM,SAAS,MAAM,UAAU,MAAM;AACrC,QAAM,OAAO,MAAM;AAEnB,MAAI,WAAW,KAAK;AAClB;AAAA,EACF;AAEA,MAAI,MAAM,UAAU,OAAO,KAAK,WAAW,UAAU;AACnD,UAAM,SAAS,KAAK;AAEpB,QAAI,WAAW;AACb,0BAAoB,2BAA2B,MAAM,CAAC;AAAA,IACxD,OAAO;AACL,aAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,UAAU;AACrC,SAAC,OAAO,KAAK,KAAK,CAAC,GAAG,QAAQ,CAAC,iBAAiB;AAC9C,gBAAM,MAAM,YAAY;AAAA,QAC1B,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,MAAI,WAAW,KAAK;AAClB;AAAA,EACF;AAEA,QAAM,UACJ,mBACA,MAAM,WACN,MAAM,SACN,MAAM,SACN;AAEF,QAAM,MAAM,OAAO;AACrB;AAUO,SAAS,kBACd,WACA,UAAoC,CAAC,GAC/B;AACN,OAAK,KAAK,KAAK;AAAA,IACb,OAAO,QAAQ,SAAS;AAAA,IACxB,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ,QAAQ;AAAA,IACtB,kBAAkB;AAAA,IAClB,mBAAmB,QAAQ,qBAAqB;AAAA,IAChD,kBAAkB,QAAQ,oBAAoB;AAAA,IAC9C,oBAAoB;AAAA,EACtB,CAAC,EAAE,KAAK,CAAC,WAAW;AAClB,QAAI,OAAO,aAAa;AACtB,gBAAU;AAAA,IACZ;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -179,59 +179,6 @@ var getFieldsByInputType = (fields = [], key) => {
179
179
  return fields.filter(({ inputType = "" }) => inputType === key);
180
180
  };
181
181
 
182
- // src/utils/handleApiError.ts
183
- import toast from "react-hot-toast";
184
- import Swal from "sweetalert2";
185
- function formatValidationErrorsHtml(errors) {
186
- const items = Object.keys(errors).flatMap(
187
- (field) => (errors[field] || []).map((msg) => `<li>${msg}</li>`)
188
- );
189
- return `
190
- <div style="text-align:left">
191
- <p>Please review and correct the following:</p>
192
- <ul style="margin:8px 0 0 16px; list-style:disc;">
193
- ${items.join("")}
194
- </ul>
195
- </div>
196
- `;
197
- }
198
- function showValidationAlert(html) {
199
- void Swal.fire({
200
- title: "Validation errors",
201
- html,
202
- icon: "error",
203
- confirmButtonText: "OK",
204
- showCancelButton: false,
205
- confirmButtonColor: "#FF7335"
206
- });
207
- }
208
- function handleApiError(error, { showAlert = false, fallbackMessage } = {}) {
209
- if (!error) return;
210
- const status = error.status ?? error.originalStatus;
211
- const data = error.data;
212
- if (status === 200) {
213
- return;
214
- }
215
- if (data?.errors && typeof data.errors === "object") {
216
- const errors = data.errors;
217
- if (showAlert) {
218
- showValidationAlert(formatValidationErrorsHtml(errors));
219
- } else {
220
- Object.keys(errors).forEach((field) => {
221
- (errors[field] || []).forEach((errorMessage) => {
222
- toast.error(errorMessage);
223
- });
224
- });
225
- }
226
- return;
227
- }
228
- if (status === 404) {
229
- return;
230
- }
231
- const message = fallbackMessage || data?.message || data?.title || error.error || "An error occurred. Please try again.";
232
- toast.error(message);
233
- }
234
-
235
182
  export {
236
183
  truncateText,
237
184
  getApiError,
@@ -252,8 +199,6 @@ export {
252
199
  deserializeJson,
253
200
  formatDateTime,
254
201
  removeByPropertyValues,
255
- getFieldsByInputType,
256
- formatValidationErrorsHtml,
257
- handleApiError
202
+ getFieldsByInputType
258
203
  };
259
- //# sourceMappingURL=chunk-AR7LUJY3.mjs.map
204
+ //# sourceMappingURL=chunk-BZPURU6Q.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/helpers.ts"],"sourcesContent":["export const truncateText = (text: string | null | undefined, maxLength = 20) => {\n if (!text) return \"\";\n if (text.length <= maxLength) {\n return text;\n }\n return text.slice(0, maxLength - 3) + \"...\";\n};\n\nexport function getApiError(apiError: unknown): string {\n if (!apiError) return \"An unknown error occurred\";\n\n if (typeof apiError === \"string\") {\n return apiError;\n }\n\n const error = apiError as {\n response?: { data?: Record<string, unknown> };\n message?: string;\n };\n\n if (error.response?.data) {\n const responseData = error.response.data;\n\n if (typeof responseData === \"string\") {\n return responseData;\n }\n\n const errors = responseData.errors;\n if (errors && typeof errors === \"object\") {\n for (const key in errors) {\n const messages = (errors as Record<string, unknown>)[key];\n if (Array.isArray(messages) && messages.length > 0) {\n return String(messages[0]);\n }\n }\n }\n\n if (responseData.message) {\n return String(responseData.message);\n }\n\n if (responseData.title) {\n return String(responseData.title);\n }\n }\n\n if (error.message) {\n return error.message;\n }\n\n return \"An unknown error occurred\";\n}\n\nexport const normalizeApiListItems = (responseData: unknown): unknown[] => {\n if (responseData == null) return [];\n if (Array.isArray(responseData)) return responseData;\n\n const record = responseData as Record<string, unknown>;\n const data = record.data;\n\n if (\n data &&\n typeof data === \"object\" &&\n !Array.isArray(data) &&\n Array.isArray((data as { items?: unknown[] }).items)\n ) {\n return (data as { items: unknown[] }).items;\n }\n\n const returnModel = record.returnModel as { items?: unknown[] } | undefined;\n if (Array.isArray(returnModel?.items)) return returnModel.items;\n if (Array.isArray(data)) return data;\n if (typeof data === \"object\" && data !== null) {\n return [data];\n }\n return [];\n};\n\nexport function capitalizeText(name: string | null | undefined): string {\n if (!name) return \"\";\n return name\n .split(\" \")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\" \");\n}\n\nexport function toLowerCaseText(text: string | null | undefined): string {\n if (!text) return \"\";\n return text.toLowerCase();\n}\n\nexport const formatDate = (dateString: string | null | undefined): string => {\n if (dateString === null || dateString === undefined) return \"-\";\n const date = new Date(dateString);\n\n if (isNaN(date.getTime())) {\n return dateString;\n }\n\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const day = date.getDate().toString().padStart(2, \"0\");\n const year = date.getFullYear().toString().slice(-2);\n return `${month}/${day}/${year}`;\n};\n\nexport const isEmpty = (obj: object): boolean => {\n return Object.keys(obj).length === 0;\n};\n\nexport const toDateInputValue = (date: string | Date | null | undefined): string => {\n if (!date) return \"\";\n\n let d: Date;\n\n if (typeof date === \"string\") {\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(date)) {\n return date;\n }\n d = new Date(date);\n } else {\n d = new Date(date);\n }\n\n if (isNaN(d.getTime())) return \"\";\n\n const localDate = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n const year = localDate.getFullYear();\n const month = (localDate.getMonth() + 1).toString().padStart(2, \"0\");\n const day = localDate.getDate().toString().padStart(2, \"0\");\n\n return `${year}-${month}-${day}`;\n};\n\nexport function convertCommaDataToArray(data: string | null | undefined): string[] {\n if (!data || data.trim() === \"\") {\n return [];\n }\n return data.split(\",\").map((item) => item.trim());\n}\n\nexport function checkIfExistsInArray<T>(array: T[], searchValue: T): boolean {\n return array.includes(searchValue);\n}\n\nexport const currencyFormatter = (value: number | null | undefined): string => {\n return value?.toLocaleString(\"en-US\", {\n style: \"currency\",\n currency: \"USD\",\n }) ?? \"\";\n};\n\nexport const parseCurrencyValueToNumber = (value: unknown): number => {\n if (value == null || value === \"\") return 0;\n\n const raw =\n typeof value === \"object\" && value !== null && \"value\" in value\n ? (value as { value: unknown }).value\n : value;\n\n const stringValue = raw == null || raw === \"\" ? \"\" : String(raw);\n const numericValue = stringValue.replace(/[$,]/g, \"\");\n return parseFloat(numericValue) || 0;\n};\n\nexport function flattenTableData<T extends Record<string, unknown>>(\n data: T[] | null | undefined,\n columns: { field?: string }[] | null | undefined\n): T[] {\n if (!Array.isArray(data)) return [];\n return data.map((row) => {\n const flatRow = { ...row };\n (columns ?? []).forEach((col) => {\n const key = col.field;\n if (!key) return;\n if (key.includes(\".\")) {\n const value = key.split(\".\").reduce<unknown>(\n (acc, part) => (acc as Record<string, unknown> | undefined)?.[part],\n row\n );\n (flatRow as Record<string, unknown>)[key] = value ?? \"\";\n }\n });\n return flatRow;\n });\n}\n\nexport const getErrorMessages = (error: {\n data?: {\n errors?: Record<string, string[]>;\n title?: string;\n error?: { name?: string[] };\n };\n} | null | undefined): string => {\n if (error?.data?.errors) {\n return Object.values(error.data.errors).flat().join(\"\\n\");\n }\n return (\n error?.data?.title ??\n error?.data?.error?.name?.[0] ??\n \"Failed to save\"\n );\n};\n\nexport const getFieldsByCategory = <T extends { category?: string }>(\n fields: T[] = [],\n key: string\n): T[] => {\n return fields?.filter(({ category = \"\" }) => category === key);\n};\n\nexport const getFieldsByName = <T extends { name?: string }>(\n fields: T[] = [],\n key: string\n): T[] => {\n return fields.filter(({ name = \"\" }) => name === key);\n};\n\nexport const deserializeJson = <T = unknown>(jsonString: string): T | null => {\n try {\n return JSON.parse(jsonString) as T;\n } catch (error) {\n console.error(\"Error parsing JSON:\", error);\n return null;\n }\n};\n\nexport const formatDateTime = (dateString: string | null | undefined): string => {\n if (!dateString) return \"Invalid Date\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) return \"Invalid Date\";\n const options: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"numeric\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n hour12: true,\n };\n return date.toLocaleString(\"en-US\", options);\n};\n\nexport const removeByPropertyValues = <T extends Record<string, unknown>>(\n items: T[] = [],\n property: keyof T & string,\n values: unknown[] = [],\n { caseInsensitive = false }: { caseInsensitive?: boolean } = {}\n): T[] => {\n if (!Array.isArray(items) || !property) return Array.isArray(items) ? items : [];\n const normalize = (v: unknown) =>\n caseInsensitive && typeof v === \"string\" ? v.toLowerCase() : v;\n const valuesArr = Array.isArray(values) ? values : [values];\n const toRemove = new Set(valuesArr.map(normalize));\n return items.filter((item) => !toRemove.has(normalize(item?.[property])));\n};\n\nexport const getFieldsByInputType = <T extends { inputType?: string }>(\n fields: T[] = [],\n key: string\n): T[] => {\n return fields.filter(({ inputType = \"\" }) => inputType === key);\n};\n"],"mappings":"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","names":[]}
@@ -39,4 +39,57 @@ type EztrakTabsProps = {
39
39
 
40
40
  declare function EztrakTabs({ tabs, activeTab, defaultTabId, onTabChange, showPanels, keepMounted, classNames, className, }: EztrakTabsProps): react.JSX.Element | null;
41
41
 
42
- export { type EztrakTab, EztrakTabs, type EztrakTabsClassNames, type EztrakTabsProps };
42
+ interface PaginationData {
43
+ currentPage: number;
44
+ pageCount: number;
45
+ perPage: number;
46
+ totalCount: number;
47
+ }
48
+ interface CustomPaginationClassNames {
49
+ root?: string;
50
+ info?: string;
51
+ nav?: string;
52
+ pageButton?: string;
53
+ activePageButton?: string;
54
+ select?: string;
55
+ }
56
+ interface LayoutStatus {
57
+ isLayoutLoading?: boolean;
58
+ isLayoutSaving?: boolean;
59
+ isLayoutResetting?: boolean;
60
+ }
61
+ interface ResetColumnsButtonProps {
62
+ onReset: () => void;
63
+ disabled?: boolean;
64
+ isLoading?: boolean;
65
+ }
66
+ interface TableLayoutToolbarControlsProps {
67
+ layoutStatus?: LayoutStatus;
68
+ onResetLayout?: () => void;
69
+ renderResetControl?: (onReset: () => void) => ReactNode;
70
+ isLoading?: boolean;
71
+ }
72
+ interface CustomPaginationProps {
73
+ paginationData: PaginationData | null;
74
+ /** @deprecated Use pageSizeOptions instead */
75
+ paginationPageSize?: number[];
76
+ pageSizeOptions?: number[];
77
+ isLoading?: boolean;
78
+ paramNames?: {
79
+ page?: string;
80
+ perPage?: string;
81
+ };
82
+ onPageChange?: (page: number) => void;
83
+ onPageSizeChange?: (perPage: number) => void;
84
+ onResetLayout?: () => void;
85
+ renderResetControl?: (onReset: () => void) => ReactNode;
86
+ layoutStatus?: LayoutStatus;
87
+ classNames?: CustomPaginationClassNames;
88
+ className?: string;
89
+ }
90
+
91
+ declare function ResetColumnsButton({ onReset, disabled, isLoading, }: ResetColumnsButtonProps): react.JSX.Element;
92
+ declare function TableLayoutToolbarControls({ layoutStatus, onResetLayout, renderResetControl, isLoading, }: TableLayoutToolbarControlsProps): react.JSX.Element;
93
+ declare function CustomPagination({ paginationData, paginationPageSize, pageSizeOptions: pageSizeOptionsProp, isLoading, paramNames, onPageChange, onPageSizeChange, onResetLayout, renderResetControl, layoutStatus, classNames, className, }: CustomPaginationProps): react.JSX.Element;
94
+
95
+ export { CustomPagination, type CustomPaginationClassNames, type CustomPaginationProps, type EztrakTab, EztrakTabs, type EztrakTabsClassNames, type EztrakTabsProps, type LayoutStatus, type PaginationData, ResetColumnsButton, type ResetColumnsButtonProps, TableLayoutToolbarControls, type TableLayoutToolbarControlsProps };
@@ -39,4 +39,57 @@ type EztrakTabsProps = {
39
39
 
40
40
  declare function EztrakTabs({ tabs, activeTab, defaultTabId, onTabChange, showPanels, keepMounted, classNames, className, }: EztrakTabsProps): react.JSX.Element | null;
41
41
 
42
- export { type EztrakTab, EztrakTabs, type EztrakTabsClassNames, type EztrakTabsProps };
42
+ interface PaginationData {
43
+ currentPage: number;
44
+ pageCount: number;
45
+ perPage: number;
46
+ totalCount: number;
47
+ }
48
+ interface CustomPaginationClassNames {
49
+ root?: string;
50
+ info?: string;
51
+ nav?: string;
52
+ pageButton?: string;
53
+ activePageButton?: string;
54
+ select?: string;
55
+ }
56
+ interface LayoutStatus {
57
+ isLayoutLoading?: boolean;
58
+ isLayoutSaving?: boolean;
59
+ isLayoutResetting?: boolean;
60
+ }
61
+ interface ResetColumnsButtonProps {
62
+ onReset: () => void;
63
+ disabled?: boolean;
64
+ isLoading?: boolean;
65
+ }
66
+ interface TableLayoutToolbarControlsProps {
67
+ layoutStatus?: LayoutStatus;
68
+ onResetLayout?: () => void;
69
+ renderResetControl?: (onReset: () => void) => ReactNode;
70
+ isLoading?: boolean;
71
+ }
72
+ interface CustomPaginationProps {
73
+ paginationData: PaginationData | null;
74
+ /** @deprecated Use pageSizeOptions instead */
75
+ paginationPageSize?: number[];
76
+ pageSizeOptions?: number[];
77
+ isLoading?: boolean;
78
+ paramNames?: {
79
+ page?: string;
80
+ perPage?: string;
81
+ };
82
+ onPageChange?: (page: number) => void;
83
+ onPageSizeChange?: (perPage: number) => void;
84
+ onResetLayout?: () => void;
85
+ renderResetControl?: (onReset: () => void) => ReactNode;
86
+ layoutStatus?: LayoutStatus;
87
+ classNames?: CustomPaginationClassNames;
88
+ className?: string;
89
+ }
90
+
91
+ declare function ResetColumnsButton({ onReset, disabled, isLoading, }: ResetColumnsButtonProps): react.JSX.Element;
92
+ declare function TableLayoutToolbarControls({ layoutStatus, onResetLayout, renderResetControl, isLoading, }: TableLayoutToolbarControlsProps): react.JSX.Element;
93
+ declare function CustomPagination({ paginationData, paginationPageSize, pageSizeOptions: pageSizeOptionsProp, isLoading, paramNames, onPageChange, onPageSizeChange, onResetLayout, renderResetControl, layoutStatus, classNames, className, }: CustomPaginationProps): react.JSX.Element;
94
+
95
+ export { CustomPagination, type CustomPaginationClassNames, type CustomPaginationProps, type EztrakTab, EztrakTabs, type EztrakTabsClassNames, type EztrakTabsProps, type LayoutStatus, type PaginationData, ResetColumnsButton, type ResetColumnsButtonProps, TableLayoutToolbarControls, type TableLayoutToolbarControlsProps };
@@ -30,7 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/components/index.ts
31
31
  var components_exports = {};
32
32
  __export(components_exports, {
33
- EztrakTabs: () => EztrakTabs
33
+ CustomPagination: () => CustomPagination,
34
+ EztrakTabs: () => EztrakTabs,
35
+ ResetColumnsButton: () => ResetColumnsButton,
36
+ TableLayoutToolbarControls: () => TableLayoutToolbarControls
34
37
  });
35
38
  module.exports = __toCommonJS(components_exports);
36
39
 
@@ -248,8 +251,348 @@ function EztrakTabs({
248
251
  }) }) : null
249
252
  ] }) });
250
253
  }
254
+
255
+ // src/components/pagination/CustomPagination.tsx
256
+ var import_react_router_dom = require("react-router-dom");
257
+
258
+ // src/utils/handleApiError.ts
259
+ var import_react_hot_toast = __toESM(require("react-hot-toast"));
260
+ var import_sweetalert2 = __toESM(require("sweetalert2"));
261
+ function confirmationAlert(onConfirm, options = {}) {
262
+ void import_sweetalert2.default.fire({
263
+ title: options.title ?? "Are you sure?",
264
+ text: options.text,
265
+ icon: options.icon ?? "warning",
266
+ showCancelButton: true,
267
+ confirmButtonText: options.confirmButtonText ?? "Yes",
268
+ cancelButtonText: options.cancelButtonText ?? "Cancel",
269
+ confirmButtonColor: "#FF7335"
270
+ }).then((result) => {
271
+ if (result.isConfirmed) {
272
+ onConfirm();
273
+ }
274
+ });
275
+ }
276
+
277
+ // src/components/pagination/CustomPagination.tsx
278
+ var import_jsx_runtime2 = require("react/jsx-runtime");
279
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 30, 50, 100];
280
+ function padCount(num) {
281
+ return num.toString().padStart(2, "0");
282
+ }
283
+ function RefreshIcon({
284
+ size = 14,
285
+ className
286
+ }) {
287
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
288
+ "svg",
289
+ {
290
+ width: size,
291
+ height: size,
292
+ viewBox: "0 0 24 24",
293
+ fill: "none",
294
+ stroke: "currentColor",
295
+ strokeWidth: "2",
296
+ strokeLinecap: "round",
297
+ strokeLinejoin: "round",
298
+ "aria-hidden": "true",
299
+ className,
300
+ children: [
301
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
302
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 3v5h-5" }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
304
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 16H3v5" })
305
+ ]
306
+ }
307
+ );
308
+ }
309
+ function ResetColumnsButton({
310
+ onReset,
311
+ disabled = false,
312
+ isLoading = false
313
+ }) {
314
+ const handleClick = () => {
315
+ confirmationAlert(onReset, {
316
+ text: "Reset column order, widths, and visibility to defaults?",
317
+ icon: "warning",
318
+ title: "Reset column layout to default"
319
+ });
320
+ };
321
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
322
+ "button",
323
+ {
324
+ type: "button",
325
+ onClick: handleClick,
326
+ disabled: disabled || isLoading,
327
+ title: "Reset column layout to default",
328
+ className: "inline-flex items-center gap-1.5 rounded-xl border border-primary-150 bg-white px-3 py-1.5 text-sm font-medium text-primary-150 hover:bg-gray-50 focus:outline-none disabled:hover:bg-white",
329
+ children: [
330
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RefreshIcon, { size: 14, className: cn(isLoading ? "animate-spin" : "") }),
331
+ "Reset columns"
332
+ ]
333
+ }
334
+ );
335
+ }
336
+ function TableLayoutToolbarControls({
337
+ layoutStatus = {},
338
+ onResetLayout,
339
+ renderResetControl,
340
+ isLoading = false
341
+ }) {
342
+ const { isLayoutLoading, isLayoutSaving, isLayoutResetting } = layoutStatus;
343
+ const isLayoutBusy = isLayoutLoading || isLayoutSaving || isLayoutResetting;
344
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center gap-3", children: onResetLayout && (renderResetControl ? renderResetControl(onResetLayout) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
345
+ ResetColumnsButton,
346
+ {
347
+ onReset: onResetLayout,
348
+ disabled: isLayoutBusy,
349
+ isLoading
350
+ }
351
+ )) });
352
+ }
353
+ function CustomPagination({
354
+ paginationData,
355
+ paginationPageSize,
356
+ pageSizeOptions: pageSizeOptionsProp,
357
+ isLoading = false,
358
+ paramNames,
359
+ onPageChange,
360
+ onPageSizeChange,
361
+ onResetLayout,
362
+ renderResetControl,
363
+ layoutStatus,
364
+ classNames,
365
+ className
366
+ }) {
367
+ const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
368
+ const pageKey = paramNames?.page ?? "page";
369
+ const perPageKey = paramNames?.perPage ?? "perPage";
370
+ const pageSizeOptions = pageSizeOptionsProp ?? paginationPageSize ?? DEFAULT_PAGE_SIZE_OPTIONS;
371
+ const currentPage = paginationData ? Math.max(0, paginationData.currentPage - 1) : 0;
372
+ const handlePageChange = (pageNumber) => {
373
+ if (!paginationData || pageNumber < 0 || pageNumber >= paginationData.pageCount) {
374
+ return;
375
+ }
376
+ const safePage = Math.max(
377
+ 1,
378
+ Math.min(pageNumber + 1, paginationData.pageCount)
379
+ );
380
+ const newParams = new URLSearchParams(searchParams);
381
+ newParams.set(pageKey, String(safePage));
382
+ setSearchParams(newParams, { replace: true });
383
+ onPageChange?.(safePage);
384
+ };
385
+ const handlePageSizeChange = (event) => {
386
+ const parsed = parseInt(event.target.value, 10);
387
+ if (!Number.isFinite(parsed) || parsed < 1) {
388
+ return;
389
+ }
390
+ const newParams = new URLSearchParams(searchParams);
391
+ newParams.set(pageKey, "1");
392
+ newParams.set(perPageKey, String(parsed));
393
+ setSearchParams(newParams, { replace: true });
394
+ onPageSizeChange?.(parsed);
395
+ };
396
+ const createPageButtons = () => {
397
+ const buttons = [];
398
+ const maxButtons = 5;
399
+ const totalPages = Math.max(1, paginationData?.pageCount ?? 1);
400
+ let startPage = Math.max(0, currentPage - Math.floor(maxButtons / 2));
401
+ let endPage = Math.min(totalPages - 1, startPage + maxButtons - 1);
402
+ if (endPage - startPage + 1 < maxButtons) {
403
+ startPage = Math.max(0, endPage - maxButtons + 1);
404
+ }
405
+ if (startPage > 0) {
406
+ buttons.push(
407
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
408
+ "button",
409
+ {
410
+ type: "button",
411
+ onClick: () => handlePageChange(0),
412
+ "aria-label": "Page 1",
413
+ className: cn(
414
+ "px-3 py-2 text-sm text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-md transition-colors",
415
+ classNames?.pageButton
416
+ ),
417
+ children: "1"
418
+ },
419
+ "1"
420
+ )
421
+ );
422
+ if (startPage > 1) {
423
+ buttons.push(
424
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
425
+ "span",
426
+ {
427
+ "aria-hidden": "true",
428
+ className: "px-2 py-2 text-gray-400",
429
+ children: "..."
430
+ },
431
+ "ellipsis1"
432
+ )
433
+ );
434
+ }
435
+ }
436
+ for (let i = startPage; i <= endPage; i++) {
437
+ const isActive = currentPage === i;
438
+ buttons.push(
439
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
440
+ "button",
441
+ {
442
+ type: "button",
443
+ onClick: () => handlePageChange(i),
444
+ "aria-label": `Page ${i + 1}`,
445
+ "aria-current": isActive ? "page" : void 0,
446
+ className: cn(
447
+ "px-3 py-2 text-sm rounded-md transition-colors",
448
+ isActive ? cn("bg-gray-400 text-white", classNames?.activePageButton) : cn(
449
+ "text-gray-600 hover:text-gray-600 hover:bg-gray-50",
450
+ classNames?.pageButton
451
+ )
452
+ ),
453
+ children: i + 1
454
+ },
455
+ i + 1
456
+ )
457
+ );
458
+ }
459
+ if (endPage < totalPages - 1) {
460
+ if (endPage < totalPages - 2) {
461
+ buttons.push(
462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
463
+ "span",
464
+ {
465
+ "aria-hidden": "true",
466
+ className: "px-2 py-2 text-gray-400",
467
+ children: "..."
468
+ },
469
+ "ellipsis2"
470
+ )
471
+ );
472
+ }
473
+ buttons.push(
474
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
475
+ "button",
476
+ {
477
+ type: "button",
478
+ onClick: () => handlePageChange(totalPages - 1),
479
+ "aria-label": `Page ${totalPages}`,
480
+ className: cn(
481
+ "px-3 py-2 text-sm text-gray-600 hover:text-gray-600 hover:bg-gray-50 rounded-md transition-colors",
482
+ classNames?.pageButton
483
+ ),
484
+ children: totalPages
485
+ },
486
+ totalPages
487
+ )
488
+ );
489
+ }
490
+ return buttons;
491
+ };
492
+ if (isLoading || !paginationData) {
493
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center justify-center p-6 bg-gray-50", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-gray-500", children: isLoading ? "Loading..." : "No pagination data" }) });
494
+ }
495
+ const effectiveSizeOptions = pageSizeOptions.includes(paginationData.perPage) ? pageSizeOptions : [...pageSizeOptions, paginationData.perPage].sort((a, b) => a - b);
496
+ const startItem = paginationData.totalCount === 0 ? 0 : (paginationData.currentPage - 1) * paginationData.perPage + 1;
497
+ const endItem = Math.min(
498
+ startItem + paginationData.perPage - 1,
499
+ paginationData.totalCount
500
+ );
501
+ const { isLayoutLoading, isLayoutSaving, isLayoutResetting } = layoutStatus ?? {};
502
+ const isLayoutBusy = isLayoutLoading || isLayoutSaving || isLayoutResetting;
503
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
504
+ "div",
505
+ {
506
+ className: cn(
507
+ "flex items-center justify-between pb-4",
508
+ className,
509
+ classNames?.root
510
+ ),
511
+ children: [
512
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn("flex-1", classNames?.info), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs font-medium text-neutral-400", children: [
513
+ "Showing ",
514
+ padCount(startItem),
515
+ " - ",
516
+ padCount(endItem),
517
+ " of",
518
+ " ",
519
+ paginationData.totalCount ?? 0,
520
+ " results"
521
+ ] }) }),
522
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
523
+ "nav",
524
+ {
525
+ "aria-label": "Pagination",
526
+ className: cn("flex items-center space-x-1", classNames?.nav),
527
+ children: [
528
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
529
+ "button",
530
+ {
531
+ type: "button",
532
+ onClick: () => handlePageChange(currentPage - 1),
533
+ disabled: currentPage === 0,
534
+ className: "p-2 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",
535
+ "aria-label": "Previous page",
536
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10.7 2.3a1 1 0 0 1 0 1.4L6.4 8l4.3 4.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 0z" }) })
537
+ }
538
+ ),
539
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center space-x-1", children: createPageButtons() }),
540
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
541
+ "button",
542
+ {
543
+ type: "button",
544
+ onClick: () => handlePageChange(currentPage + 1),
545
+ disabled: currentPage >= paginationData.pageCount - 1,
546
+ className: "p-2 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",
547
+ "aria-label": "Next page",
548
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5.3 13.7a1 1 0 0 1 0-1.4L9.6 8 5.3 3.7a1 1 0 0 1 1.4-1.4l5 5a1 1 0 0 1 0 1.4l-5 5a1 1 0 0 1-1.4 0z" }) })
549
+ }
550
+ )
551
+ ]
552
+ }
553
+ ),
554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-3", children: [
555
+ onResetLayout && (renderResetControl ? renderResetControl(onResetLayout) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
556
+ ResetColumnsButton,
557
+ {
558
+ onReset: onResetLayout,
559
+ disabled: isLayoutBusy,
560
+ isLoading
561
+ }
562
+ )),
563
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center space-x-2", children: [
564
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
565
+ "label",
566
+ {
567
+ htmlFor: "pagination-per-page",
568
+ className: "text-sm text-gray-600",
569
+ children: "Items per page"
570
+ }
571
+ ),
572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
573
+ "select",
574
+ {
575
+ id: "pagination-per-page",
576
+ value: paginationData.perPage || 10,
577
+ onChange: handlePageSizeChange,
578
+ className: cn(
579
+ "px-3 py-1.5 text-sm border border-gray-300 rounded-md bg-white text-gray-700 hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors",
580
+ classNames?.select
581
+ ),
582
+ children: effectiveSizeOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: option, children: option }, option))
583
+ }
584
+ )
585
+ ] })
586
+ ] }) })
587
+ ]
588
+ }
589
+ );
590
+ }
251
591
  // Annotate the CommonJS export names for ESM import in node:
252
592
  0 && (module.exports = {
253
- EztrakTabs
593
+ CustomPagination,
594
+ EztrakTabs,
595
+ ResetColumnsButton,
596
+ TableLayoutToolbarControls
254
597
  });
255
598
  //# sourceMappingURL=index.js.map