@burdenoff/microfe-export 2026.510.111
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 +243 -0
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/export/ExportRoot.d.ts +31 -0
- package/dist/export/ExportRoot.d.ts.map +1 -0
- package/dist/export/ExportRoot.js +70 -0
- package/dist/export/ExportRoot.js.map +1 -0
- package/dist/export/ExportRoutes.d.ts +14 -0
- package/dist/export/ExportRoutes.d.ts.map +1 -0
- package/dist/export/ExportRoutes.js +104 -0
- package/dist/export/ExportRoutes.js.map +1 -0
- package/dist/export/components/ExportProgressBar.d.ts +12 -0
- package/dist/export/components/ExportProgressBar.d.ts.map +1 -0
- package/dist/export/components/ExportProgressBar.js +34 -0
- package/dist/export/components/ExportProgressBar.js.map +1 -0
- package/dist/export/components/ExportStatusBadge.d.ts +12 -0
- package/dist/export/components/ExportStatusBadge.d.ts.map +1 -0
- package/dist/export/components/ExportStatusBadge.js +39 -0
- package/dist/export/components/ExportStatusBadge.js.map +1 -0
- package/dist/export/components/PageLayout.d.ts +55 -0
- package/dist/export/components/PageLayout.d.ts.map +1 -0
- package/dist/export/components/PageLayout.js +93 -0
- package/dist/export/components/PageLayout.js.map +1 -0
- package/dist/export/components/ui.d.ts +6 -0
- package/dist/export/components/ui.d.ts.map +1 -0
- package/dist/export/constants/enums.d.ts +79 -0
- package/dist/export/constants/enums.d.ts.map +1 -0
- package/dist/export/constants/enums.js +27 -0
- package/dist/export/constants/enums.js.map +1 -0
- package/dist/export/constants/index.d.ts +7 -0
- package/dist/export/constants/index.d.ts.map +1 -0
- package/dist/export/hooks/useExportQueries.d.ts +224 -0
- package/dist/export/hooks/useExportQueries.d.ts.map +1 -0
- package/dist/export/hooks/useExportQueries.js +401 -0
- package/dist/export/hooks/useExportQueries.js.map +1 -0
- package/dist/export/index.d.ts +29 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +3 -0
- package/dist/export/pages/CreateExportPage.d.ts +2 -0
- package/dist/export/pages/CreateExportPage.d.ts.map +1 -0
- package/dist/export/pages/CreateExportPage.js +495 -0
- package/dist/export/pages/CreateExportPage.js.map +1 -0
- package/dist/export/pages/ExportDashboardPage.d.ts +6 -0
- package/dist/export/pages/ExportDashboardPage.d.ts.map +1 -0
- package/dist/export/pages/ExportDashboardPage.js +166 -0
- package/dist/export/pages/ExportDashboardPage.js.map +1 -0
- package/dist/export/pages/ExportDetailPage.d.ts +7 -0
- package/dist/export/pages/ExportDetailPage.d.ts.map +1 -0
- package/dist/export/pages/ExportDetailPage.js +327 -0
- package/dist/export/pages/ExportDetailPage.js.map +1 -0
- package/dist/export/pages/ExportHistoryPage.d.ts +6 -0
- package/dist/export/pages/ExportHistoryPage.d.ts.map +1 -0
- package/dist/export/pages/ExportHistoryPage.js +261 -0
- package/dist/export/pages/ExportHistoryPage.js.map +1 -0
- package/dist/export/pages/ExportLandingPage.d.ts +9 -0
- package/dist/export/pages/ExportLandingPage.d.ts.map +1 -0
- package/dist/export/pages/ExportLandingPage.js +56 -0
- package/dist/export/pages/ExportLandingPage.js.map +1 -0
- package/dist/export/pages/ExportTemplatesPage.d.ts +6 -0
- package/dist/export/pages/ExportTemplatesPage.d.ts.map +1 -0
- package/dist/export/pages/ExportTemplatesPage.js +343 -0
- package/dist/export/pages/ExportTemplatesPage.js.map +1 -0
- package/dist/export/pages/TemplateDetailPage.d.ts +7 -0
- package/dist/export/pages/TemplateDetailPage.d.ts.map +1 -0
- package/dist/export/pages/TemplateDetailPage.js +215 -0
- package/dist/export/pages/TemplateDetailPage.js.map +1 -0
- package/dist/export/providers/ExportProvider.d.ts +13 -0
- package/dist/export/providers/ExportProvider.d.ts.map +1 -0
- package/dist/export/providers/ExportProvider.js +103 -0
- package/dist/export/providers/ExportProvider.js.map +1 -0
- package/dist/export/store/exportStore.d.ts +17 -0
- package/dist/export/store/exportStore.d.ts.map +1 -0
- package/dist/export/store/exportStore.js +43 -0
- package/dist/export/store/exportStore.js.map +1 -0
- package/dist/export/types/index.d.ts +230 -0
- package/dist/export/types/index.d.ts.map +1 -0
- package/dist/export/utils/cn.d.ts +8 -0
- package/dist/export/utils/cn.d.ts.map +1 -0
- package/dist/export/utils/cn.js +10 -0
- package/dist/export/utils/cn.js.map +1 -0
- package/dist/export/utils/events.d.ts +7 -0
- package/dist/export/utils/events.d.ts.map +1 -0
- package/dist/export/utils/events.js +16 -0
- package/dist/export/utils/events.js.map +1 -0
- package/dist/export/utils/i18n.d.ts +6 -0
- package/dist/export/utils/i18n.d.ts.map +1 -0
- package/dist/export/utils/i18n.js +15 -0
- package/dist/export/utils/i18n.js.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +21253 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +773 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-operations.js +319 -0
- package/dist/generated/wspace-operations.js.map +1 -0
- package/dist/generated/wspace-types.d.ts +55214 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/generated/wspace-types.js +12 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js +24 -0
- package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js.map +1 -0
- package/dist/node_modules/@apollo/client/errors/utils.js +16 -0
- package/dist/node_modules/@apollo/client/errors/utils.js.map +1 -0
- package/dist/node_modules/@apollo/client/link/core/ApolloLink.js +52 -0
- package/dist/node_modules/@apollo/client/link/core/ApolloLink.js.map +1 -0
- package/dist/node_modules/@apollo/client/link/subscriptions/index.js +41 -0
- package/dist/node_modules/@apollo/client/link/subscriptions/index.js.map +1 -0
- package/dist/node_modules/@apollo/client/link/utils/createOperation.js +34 -0
- package/dist/node_modules/@apollo/client/link/utils/createOperation.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/caching/sizes.js +6 -0
- package/dist/node_modules/@apollo/client/utilities/caching/sizes.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/graphql/print.js +16 -0
- package/dist/node_modules/@apollo/client/utilities/graphql/print.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/caches.js +19 -0
- package/dist/node_modules/@apollo/client/utilities/internal/caches.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/checkDocument.js +21 -0
- package/dist/node_modules/@apollo/client/utilities/internal/checkDocument.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getMainDefinition.js +17 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getMainDefinition.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getMemoryInternals.js +9 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getMemoryInternals.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getOperationDefinition.js +9 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getOperationDefinition.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getOperationName.js +8 -0
- package/dist/node_modules/@apollo/client/utilities/internal/getOperationName.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/globals/global.js +9 -0
- package/dist/node_modules/@apollo/client/utilities/internal/globals/global.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/globals/maybe.js +10 -0
- package/dist/node_modules/@apollo/client/utilities/internal/globals/maybe.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/isNonNullObject.js +8 -0
- package/dist/node_modules/@apollo/client/utilities/internal/isNonNullObject.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/makeUniqueId.js +10 -0
- package/dist/node_modules/@apollo/client/utilities/internal/makeUniqueId.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/memoize.js +23 -0
- package/dist/node_modules/@apollo/client/utilities/internal/memoize.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/internal/stringifyForDisplay.js +10 -0
- package/dist/node_modules/@apollo/client/utilities/internal/stringifyForDisplay.js.map +1 -0
- package/dist/node_modules/@apollo/client/utilities/invariant/index.js +57 -0
- package/dist/node_modules/@apollo/client/utilities/invariant/index.js.map +1 -0
- package/dist/node_modules/@apollo/client/version.js +6 -0
- package/dist/node_modules/@apollo/client/version.js.map +1 -0
- package/dist/node_modules/@wry/caches/lib/weak.js +67 -0
- package/dist/node_modules/@wry/caches/lib/weak.js.map +1 -0
- package/dist/node_modules/@wry/trie/lib/index.js +53 -0
- package/dist/node_modules/@wry/trie/lib/index.js.map +1 -0
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/graphql-ws/dist/client.js +272 -0
- package/dist/node_modules/graphql-ws/dist/client.js.map +1 -0
- package/dist/node_modules/graphql-ws/dist/common-CGW11Fyb.js +62 -0
- package/dist/node_modules/graphql-ws/dist/common-CGW11Fyb.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/NotificationFactories.js +21 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/NotificationFactories.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/Observable.js +78 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/Observable.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/Subscriber.js +117 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/Subscriber.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/Subscription.js +93 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/Subscription.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/config.js +12 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/config.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/observable/empty.js +9 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/observable/empty.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/scheduler/timeoutProvider.js +16 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/scheduler/timeoutProvider.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/symbol/observable.js +8 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/symbol/observable.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/UnsubscriptionError.js +13 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/UnsubscriptionError.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/arrRemove.js +11 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/arrRemove.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/createErrorClass.js +11 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/createErrorClass.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/errorContext.js +22 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/errorContext.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/identity.js +8 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/identity.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/isFunction.js +8 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/isFunction.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/noop.js +6 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/noop.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/pipe.js +13 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/pipe.js.map +1 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/reportUnhandledError.js +14 -0
- package/dist/node_modules/rxjs/dist/esm5/internal/util/reportUnhandledError.js.map +1 -0
- package/dist/node_modules/rxjs/node_modules/tslib/tslib.es6.js +52 -0
- package/dist/node_modules/rxjs/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1674 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/utils/nativeBridge.d.ts +3 -0
- package/dist/utils/nativeBridge.d.ts.map +1 -0
- package/dist/utils/nativeBridge.js +2 -0
- package/package.json +107 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { useExport as e } from "../providers/ExportProvider.js";
|
|
2
|
+
import { useAvailableEntities as t, useCreateExportTemplate as n, useDeleteExportTemplate as r, useExportTemplates as i } from "../hooks/useExportQueries.js";
|
|
3
|
+
import { cn as a } from "../utils/cn.js";
|
|
4
|
+
import { getFormatDisplayName as o } from "../constants/enums.js";
|
|
5
|
+
import { tWithFallback as s } from "../utils/i18n.js";
|
|
6
|
+
import { EmptyState as c, PageLayout as l } from "../components/PageLayout.js";
|
|
7
|
+
import { nativeImpact as u, nativeNotify as d } from "../../utils/nativeBridge.js";
|
|
8
|
+
import { useMemo as f, useState as p } from "react";
|
|
9
|
+
import { ChevronRight as ee, FileBox as m, Loader2 as h, Plus as g, Search as _, Sparkles as v, X as y } from "lucide-react";
|
|
10
|
+
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
11
|
+
import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
12
|
+
//#region src/export/pages/ExportTemplatesPage.tsx
|
|
13
|
+
var C = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
14
|
+
function w() {
|
|
15
|
+
let { basePath: w = "/", navigate: T } = e(), { t: E } = S(), D = (e, t, n) => s(E, e, t, n), { data: O, isLoading: k } = i(), { deleteTemplate: A } = r(), { createTemplate: j, isPending: M } = n(), { data: N } = t(), [P, F] = p(""), [te, I] = p(!1), [L, R] = p(""), [z, B] = p(""), [V, H] = p([]), [U, W] = p("CSV"), [G, K] = p(null), [q, J] = p(null), Y = () => {
|
|
16
|
+
R(""), B(""), H([]), W("CSV"), K(null), I(!0);
|
|
17
|
+
}, X = async () => {
|
|
18
|
+
if (!L.trim()) {
|
|
19
|
+
K(D("export.templates.form.errors.nameRequired", "Template name is required."));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (V.length === 0) {
|
|
23
|
+
K(D("export.templates.form.errors.entityRequired", "Select at least one entity type."));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
K(null);
|
|
27
|
+
try {
|
|
28
|
+
await j({
|
|
29
|
+
name: L.trim(),
|
|
30
|
+
description: z.trim() || void 0,
|
|
31
|
+
entityTypes: V,
|
|
32
|
+
format: U
|
|
33
|
+
}), I(!1);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
K(e instanceof Error ? e.message : D("export.templates.form.errors.createFailed", "Failed to create template."));
|
|
36
|
+
}
|
|
37
|
+
}, Z = (e) => {
|
|
38
|
+
H((t) => t.includes(e) ? t.filter((t) => t !== e) : [...t, e]);
|
|
39
|
+
}, Q = f(() => {
|
|
40
|
+
if (!P) return O;
|
|
41
|
+
let e = P.toLowerCase();
|
|
42
|
+
return O.filter((t) => t.name.toLowerCase().includes(e) || t.entityTypes.some((t) => t.toLowerCase().includes(e)));
|
|
43
|
+
}, [O, P]), $ = (e) => {
|
|
44
|
+
T?.(C(w, `templates/${e}`));
|
|
45
|
+
}, ne = async (e, t) => {
|
|
46
|
+
e.stopPropagation(), u("medium"), J(t);
|
|
47
|
+
}, re = async () => {
|
|
48
|
+
if (q) {
|
|
49
|
+
try {
|
|
50
|
+
await A(q), d("success");
|
|
51
|
+
} catch {
|
|
52
|
+
d("error");
|
|
53
|
+
}
|
|
54
|
+
J(null);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return /* @__PURE__ */ x(l, {
|
|
58
|
+
title: D("export.templates.title", "Export Templates"),
|
|
59
|
+
subtitle: D("export.templates.subtitle", "Reusable export configurations for common data exports"),
|
|
60
|
+
icon: /* @__PURE__ */ b(m, { size: 24 }),
|
|
61
|
+
headerActions: /* @__PURE__ */ x("div", {
|
|
62
|
+
className: "flex flex-wrap items-center gap-2",
|
|
63
|
+
children: [/* @__PURE__ */ x("button", {
|
|
64
|
+
onClick: Y,
|
|
65
|
+
className: "inline-flex items-center gap-2 px-3 py-2 sm:px-4 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium text-sm",
|
|
66
|
+
children: [/* @__PURE__ */ b(g, { size: 16 }), D("export.templates.actions.newTemplate", "New Template")]
|
|
67
|
+
}), /* @__PURE__ */ x("button", {
|
|
68
|
+
onClick: () => T?.(C(w, "new")),
|
|
69
|
+
className: "inline-flex items-center gap-2 px-3 py-2 sm:px-4 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium text-sm",
|
|
70
|
+
children: [/* @__PURE__ */ b(g, { size: 16 }), D("export.templates.actions.newExport", "New Export")]
|
|
71
|
+
})]
|
|
72
|
+
}),
|
|
73
|
+
headerContent: /* @__PURE__ */ x("span", {
|
|
74
|
+
className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground",
|
|
75
|
+
children: [/* @__PURE__ */ b(v, { size: 12 }), D("export.templates.summary.available", "{count} template{suffix} available", {
|
|
76
|
+
count: Q.length,
|
|
77
|
+
suffix: Q.length === 1 ? "" : "s"
|
|
78
|
+
})]
|
|
79
|
+
}),
|
|
80
|
+
children: [
|
|
81
|
+
te && /* @__PURE__ */ x("div", {
|
|
82
|
+
className: "border-2 border-primary/30 rounded-xl p-6 bg-primary/5 space-y-4",
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ x("div", {
|
|
85
|
+
className: "flex items-center justify-between",
|
|
86
|
+
children: [/* @__PURE__ */ b("h3", {
|
|
87
|
+
className: "font-semibold text-lg",
|
|
88
|
+
children: D("export.templates.form.title", "New Template")
|
|
89
|
+
}), /* @__PURE__ */ b("button", {
|
|
90
|
+
onClick: () => I(!1),
|
|
91
|
+
className: "p-1.5 rounded-md hover:bg-accent",
|
|
92
|
+
children: /* @__PURE__ */ b(y, { size: 16 })
|
|
93
|
+
})]
|
|
94
|
+
}),
|
|
95
|
+
G && /* @__PURE__ */ b("p", {
|
|
96
|
+
className: "text-sm text-destructive bg-destructive/10 rounded-lg px-3 py-2",
|
|
97
|
+
children: G
|
|
98
|
+
}),
|
|
99
|
+
/* @__PURE__ */ x("div", {
|
|
100
|
+
className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
|
|
101
|
+
children: [/* @__PURE__ */ x("div", {
|
|
102
|
+
className: "space-y-1",
|
|
103
|
+
children: [/* @__PURE__ */ x("label", {
|
|
104
|
+
className: "text-sm font-medium",
|
|
105
|
+
children: [D("export.templates.form.fields.name", "Name"), " *"]
|
|
106
|
+
}), /* @__PURE__ */ b("input", {
|
|
107
|
+
type: "text",
|
|
108
|
+
placeholder: D("export.templates.form.fields.namePlaceholder", "e.g. Weekly Tags Export"),
|
|
109
|
+
value: L,
|
|
110
|
+
onChange: (e) => R(e.target.value),
|
|
111
|
+
className: "w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all"
|
|
112
|
+
})]
|
|
113
|
+
}), /* @__PURE__ */ x("div", {
|
|
114
|
+
className: "space-y-1",
|
|
115
|
+
children: [/* @__PURE__ */ x("label", {
|
|
116
|
+
className: "text-sm font-medium",
|
|
117
|
+
children: [D("export.templates.form.fields.format", "Format"), " *"]
|
|
118
|
+
}), /* @__PURE__ */ x("select", {
|
|
119
|
+
value: U,
|
|
120
|
+
onChange: (e) => W(e.target.value),
|
|
121
|
+
className: "w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all",
|
|
122
|
+
children: [/* @__PURE__ */ b("option", {
|
|
123
|
+
value: "CSV",
|
|
124
|
+
children: "CSV"
|
|
125
|
+
}), /* @__PURE__ */ b("option", {
|
|
126
|
+
value: "JSON",
|
|
127
|
+
children: "JSON"
|
|
128
|
+
})]
|
|
129
|
+
})]
|
|
130
|
+
})]
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ x("div", {
|
|
133
|
+
className: "space-y-1",
|
|
134
|
+
children: [/* @__PURE__ */ b("label", {
|
|
135
|
+
className: "text-sm font-medium",
|
|
136
|
+
children: D("export.templates.form.fields.description", "Description")
|
|
137
|
+
}), /* @__PURE__ */ b("input", {
|
|
138
|
+
type: "text",
|
|
139
|
+
placeholder: D("export.templates.form.fields.descriptionPlaceholder", "Optional description"),
|
|
140
|
+
value: z,
|
|
141
|
+
onChange: (e) => B(e.target.value),
|
|
142
|
+
className: "w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all"
|
|
143
|
+
})]
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ x("div", {
|
|
146
|
+
className: "space-y-2",
|
|
147
|
+
children: [/* @__PURE__ */ x("label", {
|
|
148
|
+
className: "text-sm font-medium",
|
|
149
|
+
children: [
|
|
150
|
+
D("export.templates.form.fields.entityTypes", "Entity Types"),
|
|
151
|
+
" *",
|
|
152
|
+
" ",
|
|
153
|
+
/* @__PURE__ */ b("span", {
|
|
154
|
+
className: "text-muted-foreground font-normal",
|
|
155
|
+
children: D("export.templates.form.fields.entityTypesHint", "(select at least one)")
|
|
156
|
+
})
|
|
157
|
+
]
|
|
158
|
+
}), /* @__PURE__ */ b("div", {
|
|
159
|
+
className: "flex flex-wrap gap-2",
|
|
160
|
+
children: N.map((e) => /* @__PURE__ */ b("button", {
|
|
161
|
+
type: "button",
|
|
162
|
+
onClick: () => Z(e.entityType),
|
|
163
|
+
className: a("px-3 py-1.5 rounded-lg text-sm font-medium border-2 transition-all", V.includes(e.entityType) ? "border-primary bg-primary/10 text-primary" : "border-border bg-background hover:border-primary/50"),
|
|
164
|
+
children: e.displayName
|
|
165
|
+
}, e.entityType))
|
|
166
|
+
})]
|
|
167
|
+
}),
|
|
168
|
+
/* @__PURE__ */ x("div", {
|
|
169
|
+
className: "flex gap-3 pt-2",
|
|
170
|
+
children: [/* @__PURE__ */ x("button", {
|
|
171
|
+
onClick: X,
|
|
172
|
+
disabled: M,
|
|
173
|
+
className: "inline-flex items-center gap-2 px-5 py-2.5 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium disabled:opacity-50",
|
|
174
|
+
children: [M ? /* @__PURE__ */ b(h, {
|
|
175
|
+
size: 16,
|
|
176
|
+
className: "animate-spin"
|
|
177
|
+
}) : /* @__PURE__ */ b(g, { size: 16 }), D("export.templates.form.actions.create", "Create Template")]
|
|
178
|
+
}), /* @__PURE__ */ b("button", {
|
|
179
|
+
onClick: () => I(!1),
|
|
180
|
+
className: "px-5 py-2.5 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium",
|
|
181
|
+
children: D("export.templates.form.actions.cancel", "Cancel")
|
|
182
|
+
})]
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ x("div", {
|
|
187
|
+
className: "relative group",
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ b(_, {
|
|
190
|
+
size: 18,
|
|
191
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors"
|
|
192
|
+
}),
|
|
193
|
+
/* @__PURE__ */ b("input", {
|
|
194
|
+
type: "text",
|
|
195
|
+
placeholder: D("export.templates.search.placeholder", "Search templates..."),
|
|
196
|
+
value: P,
|
|
197
|
+
onChange: (e) => F(e.target.value),
|
|
198
|
+
className: "w-full pl-[3.25rem] pr-4 py-2.5 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all duration-200"
|
|
199
|
+
}),
|
|
200
|
+
P && /* @__PURE__ */ b("button", {
|
|
201
|
+
onClick: () => F(""),
|
|
202
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent",
|
|
203
|
+
children: /* @__PURE__ */ b(y, { size: 16 })
|
|
204
|
+
})
|
|
205
|
+
]
|
|
206
|
+
}),
|
|
207
|
+
k && /* @__PURE__ */ b("div", {
|
|
208
|
+
className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
|
|
209
|
+
children: [...[
|
|
210
|
+
,
|
|
211
|
+
,
|
|
212
|
+
,
|
|
213
|
+
,
|
|
214
|
+
]].map((e, t) => /* @__PURE__ */ x("div", {
|
|
215
|
+
className: "border-2 border-border/50 rounded-xl p-5 animate-pulse",
|
|
216
|
+
children: [
|
|
217
|
+
/* @__PURE__ */ b("div", { className: "h-5 w-40 bg-muted rounded mb-3" }),
|
|
218
|
+
/* @__PURE__ */ b("div", { className: "h-3 w-full bg-muted rounded mb-2" }),
|
|
219
|
+
/* @__PURE__ */ x("div", {
|
|
220
|
+
className: "flex gap-2 mt-4",
|
|
221
|
+
children: [/* @__PURE__ */ b("div", { className: "h-5 w-16 bg-muted rounded-full" }), /* @__PURE__ */ b("div", { className: "h-5 w-16 bg-muted rounded-full" })]
|
|
222
|
+
})
|
|
223
|
+
]
|
|
224
|
+
}, t))
|
|
225
|
+
}),
|
|
226
|
+
k ? null : Q.length > 0 ? /* @__PURE__ */ b("div", {
|
|
227
|
+
className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
|
|
228
|
+
children: Q.map((e, t) => /* @__PURE__ */ x("div", {
|
|
229
|
+
role: "button",
|
|
230
|
+
tabIndex: 0,
|
|
231
|
+
onClick: () => $(e.id),
|
|
232
|
+
onKeyDown: (t) => {
|
|
233
|
+
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), $(e.id));
|
|
234
|
+
},
|
|
235
|
+
className: a("text-left group cursor-pointer border-2 border-border/50 rounded-xl overflow-hidden", "hover:shadow-md hover:border-primary/30 transition-all duration-200", "animate-in fade-in-0 slide-in-from-bottom-2"),
|
|
236
|
+
style: { animationDelay: `${t * 50}ms` },
|
|
237
|
+
children: [/* @__PURE__ */ b("div", {
|
|
238
|
+
className: "p-4 border-b bg-muted/30",
|
|
239
|
+
children: /* @__PURE__ */ x("div", {
|
|
240
|
+
className: "flex items-start justify-between",
|
|
241
|
+
children: [/* @__PURE__ */ x("div", {
|
|
242
|
+
className: "flex items-center gap-3",
|
|
243
|
+
children: [/* @__PURE__ */ b("div", {
|
|
244
|
+
className: "p-2 rounded-lg bg-primary/10",
|
|
245
|
+
children: /* @__PURE__ */ b(m, {
|
|
246
|
+
size: 20,
|
|
247
|
+
className: "text-primary"
|
|
248
|
+
})
|
|
249
|
+
}), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h3", {
|
|
250
|
+
className: "font-semibold",
|
|
251
|
+
children: e.name
|
|
252
|
+
}), /* @__PURE__ */ x("p", {
|
|
253
|
+
className: "text-xs text-muted-foreground",
|
|
254
|
+
children: [o(e.format), e.isDefault && /* @__PURE__ */ b("span", {
|
|
255
|
+
className: "ml-1.5 inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-primary/10 text-primary font-medium",
|
|
256
|
+
children: D("export.templates.card.default", "Default")
|
|
257
|
+
})]
|
|
258
|
+
})] })]
|
|
259
|
+
}), /* @__PURE__ */ x("div", {
|
|
260
|
+
className: "flex items-center gap-2",
|
|
261
|
+
children: [/* @__PURE__ */ b("button", {
|
|
262
|
+
onClick: (t) => ne(t, e.id),
|
|
263
|
+
className: "p-1.5 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
|
|
264
|
+
"aria-label": D("export.templates.card.deleteAriaLabel", "Delete {name}", { name: e.name }),
|
|
265
|
+
children: /* @__PURE__ */ b(y, { size: 14 })
|
|
266
|
+
}), /* @__PURE__ */ b(ee, {
|
|
267
|
+
size: 18,
|
|
268
|
+
className: "text-muted-foreground group-hover:text-primary transition-colors mt-1"
|
|
269
|
+
})]
|
|
270
|
+
})]
|
|
271
|
+
})
|
|
272
|
+
}), /* @__PURE__ */ x("div", {
|
|
273
|
+
className: "p-4",
|
|
274
|
+
children: [e.description && /* @__PURE__ */ b("p", {
|
|
275
|
+
className: "text-sm text-muted-foreground mb-3 line-clamp-2",
|
|
276
|
+
children: e.description
|
|
277
|
+
}), /* @__PURE__ */ x("div", {
|
|
278
|
+
className: "flex flex-wrap gap-1.5 overflow-hidden",
|
|
279
|
+
children: [e.entityTypes.slice(0, 6).map((e) => /* @__PURE__ */ b("span", {
|
|
280
|
+
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground max-w-full truncate",
|
|
281
|
+
children: e
|
|
282
|
+
}, e)), e.entityTypes.length > 6 && /* @__PURE__ */ b("span", {
|
|
283
|
+
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground",
|
|
284
|
+
children: D("export.templates.card.moreEntities", "+{count} more", { count: e.entityTypes.length - 6 })
|
|
285
|
+
})]
|
|
286
|
+
})]
|
|
287
|
+
})]
|
|
288
|
+
}, e.id))
|
|
289
|
+
}) : /* @__PURE__ */ b(c, {
|
|
290
|
+
icon: b(P ? _ : m, { size: 48 }),
|
|
291
|
+
title: P ? D("export.templates.empty.filtered.title", "No matching templates") : D("export.templates.empty.title", "No templates yet"),
|
|
292
|
+
description: P ? D("export.templates.empty.filtered.description", "Try adjusting your search to find what you are looking for.") : D("export.templates.empty.description", "Create a template to save reusable export configurations."),
|
|
293
|
+
action: P ? /* @__PURE__ */ x("button", {
|
|
294
|
+
onClick: () => F(""),
|
|
295
|
+
className: "inline-flex items-center gap-2 px-4 py-2 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium",
|
|
296
|
+
children: [/* @__PURE__ */ b(y, { size: 16 }), D("export.templates.empty.filtered.actions.clear", "Clear Search")]
|
|
297
|
+
}) : /* @__PURE__ */ x("button", {
|
|
298
|
+
onClick: Y,
|
|
299
|
+
className: "inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium",
|
|
300
|
+
children: [/* @__PURE__ */ b(g, { size: 16 }), D("export.templates.empty.actions.newTemplate", "New Template")]
|
|
301
|
+
})
|
|
302
|
+
}),
|
|
303
|
+
q && /* @__PURE__ */ b("div", {
|
|
304
|
+
role: "dialog",
|
|
305
|
+
"aria-modal": "true",
|
|
306
|
+
"aria-labelledby": "template-list-delete-title",
|
|
307
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4",
|
|
308
|
+
children: /* @__PURE__ */ x("div", {
|
|
309
|
+
className: "w-full max-w-md rounded-xl border-2 border-border bg-card p-6 shadow-xl",
|
|
310
|
+
children: [
|
|
311
|
+
/* @__PURE__ */ b("h2", {
|
|
312
|
+
id: "template-list-delete-title",
|
|
313
|
+
className: "text-lg font-bold",
|
|
314
|
+
children: D("export.templates.delete.title", "Delete template?")
|
|
315
|
+
}),
|
|
316
|
+
/* @__PURE__ */ b("p", {
|
|
317
|
+
className: "mt-2 text-sm text-muted-foreground",
|
|
318
|
+
children: D("export.templates.delete.description", "This removes the saved export configuration. Existing export jobs are not deleted.")
|
|
319
|
+
}),
|
|
320
|
+
/* @__PURE__ */ x("div", {
|
|
321
|
+
className: "mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3",
|
|
322
|
+
children: [/* @__PURE__ */ b("button", {
|
|
323
|
+
type: "button",
|
|
324
|
+
onClick: () => J(null),
|
|
325
|
+
className: "px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium",
|
|
326
|
+
children: D("export.templates.delete.cancel", "Keep Template")
|
|
327
|
+
}), /* @__PURE__ */ b("button", {
|
|
328
|
+
type: "button",
|
|
329
|
+
onClick: re,
|
|
330
|
+
className: "px-4 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors font-medium",
|
|
331
|
+
children: D("export.templates.delete.confirmAction", "Delete Template")
|
|
332
|
+
})]
|
|
333
|
+
})
|
|
334
|
+
]
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
]
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
//#endregion
|
|
341
|
+
export { w as ExportTemplatesPage };
|
|
342
|
+
|
|
343
|
+
//# sourceMappingURL=ExportTemplatesPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExportTemplatesPage.js","names":[],"sources":["../../../src/export/pages/ExportTemplatesPage.tsx"],"sourcesContent":["import { useState, useMemo } from \"react\";\nimport {\n Search,\n X,\n FileBox,\n Plus,\n ChevronRight,\n Sparkles,\n Loader2,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplates,\n useDeleteExportTemplate,\n useCreateExportTemplate,\n useAvailableEntities,\n} from \"../hooks/useExportQueries\";\nimport { PageLayout, EmptyState } from \"../components/PageLayout\";\nimport { getFormatDisplayName } from \"../constants/enums\";\nimport { cn } from \"../utils/cn\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport type { ExportFormat } from \"../types\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/**\n * Helper to join paths correctly\n */\nconst joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n};\n\n/**\n * Export Templates Page\n * Template management list for reusable export configurations\n */\nexport function ExportTemplatesPage() {\n const { basePath = \"/\", navigate } = useExport();\n const { t } = useI18n();\n const tr = (\n key: string,\n fallback: string,\n params?: Record<string, string | number>,\n ) => tWithFallback(t, key, fallback, params);\n const { data: templates, isLoading } = useExportTemplates();\n const { deleteTemplate } = useDeleteExportTemplate();\n const { createTemplate, isPending: creating } = useCreateExportTemplate();\n const { data: availableEntities } = useAvailableEntities();\n\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [newTemplateName, setNewTemplateName] = useState(\"\");\n const [newTemplateDescription, setNewTemplateDescription] = useState(\"\");\n const [newTemplateEntityTypes, setNewTemplateEntityTypes] = useState<\n string[]\n >([]);\n const [newTemplateFormat, setNewTemplateFormat] =\n useState<ExportFormat>(\"CSV\");\n const [createError, setCreateError] = useState<string | null>(null);\n const [templatePendingDelete, setTemplatePendingDelete] = useState<\n string | null\n >(null);\n\n const handleOpenCreate = () => {\n setNewTemplateName(\"\");\n setNewTemplateDescription(\"\");\n setNewTemplateEntityTypes([]);\n setNewTemplateFormat(\"CSV\");\n setCreateError(null);\n setShowCreateForm(true);\n };\n\n const handleCreateTemplate = async () => {\n if (!newTemplateName.trim()) {\n setCreateError(\n tr(\n \"export.templates.form.errors.nameRequired\",\n \"Template name is required.\",\n ),\n );\n return;\n }\n if (newTemplateEntityTypes.length === 0) {\n setCreateError(\n tr(\n \"export.templates.form.errors.entityRequired\",\n \"Select at least one entity type.\",\n ),\n );\n return;\n }\n setCreateError(null);\n try {\n await createTemplate({\n name: newTemplateName.trim(),\n description: newTemplateDescription.trim() || undefined,\n entityTypes: newTemplateEntityTypes,\n format: newTemplateFormat,\n });\n setShowCreateForm(false);\n } catch (err) {\n setCreateError(\n err instanceof Error\n ? err.message\n : tr(\n \"export.templates.form.errors.createFailed\",\n \"Failed to create template.\",\n ),\n );\n }\n };\n\n const toggleEntity = (entityType: string) => {\n setNewTemplateEntityTypes((prev) =>\n prev.includes(entityType)\n ? prev.filter((e) => e !== entityType)\n : [...prev, entityType],\n );\n };\n\n // Client-side search filter\n const filteredTemplates = useMemo(() => {\n if (!searchQuery) return templates;\n const q = searchQuery.toLowerCase();\n return templates.filter(\n (t) =>\n t.name.toLowerCase().includes(q) ||\n t.entityTypes.some((e) => e.toLowerCase().includes(q)),\n );\n }, [templates, searchQuery]);\n\n const handleTemplateClick = (templateId: string) => {\n navigate?.(joinPath(basePath, `templates/${templateId}`));\n };\n\n const handleDeleteTemplate = async (\n e: React.MouseEvent,\n templateId: string,\n ) => {\n e.stopPropagation();\n nativeImpact(\"medium\");\n setTemplatePendingDelete(templateId);\n };\n\n const confirmDeleteTemplate = async () => {\n if (!templatePendingDelete) return;\n try {\n await deleteTemplate(templatePendingDelete);\n nativeNotify(\"success\");\n } catch {\n nativeNotify(\"error\");\n }\n setTemplatePendingDelete(null);\n };\n\n return (\n <PageLayout\n title={tr(\"export.templates.title\", \"Export Templates\")}\n subtitle={tr(\n \"export.templates.subtitle\",\n \"Reusable export configurations for common data exports\",\n )}\n icon={<FileBox size={24} />}\n headerActions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n onClick={handleOpenCreate}\n className=\"inline-flex items-center gap-2 px-3 py-2 sm:px-4 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium text-sm\"\n >\n <Plus size={16} />\n {tr(\"export.templates.actions.newTemplate\", \"New Template\")}\n </button>\n <button\n onClick={() => navigate?.(joinPath(basePath, \"new\"))}\n className=\"inline-flex items-center gap-2 px-3 py-2 sm:px-4 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium text-sm\"\n >\n <Plus size={16} />\n {tr(\"export.templates.actions.newExport\", \"New Export\")}\n </button>\n </div>\n }\n headerContent={\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground\">\n <Sparkles size={12} />\n {tr(\n \"export.templates.summary.available\",\n \"{count} template{suffix} available\",\n {\n count: filteredTemplates.length,\n suffix: filteredTemplates.length !== 1 ? \"s\" : \"\",\n },\n )}\n </span>\n }\n >\n {/* Create Template Form */}\n {showCreateForm && (\n <div className=\"border-2 border-primary/30 rounded-xl p-6 bg-primary/5 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-semibold text-lg\">\n {tr(\"export.templates.form.title\", \"New Template\")}\n </h3>\n <button\n onClick={() => setShowCreateForm(false)}\n className=\"p-1.5 rounded-md hover:bg-accent\"\n >\n <X size={16} />\n </button>\n </div>\n {createError && (\n <p className=\"text-sm text-destructive bg-destructive/10 rounded-lg px-3 py-2\">\n {createError}\n </p>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.name\", \"Name\")} *\n </label>\n <input\n type=\"text\"\n placeholder={tr(\n \"export.templates.form.fields.namePlaceholder\",\n \"e.g. Weekly Tags Export\",\n )}\n value={newTemplateName}\n onChange={(e) => setNewTemplateName(e.target.value)}\n className=\"w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n />\n </div>\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.format\", \"Format\")} *\n </label>\n <select\n value={newTemplateFormat}\n onChange={(e) =>\n setNewTemplateFormat(e.target.value as ExportFormat)\n }\n className=\"w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n >\n <option value=\"CSV\">CSV</option>\n <option value=\"JSON\">JSON</option>\n </select>\n </div>\n </div>\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.description\", \"Description\")}\n </label>\n <input\n type=\"text\"\n placeholder={tr(\n \"export.templates.form.fields.descriptionPlaceholder\",\n \"Optional description\",\n )}\n value={newTemplateDescription}\n onChange={(e) => setNewTemplateDescription(e.target.value)}\n className=\"w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.entityTypes\", \"Entity Types\")} *{\" \"}\n <span className=\"text-muted-foreground font-normal\">\n {tr(\n \"export.templates.form.fields.entityTypesHint\",\n \"(select at least one)\",\n )}\n </span>\n </label>\n <div className=\"flex flex-wrap gap-2\">\n {availableEntities.map((entity) => (\n <button\n key={entity.entityType}\n type=\"button\"\n onClick={() => toggleEntity(entity.entityType)}\n className={cn(\n \"px-3 py-1.5 rounded-lg text-sm font-medium border-2 transition-all\",\n newTemplateEntityTypes.includes(entity.entityType)\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border bg-background hover:border-primary/50\",\n )}\n >\n {entity.displayName}\n </button>\n ))}\n </div>\n </div>\n <div className=\"flex gap-3 pt-2\">\n <button\n onClick={handleCreateTemplate}\n disabled={creating}\n className=\"inline-flex items-center gap-2 px-5 py-2.5 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium disabled:opacity-50\"\n >\n {creating ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Plus size={16} />\n )}\n {tr(\"export.templates.form.actions.create\", \"Create Template\")}\n </button>\n <button\n onClick={() => setShowCreateForm(false)}\n className=\"px-5 py-2.5 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium\"\n >\n {tr(\"export.templates.form.actions.cancel\", \"Cancel\")}\n </button>\n </div>\n </div>\n )}\n {/* Search */}\n <div className=\"relative group\">\n <Search\n size={18}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors\"\n />\n <input\n type=\"text\"\n placeholder={tr(\n \"export.templates.search.placeholder\",\n \"Search templates...\",\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2.5 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all duration-200\"\n />\n {searchQuery && (\n <button\n onClick={() => setSearchQuery(\"\")}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent\"\n >\n <X size={16} />\n </button>\n )}\n </div>\n\n {/* Loading */}\n {isLoading && (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n {[...Array(4)].map((_, i) => (\n <div\n key={i}\n className=\"border-2 border-border/50 rounded-xl p-5 animate-pulse\"\n >\n <div className=\"h-5 w-40 bg-muted rounded mb-3\" />\n <div className=\"h-3 w-full bg-muted rounded mb-2\" />\n <div className=\"flex gap-2 mt-4\">\n <div className=\"h-5 w-16 bg-muted rounded-full\" />\n <div className=\"h-5 w-16 bg-muted rounded-full\" />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Templates Grid */}\n {!isLoading ? (\n filteredTemplates.length > 0 ? (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n {filteredTemplates.map((template, index) => (\n <div\n key={template.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => handleTemplateClick(template.id)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleTemplateClick(template.id);\n }\n }}\n className={cn(\n \"text-left group cursor-pointer border-2 border-border/50 rounded-xl overflow-hidden\",\n \"hover:shadow-md hover:border-primary/30 transition-all duration-200\",\n \"animate-in fade-in-0 slide-in-from-bottom-2\",\n )}\n style={{ animationDelay: `${index * 50}ms` }}\n >\n {/* Header */}\n <div className=\"p-4 border-b bg-muted/30\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-lg bg-primary/10\">\n <FileBox size={20} className=\"text-primary\" />\n </div>\n <div>\n <h3 className=\"font-semibold\">{template.name}</h3>\n <p className=\"text-xs text-muted-foreground\">\n {getFormatDisplayName(template.format)}\n {template.isDefault && (\n <span className=\"ml-1.5 inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-primary/10 text-primary font-medium\">\n {tr(\"export.templates.card.default\", \"Default\")}\n </span>\n )}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={(e) => handleDeleteTemplate(e, template.id)}\n className=\"p-1.5 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors\"\n aria-label={tr(\n \"export.templates.card.deleteAriaLabel\",\n \"Delete {name}\",\n { name: template.name },\n )}\n >\n <X size={14} />\n </button>\n <ChevronRight\n size={18}\n className=\"text-muted-foreground group-hover:text-primary transition-colors mt-1\"\n />\n </div>\n </div>\n </div>\n\n {/* Body */}\n <div className=\"p-4\">\n {template.description && (\n <p className=\"text-sm text-muted-foreground mb-3 line-clamp-2\">\n {template.description}\n </p>\n )}\n <div className=\"flex flex-wrap gap-1.5 overflow-hidden\">\n {template.entityTypes.slice(0, 6).map((entity) => (\n <span\n key={entity}\n className=\"inline-flex px-2 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground max-w-full truncate\"\n >\n {entity}\n </span>\n ))}\n {template.entityTypes.length > 6 && (\n <span className=\"inline-flex px-2 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground\">\n {tr(\n \"export.templates.card.moreEntities\",\n \"+{count} more\",\n {\n count: template.entityTypes.length - 6,\n },\n )}\n </span>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n icon={searchQuery ? <Search size={48} /> : <FileBox size={48} />}\n title={\n searchQuery\n ? tr(\n \"export.templates.empty.filtered.title\",\n \"No matching templates\",\n )\n : tr(\"export.templates.empty.title\", \"No templates yet\")\n }\n description={\n searchQuery\n ? tr(\n \"export.templates.empty.filtered.description\",\n \"Try adjusting your search to find what you are looking for.\",\n )\n : tr(\n \"export.templates.empty.description\",\n \"Create a template to save reusable export configurations.\",\n )\n }\n action={\n searchQuery ? (\n <button\n onClick={() => setSearchQuery(\"\")}\n className=\"inline-flex items-center gap-2 px-4 py-2 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium\"\n >\n <X size={16} />\n {tr(\n \"export.templates.empty.filtered.actions.clear\",\n \"Clear Search\",\n )}\n </button>\n ) : (\n <button\n onClick={handleOpenCreate}\n className=\"inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium\"\n >\n <Plus size={16} />\n {tr(\n \"export.templates.empty.actions.newTemplate\",\n \"New Template\",\n )}\n </button>\n )\n }\n />\n )\n ) : null}\n {templatePendingDelete && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"template-list-delete-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border bg-card p-6 shadow-xl\">\n <h2 id=\"template-list-delete-title\" className=\"text-lg font-bold\">\n {tr(\"export.templates.delete.title\", \"Delete template?\")}\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {tr(\n \"export.templates.delete.description\",\n \"This removes the saved export configuration. Existing export jobs are not deleted.\",\n )}\n </p>\n <div className=\"mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setTemplatePendingDelete(null)}\n className=\"px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium\"\n >\n {tr(\"export.templates.delete.cancel\", \"Keep Template\")}\n </button>\n <button\n type=\"button\"\n onClick={confirmDeleteTemplate}\n className=\"px-4 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors font-medium\"\n >\n {tr(\"export.templates.delete.confirmAction\", \"Delete Template\")}\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;AA4BA,IAAM,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AAQtD,SAAgB,IAAsB;CACpC,IAAM,EAAE,cAAW,KAAK,gBAAa,GAAW,EAC1C,EAAE,SAAM,GAAS,EACjB,KACJ,GACA,GACA,MACG,EAAc,GAAG,GAAK,GAAU,EAAO,EACtC,EAAE,MAAM,GAAW,iBAAc,GAAoB,EACrD,EAAE,sBAAmB,GAAyB,EAC9C,EAAE,mBAAgB,WAAW,MAAa,GAAyB,EACnE,EAAE,MAAM,MAAsB,GAAsB,EAEpD,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,IAAgB,KAAqB,EAAS,GAAM,EACrD,CAAC,GAAiB,KAAsB,EAAS,GAAG,EACpD,CAAC,GAAwB,KAA6B,EAAS,GAAG,EAClE,CAAC,GAAwB,KAA6B,EAE1D,EAAE,CAAC,EACC,CAAC,GAAmB,KACxB,EAAuB,MAAM,EACzB,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAuB,KAA4B,EAExD,KAAK,EAED,UAAyB;AAM7B,EALA,EAAmB,GAAG,EACtB,EAA0B,GAAG,EAC7B,EAA0B,EAAE,CAAC,EAC7B,EAAqB,MAAM,EAC3B,EAAe,KAAK,EACpB,EAAkB,GAAK;IAGnB,IAAuB,YAAY;AACvC,MAAI,CAAC,EAAgB,MAAM,EAAE;AAC3B,KACE,EACE,6CACA,6BACD,CACF;AACD;;AAEF,MAAI,EAAuB,WAAW,GAAG;AACvC,KACE,EACE,+CACA,mCACD,CACF;AACD;;AAEF,IAAe,KAAK;AACpB,MAAI;AAOF,GANA,MAAM,EAAe;IACnB,MAAM,EAAgB,MAAM;IAC5B,aAAa,EAAuB,MAAM,IAAI,KAAA;IAC9C,aAAa;IACb,QAAQ;IACT,CAAC,EACF,EAAkB,GAAM;WACjB,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EACE,6CACA,6BACD,CACN;;IAIC,KAAgB,MAAuB;AAC3C,KAA2B,MACzB,EAAK,SAAS,EAAW,GACrB,EAAK,QAAQ,MAAM,MAAM,EAAW,GACpC,CAAC,GAAG,GAAM,EAAW,CAC1B;IAIG,IAAoB,QAAc;AACtC,MAAI,CAAC,EAAa,QAAO;EACzB,IAAM,IAAI,EAAY,aAAa;AACnC,SAAO,EAAU,QACd,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,EAAE,IAChC,EAAE,YAAY,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,CAAC,CACzD;IACA,CAAC,GAAW,EAAY,CAAC,EAEtB,KAAuB,MAAuB;AAClD,MAAW,EAAS,GAAU,aAAa,IAAa,CAAC;IAGrD,KAAuB,OAC3B,GACA,MACG;AAGH,EAFA,EAAE,iBAAiB,EACnB,EAAa,SAAS,EACtB,EAAyB,EAAW;IAGhC,KAAwB,YAAY;AACnC,SACL;OAAI;AAEF,IADA,MAAM,EAAe,EAAsB,EAC3C,EAAa,UAAU;WACjB;AACN,MAAa,QAAQ;;AAEvB,KAAyB,KAAK;;;AAGhC,QACE,kBAAC,GAAD;EACE,OAAO,EAAG,0BAA0B,mBAAmB;EACvD,UAAU,EACR,6BACA,yDACD;EACD,MAAM,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;EAC3B,eACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,SAAS;IACT,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,wCAAwC,eAAe,CACpD;OACT,kBAAC,UAAD;IACE,eAAe,IAAW,EAAS,GAAU,MAAM,CAAC;IACpD,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,sCAAsC,aAAa,CAChD;MACL;;EAER,eACE,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EACrB,EACC,sCACA,sCACA;IACE,OAAO,EAAkB;IACzB,QAAQ,EAAkB,WAAW,IAAU,KAAN;IAC1C,CACF,CACI;;YApCX;GAwCG,MACC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,+BAA+B,eAAe;OAC/C,CAAA,EACL,kBAAC,UAAD;OACE,eAAe,EAAkB,GAAM;OACvC,WAAU;iBAEV,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;OACR,CAAA,CACL;;KACL,KACC,kBAAC,KAAD;MAAG,WAAU;gBACV;MACC,CAAA;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACG,EAAG,qCAAqC,OAAO,EAAC,KAC3C;WACR,kBAAC,SAAD;QACE,MAAK;QACL,aAAa,EACX,gDACA,0BACD;QACD,OAAO;QACP,WAAW,MAAM,EAAmB,EAAE,OAAO,MAAM;QACnD,WAAU;QACV,CAAA,CACE;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACG,EAAG,uCAAuC,SAAS,EAAC,KAC/C;WACR,kBAAC,UAAD;QACE,OAAO;QACP,WAAW,MACT,EAAqB,EAAE,OAAO,MAAsB;QAEtD,WAAU;kBALZ,CAOE,kBAAC,UAAD;SAAQ,OAAM;mBAAM;SAAY,CAAA,EAChC,kBAAC,UAAD;SAAQ,OAAM;mBAAO;SAAa,CAAA,CAC3B;UACL;SACF;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OAAO,WAAU;iBACd,EAAG,4CAA4C,cAAc;OACxD,CAAA,EACR,kBAAC,SAAD;OACE,MAAK;OACL,aAAa,EACX,uDACA,uBACD;OACD,OAAO;OACP,WAAW,MAAM,EAA0B,EAAE,OAAO,MAAM;OAC1D,WAAU;OACV,CAAA,CACE;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OAAO,WAAU;iBAAjB;QACG,EAAG,4CAA4C,eAAe;QAAC;QAAG;QACnE,kBAAC,QAAD;SAAM,WAAU;mBACb,EACC,gDACA,wBACD;SACI,CAAA;QACD;UACR,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAkB,KAAK,MACtB,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAa,EAAO,WAAW;QAC9C,WAAW,EACT,sEACA,EAAuB,SAAS,EAAO,WAAW,GAC9C,8CACA,sDACL;kBAEA,EAAO;QACD,EAXF,EAAO,WAWL,CACT;OACE,CAAA,CACF;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,SAAS;OACT,UAAU;OACV,WAAU;iBAHZ,CAKG,IACC,kBAAC,GAAD;QAAS,MAAM;QAAI,WAAU;QAAiB,CAAA,GAE9C,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAEnB,EAAG,wCAAwC,kBAAkB,CACvD;UACT,kBAAC,UAAD;OACE,eAAe,EAAkB,GAAM;OACvC,WAAU;iBAET,EAAG,wCAAwC,SAAS;OAC9C,CAAA,CACL;;KACF;;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,MAAM;MACN,WAAU;MACV,CAAA;KACF,kBAAC,SAAD;MACE,MAAK;MACL,aAAa,EACX,uCACA,sBACD;MACD,OAAO;MACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;MAC/C,WAAU;MACV,CAAA;KACD,KACC,kBAAC,UAAD;MACE,eAAe,EAAe,GAAG;MACjC,WAAU;gBAEV,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;MACR,CAAA;KAEP;;GAGL,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ,CAAC,GAAG;;;;;KAAQ,CAAC,CAAC,KAAK,GAAG,MACrB,kBAAC,OAAD;KAEE,WAAU;eAFZ;MAIE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA;MAClD,kBAAC,OAAD,EAAK,WAAU,oCAAqC,CAAA;MACpD,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,EAClD,kBAAC,OAAD,EAAK,WAAU,kCAAmC,CAAA,CAC9C;;MACF;OATC,EASD,CACN;IACE,CAAA;GAIN,IA8IE,OA7IF,EAAkB,SAAS,IACzB,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAkB,KAAK,GAAU,MAChC,kBAAC,OAAD;KAEE,MAAK;KACL,UAAU;KACV,eAAe,EAAoB,EAAS,GAAG;KAC/C,YAAY,MAAM;AAChB,OAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EAClB,EAAoB,EAAS,GAAG;;KAGpC,WAAW,EACT,uFACA,uEACA,8CACD;KACD,OAAO,EAAE,gBAAgB,GAAG,IAAQ,GAAG,KAAK;eAhB9C,CAmBE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD;UAAS,MAAM;UAAI,WAAU;UAAiB,CAAA;SAC1C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAiB,EAAS;SAAU,CAAA,EAClD,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACG,EAAqB,EAAS,OAAO,EACrC,EAAS,aACR,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAG,iCAAiC,UAAU;UAC1C,CAAA,CAEP;WACA,EAAA,CAAA,CACF;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,UAAU,MAAM,GAAqB,GAAG,EAAS,GAAG;SACpD,WAAU;SACV,cAAY,EACV,yCACA,iBACA,EAAE,MAAM,EAAS,MAAM,CACxB;mBAED,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;SACR,CAAA,EACT,kBAAC,IAAD;SACE,MAAM;SACN,WAAU;SACV,CAAA,CACE;UACF;;MACF,CAAA,EAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAS,eACR,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAS;OACR,CAAA,EAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAS,YAAY,MAAM,GAAG,EAAE,CAAC,KAAK,MACrC,kBAAC,QAAD;QAEE,WAAU;kBAET;QACI,EAJA,EAIA,CACP,EACD,EAAS,YAAY,SAAS,KAC7B,kBAAC,QAAD;QAAM,WAAU;kBACb,EACC,sCACA,iBACA,EACE,OAAO,EAAS,YAAY,SAAS,GACtC,CACF;QACI,CAAA,CAEL;SACF;QACF;OArFC,EAAS,GAqFV,CACN;IACE,CAAA,GAEN,kBAAC,GAAD;IACE,MAAoB,EAAd,IAAe,IAAuB,GAAxB,EAAQ,MAAM,IAAM,CAAwB;IAChE,OACE,IACI,EACE,yCACA,wBACD,GACD,EAAG,gCAAgC,mBAAmB;IAE5D,aACE,IACI,EACE,+CACA,8DACD,GACD,EACE,sCACA,4DACD;IAEP,QACE,IACE,kBAAC,UAAD;KACE,eAAe,EAAe,GAAG;KACjC,WAAU;eAFZ,CAIE,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA,EACd,EACC,iDACA,eACD,CACM;SAET,kBAAC,UAAD;KACE,SAAS;KACT,WAAU;eAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EACC,8CACA,eACD,CACM;;IAGb,CAAA;GAGL,KACC,kBAAC,OAAD;IACE,MAAK;IACL,cAAW;IACX,mBAAgB;IAChB,WAAU;cAEV,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,IAAG;OAA6B,WAAU;iBAC3C,EAAG,iCAAiC,mBAAmB;OACrD,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,uCACA,qFACD;OACC,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAyB,KAAK;QAC7C,WAAU;kBAET,EAAG,kCAAkC,gBAAgB;QAC/C,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAET,EAAG,yCAAyC,kBAAkB;QACxD,CAAA,CACL;;MACF;;IACF,CAAA;GAEG"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Detail Page
|
|
3
|
+
* View template details and use it to create a new export.
|
|
4
|
+
* Fetches template from the backend via useExportTemplate hook.
|
|
5
|
+
*/
|
|
6
|
+
export declare function TemplateDetailPage(): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=TemplateDetailPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TemplateDetailPage.d.ts","sourceRoot":"","sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"names":[],"mappings":"AAmCA;;;;GAIG;AACH,wBAAgB,kBAAkB,4CAoOjC"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { useExport as e } from "../providers/ExportProvider.js";
|
|
2
|
+
import { useDeleteExportTemplate as t, useExportTemplate as n } from "../hooks/useExportQueries.js";
|
|
3
|
+
import { getArchiveFormatDisplayName as r, getFormatDisplayName as i } from "../constants/enums.js";
|
|
4
|
+
import { PageLayout as a, PageSection as o } from "../components/PageLayout.js";
|
|
5
|
+
import { nativeImpact as s, nativeNotify as c } from "../../utils/nativeBridge.js";
|
|
6
|
+
import { useState as l } from "react";
|
|
7
|
+
import { AlertCircle as u, ArrowLeft as d, Clock as f, Download as p, FileBox as m, FileText as h, HardDrive as g, Loader2 as _, Play as v, Trash2 as y } from "lucide-react";
|
|
8
|
+
import { useParams as b } from "react-router-dom";
|
|
9
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
10
|
+
//#region src/export/pages/TemplateDetailPage.tsx
|
|
11
|
+
var C = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
12
|
+
function w() {
|
|
13
|
+
let { templateId: w } = b(), { basePath: T = "/", navigate: E } = e(), [D, O] = l(!1), { data: k, isLoading: A } = n(w), { deleteTemplate: j, isPending: M } = t();
|
|
14
|
+
return A ? /* @__PURE__ */ S("div", {
|
|
15
|
+
className: "flex flex-col items-center justify-center py-20",
|
|
16
|
+
children: [/* @__PURE__ */ x(_, {
|
|
17
|
+
size: 48,
|
|
18
|
+
className: "animate-spin text-primary"
|
|
19
|
+
}), /* @__PURE__ */ x("span", {
|
|
20
|
+
className: "mt-6 text-sm font-medium text-muted-foreground",
|
|
21
|
+
children: "Loading template details..."
|
|
22
|
+
})]
|
|
23
|
+
}) : k ? /* @__PURE__ */ S(a, {
|
|
24
|
+
showHeader: !1,
|
|
25
|
+
children: [
|
|
26
|
+
/* @__PURE__ */ x("div", {
|
|
27
|
+
className: "relative overflow-hidden rounded-xl bg-gradient-to-br from-primary/5 via-primary/3 to-background border border-border/50 p-6 shadow-sm",
|
|
28
|
+
children: /* @__PURE__ */ S("div", {
|
|
29
|
+
className: "relative z-10 flex items-center gap-4",
|
|
30
|
+
children: [/* @__PURE__ */ x("button", {
|
|
31
|
+
onClick: () => E?.(C(T, "templates")),
|
|
32
|
+
className: "p-2.5 hover:bg-accent rounded-lg transition-all duration-200",
|
|
33
|
+
children: /* @__PURE__ */ x(d, { size: 20 })
|
|
34
|
+
}), /* @__PURE__ */ S("div", {
|
|
35
|
+
className: "flex items-center gap-3 flex-1",
|
|
36
|
+
children: [/* @__PURE__ */ x("div", {
|
|
37
|
+
className: "p-2.5 rounded-lg bg-primary/10",
|
|
38
|
+
children: /* @__PURE__ */ x(m, {
|
|
39
|
+
size: 24,
|
|
40
|
+
className: "text-primary"
|
|
41
|
+
})
|
|
42
|
+
}), /* @__PURE__ */ S("div", {
|
|
43
|
+
className: "flex-1 min-w-0",
|
|
44
|
+
children: [/* @__PURE__ */ x("h1", {
|
|
45
|
+
className: "text-xl sm:text-2xl font-bold truncate",
|
|
46
|
+
children: k.name
|
|
47
|
+
}), /* @__PURE__ */ x("p", {
|
|
48
|
+
className: "text-sm text-muted-foreground mt-1",
|
|
49
|
+
children: k.isDefault ? "Default template" : "Custom template"
|
|
50
|
+
})]
|
|
51
|
+
})]
|
|
52
|
+
})]
|
|
53
|
+
})
|
|
54
|
+
}),
|
|
55
|
+
k.description && /* @__PURE__ */ x("div", {
|
|
56
|
+
className: "border-2 border-border/50 rounded-xl p-5",
|
|
57
|
+
children: /* @__PURE__ */ x("p", {
|
|
58
|
+
className: "text-sm text-muted-foreground",
|
|
59
|
+
children: k.description
|
|
60
|
+
})
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ x("div", {
|
|
63
|
+
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",
|
|
64
|
+
children: [
|
|
65
|
+
{
|
|
66
|
+
icon: /* @__PURE__ */ x(h, { size: 18 }),
|
|
67
|
+
label: "Format",
|
|
68
|
+
value: i(k.format)
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
icon: /* @__PURE__ */ x(g, { size: 18 }),
|
|
72
|
+
label: "Archive",
|
|
73
|
+
value: r("ZIP")
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
icon: /* @__PURE__ */ x(p, { size: 18 }),
|
|
77
|
+
label: "Entity Types",
|
|
78
|
+
value: `${k.entityTypes.length} type${k.entityTypes.length === 1 ? "" : "s"}`
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
icon: /* @__PURE__ */ x(f, { size: 18 }),
|
|
82
|
+
label: "Last Updated",
|
|
83
|
+
value: new Date(k.updatedAt).toLocaleDateString()
|
|
84
|
+
}
|
|
85
|
+
].map((e) => /* @__PURE__ */ S("div", {
|
|
86
|
+
className: "border-2 border-border/50 rounded-xl p-4 bg-gradient-to-br from-card/50 to-background",
|
|
87
|
+
children: [/* @__PURE__ */ S("div", {
|
|
88
|
+
className: "flex items-center gap-2 text-muted-foreground mb-1",
|
|
89
|
+
children: [e.icon, /* @__PURE__ */ x("span", {
|
|
90
|
+
className: "text-xs font-medium",
|
|
91
|
+
children: e.label
|
|
92
|
+
})]
|
|
93
|
+
}), /* @__PURE__ */ x("p", {
|
|
94
|
+
className: "font-bold text-lg",
|
|
95
|
+
children: e.value
|
|
96
|
+
})]
|
|
97
|
+
}, e.label))
|
|
98
|
+
}),
|
|
99
|
+
/* @__PURE__ */ x(o, {
|
|
100
|
+
title: "Included Entity Types",
|
|
101
|
+
description: "Data types that will be exported",
|
|
102
|
+
children: /* @__PURE__ */ x("div", {
|
|
103
|
+
className: "flex flex-wrap gap-2",
|
|
104
|
+
children: k.entityTypes.map((e) => /* @__PURE__ */ x("span", {
|
|
105
|
+
className: "inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border-2 border-border/50 bg-muted/30",
|
|
106
|
+
children: e
|
|
107
|
+
}, e))
|
|
108
|
+
})
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ S("div", {
|
|
111
|
+
className: "flex flex-col sm:flex-row gap-3 pt-2",
|
|
112
|
+
children: [/* @__PURE__ */ S("button", {
|
|
113
|
+
onClick: () => {
|
|
114
|
+
E?.(C(T, `new?templateId=${w}`));
|
|
115
|
+
},
|
|
116
|
+
className: "flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-all duration-200 hover:scale-105 font-bold shadow-lg",
|
|
117
|
+
children: [/* @__PURE__ */ x(v, { size: 18 }), "Use Template"]
|
|
118
|
+
}), !k.isDefault && /* @__PURE__ */ S("button", {
|
|
119
|
+
onClick: () => O(!0),
|
|
120
|
+
disabled: M,
|
|
121
|
+
className: "inline-flex items-center justify-center gap-2 px-6 py-3 border-2 border-status-error-bg/50 text-status-error-text rounded-lg hover:bg-status-error-bg-subtle transition-all duration-200 font-medium disabled:opacity-50",
|
|
122
|
+
children: [M ? /* @__PURE__ */ x(_, {
|
|
123
|
+
size: 18,
|
|
124
|
+
className: "animate-spin"
|
|
125
|
+
}) : /* @__PURE__ */ x(y, { size: 18 }), "Delete Template"]
|
|
126
|
+
})]
|
|
127
|
+
}),
|
|
128
|
+
D && /* @__PURE__ */ x("div", {
|
|
129
|
+
role: "dialog",
|
|
130
|
+
"aria-modal": "true",
|
|
131
|
+
"aria-labelledby": "template-delete-title",
|
|
132
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4",
|
|
133
|
+
children: /* @__PURE__ */ S("div", {
|
|
134
|
+
className: "w-full max-w-md rounded-xl border-2 border-border bg-card p-6 shadow-xl",
|
|
135
|
+
children: [
|
|
136
|
+
/* @__PURE__ */ x("h2", {
|
|
137
|
+
id: "template-delete-title",
|
|
138
|
+
className: "text-lg font-bold",
|
|
139
|
+
children: "Delete template?"
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ x("p", {
|
|
142
|
+
className: "mt-2 text-sm text-muted-foreground",
|
|
143
|
+
children: "This removes the saved export configuration. Existing export jobs are not deleted."
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ S("div", {
|
|
146
|
+
className: "mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3",
|
|
147
|
+
children: [/* @__PURE__ */ x("button", {
|
|
148
|
+
type: "button",
|
|
149
|
+
onClick: () => O(!1),
|
|
150
|
+
className: "px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium",
|
|
151
|
+
children: "Keep Template"
|
|
152
|
+
}), /* @__PURE__ */ S("button", {
|
|
153
|
+
type: "button",
|
|
154
|
+
onClick: async () => {
|
|
155
|
+
if (w) {
|
|
156
|
+
s("medium");
|
|
157
|
+
try {
|
|
158
|
+
await j(w), c("success"), E?.(C(T, "templates"));
|
|
159
|
+
} catch {
|
|
160
|
+
c("error");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
disabled: M,
|
|
165
|
+
className: "inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors font-medium disabled:opacity-50",
|
|
166
|
+
children: [M ? /* @__PURE__ */ x(_, {
|
|
167
|
+
size: 16,
|
|
168
|
+
className: "animate-spin"
|
|
169
|
+
}) : null, "Delete Template"]
|
|
170
|
+
})]
|
|
171
|
+
})
|
|
172
|
+
]
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
}) : /* @__PURE__ */ S(a, {
|
|
177
|
+
showHeader: !1,
|
|
178
|
+
children: [/* @__PURE__ */ S("div", {
|
|
179
|
+
className: "flex items-center gap-3 mb-6",
|
|
180
|
+
children: [/* @__PURE__ */ x("button", {
|
|
181
|
+
onClick: () => E?.(C(T, "templates")),
|
|
182
|
+
className: "p-2.5 hover:bg-accent rounded-lg transition-all duration-200",
|
|
183
|
+
children: /* @__PURE__ */ x(d, { size: 20 })
|
|
184
|
+
}), /* @__PURE__ */ x("h1", {
|
|
185
|
+
className: "text-xl font-bold",
|
|
186
|
+
children: "Template Not Found"
|
|
187
|
+
})]
|
|
188
|
+
}), /* @__PURE__ */ S("div", {
|
|
189
|
+
className: "border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-12 text-center",
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ x(u, {
|
|
192
|
+
size: 48,
|
|
193
|
+
className: "text-status-error-text mx-auto mb-4"
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ x("h3", {
|
|
196
|
+
className: "text-lg font-bold text-status-error-text mb-2",
|
|
197
|
+
children: "Template Not Found"
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ x("p", {
|
|
200
|
+
className: "text-status-error-text mb-6",
|
|
201
|
+
children: "The template you are looking for does not exist."
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ S("button", {
|
|
204
|
+
onClick: () => E?.(C(T, "templates")),
|
|
205
|
+
className: "inline-flex items-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium",
|
|
206
|
+
children: [/* @__PURE__ */ x(d, { size: 18 }), "Back to Templates"]
|
|
207
|
+
})
|
|
208
|
+
]
|
|
209
|
+
})]
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { w as TemplateDetailPage };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=TemplateDetailPage.js.map
|