@yca-software/yca-react-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +129 -0
  2. package/dist/admin/index.d.ts +10 -0
  3. package/dist/admin/index.js +3 -0
  4. package/dist/admin/index.js.map +1 -0
  5. package/dist/admin-BVYdWtEy.d.ts +3 -0
  6. package/dist/api/index.d.ts +68 -0
  7. package/dist/api/index.js +3 -0
  8. package/dist/api/index.js.map +1 -0
  9. package/dist/auth/index.d.ts +49 -0
  10. package/dist/auth/index.js +3 -0
  11. package/dist/auth/index.js.map +1 -0
  12. package/dist/chunk-2HJO2ICF.js +339 -0
  13. package/dist/chunk-2HJO2ICF.js.map +1 -0
  14. package/dist/chunk-3AYG73DK.js +174 -0
  15. package/dist/chunk-3AYG73DK.js.map +1 -0
  16. package/dist/chunk-3SCZ4Z6P.js +26 -0
  17. package/dist/chunk-3SCZ4Z6P.js.map +1 -0
  18. package/dist/chunk-3ZLODBRJ.js +21 -0
  19. package/dist/chunk-3ZLODBRJ.js.map +1 -0
  20. package/dist/chunk-6LO5O4G6.js +224 -0
  21. package/dist/chunk-6LO5O4G6.js.map +1 -0
  22. package/dist/chunk-APZVW5SW.js +75 -0
  23. package/dist/chunk-APZVW5SW.js.map +1 -0
  24. package/dist/chunk-EVZBADFR.js +583 -0
  25. package/dist/chunk-EVZBADFR.js.map +1 -0
  26. package/dist/chunk-GXWVFQCK.js +52 -0
  27. package/dist/chunk-GXWVFQCK.js.map +1 -0
  28. package/dist/chunk-KG4JVAKS.js +13 -0
  29. package/dist/chunk-KG4JVAKS.js.map +1 -0
  30. package/dist/chunk-KZH56VPP.js +183 -0
  31. package/dist/chunk-KZH56VPP.js.map +1 -0
  32. package/dist/chunk-LP7Q75C6.js +96 -0
  33. package/dist/chunk-LP7Q75C6.js.map +1 -0
  34. package/dist/chunk-NL4NAHXG.js +3 -0
  35. package/dist/chunk-NL4NAHXG.js.map +1 -0
  36. package/dist/chunk-PLSNCPEC.js +2507 -0
  37. package/dist/chunk-PLSNCPEC.js.map +1 -0
  38. package/dist/chunk-QFKVPVFC.js +1391 -0
  39. package/dist/chunk-QFKVPVFC.js.map +1 -0
  40. package/dist/chunk-TSXUUD64.js +64 -0
  41. package/dist/chunk-TSXUUD64.js.map +1 -0
  42. package/dist/chunk-UCJ76BEC.js +32 -0
  43. package/dist/chunk-UCJ76BEC.js.map +1 -0
  44. package/dist/chunk-WFTIYYII.js +3 -0
  45. package/dist/chunk-WFTIYYII.js.map +1 -0
  46. package/dist/components/forms/index.d.ts +101 -0
  47. package/dist/components/forms/index.js +7 -0
  48. package/dist/components/forms/index.js.map +1 -0
  49. package/dist/components/marketing/index.d.ts +473 -0
  50. package/dist/components/marketing/index.js +5 -0
  51. package/dist/components/marketing/index.js.map +1 -0
  52. package/dist/components/spa/index.d.ts +275 -0
  53. package/dist/components/spa/index.js +8 -0
  54. package/dist/components/spa/index.js.map +1 -0
  55. package/dist/components/ui/index.d.ts +420 -0
  56. package/dist/components/ui/index.js +6 -0
  57. package/dist/components/ui/index.js.map +1 -0
  58. package/dist/constants/index.d.ts +2 -0
  59. package/dist/constants/index.js +4 -0
  60. package/dist/constants/index.js.map +1 -0
  61. package/dist/cookies-ZS_QYOR7.d.ts +8 -0
  62. package/dist/dateRangePickerTranslations-B99F2WQA.d.ts +9 -0
  63. package/dist/errors/index.d.ts +48 -0
  64. package/dist/errors/index.js +3 -0
  65. package/dist/errors/index.js.map +1 -0
  66. package/dist/forms-VpLvEFob.d.ts +18 -0
  67. package/dist/hooks/index.d.ts +18 -0
  68. package/dist/hooks/index.js +4 -0
  69. package/dist/hooks/index.js.map +1 -0
  70. package/dist/index.d.ts +42 -0
  71. package/dist/index.js +19 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/lib/index.d.ts +49 -0
  74. package/dist/lib/index.js +7 -0
  75. package/dist/lib/index.js.map +1 -0
  76. package/dist/pagination-CFqoqiAG.d.ts +10 -0
  77. package/dist/radix-overlays-BlCr-uem.d.ts +10 -0
  78. package/dist/styles.css +9 -0
  79. package/dist/theme/marketing.css +750 -0
  80. package/dist/theme/tokens.css +115 -0
  81. package/dist/types/index.d.ts +2 -0
  82. package/dist/types/index.js +3 -0
  83. package/dist/types/index.js.map +1 -0
  84. package/dist/types-BuNHOJ1h.d.ts +58 -0
  85. package/dist/types-CN73TWSY.d.ts +51 -0
  86. package/dist/useAdminListPage-C4ZTTllP.d.ts +26 -0
  87. package/package.json +152 -0
@@ -0,0 +1,75 @@
1
+ import Cookies from 'js-cookie';
2
+ import { jwtDecode } from 'jwt-decode';
3
+
4
+ // src/auth/cookie.ts
5
+ var tokenCookieOptions = (options) => {
6
+ const opts = { path: "/" };
7
+ if (options.domain) opts.domain = options.domain;
8
+ if (options.secure) opts.secure = true;
9
+ if (options.sameSite) opts.sameSite = options.sameSite;
10
+ return opts;
11
+ };
12
+ function getAccessTokenFromCookies(names) {
13
+ return Cookies.get(names.accessToken) || null;
14
+ }
15
+ function getRefreshTokenFromCookies(names) {
16
+ return Cookies.get(names.refreshToken) || null;
17
+ }
18
+ function getCookieConsentFromCookies(names) {
19
+ const consentStr = Cookies.get(names.cookieConsent);
20
+ if (!consentStr) return null;
21
+ try {
22
+ return JSON.parse(consentStr);
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ function removeAccessTokenCookie(options) {
28
+ Cookies.remove(options.names.accessToken, tokenCookieOptions(options));
29
+ }
30
+ function removeRefreshTokenCookie(options) {
31
+ Cookies.remove(options.names.refreshToken, tokenCookieOptions(options));
32
+ }
33
+ function removeCookieConsentCookie(names) {
34
+ Cookies.remove(names.cookieConsent);
35
+ }
36
+ function setCookieConsentCookie(names, timestamp, analytics, domain) {
37
+ const consent = { analytics, timestamp };
38
+ const cookieOptions = {};
39
+ if (domain) cookieOptions.domain = domain;
40
+ Cookies.set(names.cookieConsent, JSON.stringify(consent), cookieOptions);
41
+ }
42
+ function setAccessTokenCookie(token, options) {
43
+ Cookies.set(options.names.accessToken, token, tokenCookieOptions(options));
44
+ }
45
+ function setRefreshTokenCookie(token, options) {
46
+ Cookies.set(options.names.refreshToken, token, tokenCookieOptions(options));
47
+ }
48
+ function setTokens(accessToken, refreshToken, options) {
49
+ setAccessTokenCookie(accessToken, options);
50
+ setRefreshTokenCookie(refreshToken, options);
51
+ }
52
+ function getAccessInfoFromToken(accessToken) {
53
+ if (!accessToken?.trim()) return null;
54
+ try {
55
+ const decoded = jwtDecode(accessToken);
56
+ return {
57
+ userId: decoded.sub,
58
+ email: decoded.email ?? "",
59
+ permissions: decoded.permissions ?? [],
60
+ isAdmin: decoded.isAdmin ?? false,
61
+ impersonatedBy: decoded.impersonatedBy ?? null,
62
+ impersonatedByEmail: decoded.impersonatedByEmail ?? null
63
+ };
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+ function getInitials(firstName, lastName) {
69
+ if (!firstName && !lastName) return "U";
70
+ return `${firstName?.[0] || ""}${lastName?.[0] || ""}`.toUpperCase();
71
+ }
72
+
73
+ export { getAccessInfoFromToken, getAccessTokenFromCookies, getCookieConsentFromCookies, getInitials, getRefreshTokenFromCookies, removeAccessTokenCookie, removeCookieConsentCookie, removeRefreshTokenCookie, setAccessTokenCookie, setCookieConsentCookie, setRefreshTokenCookie, setTokens };
74
+ //# sourceMappingURL=chunk-APZVW5SW.js.map
75
+ //# sourceMappingURL=chunk-APZVW5SW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/cookie.ts","../src/auth/jwt.ts"],"names":[],"mappings":"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","file":"chunk-APZVW5SW.js","sourcesContent":["import Cookies from 'js-cookie';\nimport type { CookieNames } from '../constants/cookies';\n\nexport interface CookieConsent {\n analytics: boolean;\n timestamp: string;\n}\n\nexport interface TokenCookieOptions {\n names: CookieNames;\n domain?: string;\n secure?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\nconst tokenCookieOptions = (options: TokenCookieOptions): Cookies.CookieAttributes => {\n const opts: Cookies.CookieAttributes = { path: '/' };\n if (options.domain) opts.domain = options.domain;\n if (options.secure) opts.secure = true;\n if (options.sameSite) opts.sameSite = options.sameSite;\n return opts;\n};\n\nexport function getAccessTokenFromCookies(names: CookieNames): string | null {\n return Cookies.get(names.accessToken) || null;\n}\n\nexport function getRefreshTokenFromCookies(names: CookieNames): string | null {\n return Cookies.get(names.refreshToken) || null;\n}\n\nexport function getCookieConsentFromCookies(names: CookieNames): CookieConsent | null {\n const consentStr = Cookies.get(names.cookieConsent);\n if (!consentStr) return null;\n try {\n return JSON.parse(consentStr) as CookieConsent;\n } catch {\n return null;\n }\n}\n\nexport function removeAccessTokenCookie(options: TokenCookieOptions) {\n Cookies.remove(options.names.accessToken, tokenCookieOptions(options));\n}\n\nexport function removeRefreshTokenCookie(options: TokenCookieOptions) {\n Cookies.remove(options.names.refreshToken, tokenCookieOptions(options));\n}\n\nexport function removeCookieConsentCookie(names: CookieNames) {\n Cookies.remove(names.cookieConsent);\n}\n\nexport function setCookieConsentCookie(\n names: CookieNames,\n timestamp: string,\n analytics: boolean,\n domain?: string,\n) {\n const consent: CookieConsent = { analytics, timestamp };\n const cookieOptions: Cookies.CookieAttributes = {};\n if (domain) cookieOptions.domain = domain;\n Cookies.set(names.cookieConsent, JSON.stringify(consent), cookieOptions);\n}\n\nexport function setAccessTokenCookie(token: string, options: TokenCookieOptions) {\n Cookies.set(options.names.accessToken, token, tokenCookieOptions(options));\n}\n\nexport function setRefreshTokenCookie(token: string, options: TokenCookieOptions) {\n Cookies.set(options.names.refreshToken, token, tokenCookieOptions(options));\n}\n\nexport function setTokens(accessToken: string, refreshToken: string, options: TokenCookieOptions) {\n setAccessTokenCookie(accessToken, options);\n setRefreshTokenCookie(refreshToken, options);\n}\n","import { jwtDecode } from 'jwt-decode';\nimport type { AccessInfo, DecodedAccessToken } from './types';\n\nexport function getAccessInfoFromToken(accessToken: string): AccessInfo | null {\n if (!accessToken?.trim()) return null;\n try {\n const decoded = jwtDecode<DecodedAccessToken>(accessToken);\n return {\n userId: decoded.sub,\n email: decoded.email ?? '',\n permissions: decoded.permissions ?? [],\n isAdmin: decoded.isAdmin ?? false,\n impersonatedBy: decoded.impersonatedBy ?? null,\n impersonatedByEmail: decoded.impersonatedByEmail ?? null,\n };\n } catch {\n return null;\n }\n}\n\nexport function getInitials(firstName?: string, lastName?: string): string {\n if (!firstName && !lastName) return 'U';\n return `${firstName?.[0] || ''}${lastName?.[0] || ''}`.toUpperCase();\n}\n"]}
@@ -0,0 +1,583 @@
1
+ import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Select, Alert, AlertDescription, DateRangePicker, Tooltip, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from './chunk-PLSNCPEC.js';
2
+ import { Button, Card, CardHeader, CardTitle, CardDescription, Input, CardContent } from './chunk-3AYG73DK.js';
3
+ import { dateRangePickerTranslations } from './chunk-UCJ76BEC.js';
4
+ import { cn } from './chunk-GXWVFQCK.js';
5
+ import { Loader2, ArrowLeft, X, Search, MoreVertical, Moon, Sun } from 'lucide-react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
8
+ import { Link } from 'react-router';
9
+ import React, { Suspense, useState, useEffect } from 'react';
10
+
11
+ function PageLoader({
12
+ className,
13
+ spinnerClassName,
14
+ variant = "section",
15
+ loadingLabel
16
+ }) {
17
+ const { t } = useTranslation("common");
18
+ const label = loadingLabel ?? t("loading");
19
+ const spinnerSize = variant === "inline" ? "h-6 w-6 animate-spin text-primary" : "h-8 w-8 animate-spin text-primary";
20
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
21
+ /* @__PURE__ */ jsx(Loader2, { className: cn(spinnerSize, spinnerClassName), "aria-hidden": true }),
22
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: label })
23
+ ] });
24
+ if (variant === "page") {
25
+ return /* @__PURE__ */ jsx(
26
+ "div",
27
+ {
28
+ className: cn("min-h-screen flex items-center justify-center bg-background", className),
29
+ role: "status",
30
+ "aria-live": "polite",
31
+ children: content
32
+ }
33
+ );
34
+ }
35
+ if (variant === "route") {
36
+ return /* @__PURE__ */ jsx(
37
+ "div",
38
+ {
39
+ className: cn("flex min-h-[40vh] items-center justify-center", className),
40
+ role: "status",
41
+ "aria-live": "polite",
42
+ children: content
43
+ }
44
+ );
45
+ }
46
+ if (variant === "inline") {
47
+ return /* @__PURE__ */ jsx(
48
+ "div",
49
+ {
50
+ className: cn("flex items-center justify-center", className),
51
+ role: "status",
52
+ "aria-live": "polite",
53
+ children: content
54
+ }
55
+ );
56
+ }
57
+ return /* @__PURE__ */ jsx(
58
+ "div",
59
+ {
60
+ className: cn("flex items-center justify-center py-12", className),
61
+ role: "status",
62
+ "aria-live": "polite",
63
+ children: content
64
+ }
65
+ );
66
+ }
67
+ function AdminDetailPage({
68
+ backHref,
69
+ backLabel,
70
+ isLoading,
71
+ errorKind,
72
+ error,
73
+ notFoundMessage,
74
+ forbiddenMessage,
75
+ loadErrorMessage,
76
+ formatError,
77
+ children,
78
+ headerActions
79
+ }) {
80
+ if (isLoading) {
81
+ return /* @__PURE__ */ jsx(PageLoader, { variant: "route" });
82
+ }
83
+ if (errorKind) {
84
+ const message = errorKind === "forbidden" ? forbiddenMessage : errorKind === "load" ? formatError?.(error) || loadErrorMessage : notFoundMessage;
85
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 sm:space-y-6", children: [
86
+ /* @__PURE__ */ jsx("p", { className: "text-destructive", children: message }),
87
+ /* @__PURE__ */ jsx(Link, { to: backHref, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: backLabel }) })
88
+ ] });
89
+ }
90
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 sm:space-y-6", children: [
91
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
92
+ /* @__PURE__ */ jsx(Link, { to: backHref, children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", children: [
93
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "mr-2 h-4 w-4", "aria-hidden": true }),
94
+ backLabel
95
+ ] }) }),
96
+ headerActions
97
+ ] }),
98
+ children
99
+ ] });
100
+ }
101
+ function AdminListPage({
102
+ title,
103
+ description,
104
+ headerActions,
105
+ cardTitle,
106
+ searchPlaceholder,
107
+ emptyMessage,
108
+ columns,
109
+ items,
110
+ isLoading,
111
+ hasNextPage,
112
+ isFetchingNextPage,
113
+ loadMoreRef,
114
+ search,
115
+ onSearchChange,
116
+ onRowClick,
117
+ getRowKey,
118
+ loadingAriaLabel = "Loading...",
119
+ searchMode = "instant",
120
+ searchButtonLabel = "Search",
121
+ clearButtonLabel = "Clear",
122
+ onSearchSubmit,
123
+ onSearchClear,
124
+ onSearchKeyDown
125
+ }) {
126
+ const safeItems = items.filter((item) => item != null && typeof item === "object");
127
+ const isSubmitMode = searchMode === "submit";
128
+ const hasSearchText = search.trim().length > 0;
129
+ const handleSearchInputKeyDown = (event) => {
130
+ if (isSubmitMode && event.key === "Enter") {
131
+ event.preventDefault();
132
+ onSearchSubmit?.();
133
+ }
134
+ onSearchKeyDown?.(event);
135
+ };
136
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 sm:space-y-6", children: [
137
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
138
+ /* @__PURE__ */ jsxs("div", { children: [
139
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold sm:text-3xl", children: title }),
140
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground sm:text-base", children: description })
141
+ ] }),
142
+ headerActions
143
+ ] }),
144
+ /* @__PURE__ */ jsxs(Card, { children: [
145
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4", children: [
146
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
147
+ /* @__PURE__ */ jsx(CardTitle, { children: cardTitle }),
148
+ !isSubmitMode ? /* @__PURE__ */ jsx(CardDescription, { children: searchPlaceholder }) : null
149
+ ] }),
150
+ isSubmitMode ? /* @__PURE__ */ jsxs(
151
+ "div",
152
+ {
153
+ className: cn(
154
+ "flex h-9 w-full min-w-0 max-w-full overflow-hidden rounded-md border border-input bg-background shadow-xs sm:max-w-sm",
155
+ "transition-[border-color,box-shadow] focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/20"
156
+ ),
157
+ children: [
158
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 items-center", children: [
159
+ /* @__PURE__ */ jsx(
160
+ Input,
161
+ {
162
+ placeholder: searchPlaceholder,
163
+ value: search,
164
+ onChange: (e) => onSearchChange(e.target.value),
165
+ onKeyDown: handleSearchInputKeyDown,
166
+ className: cn(
167
+ "h-9 rounded-none rounded-l-md border-0 bg-transparent px-3 shadow-none",
168
+ "focus-visible:border-transparent focus-visible:ring-0",
169
+ hasSearchText && "pr-9"
170
+ ),
171
+ "aria-label": searchPlaceholder
172
+ }
173
+ ),
174
+ hasSearchText ? /* @__PURE__ */ jsx(
175
+ Button,
176
+ {
177
+ type: "button",
178
+ variant: "ghost",
179
+ size: "icon-sm",
180
+ onClick: onSearchClear,
181
+ "aria-label": clearButtonLabel,
182
+ className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:bg-muted/60 hover:text-foreground",
183
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", "aria-hidden": true })
184
+ }
185
+ ) : null
186
+ ] }),
187
+ /* @__PURE__ */ jsx(
188
+ Button,
189
+ {
190
+ type: "button",
191
+ variant: "default",
192
+ onClick: onSearchSubmit,
193
+ "aria-label": searchButtonLabel,
194
+ className: cn(
195
+ "h-full w-10 shrink-0 rounded-none rounded-r-md border-0 border-l border-primary-foreground/15 px-0 shadow-none",
196
+ "hover:bg-primary/90 active:bg-primary/95",
197
+ "shadow-[inset_0_1px_0_0_color-mix(in_oklch,var(--primary-foreground)_20%,transparent)]"
198
+ ),
199
+ children: /* @__PURE__ */ jsx(Search, { className: "h-4 w-4", "aria-hidden": true })
200
+ }
201
+ )
202
+ ]
203
+ }
204
+ ) : /* @__PURE__ */ jsxs("div", { className: "relative max-w-full sm:max-w-sm", children: [
205
+ /* @__PURE__ */ jsx(
206
+ Search,
207
+ {
208
+ className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground",
209
+ "aria-hidden": true
210
+ }
211
+ ),
212
+ /* @__PURE__ */ jsx(
213
+ Input,
214
+ {
215
+ placeholder: searchPlaceholder,
216
+ value: search,
217
+ onChange: (e) => onSearchChange(e.target.value),
218
+ className: "pl-9",
219
+ "aria-label": searchPlaceholder
220
+ }
221
+ )
222
+ ] })
223
+ ] }),
224
+ /* @__PURE__ */ jsx(CardContent, { children: isLoading ? /* @__PURE__ */ jsx(PageLoader, { loadingLabel: loadingAriaLabel }) : safeItems.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx("div", { className: "-mx-4 overflow-x-auto sm:mx-0", children: /* @__PURE__ */ jsxs(Table, { className: "min-w-[400px]", children: [
225
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { className: "bg-muted/50 hover:bg-muted/50", children: columns.map((col) => /* @__PURE__ */ jsx(TableHead, { className: col.className, children: col.header }, col.key)) }) }),
226
+ /* @__PURE__ */ jsxs(TableBody, { children: [
227
+ safeItems.map((item, index) => /* @__PURE__ */ jsx(
228
+ TableRow,
229
+ {
230
+ role: "button",
231
+ tabIndex: 0,
232
+ onClick: () => onRowClick(item),
233
+ onKeyDown: (e) => {
234
+ if (e.key === "Enter" || e.key === " ") {
235
+ e.preventDefault();
236
+ onRowClick(item);
237
+ }
238
+ },
239
+ className: "cursor-pointer hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
240
+ children: columns.map((col) => /* @__PURE__ */ jsx(
241
+ TableCell,
242
+ {
243
+ className: cn(col.className ?? "text-muted-foreground"),
244
+ children: col.render(item)
245
+ },
246
+ col.key
247
+ ))
248
+ },
249
+ getRowKey(item) || `row-${index}`
250
+ )),
251
+ hasNextPage && /* @__PURE__ */ jsx(TableRow, { ref: loadMoreRef, children: /* @__PURE__ */ jsx(
252
+ TableCell,
253
+ {
254
+ colSpan: columns.length,
255
+ className: "text-center text-muted-foreground",
256
+ children: isFetchingNextPage ? /* @__PURE__ */ jsx(Loader2, { className: "inline-block h-5 w-5 animate-spin", "aria-hidden": true }) : null
257
+ }
258
+ ) })
259
+ ] })
260
+ ] }) }) })
261
+ ] })
262
+ ] });
263
+ }
264
+ function AuditLogListFilters({
265
+ search,
266
+ onSearchChange,
267
+ action,
268
+ onActionChange,
269
+ resourceType,
270
+ onResourceTypeChange,
271
+ actionOptions,
272
+ resourceOptions,
273
+ searchPlaceholder,
274
+ actionFilterLabel,
275
+ resourceFilterLabel
276
+ }) {
277
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 lg:flex-row lg:flex-wrap lg:items-center", children: [
278
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full lg:max-w-md", children: [
279
+ /* @__PURE__ */ jsx(
280
+ Search,
281
+ {
282
+ className: "pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground",
283
+ "aria-hidden": true
284
+ }
285
+ ),
286
+ /* @__PURE__ */ jsx(
287
+ Input,
288
+ {
289
+ className: "pl-9",
290
+ placeholder: searchPlaceholder,
291
+ value: search,
292
+ onChange: (event) => onSearchChange(event.target.value),
293
+ "aria-label": searchPlaceholder
294
+ }
295
+ )
296
+ ] }),
297
+ /* @__PURE__ */ jsx(
298
+ Select,
299
+ {
300
+ className: "w-full lg:w-44",
301
+ value: action,
302
+ onValueChange: onActionChange,
303
+ options: actionOptions,
304
+ "aria-label": actionFilterLabel
305
+ }
306
+ ),
307
+ /* @__PURE__ */ jsx(
308
+ Select,
309
+ {
310
+ className: "w-full lg:w-52",
311
+ value: resourceType,
312
+ onValueChange: onResourceTypeChange,
313
+ options: resourceOptions,
314
+ "aria-label": resourceFilterLabel
315
+ }
316
+ )
317
+ ] });
318
+ }
319
+ function QueryState({
320
+ isLoading,
321
+ isError,
322
+ error,
323
+ onRetry,
324
+ loadingClassName,
325
+ children,
326
+ formatError,
327
+ errorDescription = "We could not load this data. Please try again.",
328
+ retryLabel = "Try again",
329
+ loadingLabel = "Loading..."
330
+ }) {
331
+ if (isLoading) {
332
+ return /* @__PURE__ */ jsx(PageLoader, { variant: "section", className: loadingClassName, loadingLabel });
333
+ }
334
+ if (isError) {
335
+ const message = formatError?.(error) || errorDescription;
336
+ return /* @__PURE__ */ jsx(Alert, { variant: "destructive", className: "max-w-xl", children: /* @__PURE__ */ jsxs(AlertDescription, { className: "space-y-3", children: [
337
+ /* @__PURE__ */ jsx("p", { children: message }),
338
+ onRetry ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: retryLabel }) : null
339
+ ] }) });
340
+ }
341
+ return /* @__PURE__ */ jsx(Fragment, { children });
342
+ }
343
+ function createBoundQueryState(useBindings) {
344
+ function BoundQueryState(props) {
345
+ const bindings = useBindings();
346
+ return /* @__PURE__ */ jsx(QueryState, { ...bindings, ...props });
347
+ }
348
+ BoundQueryState.displayName = "BoundQueryState";
349
+ return BoundQueryState;
350
+ }
351
+ function DateRangeFilter({
352
+ label,
353
+ value,
354
+ onChange,
355
+ onApply,
356
+ minDate,
357
+ maxDate,
358
+ className,
359
+ hint,
360
+ variant = "panel",
361
+ t,
362
+ translationNs = "common"
363
+ }) {
364
+ const inline = variant === "inline";
365
+ const toolbar = variant === "toolbar";
366
+ const picker = /* @__PURE__ */ jsx(
367
+ DateRangePicker,
368
+ {
369
+ className: cn(
370
+ "w-full min-w-0 border-border/80 bg-background",
371
+ toolbar ? "w-full min-w-0" : inline ? "sm:max-w-[280px]" : "sm:max-w-[320px]"
372
+ ),
373
+ value,
374
+ onChange,
375
+ minDate,
376
+ maxDate,
377
+ translations: dateRangePickerTranslations(t, translationNs),
378
+ onApply
379
+ }
380
+ );
381
+ if (toolbar) {
382
+ return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0 w-full", className), children: [
383
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }),
384
+ picker
385
+ ] });
386
+ }
387
+ if (inline) {
388
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full shrink-0 sm:w-auto", className), children: [
389
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }),
390
+ picker,
391
+ hint ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-[0.65rem] text-muted-foreground", children: hint }) : null
392
+ ] });
393
+ }
394
+ return /* @__PURE__ */ jsx("div", { className: cn("rounded-lg border border-border/80 bg-muted/25 p-3 shadow-sm", className), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [
395
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
396
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: label }),
397
+ hint ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: hint }) : null
398
+ ] }),
399
+ picker
400
+ ] }) });
401
+ }
402
+ function DetailFieldList({
403
+ fields,
404
+ className,
405
+ fieldVariant = "plain"
406
+ }) {
407
+ return /* @__PURE__ */ jsx("dl", { className: cn("grid gap-4 sm:grid-cols-2", className), children: fields.map((field, i) => /* @__PURE__ */ jsxs(
408
+ "div",
409
+ {
410
+ className: cn(
411
+ "space-y-1",
412
+ fieldVariant === "bordered" && "rounded-lg border bg-muted/20 px-3 py-2.5",
413
+ field.span === 2 && "sm:col-span-2"
414
+ ),
415
+ children: [
416
+ /* @__PURE__ */ jsx("dt", { className: "text-sm font-medium text-muted-foreground", children: field.label }),
417
+ /* @__PURE__ */ jsx("dd", { className: "text-sm break-words", children: field.value })
418
+ ]
419
+ },
420
+ `${field.label}-${i}`
421
+ )) });
422
+ }
423
+ function EntityRow({
424
+ icon: Icon,
425
+ title,
426
+ titleTooltip = typeof title === "string" ? title : void 0,
427
+ description,
428
+ subtitle,
429
+ onClick,
430
+ actions = [],
431
+ archived = false,
432
+ children,
433
+ className
434
+ }) {
435
+ const hasActions = actions.length > 0;
436
+ const isClickable = !!onClick;
437
+ return /* @__PURE__ */ jsxs(
438
+ "div",
439
+ {
440
+ role: isClickable ? "button" : void 0,
441
+ tabIndex: isClickable ? 0 : void 0,
442
+ onClick,
443
+ onKeyDown: isClickable ? (e) => {
444
+ if (e.key === "Enter" || e.key === " ") {
445
+ e.preventDefault();
446
+ onClick?.();
447
+ }
448
+ } : void 0,
449
+ className: cn(
450
+ "flex flex-col gap-2 rounded-lg border p-4 transition-colors",
451
+ archived ? "bg-muted/30" : "hover:bg-muted/40",
452
+ isClickable && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
453
+ className
454
+ ),
455
+ children: [
456
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
457
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
458
+ /* @__PURE__ */ jsx(
459
+ "div",
460
+ {
461
+ className: cn(
462
+ "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg",
463
+ archived ? "bg-muted" : "bg-primary/10"
464
+ ),
465
+ children: /* @__PURE__ */ jsx(
466
+ Icon,
467
+ {
468
+ className: cn("h-5 w-5", archived ? "text-muted-foreground" : "text-primary"),
469
+ "aria-hidden": true
470
+ }
471
+ )
472
+ }
473
+ ),
474
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
475
+ titleTooltip ? /* @__PURE__ */ jsx(Tooltip, { content: titleTooltip, side: "top", align: "start", children: /* @__PURE__ */ jsx("p", { className: "truncate font-medium max-w-full sm:max-w-md", children: title }) }) : /* @__PURE__ */ jsx("p", { className: "truncate font-medium max-w-full sm:max-w-md", children: title }),
476
+ subtitle && /* @__PURE__ */ jsx("p", { className: "mt-0.5 truncate text-xs text-muted-foreground max-w-full sm:max-w-md", children: subtitle })
477
+ ] })
478
+ ] }),
479
+ hasActions && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
480
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
481
+ Button,
482
+ {
483
+ type: "button",
484
+ variant: "ghost",
485
+ size: "icon",
486
+ className: "shrink-0 -mr-1",
487
+ onClick: (e) => e.stopPropagation(),
488
+ "aria-label": "Open menu",
489
+ children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4", "aria-hidden": true })
490
+ }
491
+ ) }),
492
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", sideOffset: 4, onClick: (e) => e.stopPropagation(), children: actions.map((action) => {
493
+ const ActionIcon = action.icon;
494
+ return /* @__PURE__ */ jsxs(
495
+ DropdownMenuItem,
496
+ {
497
+ onClick: action.onClick,
498
+ className: action.variant === "destructive" ? "text-destructive focus:text-destructive" : void 0,
499
+ children: [
500
+ /* @__PURE__ */ jsx(ActionIcon, { className: "mr-2 h-4 w-4", "aria-hidden": true }),
501
+ action.label
502
+ ]
503
+ },
504
+ action.label
505
+ );
506
+ }) })
507
+ ] })
508
+ ] }),
509
+ description && /* @__PURE__ */ jsx(Tooltip, { content: description, side: "top", align: "start", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground overflow-hidden line-clamp-2 [display:-webkit-box] [-webkit-line-clamp:2] [-webkit-box-orient:vertical]", children: description }) }),
510
+ children
511
+ ]
512
+ }
513
+ );
514
+ }
515
+ function RouteSuspense({ children, loadingLabel }) {
516
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(PageLoader, { variant: "route", loadingLabel }), children });
517
+ }
518
+ function useDefaultThemeStore() {
519
+ const [theme, setTheme] = useState("light");
520
+ return { theme, setTheme };
521
+ }
522
+ var ThemeStoreContext = React.createContext(null);
523
+ function useThemeStoreContext() {
524
+ return React.useContext(ThemeStoreContext);
525
+ }
526
+ function ThemeProvider({ children, useThemeStore: useThemeStoreProp }) {
527
+ const useResolvedStore = useThemeStoreProp ?? useDefaultThemeStore;
528
+ const store = useResolvedStore();
529
+ useEffect(() => {
530
+ const root = window.document.documentElement;
531
+ root.classList.remove("light", "dark");
532
+ root.classList.add(store.theme);
533
+ }, [store.theme]);
534
+ return /* @__PURE__ */ jsx(ThemeStoreContext.Provider, { value: store, children });
535
+ }
536
+ function ThemeToggleWithStore({
537
+ useThemeStore,
538
+ ariaLabel
539
+ }) {
540
+ const { theme, setTheme } = useThemeStore();
541
+ const toggleTheme = () => setTheme(theme === "dark" ? "light" : "dark");
542
+ return /* @__PURE__ */ jsx(
543
+ Button,
544
+ {
545
+ variant: "ghost",
546
+ size: "icon",
547
+ onClick: toggleTheme,
548
+ "aria-label": ariaLabel,
549
+ className: "cursor-pointer",
550
+ children: theme === "dark" ? /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" })
551
+ }
552
+ );
553
+ }
554
+ function ThemeToggleWithContext({ ariaLabel }) {
555
+ const fromContext = useThemeStoreContext();
556
+ const defaultStore = useDefaultThemeStore();
557
+ const store = fromContext ?? defaultStore;
558
+ const toggleTheme = () => store.setTheme(store.theme === "dark" ? "light" : "dark");
559
+ return /* @__PURE__ */ jsx(
560
+ Button,
561
+ {
562
+ variant: "ghost",
563
+ size: "icon",
564
+ onClick: toggleTheme,
565
+ "aria-label": ariaLabel,
566
+ className: "cursor-pointer",
567
+ children: store.theme === "dark" ? /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" })
568
+ }
569
+ );
570
+ }
571
+ function ThemeToggle({
572
+ useThemeStore: useThemeStoreProp,
573
+ ariaLabel = "Toggle theme"
574
+ }) {
575
+ if (useThemeStoreProp) {
576
+ return /* @__PURE__ */ jsx(ThemeToggleWithStore, { useThemeStore: useThemeStoreProp, ariaLabel });
577
+ }
578
+ return /* @__PURE__ */ jsx(ThemeToggleWithContext, { ariaLabel });
579
+ }
580
+
581
+ export { AdminDetailPage, AdminListPage, AuditLogListFilters, DateRangeFilter, DetailFieldList, EntityRow, PageLoader, QueryState, RouteSuspense, ThemeProvider, ThemeToggle, createBoundQueryState, useDefaultThemeStore, useThemeStoreContext };
582
+ //# sourceMappingURL=chunk-EVZBADFR.js.map
583
+ //# sourceMappingURL=chunk-EVZBADFR.js.map