@yca-software/yca-react-core 0.0.22 → 0.0.24
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 +12 -1
- package/dist/api/index.d.ts +7 -2
- package/dist/api/index.js +1 -1
- package/dist/{chunk-WZ64FPVI.js → chunk-BL4GPQGY.js} +32 -23
- package/dist/chunk-BL4GPQGY.js.map +1 -0
- package/dist/{chunk-QHNZTXPJ.js → chunk-GDYI2I74.js} +391 -139
- package/dist/chunk-GDYI2I74.js.map +1 -0
- package/dist/{chunk-HJZNKZHZ.js → chunk-L4KNKLFW.js} +3 -3
- package/dist/{chunk-HJZNKZHZ.js.map → chunk-L4KNKLFW.js.map} +1 -1
- package/dist/{chunk-2HTQDFQJ.js → chunk-V6EYEN2F.js} +18 -5
- package/dist/chunk-V6EYEN2F.js.map +1 -0
- package/dist/components/forms/index.js +2 -2
- package/dist/components/spa/index.d.ts +7 -2
- package/dist/components/spa/index.js +2 -2
- package/dist/components/ui/index.d.ts +46 -17
- package/dist/components/ui/index.js +1 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/{types-CN73TWSY.d.ts → types-BYLBukHJ.d.ts} +5 -0
- package/package.json +1 -1
- package/dist/chunk-2HTQDFQJ.js.map +0 -1
- package/dist/chunk-QHNZTXPJ.js.map +0 -1
- package/dist/chunk-WZ64FPVI.js.map +0 -1
package/README.md
CHANGED
|
@@ -73,6 +73,15 @@ export function useTranslationNamespace(ns: string | string[]) {
|
|
|
73
73
|
|
|
74
74
|
**Keep in the app, not the library:** routes, TanStack Query hooks per resource, zustand stores, `locales/**`, billing/Paddle, permission constants, PostHog.
|
|
75
75
|
|
|
76
|
+
Native shells may supply `refreshExecutor` to `createApiProvider` options. The
|
|
77
|
+
executor must call the host's secure session command and return only
|
|
78
|
+
`{ accessToken, expiresAt }`; the host keeps its rotating refresh credential in
|
|
79
|
+
the OS vault. The API client shares concurrent refresh attempts, validates the
|
|
80
|
+
returned expiry, and retries a protected 401 once. Browser consumers omit this
|
|
81
|
+
option and retain their existing HttpOnly-cookie refresh behavior. The app must
|
|
82
|
+
hydrate its initial native access session and clear its own state after host
|
|
83
|
+
revocation; this library does not own a platform vault or navigation.
|
|
84
|
+
|
|
76
85
|
## Theming
|
|
77
86
|
|
|
78
87
|
Import styles once, then override CSS variables in your global stylesheet:
|
|
@@ -126,4 +135,6 @@ Publishing is automated when you [create a GitHub Release](https://github.com/yc
|
|
|
126
135
|
2. Commit and push to `main`
|
|
127
136
|
3. Tag `vX.Y.Z` and publish the release
|
|
128
137
|
|
|
129
|
-
CI runs build, lint,
|
|
138
|
+
CI runs build, lint, types, and tests before publishing to npm with the
|
|
139
|
+
repository's renewed `NPM_TOKEN` secret and provenance. Verify the public
|
|
140
|
+
registry version before updating any template or app consumer.
|
package/dist/api/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { a as ApiClientProviderConfig, F as FetchFn, A as ApiClientConfig, d as RefreshRequestConfig, e as RequestConfig } from '../types-
|
|
4
|
-
export { b as ApiErrorBody, R as RefreshBodyContext, c as RefreshConfig, f as RequestError } from '../types-
|
|
3
|
+
import { a as ApiClientProviderConfig, F as FetchFn, A as ApiClientConfig, d as RefreshRequestConfig, e as RequestConfig } from '../types-BYLBukHJ.js';
|
|
4
|
+
export { b as ApiErrorBody, R as RefreshBodyContext, c as RefreshConfig, f as RequestError } from '../types-BYLBukHJ.js';
|
|
5
5
|
|
|
6
6
|
declare function ApiClientProvider({ baseURL, config, children, }: {
|
|
7
7
|
baseURL: string;
|
|
@@ -22,6 +22,11 @@ interface CreateApiProviderOptions {
|
|
|
22
22
|
getAcceptLanguage?: () => string;
|
|
23
23
|
onRefreshStart?: () => void;
|
|
24
24
|
onRefreshEnd?: () => void;
|
|
25
|
+
/** Optional native refresh boundary; browser apps retain the default HTTP cookie flow. */
|
|
26
|
+
refreshExecutor?: () => Promise<{
|
|
27
|
+
accessToken: string;
|
|
28
|
+
expiresAt: string;
|
|
29
|
+
}>;
|
|
25
30
|
/** Refresh endpoint path relative to baseURL. Defaults to `auth/refresh`. */
|
|
26
31
|
refreshEndpoint?: string;
|
|
27
32
|
/** Paths that must not trigger automatic refresh retry on 401. Defaults to `['auth/']`. */
|
package/dist/api/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ApiClientProvider, DEFAULT_REFRESH_ENDPOINT, DEFAULT_REFRESH_EXCLUDED_PREFIXES, accessTokenNeedsRefresh, buildRequestUrl, createApiProvider, createDefaultRefreshRequest, createResponseHandler, executeConfiguredRefresh, getRequestHeaders, getRequestOptions, isFatalRefreshFailureStatus, msUntilAccessRefresh, performAccessTokenRefresh, shouldClearSessionOnRefreshFailure, useAPI, useAccessTokenKeepAlive, useApiClientConfig } from '../chunk-
|
|
1
|
+
export { ApiClientProvider, DEFAULT_REFRESH_ENDPOINT, DEFAULT_REFRESH_EXCLUDED_PREFIXES, accessTokenNeedsRefresh, buildRequestUrl, createApiProvider, createDefaultRefreshRequest, createResponseHandler, executeConfiguredRefresh, getRequestHeaders, getRequestOptions, isFatalRefreshFailureStatus, msUntilAccessRefresh, performAccessTokenRefresh, shouldClearSessionOnRefreshFailure, useAPI, useAccessTokenKeepAlive, useApiClientConfig } from '../chunk-V6EYEN2F.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SearchField, Table, TableHeader, TableRow, TableHead, Checkbox, TableBody, TableCell, Select, Alert, AlertDescription, DateRangePicker, Tooltip, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from './chunk-
|
|
1
|
+
import { SearchField, Table, TableHeader, TableRow, TableHead, Checkbox, TableBody, TableCell, Select, Alert, AlertDescription, DateRangePicker, Tooltip, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Label } from './chunk-GDYI2I74.js';
|
|
2
2
|
import { Button, Card, CardHeader, CardTitle, CardDescription, Input, CardContent } from './chunk-3AYG73DK.js';
|
|
3
3
|
import { dateRangePickerTranslations } from './chunk-UCJ76BEC.js';
|
|
4
4
|
import { cn } from './chunk-GXWVFQCK.js';
|
|
@@ -390,15 +390,16 @@ function DateRangeFilter({
|
|
|
390
390
|
onApply
|
|
391
391
|
}
|
|
392
392
|
);
|
|
393
|
+
const toolbarLabel = /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium tracking-normal text-muted-foreground", children: label });
|
|
393
394
|
if (toolbar) {
|
|
394
395
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 w-full flex-col gap-1", className), children: [
|
|
395
|
-
|
|
396
|
+
toolbarLabel,
|
|
396
397
|
picker
|
|
397
398
|
] });
|
|
398
399
|
}
|
|
399
400
|
if (inline) {
|
|
400
401
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full shrink-0 flex-col gap-1 sm:w-auto", className), children: [
|
|
401
|
-
|
|
402
|
+
toolbarLabel,
|
|
402
403
|
picker,
|
|
403
404
|
hint ? /* @__PURE__ */ jsx("p", { className: "text-[0.65rem] text-muted-foreground", children: hint }) : null
|
|
404
405
|
] });
|
|
@@ -534,25 +535,33 @@ function LabeledSelect({
|
|
|
534
535
|
className,
|
|
535
536
|
triggerClassName,
|
|
536
537
|
searchable,
|
|
537
|
-
translations
|
|
538
|
+
translations,
|
|
539
|
+
layout = "stacked"
|
|
538
540
|
}) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
541
|
+
const select = /* @__PURE__ */ jsx(
|
|
542
|
+
Select,
|
|
543
|
+
{
|
|
544
|
+
className: className ?? (layout === "stacked" ? "w-full min-w-[8.5rem]" : "w-[8.5rem]"),
|
|
545
|
+
triggerClassName: triggerClassName ?? "h-9",
|
|
546
|
+
value,
|
|
547
|
+
onValueChange,
|
|
548
|
+
options,
|
|
549
|
+
placeholder,
|
|
550
|
+
disabled,
|
|
551
|
+
searchable,
|
|
552
|
+
translations,
|
|
553
|
+
"aria-label": label
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
if (layout === "inline") {
|
|
557
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
558
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs font-medium text-muted-foreground", children: label }),
|
|
559
|
+
select
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
563
|
+
/* @__PURE__ */ jsx(Label, { className: "text-xs font-medium tracking-normal text-muted-foreground", children: label }),
|
|
564
|
+
select
|
|
556
565
|
] });
|
|
557
566
|
}
|
|
558
567
|
function RouteSuspense({ children, loadingLabel }) {
|
|
@@ -622,5 +631,5 @@ function ThemeToggle({
|
|
|
622
631
|
}
|
|
623
632
|
|
|
624
633
|
export { AdminDetailPage, AdminListPage, AuditLogListFilters, DateRangeFilter, DetailFieldList, EntityRow, LabeledSelect, PageLoader, QueryState, RouteSuspense, ThemeProvider, ThemeToggle, createBoundQueryState, useDefaultThemeStore, useThemeStoreContext };
|
|
625
|
-
//# sourceMappingURL=chunk-
|
|
626
|
-
//# sourceMappingURL=chunk-
|
|
634
|
+
//# sourceMappingURL=chunk-BL4GPQGY.js.map
|
|
635
|
+
//# sourceMappingURL=chunk-BL4GPQGY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/spa/page-loader/index.tsx","../src/components/spa/admin-detail-page/index.tsx","../src/components/spa/admin-list-page/index.tsx","../src/components/spa/audit-log-list-filters/index.tsx","../src/components/spa/query-state/index.tsx","../src/components/spa/create-bound-query-state/index.tsx","../src/components/spa/date-range-filter/index.tsx","../src/components/spa/detail-field-list/index.tsx","../src/components/spa/entity-row/index.tsx","../src/components/spa/labeled-select/index.tsx","../src/components/spa/route-suspense/index.tsx","../src/components/spa/theme-provider/index.tsx","../src/components/spa/theme-toggle/index.tsx"],"names":["jsx","jsxs","Loader2","Search","Fragment"],"mappings":";;;;;;;;;;AAmBO,SAAS,UAAA,CAAW;AAAA,EACzB,SAAA;AAAA,EACA,gBAAA;AAAA,EACA,OAAA,GAAU,SAAA;AAAA,EACV;AACF,CAAA,EAAoB;AAClB,EAAA,MAAM,EAAE,CAAA,EAAE,GAAI,cAAA,CAAe,QAAQ,CAAA;AACrC,EAAA,MAAM,KAAA,GAAQ,YAAA,IAAgB,CAAA,CAAE,SAAS,CAAA;AACzC,EAAA,MAAM,WAAA,GACJ,OAAA,KAAY,QAAA,GACR,mCAAA,GACA,mCAAA;AAEN,EAAA,MAAM,0BACJ,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,WAAQ,SAAA,EAAW,EAAA,CAAG,aAAa,gBAAgB,CAAA,EAAG,eAAW,IAAA,EAAC,CAAA;AAAA,oBACnE,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,KAAA,EAAM;AAAA,GAAA,EACnC,CAAA;AAGF,EAAA,IAAI,YAAY,MAAA,EAAQ;AACtB,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAA,CAAG,6DAAA,EAA+D,SAAS,CAAA;AAAA,QACtF,IAAA,EAAK,QAAA;AAAA,QACL,WAAA,EAAU,QAAA;AAAA,QAET,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,EAEJ;AAEA,EAAA,IAAI,YAAY,OAAA,EAAS;AACvB,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAA,CAAG,+CAAA,EAAiD,SAAS,CAAA;AAAA,QACxE,IAAA,EAAK,QAAA;AAAA,QACL,WAAA,EAAU,QAAA;AAAA,QAET,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,EAEJ;AAEA,EAAA,IAAI,YAAY,QAAA,EAAU;AACxB,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAA,CAAG,kCAAA,EAAoC,SAAS,CAAA;AAAA,QAC3D,IAAA,EAAK,QAAA;AAAA,QACL,WAAA,EAAU,QAAA;AAAA,QAET,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,EAEJ;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA,CAAG,wCAAA,EAA0C,SAAS,CAAA;AAAA,MACjE,IAAA,EAAK,QAAA;AAAA,MACL,WAAA,EAAU,QAAA;AAAA,MAET,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ;AC5DO,SAAS,eAAA,CAAgB;AAAA,EAC9B,QAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,eAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,EAAyB;AACvB,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,uBAAOA,GAAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,CAAA;AAAA,EACrC;AAEA,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,MAAM,OAAA,GACJ,cAAc,WAAA,GACV,gBAAA,GACA,cAAc,MAAA,GACZ,WAAA,GAAc,KAAK,CAAA,IAAK,gBAAA,GACxB,eAAA;AAER,IAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,kBAAA,EAAoB,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,sBACzCA,GAAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,QAAA,EACR,QAAA,kBAAAA,GAAAA,CAAC,MAAA,EAAA,EAAO,OAAA,EAAQ,SAAA,EAAW,QAAA,EAAA,SAAA,EAAU,CAAA,EACvC;AAAA,KAAA,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACb,QAAA,EAAA;AAAA,oBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oEAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,QAAA,EACR,QAAA,kBAAAC,KAAC,MAAA,EAAA,EAAO,OAAA,EAAQ,OAAA,EAAQ,IAAA,EAAK,IAAA,EAC3B,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,SAAA,EAAA,EAAU,SAAA,EAAU,cAAA,EAAe,eAAW,IAAA,EAAC,CAAA;AAAA,QAC/C;AAAA,OAAA,EACH,CAAA,EACF,CAAA;AAAA,MACC;AAAA,KAAA,EACH,CAAA;AAAA,IACC;AAAA,GAAA,EACH,CAAA;AAEJ;ACOO,SAAS,aAAA,CAAiB;AAAA,EAC/B,KAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,iBAAA;AAAA,EACA,YAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,gBAAA,GAAmB,YAAA;AAAA,EACnB,UAAA,GAAa,SAAA;AAAA,EACb,iBAAA,GAAoB,QAAA;AAAA,EACpB,gBAAA,GAAmB,OAAA;AAAA,EACnB,cAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,aAAA,GAAgB,MAAA;AAAA,EAChB,YAAA;AAAA,EACA,oBAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA,GAAiB;AACnB,CAAA,EAA0B;AACxB,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,CAAC,SAAoB,IAAA,IAAQ,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAQ,CAAA;AAC5F,EAAA,MAAM,eAAe,UAAA,KAAe,QAAA;AACpC,EAAA,MAAM,gBAAA,GAAmB,aAAA,KAAkB,UAAA,IAAc,oBAAA,IAAwB,IAAA;AACjF,EAAA,MAAM,QAAA,GAAW,YAAA,oBAAgB,IAAI,GAAA,EAAY;AACjD,EAAA,MAAM,WAAA,GAAc,SAAA,CAAU,GAAA,CAAI,CAAC,IAAA,KAAS,UAAU,IAAI,CAAC,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AAC3E,EAAA,MAAM,oBAAA,GAAuB,YAAY,MAAA,CAAO,CAAC,QAAQ,QAAA,CAAS,GAAA,CAAI,GAAG,CAAC,CAAA,CAAE,MAAA;AAC5E,EAAA,MAAM,kBAAA,GAAqB,WAAA,CAAY,MAAA,GAAS,CAAA,IAAK,yBAAyB,WAAA,CAAY,MAAA;AAC1F,EAAA,MAAM,mBAAA,GAAsB,oBAAA,GAAuB,CAAA,IAAK,CAAC,kBAAA;AACzD,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,MAAA,IAAU,gBAAA,GAAmB,CAAA,GAAI,CAAA,CAAA;AAE7D,EAAA,MAAM,SAAA,GAAY,CAAC,GAAA,EAAa,OAAA,KAAqB;AACnD,IAAA,IAAI,CAAC,oBAAA,EAAsB;AAC3B,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,QAAQ,CAAA;AAC7B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA,IACd,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AAAA,IACjB;AACA,IAAA,oBAAA,CAAqB,IAAI,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,CAAC,OAAA,KAAqB;AAC7C,IAAA,IAAI,CAAC,oBAAA,EAAsB;AAC3B,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,QAAQ,CAAA;AAC7B,IAAA,KAAA,MAAW,OAAO,WAAA,EAAa;AAC7B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,IAAA,CAAK,IAAI,GAAG,CAAA;AAAA,MACd,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AAAA,MACjB;AAAA,IACF;AACA,IAAA,oBAAA,CAAqB,IAAI,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACb,QAAA,EAAA;AAAA,oBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oEAAA,EACb,QAAA,EAAA;AAAA,sBAAAA,KAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,gCAAA,EAAkC,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,wBACtDA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,mDAAmD,QAAA,EAAA,WAAA,EAAY;AAAA,OAAA,EAC9E,CAAA;AAAA,MACC;AAAA,KAAA,EACH,CAAA;AAAA,oBAEAC,KAAC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAAA,IAAAA,CAAC,UAAA,EAAA,EAAW,SAAA,EAAU,OAAA,EACpB,QAAA,EAAA;AAAA,wBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,WAAA,EACb,QAAA,EAAA;AAAA,0BAAAD,GAAAA,CAAC,aAAW,QAAA,EAAA,SAAA,EAAU,CAAA;AAAA,UACrB,CAAC,YAAA,mBAAeA,GAAAA,CAAC,eAAA,EAAA,EAAiB,6BAAkB,CAAA,GAAqB;AAAA,SAAA,EAC5E,CAAA;AAAA,QACC,gBAAA,IAAoB,QAAA,CAAS,IAAA,GAAO,CAAA,IAAK,WAAA,mBACxCA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mCAAA,EAAqC,QAAA,EAAA,WAAA,EAAY,CAAA,GAC9D,IAAA;AAAA,QACH,+BACCA,GAAAA;AAAA,UAAC,WAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,wBAAA;AAAA,YACV,KAAA,EAAO,MAAA;AAAA,YACP,aAAA,EAAe,cAAA;AAAA,YACf,QAAA,EAAU,MAAM,cAAA,IAAiB;AAAA,YACjC,OAAA,EAAS,aAAA;AAAA,YACT,WAAA,EAAa,iBAAA;AAAA,YACb,iBAAA;AAAA,YACA;AAAA;AAAA,SACF,mBAEAC,IAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,iCAAA,EACb,QAAA,EAAA;AAAA,0BAAAD,GAAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,wEAAA;AAAA,cACV,aAAA,EAAW;AAAA;AAAA,WACb;AAAA,0BACAA,GAAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,WAAA,EAAa,iBAAA;AAAA,cACb,KAAA,EAAO,MAAA;AAAA,cACP,UAAU,CAAC,CAAA,KAAM,cAAA,CAAe,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,cAC9C,SAAA,EAAW,eAAA;AAAA,cACX,SAAA,EAAU,MAAA;AAAA,cACV,YAAA,EAAY;AAAA;AAAA;AACd,SAAA,EACF;AAAA,OAAA,EAEJ,CAAA;AAAA,sBACAA,GAAAA,CAAC,WAAA,EAAA,EACE,QAAA,EAAA,SAAA,mBACCA,GAAAA,CAAC,UAAA,EAAA,EAAW,YAAA,EAAc,gBAAA,EAAkB,CAAA,GAC1C,SAAA,CAAU,MAAA,KAAW,CAAA,mBACvBA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,yCAAA,EAA2C,QAAA,EAAA,YAAA,EAAa,CAAA,mBAErEA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+BAAA,EACb,QAAA,kBAAAC,IAAAA,CAAC,KAAA,EAAA,EAAM,WAAU,eAAA,EACf,QAAA,EAAA;AAAA,wBAAAD,IAAC,WAAA,EAAA,EACC,QAAA,kBAAAC,IAAAA,CAAC,QAAA,EAAA,EAAS,WAAU,+BAAA,EACjB,QAAA,EAAA;AAAA,UAAA,gBAAA,mBACCD,GAAAA,CAAC,SAAA,EAAA,EAAU,SAAA,EAAU,QACnB,QAAA,kBAAAA,GAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,OAAA,EACE,kBAAA,GACI,IAAA,GACA,mBAAA,GACE,eAAA,GACA,KAAA;AAAA,cAER,eAAA,EAAiB,CAAC,KAAA,KAAU,gBAAA,CAAiB,UAAU,IAAI,CAAA;AAAA,cAC3D,YAAA,EAAY,cAAA;AAAA,cACZ,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA;AAAgB;AAAA,aAEtC,CAAA,GACE,IAAA;AAAA,UACH,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAA,qBACZA,GAAAA,CAAC,SAAA,EAAA,EAAwB,SAAA,EAAW,GAAA,CAAI,SAAA,EACrC,QAAA,EAAA,GAAA,CAAI,MAAA,EAAA,EADS,GAAA,CAAI,GAEpB,CACD;AAAA,SAAA,EACH,CAAA,EACF,CAAA;AAAA,wBACAC,KAAC,SAAA,EAAA,EACE,QAAA,EAAA;AAAA,UAAA,SAAA,CAAU,GAAA,CAAI,CAAC,IAAA,EAAM,KAAA,KAAU;AAC9B,YAAA,MAAM,MAAA,GAAS,SAAA,CAAU,IAAI,CAAA,IAAK,OAAO,KAAK,CAAA,CAAA;AAC9C,YAAA,MAAM,UAAA,GAAa,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AACtC,YAAA,uBACEA,IAAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBAEC,IAAA,EAAK,QAAA;AAAA,gBACL,QAAA,EAAU,CAAA;AAAA,gBACV,YAAA,EAAY,aAAa,UAAA,GAAa,MAAA;AAAA,gBACtC,OAAA,EAAS,MAAM,UAAA,CAAW,IAAI,CAAA;AAAA,gBAC9B,SAAA,EAAW,CAAC,CAAA,KAAM;AAChB,kBAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACtC,oBAAA,CAAA,CAAE,cAAA,EAAe;AACjB,oBAAA,UAAA,CAAW,IAAI,CAAA;AAAA,kBACjB;AAAA,gBACF,CAAA;AAAA,gBACA,SAAA,EAAU,mIAAA;AAAA,gBAET,QAAA,EAAA;AAAA,kBAAA,gBAAA,mBACCD,GAAAA,CAAC,SAAA,EAAA,EAAU,SAAA,EAAU,MAAA,EAAO,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB,EAC5D,QAAA,kBAAAA,GAAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,OAAA,EAAS,UAAA;AAAA,sBACT,iBAAiB,CAAC,KAAA,KAAU,SAAA,CAAU,MAAA,EAAQ,UAAU,IAAI,CAAA;AAAA,sBAC5D,YAAA,EAAY,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,MAAM,CAAA;AAAA;AAAA,qBAE3C,CAAA,GACE,IAAA;AAAA,kBACH,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAA,qBACZA,GAAAA;AAAA,oBAAC,SAAA;AAAA,oBAAA;AAAA,sBAEC,SAAA,EAAW,EAAA,CAAG,GAAA,CAAI,SAAA,IAAa,uBAAuB,CAAA;AAAA,sBAErD,QAAA,EAAA,GAAA,CAAI,OAAO,IAAI;AAAA,qBAAA;AAAA,oBAHX,GAAA,CAAI;AAAA,mBAKZ;AAAA;AAAA,eAAA;AAAA,cA7BI;AAAA,aA8BP;AAAA,UAEJ,CAAC,CAAA;AAAA,UACA,+BACCA,GAAAA,CAAC,QAAA,EAAA,EAAS,GAAA,EAAK,aACb,QAAA,kBAAAA,GAAAA;AAAA,YAAC,SAAA;AAAA,YAAA;AAAA,cACC,OAAA,EAAS,WAAA;AAAA,cACT,SAAA,EAAU,mCAAA;AAAA,cAET,QAAA,EAAA,kBAAA,mBACCA,GAAAA,CAACE,OAAAA,EAAA,EAAQ,SAAA,EAAU,mCAAA,EAAoC,aAAA,EAAW,IAAA,EAAC,CAAA,GACjE;AAAA;AAAA,WACN,EACF;AAAA,SAAA,EAEJ;AAAA,OAAA,EACF,GACF,CAAA,EAEJ;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;ACnQO,SAAS,mBAAA,CAAoB;AAAA,EAClC,MAAA;AAAA,EACA,cAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,oBAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,iBAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF,CAAA,EAA6B;AAC3B,EAAA,uBACED,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8DAAA,EACb,QAAA,EAAA;AAAA,oBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA;AAAA,QAACG,MAAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAU,4FAAA;AAAA,UACV,aAAA,EAAW;AAAA;AAAA,OACb;AAAA,sBACAH,GAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAU,MAAA;AAAA,UACV,WAAA,EAAa,iBAAA;AAAA,UACb,KAAA,EAAO,MAAA;AAAA,UACP,UAAU,CAAC,KAAA,KAAU,cAAA,CAAe,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,UACtD,YAAA,EAAY;AAAA;AAAA;AACd,KAAA,EACF,CAAA;AAAA,oBACAA,GAAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,gBAAA;AAAA,QACV,KAAA,EAAO,MAAA;AAAA,QACP,aAAA,EAAe,cAAA;AAAA,QACf,OAAA,EAAS,aAAA;AAAA,QACT,YAAA,EAAY;AAAA;AAAA,KACd;AAAA,oBACAA,GAAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,gBAAA;AAAA,QACV,KAAA,EAAO,YAAA;AAAA,QACP,aAAA,EAAe,oBAAA;AAAA,QACf,OAAA,EAAS,eAAA;AAAA,QACT,YAAA,EAAY;AAAA;AAAA;AACd,GAAA,EACF,CAAA;AAEJ;ACjDO,SAAS,UAAA,CAAW;AAAA,EACzB,SAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,gBAAA,GAAmB,gDAAA;AAAA,EACnB,UAAA,GAAa,WAAA;AAAA,EACb,YAAA,GAAe;AACjB,CAAA,EAAoB;AAClB,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,uBACEA,GAAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,SAAA,EAAU,SAAA,EAAW,kBAAkB,YAAA,EAA4B,CAAA;AAAA,EAE3F;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,OAAA,GAAU,WAAA,GAAc,KAAK,CAAA,IAAK,gBAAA;AACxC,IAAA,uBACEA,GAAAA,CAAC,KAAA,EAAA,EAAM,OAAA,EAAQ,aAAA,EAAc,SAAA,EAAU,UAAA,EACrC,QAAA,kBAAAC,IAAAA,CAAC,gBAAA,EAAA,EAAiB,SAAA,EAAU,WAAA,EAC1B,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,OAAG,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,MACX,OAAA,mBACCA,GAAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,OAAA,EAAQ,SAAA,EAAU,IAAA,EAAK,IAAA,EAAK,OAAA,EAAS,OAAA,EACxD,sBACH,CAAA,GACE;AAAA,KAAA,EACN,CAAA,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,uBAAOA,GAAAA,CAAAI,QAAAA,EAAA,EAAG,QAAA,EAAS,CAAA;AACrB;AC5CO,SAAS,sBAAsB,WAAA,EAAuC;AAC3E,EAAA,SAAS,gBAAgB,KAAA,EAA6B;AACpD,IAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,IAAA,uBAAOJ,GAAAA,CAAC,UAAA,EAAA,EAAY,GAAG,QAAA,EAAW,GAAG,KAAA,EAAO,CAAA;AAAA,EAC9C;AAEA,EAAA,eAAA,CAAgB,WAAA,GAAc,iBAAA;AAC9B,EAAA,OAAO,eAAA;AACT;ACMO,SAAS,eAAA,CAAgB;AAAA,EAC9B,KAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAA;AAAA,EACA,OAAA,GAAU,OAAA;AAAA,EACV,CAAA;AAAA,EACA,aAAA,GAAgB;AAClB,CAAA,EAAyB;AACvB,EAAA,MAAM,SAAS,OAAA,KAAY,QAAA;AAC3B,EAAA,MAAM,UAAU,OAAA,KAAY,SAAA;AAE5B,EAAA,MAAM,yBACJA,GAAAA;AAAA,IAAC,eAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,+CAAA;AAAA,QACA,OAAA,GAAU,gBAAA,GAAmB,MAAA,GAAS,kBAAA,GAAqB;AAAA,OAC7D;AAAA,MACA,KAAA;AAAA,MACA,QAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAA,EAAc,2BAAA,CAA4B,CAAA,EAAG,aAAa,CAAA;AAAA,MAC1D;AAAA;AAAA,GACF;AAGF,EAAA,MAAM,+BACJA,GAAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAU,6DAA6D,QAAA,EAAA,KAAA,EAAM,CAAA;AAGtF,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,WAAW,EAAA,CAAG,oCAAA,EAAsC,SAAS,CAAA,EAC/D,QAAA,EAAA;AAAA,MAAA,YAAA;AAAA,MACA;AAAA,KAAA,EACH,CAAA;AAAA,EAEJ;AAEA,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,uBACEA,IAAAA,CAAC,KAAA,EAAA,EAAI,WAAW,EAAA,CAAG,+CAAA,EAAiD,SAAS,CAAA,EAC1E,QAAA,EAAA;AAAA,MAAA,YAAA;AAAA,MACA,MAAA;AAAA,MACA,uBAAOD,GAAAA,CAAC,OAAE,SAAA,EAAU,sCAAA,EAAwC,gBAAK,CAAA,GAAO;AAAA,KAAA,EAC3E,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACEA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,8DAAA,EAAgE,SAAS,CAAA,EAC1F,QAAA,kBAAAC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iEAAA,EACb,QAAA,EAAA;AAAA,oBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mBAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,mEAAA,EACV,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,MACC,uBAAOA,GAAAA,CAAC,OAAE,SAAA,EAAU,+BAAA,EAAiC,gBAAK,CAAA,GAAO;AAAA,KAAA,EACpE,CAAA;AAAA,IACC;AAAA,GAAA,EACH,CAAA,EACF,CAAA;AAEJ;ACxEO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA,GAAe;AACjB,CAAA,EAAyB;AACvB,EAAA,uBACEA,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAW,EAAA,CAAG,2BAAA,EAA6B,SAAS,CAAA,EACrD,QAAA,EAAA,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,EAAO,sBAClBC,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAEC,SAAA,EAAW,EAAA;AAAA,QACT,WAAA;AAAA,QACA,iBAAiB,UAAA,IAAc,2CAAA;AAAA,QAC/B,KAAA,CAAM,SAAS,CAAA,IAAK;AAAA,OACtB;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,2CAAA,EAA6C,gBAAM,KAAA,EAAM,CAAA;AAAA,wBACvEA,GAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,qBAAA,EAAuB,gBAAM,KAAA,EAAM;AAAA;AAAA,KAAA;AAAA,IAR5C,CAAA,EAAG,KAAA,CAAM,KAAK,CAAA,CAAA,EAAI,CAAC,CAAA;AAAA,GAU3B,CAAA,EACH,CAAA;AAEJ;ACAO,SAAS,SAAA,CAAU;AAAA,EACxB,IAAA,EAAM,IAAA;AAAA,EACN,KAAA;AAAA,EACA,YAAA,GAAe,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,MAAA;AAAA,EACnD,WAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAU,EAAC;AAAA,EACX,QAAA,GAAW,KAAA;AAAA,EACX,QAAA;AAAA,EACA;AACF,CAAA,EAAmB;AACjB,EAAA,MAAM,UAAA,GAAa,QAAQ,MAAA,GAAS,CAAA;AACpC,EAAA,MAAM,WAAA,GAAc,CAAC,CAAC,OAAA;AAEtB,EAAA,uBACEC,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAM,cAAc,QAAA,GAAW,MAAA;AAAA,MAC/B,QAAA,EAAU,cAAc,CAAA,GAAI,MAAA;AAAA,MAC5B,OAAA;AAAA,MACA,SAAA,EACE,WAAA,GACI,CAAC,CAAA,KAAM;AACL,QAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACtC,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,OAAA,IAAU;AAAA,QACZ;AAAA,MACF,CAAA,GACA,MAAA;AAAA,MAEN,SAAA,EAAW,EAAA;AAAA,QACT,6DAAA;AAAA,QACA,WAAW,aAAA,GAAgB,mBAAA;AAAA,QAC3B,WAAA,IACE,oHAAA;AAAA,QACF;AAAA,OACF;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EACb,QAAA,EAAA;AAAA,0BAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wCAAA,EACb,QAAA,EAAA;AAAA,4BAAAD,GAAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAW,EAAA;AAAA,kBACT,gEAAA;AAAA,kBACA,WAAW,UAAA,GAAa;AAAA,iBAC1B;AAAA,gBAEA,QAAA,kBAAAA,GAAAA;AAAA,kBAAC,IAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA,CAAG,SAAA,EAAW,QAAA,GAAW,0BAA0B,cAAc,CAAA;AAAA,oBAC5E,aAAA,EAAW;AAAA;AAAA;AACb;AAAA,aACF;AAAA,4BACAC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gBAAA,EACZ,QAAA,EAAA;AAAA,cAAA,YAAA,mBACCD,IAAC,OAAA,EAAA,EAAQ,OAAA,EAAS,cAAc,IAAA,EAAK,KAAA,EAAM,KAAA,EAAM,OAAA,EAC/C,QAAA,kBAAAA,GAAAA,CAAC,OAAE,SAAA,EAAU,6CAAA,EAA+C,iBAAM,CAAA,EACpE,CAAA,mBAEAA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,6CAAA,EAA+C,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,cAEnE,4BACCA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,wEACV,QAAA,EAAA,QAAA,EACH;AAAA,aAAA,EAEJ;AAAA,WAAA,EACF,CAAA;AAAA,UACC,UAAA,oBACCC,IAAAA,CAAC,YAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAAD,GAAAA,CAAC,mBAAA,EAAA,EAAoB,OAAA,EAAO,IAAA,EAC1B,QAAA,kBAAAA,GAAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,OAAA,EAAQ,OAAA;AAAA,gBACR,IAAA,EAAK,MAAA;AAAA,gBACL,SAAA,EAAU,gBAAA;AAAA,gBACV,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,gBAClC,YAAA,EAAW,WAAA;AAAA,gBAEX,0BAAAA,GAAAA,CAAC,YAAA,EAAA,EAAa,SAAA,EAAU,SAAA,EAAU,eAAW,IAAA,EAAC;AAAA;AAAA,aAChD,EACF,CAAA;AAAA,4BACAA,GAAAA,CAAC,mBAAA,EAAA,EAAoB,KAAA,EAAM,KAAA,EAAM,YAAY,CAAA,EAAG,OAAA,EAAS,CAAC,CAAA,KAAM,EAAE,eAAA,EAAgB,EAC/E,QAAA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvB,cAAA,MAAM,aAAa,MAAA,CAAO,IAAA;AAC1B,cAAA,uBACEC,IAAAA;AAAA,gBAAC,gBAAA;AAAA,gBAAA;AAAA,kBAEC,SAAS,MAAA,CAAO,OAAA;AAAA,kBAChB,SAAA,EACE,MAAA,CAAO,OAAA,KAAY,aAAA,GACf,yCAAA,GACA,MAAA;AAAA,kBAGN,QAAA,EAAA;AAAA,oCAAAD,GAAAA,CAAC,UAAA,EAAA,EAAW,SAAA,EAAU,cAAA,EAAe,eAAW,IAAA,EAAC,CAAA;AAAA,oBAChD,MAAA,CAAO;AAAA;AAAA,iBAAA;AAAA,gBATH,MAAA,CAAO;AAAA,eAUd;AAAA,YAEJ,CAAC,CAAA,EACH;AAAA,WAAA,EACF;AAAA,SAAA,EAEJ,CAAA;AAAA,QACC,+BACCA,GAAAA,CAAC,OAAA,EAAA,EAAQ,OAAA,EAAS,aAAa,IAAA,EAAK,KAAA,EAAM,KAAA,EAAM,OAAA,EAC9C,0BAAAA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,uIAAA,EACV,uBACH,CAAA,EACF,CAAA;AAAA,QAED;AAAA;AAAA;AAAA,GACH;AAEJ;AClIO,SAAS,aAAA,CAAc;AAAA,EAC5B,KAAA;AAAA,EACA,KAAA;AAAA,EACA,aAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA,GAAS;AACX,CAAA,EAAuB;AACrB,EAAA,MAAM,yBACJA,GAAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,SAAA,KAAc,MAAA,KAAW,SAAA,GAAY,uBAAA,GAA0B,YAAA,CAAA;AAAA,MAC1E,kBAAkB,gBAAA,IAAoB,KAAA;AAAA,MACtC,KAAA;AAAA,MACA,aAAA;AAAA,MACA,OAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA;AAAA,MACA,UAAA;AAAA,MACA,YAAA;AAAA,MACA,YAAA,EAAY;AAAA;AAAA,GACd;AAGF,EAAA,IAAI,WAAW,QAAA,EAAU;AACvB,IAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,oDAAA,EAAsD,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,MAC3E;AAAA,KAAA,EACH,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACEC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EACb,QAAA,EAAA;AAAA,oBAAAD,GAAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAU,2DAAA,EAA6D,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,IACnF;AAAA,GAAA,EACH,CAAA;AAEJ;ACxDO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAU,YAAA,EAAa,EAAuB;AAC5E,EAAA,uBACEA,GAAAA,CAAC,QAAA,EAAA,EAAS,QAAA,kBAAUA,GAAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,YAAA,EAA4B,CAAA,EACzE,QAAA,EACH,CAAA;AAEJ;ACHO,SAAS,oBAAA,GAAmC;AACjD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAA2B,OAAO,CAAA;AAC5D,EAAA,OAAO,EAAE,OAAO,QAAA,EAAS;AAC3B;AAEA,IAAM,iBAAA,GAAoB,KAAA,CAAM,aAAA,CAAiC,IAAI,CAAA;AAG9D,SAAS,oBAAA,GAA0C;AACxD,EAAA,OAAO,KAAA,CAAM,WAAW,iBAAiB,CAAA;AAC3C;AA+BO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAU,aAAA,EAAe,mBAAkB,EAAuB;AAChG,EAAA,MAAM,mBAAmB,iBAAA,IAAqB,oBAAA;AAC9C,EAAA,MAAM,QAAQ,gBAAA,EAAiB;AAE/B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,IAAA,GAAO,OAAO,QAAA,CAAS,eAAA;AAC7B,IAAA,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,OAAA,EAAS,MAAM,CAAA;AACrC,IAAA,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA;AAAA,EAChC,CAAA,EAAG,CAAC,KAAA,CAAM,KAAK,CAAC,CAAA;AAEhB,EAAA,uBAAOA,GAAAA,CAAC,iBAAA,CAAkB,UAAlB,EAA2B,KAAA,EAAO,OAAQ,QAAA,EAAS,CAAA;AAC7D;AClDA,SAAS,oBAAA,CAAqB;AAAA,EAC5B,aAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,MAAM,EAAE,KAAA,EAAO,QAAA,EAAS,GAAI,aAAA,EAAc;AAC1C,EAAA,MAAM,cAAc,MAAM,QAAA,CAAS,KAAA,KAAU,MAAA,GAAS,UAAU,MAAM,CAAA;AACtE,EAAA,uBACEA,GAAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,OAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,OAAA,EAAS,WAAA;AAAA,MACT,YAAA,EAAY,SAAA;AAAA,MACZ,SAAA,EAAU,gBAAA;AAAA,MAET,QAAA,EAAA,KAAA,KAAU,MAAA,mBAASA,GAAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAU,CAAA,mBAAKA,GAAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAU,SAAA,EAAU;AAAA;AAAA,GAC9E;AAEJ;AAEA,SAAS,sBAAA,CAAuB,EAAE,SAAA,EAAU,EAA0B;AACpE,EAAA,MAAM,cAAc,oBAAA,EAAqB;AACzC,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAC1C,EAAA,MAAM,QAAQ,WAAA,IAAe,YAAA;AAC7B,EAAA,MAAM,WAAA,GAAc,MAAM,KAAA,CAAM,QAAA,CAAS,MAAM,KAAA,KAAU,MAAA,GAAS,UAAU,MAAM,CAAA;AAClF,EAAA,uBACEA,GAAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,OAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,OAAA,EAAS,WAAA;AAAA,MACT,YAAA,EAAY,SAAA;AAAA,MACZ,SAAA,EAAU,gBAAA;AAAA,MAET,QAAA,EAAA,KAAA,CAAM,KAAA,KAAU,MAAA,mBAASA,GAAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAU,CAAA,mBAAKA,GAAAA,CAAC,GAAA,EAAA,EAAI,WAAU,SAAA,EAAU;AAAA;AAAA,GACpF;AAEJ;AAmBO,SAAS,WAAA,CAAY;AAAA,EAC1B,aAAA,EAAe,iBAAA;AAAA,EACf,SAAA,GAAY;AACd,CAAA,EAAqB;AACnB,EAAA,IAAI,iBAAA,EAAmB;AACrB,IAAA,uBAAOA,GAAAA,CAAC,oBAAA,EAAA,EAAqB,aAAA,EAAe,mBAAmB,SAAA,EAAsB,CAAA;AAAA,EACvF;AACA,EAAA,uBAAOA,GAAAA,CAAC,sBAAA,EAAA,EAAuB,SAAA,EAAsB,CAAA;AACvD","file":"chunk-BL4GPQGY.js","sourcesContent":["import { Loader2 } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\n\nimport { cn } from '../../../lib/utils';\n\nexport interface PageLoaderProps {\n className?: string;\n spinnerClassName?: string;\n /**\n * - `page`: full-screen route guard\n * - `route`: lazy route / admin detail suspense (`min-h-[40vh]`)\n * - `section`: in-page block (`py-12`)\n * - `inline`: compact centered spinner (auth cards, nested panels)\n */\n variant?: 'page' | 'route' | 'section' | 'inline';\n /** Accessible loading text; defaults to i18n `common.loading`. */\n loadingLabel?: string;\n}\n\nexport function PageLoader({\n className,\n spinnerClassName,\n variant = 'section',\n loadingLabel,\n}: PageLoaderProps) {\n const { t } = useTranslation('common');\n const label = loadingLabel ?? t('loading');\n const spinnerSize =\n variant === 'inline'\n ? 'h-6 w-6 animate-spin text-primary'\n : 'h-8 w-8 animate-spin text-primary';\n\n const content = (\n <>\n <Loader2 className={cn(spinnerSize, spinnerClassName)} aria-hidden />\n <span className=\"sr-only\">{label}</span>\n </>\n );\n\n if (variant === 'page') {\n return (\n <div\n className={cn('min-h-screen flex items-center justify-center bg-background', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {content}\n </div>\n );\n }\n\n if (variant === 'route') {\n return (\n <div\n className={cn('flex min-h-[40vh] items-center justify-center', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {content}\n </div>\n );\n }\n\n if (variant === 'inline') {\n return (\n <div\n className={cn('flex items-center justify-center', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {content}\n </div>\n );\n }\n\n return (\n <div\n className={cn('flex items-center justify-center py-12', className)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {content}\n </div>\n );\n}\n","import { ArrowLeft } from 'lucide-react';\nimport { Link } from 'react-router';\nimport type { AdminDetailErrorKind } from '../../../types/admin';\nimport { Button } from '../../ui';\nimport { PageLoader } from '../page-loader';\n\nexport type { AdminDetailErrorKind };\n\nexport interface AdminDetailPageProps {\n backHref: string;\n backLabel: string;\n isLoading: boolean;\n errorKind?: AdminDetailErrorKind;\n error?: unknown;\n notFoundMessage: string;\n forbiddenMessage: string;\n loadErrorMessage: string;\n /** App-bound API error formatter (e.g. formatApiError from SPA helpers). */\n formatError?: (error: unknown) => string;\n children: React.ReactNode;\n headerActions?: React.ReactNode;\n}\n\n/** Reusable admin detail page: back button, loading/error states, content. */\nexport function AdminDetailPage({\n backHref,\n backLabel,\n isLoading,\n errorKind,\n error,\n notFoundMessage,\n forbiddenMessage,\n loadErrorMessage,\n formatError,\n children,\n headerActions,\n}: AdminDetailPageProps) {\n if (isLoading) {\n return <PageLoader variant=\"route\" />;\n }\n\n if (errorKind) {\n const message =\n errorKind === 'forbidden'\n ? forbiddenMessage\n : errorKind === 'load'\n ? formatError?.(error) || loadErrorMessage\n : notFoundMessage;\n\n return (\n <div className=\"space-y-4 sm:space-y-6\">\n <p className=\"text-destructive\">{message}</p>\n <Link to={backHref}>\n <Button variant=\"outline\">{backLabel}</Button>\n </Link>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <Link to={backHref}>\n <Button variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"mr-2 h-4 w-4\" aria-hidden />\n {backLabel}\n </Button>\n </Link>\n {headerActions}\n </div>\n {children}\n </div>\n );\n}\n","import { Loader2, Search } from 'lucide-react';\n\nimport type { AdminListSearchMode } from '../../../hooks/useAdminListPage';\nimport { cn } from '../../../lib/utils';\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Checkbox,\n Input,\n SearchField,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '../../ui';\nimport { PageLoader } from '../page-loader';\n\nexport type { AdminListSearchMode };\n\nexport interface AdminListPageColumn<T> {\n key: string;\n header: string;\n render: (item: T) => React.ReactNode;\n /** Column class for alignment/width */\n className?: string;\n}\n\nexport type AdminListSelectionMode = 'none' | 'multiple';\n\nexport interface AdminListPageProps<T> {\n title: string;\n description: string;\n /** Optional actions (e.g. \"Create\" button) rendered next to the page title. */\n headerActions?: React.ReactNode;\n /** Card header title (e.g. \"All Users\") */\n cardTitle: string;\n searchPlaceholder: string;\n emptyMessage: string;\n columns: AdminListPageColumn<T>[];\n items: T[];\n isLoading: boolean;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n loadMoreRef: React.RefObject<HTMLTableRowElement | null>;\n search: string;\n onSearchChange: (value: string) => void;\n onRowClick: (item: T) => void;\n getRowKey: (item: T) => string;\n /** Accessible label for the loading spinner (app i18n). */\n loadingAriaLabel?: string;\n /** `instant` updates query on debounced typing; `submit` requires Search/Clear actions. */\n searchMode?: AdminListSearchMode;\n /** Label for the Search button when `searchMode` is `submit`. */\n searchButtonLabel?: string;\n /** Label for the Clear button when `searchMode` is `submit`. */\n clearButtonLabel?: string;\n /** Called when the user clicks Search or presses Enter in submit mode. */\n onSearchSubmit?: () => void;\n /** Called when the user clicks Clear in submit mode. */\n onSearchClear?: () => void;\n /** Enter-key handler for submit mode (typically from `useAdminListPage().onSearchKeyDown`). */\n onSearchKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n /** Optional row multi-select (default none). */\n selectionMode?: AdminListSelectionMode;\n selectedKeys?: Set<string>;\n onSelectedKeysChange?: (keys: Set<string>) => void;\n /** Rendered in the card header when selectionMode is multiple and keys are selected. */\n bulkActions?: React.ReactNode;\n /** Accessible label for the selection checkbox column. */\n selectionLabel?: string;\n}\n\n/**\n * Reusable admin list page: search + card + table + infinite scroll.\n */\nexport function AdminListPage<T>({\n title,\n description,\n headerActions,\n cardTitle,\n searchPlaceholder,\n emptyMessage,\n columns,\n items,\n isLoading,\n hasNextPage,\n isFetchingNextPage,\n loadMoreRef,\n search,\n onSearchChange,\n onRowClick,\n getRowKey,\n loadingAriaLabel = 'Loading...',\n searchMode = 'instant',\n searchButtonLabel = 'Search',\n clearButtonLabel = 'Clear',\n onSearchSubmit,\n onSearchClear,\n onSearchKeyDown,\n selectionMode = 'none',\n selectedKeys,\n onSelectedKeysChange,\n bulkActions,\n selectionLabel = 'Select',\n}: AdminListPageProps<T>) {\n const safeItems = items.filter((item): item is T => item != null && typeof item === 'object');\n const isSubmitMode = searchMode === 'submit';\n const selectionEnabled = selectionMode === 'multiple' && onSelectedKeysChange != null;\n const selected = selectedKeys ?? new Set<string>();\n const visibleKeys = safeItems.map((item) => getRowKey(item)).filter(Boolean);\n const selectedVisibleCount = visibleKeys.filter((key) => selected.has(key)).length;\n const allVisibleSelected = visibleKeys.length > 0 && selectedVisibleCount === visibleKeys.length;\n const someVisibleSelected = selectedVisibleCount > 0 && !allVisibleSelected;\n const columnCount = columns.length + (selectionEnabled ? 1 : 0);\n\n const toggleKey = (key: string, checked: boolean) => {\n if (!onSelectedKeysChange) return;\n const next = new Set(selected);\n if (checked) {\n next.add(key);\n } else {\n next.delete(key);\n }\n onSelectedKeysChange(next);\n };\n\n const toggleAllVisible = (checked: boolean) => {\n if (!onSelectedKeysChange) return;\n const next = new Set(selected);\n for (const key of visibleKeys) {\n if (checked) {\n next.add(key);\n } else {\n next.delete(key);\n }\n }\n onSelectedKeysChange(next);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">{title}</h1>\n <p className=\"mt-1 text-sm text-muted-foreground sm:text-base\">{description}</p>\n </div>\n {headerActions}\n </div>\n\n <Card>\n <CardHeader className=\"gap-4\">\n <div className=\"space-y-2\">\n <CardTitle>{cardTitle}</CardTitle>\n {!isSubmitMode ? <CardDescription>{searchPlaceholder}</CardDescription> : null}\n </div>\n {selectionEnabled && selected.size > 0 && bulkActions ? (\n <div className=\"flex flex-wrap items-center gap-2\">{bulkActions}</div>\n ) : null}\n {isSubmitMode ? (\n <SearchField\n className=\"max-w-full sm:max-w-sm\"\n value={search}\n onValueChange={onSearchChange}\n onSubmit={() => onSearchSubmit?.()}\n onClear={onSearchClear}\n placeholder={searchPlaceholder}\n searchButtonLabel={searchButtonLabel}\n clearButtonLabel={clearButtonLabel}\n />\n ) : (\n <div className=\"relative max-w-full sm:max-w-sm\">\n <Search\n className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\"\n aria-hidden\n />\n <Input\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => onSearchChange(e.target.value)}\n onKeyDown={onSearchKeyDown}\n className=\"pl-9\"\n aria-label={searchPlaceholder}\n />\n </div>\n )}\n </CardHeader>\n <CardContent>\n {isLoading ? (\n <PageLoader loadingLabel={loadingAriaLabel} />\n ) : safeItems.length === 0 ? (\n <p className=\"py-12 text-center text-muted-foreground\">{emptyMessage}</p>\n ) : (\n <div className=\"-mx-4 overflow-x-auto sm:mx-0\">\n <Table className=\"min-w-[400px]\">\n <TableHeader>\n <TableRow className=\"bg-muted/50 hover:bg-muted/50\">\n {selectionEnabled ? (\n <TableHead className=\"w-10\">\n <Checkbox\n checked={\n allVisibleSelected\n ? true\n : someVisibleSelected\n ? 'indeterminate'\n : false\n }\n onCheckedChange={(value) => toggleAllVisible(value === true)}\n aria-label={selectionLabel}\n onClick={(e) => e.stopPropagation()}\n />\n </TableHead>\n ) : null}\n {columns.map((col) => (\n <TableHead key={col.key} className={col.className}>\n {col.header}\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {safeItems.map((item, index) => {\n const rowKey = getRowKey(item) || `row-${index}`;\n const isSelected = selected.has(rowKey);\n return (\n <TableRow\n key={rowKey}\n role=\"button\"\n tabIndex={0}\n data-state={isSelected ? 'selected' : undefined}\n onClick={() => onRowClick(item)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onRowClick(item);\n }\n }}\n className=\"cursor-pointer hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\"\n >\n {selectionEnabled ? (\n <TableCell className=\"w-10\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n checked={isSelected}\n onCheckedChange={(value) => toggleKey(rowKey, value === true)}\n aria-label={`${selectionLabel} ${rowKey}`}\n />\n </TableCell>\n ) : null}\n {columns.map((col) => (\n <TableCell\n key={col.key}\n className={cn(col.className ?? 'text-muted-foreground')}\n >\n {col.render(item)}\n </TableCell>\n ))}\n </TableRow>\n );\n })}\n {hasNextPage && (\n <TableRow ref={loadMoreRef}>\n <TableCell\n colSpan={columnCount}\n className=\"text-center text-muted-foreground\"\n >\n {isFetchingNextPage ? (\n <Loader2 className=\"inline-block h-5 w-5 animate-spin\" aria-hidden />\n ) : null}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n","import { Search } from 'lucide-react';\n\nimport { Input } from '../../ui/input';\nimport { Select } from '../../ui/select';\n\nexport type AuditLogFilterOption = {\n value: string;\n label: string;\n};\n\nexport type AuditLogListFiltersProps = {\n search: string;\n onSearchChange: (value: string) => void;\n action: string;\n onActionChange: (value: string) => void;\n resourceType: string;\n onResourceTypeChange: (value: string) => void;\n actionOptions: AuditLogFilterOption[];\n resourceOptions: AuditLogFilterOption[];\n searchPlaceholder: string;\n actionFilterLabel: string;\n resourceFilterLabel: string;\n};\n\nexport function AuditLogListFilters({\n search,\n onSearchChange,\n action,\n onActionChange,\n resourceType,\n onResourceTypeChange,\n actionOptions,\n resourceOptions,\n searchPlaceholder,\n actionFilterLabel,\n resourceFilterLabel,\n}: AuditLogListFiltersProps) {\n return (\n <div className=\"flex flex-col gap-2 lg:flex-row lg:flex-wrap lg:items-center\">\n <div className=\"relative w-full lg:max-w-md\">\n <Search\n className=\"pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground\"\n aria-hidden\n />\n <Input\n className=\"pl-9\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(event) => onSearchChange(event.target.value)}\n aria-label={searchPlaceholder}\n />\n </div>\n <Select\n className=\"w-full lg:w-44\"\n value={action}\n onValueChange={onActionChange}\n options={actionOptions}\n aria-label={actionFilterLabel}\n />\n <Select\n className=\"w-full lg:w-52\"\n value={resourceType}\n onValueChange={onResourceTypeChange}\n options={resourceOptions}\n aria-label={resourceFilterLabel}\n />\n </div>\n );\n}\n","import { Alert, AlertDescription, Button } from '../../ui';\nimport { PageLoader } from '../page-loader';\n\nexport interface QueryStateProps {\n isLoading: boolean;\n isError?: boolean;\n error?: unknown;\n onRetry?: () => void;\n loadingClassName?: string;\n children: React.ReactNode;\n /** App-bound API error formatter (e.g. formatApiError from SPA helpers). */\n formatError?: (error: unknown) => string;\n /** Fallback copy when formatError returns empty. */\n errorDescription?: string;\n retryLabel?: string;\n loadingLabel?: string;\n}\n\n/** Renders loading/error states for TanStack Query results, then children on success. */\nexport function QueryState({\n isLoading,\n isError,\n error,\n onRetry,\n loadingClassName,\n children,\n formatError,\n errorDescription = 'We could not load this data. Please try again.',\n retryLabel = 'Try again',\n loadingLabel = 'Loading...',\n}: QueryStateProps) {\n if (isLoading) {\n return (\n <PageLoader variant=\"section\" className={loadingClassName} loadingLabel={loadingLabel} />\n );\n }\n\n if (isError) {\n const message = formatError?.(error) || errorDescription;\n return (\n <Alert variant=\"destructive\" className=\"max-w-xl\">\n <AlertDescription className=\"space-y-3\">\n <p>{message}</p>\n {onRetry ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onRetry}>\n {retryLabel}\n </Button>\n ) : null}\n </AlertDescription>\n </Alert>\n );\n }\n\n return <>{children}</>;\n}\n","import { QueryState, type QueryStateProps } from '../query-state';\n\nexport type QueryStateBindings = Pick<\n QueryStateProps,\n 'formatError' | 'errorDescription' | 'retryLabel' | 'loadingLabel'\n>;\n\nexport type BoundQueryStateProps = Omit<QueryStateProps, keyof QueryStateBindings>;\n\n/** Factory for SPA-local i18n/error bindings around shared QueryState. */\nexport function createBoundQueryState(useBindings: () => QueryStateBindings) {\n function BoundQueryState(props: BoundQueryStateProps) {\n const bindings = useBindings();\n return <QueryState {...bindings} {...props} />;\n }\n\n BoundQueryState.displayName = 'BoundQueryState';\n return BoundQueryState;\n}\n","import type { DateRange } from 'react-day-picker';\n\nimport { dateRangePickerTranslations } from '../../../lib/dateRangePickerTranslations';\nimport { cn } from '../../../lib/utils';\nimport { DateRangePicker } from '../../ui/date-range-picker';\nimport { Label } from '../../ui/label';\n\nexport interface DateRangeFilterProps {\n label: string;\n value: DateRange | undefined;\n onChange: (value: DateRange | undefined) => void;\n onApply: (value: DateRange | undefined) => void;\n minDate?: Date;\n maxDate?: Date;\n className?: string;\n hint?: string;\n /** Layout variant for toolbar vs panel contexts. */\n variant?: 'panel' | 'inline' | 'toolbar';\n /** i18n translate function (e.g. from useTranslationNamespace). */\n t: (key: string) => string;\n /** Namespace prefix for dateRange keys (default: `common`). */\n translationNs?: string;\n}\n\nexport function DateRangeFilter({\n label,\n value,\n onChange,\n onApply,\n minDate,\n maxDate,\n className,\n hint,\n variant = 'panel',\n t,\n translationNs = 'common',\n}: DateRangeFilterProps) {\n const inline = variant === 'inline';\n const toolbar = variant === 'toolbar';\n\n const picker = (\n <DateRangePicker\n className={cn(\n 'w-full min-w-0 border-border/80 bg-background',\n toolbar ? 'w-full min-w-0' : inline ? 'sm:max-w-[280px]' : 'sm:max-w-[320px]',\n )}\n value={value}\n onChange={onChange}\n minDate={minDate}\n maxDate={maxDate}\n translations={dateRangePickerTranslations(t, translationNs)}\n onApply={onApply}\n />\n );\n\n const toolbarLabel = (\n <Label className=\"text-xs font-medium tracking-normal text-muted-foreground\">{label}</Label>\n );\n\n if (toolbar) {\n return (\n <div className={cn('flex min-w-0 w-full flex-col gap-1', className)}>\n {toolbarLabel}\n {picker}\n </div>\n );\n }\n\n if (inline) {\n return (\n <div className={cn('flex w-full shrink-0 flex-col gap-1 sm:w-auto', className)}>\n {toolbarLabel}\n {picker}\n {hint ? <p className=\"text-[0.65rem] text-muted-foreground\">{hint}</p> : null}\n </div>\n );\n }\n\n return (\n <div className={cn('rounded-lg border border-border/80 bg-muted/25 p-3 shadow-sm', className)}>\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between\">\n <div className=\"min-w-0 space-y-1\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </p>\n {hint ? <p className=\"text-xs text-muted-foreground\">{hint}</p> : null}\n </div>\n {picker}\n </div>\n </div>\n );\n}\n\nexport { dateRangePickerTranslations };\n","import { cn } from '../../../lib/utils';\n\nexport interface DetailField {\n label: string;\n value: React.ReactNode;\n /** Span 2 columns on sm+ */\n span?: 1 | 2;\n}\n\nexport type DetailFieldVariant = 'plain' | 'bordered';\n\nexport interface DetailFieldListProps {\n fields: DetailField[];\n className?: string;\n /** When `bordered`, each label/value group is shown in a subtle framed cell. */\n fieldVariant?: DetailFieldVariant;\n}\n\n/** Definition list for detail views. Responsive two-column grid on sm+. */\nexport function DetailFieldList({\n fields,\n className,\n fieldVariant = 'plain',\n}: DetailFieldListProps) {\n return (\n <dl className={cn('grid gap-4 sm:grid-cols-2', className)}>\n {fields.map((field, i) => (\n <div\n key={`${field.label}-${i}`}\n className={cn(\n 'space-y-1',\n fieldVariant === 'bordered' && 'rounded-lg border bg-muted/20 px-3 py-2.5',\n field.span === 2 && 'sm:col-span-2',\n )}\n >\n <dt className=\"text-sm font-medium text-muted-foreground\">{field.label}</dt>\n <dd className=\"text-sm break-words\">{field.value}</dd>\n </div>\n ))}\n </dl>\n );\n}\n","import type { LucideIcon } from 'lucide-react';\nimport { MoreVertical } from 'lucide-react';\n\nimport { cn } from '../../../lib/utils';\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Tooltip,\n} from '../../ui';\n\nexport interface EntityRowAction {\n label: string;\n icon: LucideIcon;\n onClick: () => void;\n variant?: 'default' | 'destructive';\n}\n\nexport interface EntityRowProps {\n icon: LucideIcon;\n title: React.ReactNode;\n /** Tooltip for title (when title is truncated). Omit when title is a ReactNode. */\n titleTooltip?: string;\n description?: string;\n /** Secondary line below title (e.g. created date, email) */\n subtitle?: string;\n onClick?: () => void;\n actions?: EntityRowAction[];\n /** Muted styling for archived items */\n archived?: boolean;\n /** Extra content below main block (e.g. key prefix, metadata) */\n children?: React.ReactNode;\n className?: string;\n}\n\n/**\n * Reusable row for settings entity lists (roles, teams, members, API keys).\n * Consistent layout: icon + title + optional description/subtitle + actions dropdown.\n */\nexport function EntityRow({\n icon: Icon,\n title,\n titleTooltip = typeof title === 'string' ? title : undefined,\n description,\n subtitle,\n onClick,\n actions = [],\n archived = false,\n children,\n className,\n}: EntityRowProps) {\n const hasActions = actions.length > 0;\n const isClickable = !!onClick;\n\n return (\n <div\n role={isClickable ? 'button' : undefined}\n tabIndex={isClickable ? 0 : undefined}\n onClick={onClick}\n onKeyDown={\n isClickable\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick?.();\n }\n }\n : undefined\n }\n className={cn(\n 'flex flex-col gap-2 rounded-lg border p-4 transition-colors',\n archived ? 'bg-muted/30' : 'hover:bg-muted/40',\n isClickable &&\n 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n className,\n )}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n archived ? 'bg-muted' : 'bg-primary/10',\n )}\n >\n <Icon\n className={cn('h-5 w-5', archived ? 'text-muted-foreground' : 'text-primary')}\n aria-hidden\n />\n </div>\n <div className=\"min-w-0 flex-1\">\n {titleTooltip ? (\n <Tooltip content={titleTooltip} side=\"top\" align=\"start\">\n <p className=\"truncate font-medium max-w-full sm:max-w-md\">{title}</p>\n </Tooltip>\n ) : (\n <p className=\"truncate font-medium max-w-full sm:max-w-md\">{title}</p>\n )}\n {subtitle && (\n <p className=\"mt-0.5 truncate text-xs text-muted-foreground max-w-full sm:max-w-md\">\n {subtitle}\n </p>\n )}\n </div>\n </div>\n {hasActions && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"shrink-0 -mr-1\"\n onClick={(e) => e.stopPropagation()}\n aria-label=\"Open menu\"\n >\n <MoreVertical className=\"h-4 w-4\" aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" sideOffset={4} onClick={(e) => e.stopPropagation()}>\n {actions.map((action) => {\n const ActionIcon = action.icon;\n return (\n <DropdownMenuItem\n key={action.label}\n onClick={action.onClick}\n className={\n action.variant === 'destructive'\n ? 'text-destructive focus:text-destructive'\n : undefined\n }\n >\n <ActionIcon className=\"mr-2 h-4 w-4\" aria-hidden />\n {action.label}\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n {description && (\n <Tooltip content={description} side=\"top\" align=\"start\">\n <p className=\"text-sm text-muted-foreground overflow-hidden line-clamp-2 [display:-webkit-box] [-webkit-line-clamp:2] [-webkit-box-orient:vertical]\">\n {description}\n </p>\n </Tooltip>\n )}\n {children}\n </div>\n );\n}\n","import { Label } from '../../ui/label';\nimport type { SelectOption, SelectTranslations } from '../../ui/select';\nimport { Select } from '../../ui/select';\n\nexport type LabeledSelectProps = {\n label: string;\n value: string;\n onValueChange: (value: string) => void;\n options: SelectOption[];\n placeholder?: string;\n disabled?: boolean;\n className?: string;\n triggerClassName?: string;\n searchable?: boolean;\n translations?: SelectTranslations;\n /**\n * `stacked` (default) — label above the field (forms / filters).\n * `inline` — label beside the field (dense toolbars only).\n */\n layout?: 'stacked' | 'inline';\n};\n\n/** Labeled Select — labels sit above the control by default. */\nexport function LabeledSelect({\n label,\n value,\n onValueChange,\n options,\n placeholder,\n disabled,\n className,\n triggerClassName,\n searchable,\n translations,\n layout = 'stacked',\n}: LabeledSelectProps) {\n const select = (\n <Select\n className={className ?? (layout === 'stacked' ? 'w-full min-w-[8.5rem]' : 'w-[8.5rem]')}\n triggerClassName={triggerClassName ?? 'h-9'}\n value={value}\n onValueChange={onValueChange}\n options={options}\n placeholder={placeholder}\n disabled={disabled}\n searchable={searchable}\n translations={translations}\n aria-label={label}\n />\n );\n\n if (layout === 'inline') {\n return (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"shrink-0 text-xs font-medium text-muted-foreground\">{label}</span>\n {select}\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n <Label className=\"text-xs font-medium tracking-normal text-muted-foreground\">{label}</Label>\n {select}\n </div>\n );\n}\n","import { type ReactNode, Suspense } from 'react';\n\nimport { PageLoader } from '../page-loader';\n\nexport interface RouteSuspenseProps {\n children: ReactNode;\n loadingLabel?: string;\n}\n\n/** Suspense boundary for lazy route modules with shared loading UX. */\nexport function RouteSuspense({ children, loadingLabel }: RouteSuspenseProps) {\n return (\n <Suspense fallback={<PageLoader variant=\"route\" loadingLabel={loadingLabel} />}>\n {children}\n </Suspense>\n );\n}\n","import React, { useEffect, useState } from 'react';\n\n/**\n * Theme store interface for theme state management.\n */\nexport interface ThemeStore {\n /** Current theme mode */\n theme: 'light' | 'dark';\n /** Function to update theme */\n setTheme: (theme: 'light' | 'dark') => void;\n}\n\n/** Default in-memory theme store when no external store is provided. */\nexport function useDefaultThemeStore(): ThemeStore {\n const [theme, setTheme] = useState<'light' | 'dark'>('light');\n return { theme, setTheme };\n}\n\nconst ThemeStoreContext = React.createContext<ThemeStore | null>(null);\n\n/** Hook to read theme store from ThemeProvider context. */\nexport function useThemeStoreContext(): ThemeStore | null {\n return React.useContext(ThemeStoreContext);\n}\n\n/**\n * Props for the ThemeProvider component.\n */\nexport interface ThemeProviderProps {\n /** Child components */\n children: React.ReactNode;\n /** Hook function that returns the theme store (e.g., from Zustand) */\n useThemeStore?: () => ThemeStore;\n}\n\n/**\n * Theme provider component that applies theme classes to the document root.\n * Works with any state management solution via the `useThemeStore` prop.\n * When `useThemeStore` is not provided, uses an internal default store so hooks are always called unconditionally.\n *\n * @example\n * ```tsx\n * // With Zustand\n * const useThemeStore = () => {\n * const theme = useStore((state) => state.theme);\n * const setTheme = useStore((state) => state.setTheme);\n * return { theme, setTheme };\n * };\n *\n * <ThemeProvider useThemeStore={useThemeStore}>\n * <App />\n * </ThemeProvider>\n * ```\n */\nexport function ThemeProvider({ children, useThemeStore: useThemeStoreProp }: ThemeProviderProps) {\n const useResolvedStore = useThemeStoreProp ?? useDefaultThemeStore;\n const store = useResolvedStore();\n\n useEffect(() => {\n const root = window.document.documentElement;\n root.classList.remove('light', 'dark');\n root.classList.add(store.theme);\n }, [store.theme]);\n\n return <ThemeStoreContext.Provider value={store}>{children}</ThemeStoreContext.Provider>;\n}\n","import { Moon, Sun } from 'lucide-react';\nimport { Button } from '../../ui/button';\nimport type { ThemeStore } from '../theme-provider';\nimport { useDefaultThemeStore, useThemeStoreContext } from '../theme-provider';\n\n/**\n * Props for the ThemeToggle component.\n */\nexport interface ThemeToggleProps {\n /** Hook function that returns the theme store (e.g., from Zustand) */\n useThemeStore?: () => ThemeStore;\n /** Accessible label for the toggle button */\n ariaLabel?: string;\n}\n\nfunction ThemeToggleWithStore({\n useThemeStore,\n ariaLabel,\n}: {\n useThemeStore: () => ThemeStore;\n ariaLabel: string;\n}) {\n const { theme, setTheme } = useThemeStore();\n const toggleTheme = () => setTheme(theme === 'dark' ? 'light' : 'dark');\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={toggleTheme}\n aria-label={ariaLabel}\n className=\"cursor-pointer\"\n >\n {theme === 'dark' ? <Moon className=\"h-4 w-4\" /> : <Sun className=\"h-4 w-4\" />}\n </Button>\n );\n}\n\nfunction ThemeToggleWithContext({ ariaLabel }: { ariaLabel: string }) {\n const fromContext = useThemeStoreContext();\n const defaultStore = useDefaultThemeStore();\n const store = fromContext ?? defaultStore;\n const toggleTheme = () => store.setTheme(store.theme === 'dark' ? 'light' : 'dark');\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={toggleTheme}\n aria-label={ariaLabel}\n className=\"cursor-pointer\"\n >\n {store.theme === 'dark' ? <Moon className=\"h-4 w-4\" /> : <Sun className=\"h-4 w-4\" />}\n </Button>\n );\n}\n\n/**\n * Theme toggle button component.\n * Displays a sun icon in dark mode and moon icon in light mode.\n * Toggles between light and dark themes when clicked.\n * When useThemeStore is passed, uses that store; otherwise uses ThemeProvider context or internal default (no conditional hook calls).\n *\n * @example\n * ```tsx\n * const useThemeStore = () => {\n * const theme = useStore((state) => state.theme);\n * const setTheme = useStore((state) => state.setTheme);\n * return { theme, setTheme };\n * };\n *\n * <ThemeToggle useThemeStore={useThemeStore} ariaLabel=\"Toggle theme\" />\n * ```\n */\nexport function ThemeToggle({\n useThemeStore: useThemeStoreProp,\n ariaLabel = 'Toggle theme',\n}: ThemeToggleProps) {\n if (useThemeStoreProp) {\n return <ThemeToggleWithStore useThemeStore={useThemeStoreProp} ariaLabel={ariaLabel} />;\n }\n return <ThemeToggleWithContext ariaLabel={ariaLabel} />;\n}\n"]}
|