@burdenoff/microfe-export 2026.830.1 → 2026.830.2
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/dist/export/ExportRoot.d.ts.map +1 -1
- package/dist/export/ExportRoot.js +41 -36
- package/dist/export/ExportRoot.js.map +1 -1
- package/dist/export/ExportRoutes.d.ts.map +1 -1
- package/dist/export/ExportRoutes.js +75 -66
- package/dist/export/ExportRoutes.js.map +1 -1
- package/dist/export/components/ExportProgressBar.d.ts.map +1 -1
- package/dist/export/components/ExportProgressBar.js +19 -18
- package/dist/export/components/ExportProgressBar.js.map +1 -1
- package/dist/export/components/ExportStatusBadge.d.ts.map +1 -1
- package/dist/export/components/ExportStatusBadge.js +19 -20
- package/dist/export/components/ExportStatusBadge.js.map +1 -1
- package/dist/export/hooks/useTr.d.ts +19 -0
- package/dist/export/hooks/useTr.d.ts.map +1 -0
- package/dist/export/hooks/useTr.js +15 -0
- package/dist/export/hooks/useTr.js.map +1 -0
- package/dist/export/index.d.ts +1 -0
- package/dist/export/index.d.ts.map +1 -1
- package/dist/export/index.js +1 -0
- package/dist/export/pages/CreateExportPage.d.ts +4 -0
- package/dist/export/pages/CreateExportPage.d.ts.map +1 -1
- package/dist/export/pages/CreateExportPage.js +271 -283
- package/dist/export/pages/CreateExportPage.js.map +1 -1
- package/dist/export/pages/ExportDashboardPage.d.ts.map +1 -1
- package/dist/export/pages/ExportDashboardPage.js +71 -72
- package/dist/export/pages/ExportDashboardPage.js.map +1 -1
- package/dist/export/pages/ExportDetailPage.d.ts.map +1 -1
- package/dist/export/pages/ExportDetailPage.js +171 -173
- package/dist/export/pages/ExportDetailPage.js.map +1 -1
- package/dist/export/pages/ExportHistoryPage.d.ts.map +1 -1
- package/dist/export/pages/ExportHistoryPage.js +110 -111
- package/dist/export/pages/ExportHistoryPage.js.map +1 -1
- package/dist/export/pages/ExportLandingPage.d.ts.map +1 -1
- package/dist/export/pages/ExportLandingPage.js +28 -27
- package/dist/export/pages/ExportLandingPage.js.map +1 -1
- package/dist/export/pages/ExportTemplatesPage.d.ts.map +1 -1
- package/dist/export/pages/ExportTemplatesPage.js +115 -116
- package/dist/export/pages/ExportTemplatesPage.js.map +1 -1
- package/dist/export/pages/TemplateDetailPage.d.ts.map +1 -1
- package/dist/export/pages/TemplateDetailPage.js +122 -118
- package/dist/export/pages/TemplateDetailPage.js.map +1 -1
- package/dist/export/utils/i18n.d.ts +18 -2
- package/dist/export/utils/i18n.d.ts.map +1 -1
- package/dist/export/utils/i18n.js +2 -9
- package/dist/export/utils/i18n.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/translations/en.d.ts +454 -0
- package/dist/translations/en.d.ts.map +1 -0
- package/dist/translations/en.js +417 -0
- package/dist/translations/en.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,106 +1,105 @@
|
|
|
1
1
|
import { useExport as e } from "../providers/ExportProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { PageLayout as
|
|
7
|
-
import { nativeImpact as
|
|
8
|
-
import { useMemo as
|
|
9
|
-
import { ChevronRight as
|
|
2
|
+
import { useTr as t } from "../hooks/useTr.js";
|
|
3
|
+
import { useAvailableEntities as n, useCreateExportTemplate as r, useDeleteExportTemplate as i, useExportTemplates as a } from "../hooks/useExportQueries.js";
|
|
4
|
+
import { cn as o } from "../utils/cn.js";
|
|
5
|
+
import { getFormatDisplayName as s } from "../constants/enums.js";
|
|
6
|
+
import { PageLayout as c } from "../components/PageLayout.js";
|
|
7
|
+
import { nativeImpact as ee, nativeNotify as l } from "../../utils/nativeBridge.js";
|
|
8
|
+
import { useMemo as u, useState as d } from "react";
|
|
9
|
+
import { ChevronRight as te, FileBox as f, Loader2 as p, Plus as m, Search as h, Sparkles as g, X as _ } from "lucide-react";
|
|
10
10
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
11
|
-
import {
|
|
12
|
-
import { CTAOverflowMenu as x, IllustratedEmptyState as S, PagePurpose as C } from "@burdenoff/fe-libs/ui";
|
|
11
|
+
import { CTAOverflowMenu as b, IllustratedEmptyState as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
|
|
13
12
|
//#region src/export/pages/ExportTemplatesPage.tsx
|
|
14
|
-
var
|
|
15
|
-
function
|
|
16
|
-
let { basePath:
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
if (!
|
|
20
|
-
|
|
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(), E = t(), { data: D, isLoading: O } = a(), { deleteTemplate: k } = i(), { createTemplate: A, isPending: j } = r(), { data: M } = n(), [N, P] = d(""), [F, I] = d(!1), [L, R] = d(""), [z, B] = d(""), [V, H] = d([]), [U, W] = d("CSV"), [G, K] = d(null), [q, J] = d(null), Y = () => {
|
|
16
|
+
R(""), B(""), H([]), W("CSV"), K(null), I(!0);
|
|
17
|
+
}, X = async () => {
|
|
18
|
+
if (!L.trim()) {
|
|
19
|
+
K(E("export.templates.form.errors.nameRequired", "Template name is required."));
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
23
|
-
if (
|
|
24
|
-
|
|
22
|
+
if (V.length === 0) {
|
|
23
|
+
K(E("export.templates.form.errors.entityRequired", "Select at least one entity type."));
|
|
25
24
|
return;
|
|
26
25
|
}
|
|
27
|
-
|
|
26
|
+
K(null);
|
|
28
27
|
try {
|
|
29
|
-
await
|
|
30
|
-
name:
|
|
31
|
-
description:
|
|
32
|
-
entityTypes:
|
|
33
|
-
format:
|
|
34
|
-
}),
|
|
28
|
+
await A({
|
|
29
|
+
name: L.trim(),
|
|
30
|
+
description: z.trim() || void 0,
|
|
31
|
+
entityTypes: V,
|
|
32
|
+
format: U
|
|
33
|
+
}), I(!1);
|
|
35
34
|
} catch (e) {
|
|
36
|
-
|
|
35
|
+
K(e instanceof Error ? e.message : E("export.templates.form.errors.createFailed", "Failed to create template."));
|
|
37
36
|
}
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
}, Q =
|
|
41
|
-
if (!
|
|
42
|
-
let e =
|
|
43
|
-
return
|
|
44
|
-
}, [
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
e.stopPropagation(),
|
|
48
|
-
},
|
|
49
|
-
if (
|
|
37
|
+
}, Z = (e) => {
|
|
38
|
+
H((t) => t.includes(e) ? t.filter((t) => t !== e) : [...t, e]);
|
|
39
|
+
}, Q = u(() => {
|
|
40
|
+
if (!N) return D;
|
|
41
|
+
let e = N.toLowerCase();
|
|
42
|
+
return D.filter((t) => t.name.toLowerCase().includes(e) || t.entityTypes.some((t) => t.toLowerCase().includes(e)));
|
|
43
|
+
}, [D, N]), $ = (e) => {
|
|
44
|
+
T?.(C(w, `templates/${e}`));
|
|
45
|
+
}, ne = async (e, t) => {
|
|
46
|
+
e.stopPropagation(), ee("medium"), J(t);
|
|
47
|
+
}, re = async () => {
|
|
48
|
+
if (q) {
|
|
50
49
|
try {
|
|
51
|
-
await
|
|
50
|
+
await k(q), l("success");
|
|
52
51
|
} catch {
|
|
53
|
-
|
|
52
|
+
l("error");
|
|
54
53
|
}
|
|
55
|
-
|
|
54
|
+
J(null);
|
|
56
55
|
}
|
|
57
56
|
};
|
|
58
|
-
return /* @__PURE__ */ y(
|
|
59
|
-
title:
|
|
60
|
-
subtitle:
|
|
57
|
+
return /* @__PURE__ */ y(c, {
|
|
58
|
+
title: E("export.templates.title", "Export Templates"),
|
|
59
|
+
subtitle: E("export.templates.subtitle", "Reusable export configurations for common data exports"),
|
|
61
60
|
icon: /* @__PURE__ */ v(f, { size: 24 }),
|
|
62
|
-
headerActions: /* @__PURE__ */ v(
|
|
61
|
+
headerActions: /* @__PURE__ */ v(b, {
|
|
63
62
|
primary: {
|
|
64
|
-
label:
|
|
63
|
+
label: E("export.templates.actions.newTemplate", "New Template"),
|
|
65
64
|
icon: /* @__PURE__ */ v(m, { size: 16 }),
|
|
66
|
-
onSelect:
|
|
65
|
+
onSelect: Y
|
|
67
66
|
},
|
|
68
67
|
actions: [{
|
|
69
|
-
label:
|
|
68
|
+
label: E("export.templates.actions.newExport", "New Export"),
|
|
70
69
|
icon: /* @__PURE__ */ v(m, { size: 16 }),
|
|
71
|
-
onSelect: () =>
|
|
70
|
+
onSelect: () => T?.(C(w, "new"))
|
|
72
71
|
}]
|
|
73
72
|
}),
|
|
74
73
|
headerContent: /* @__PURE__ */ y("span", {
|
|
75
74
|
className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-bg-sunken text-text-secondary",
|
|
76
|
-
children: [/* @__PURE__ */ v(g, { size: 12 }),
|
|
75
|
+
children: [/* @__PURE__ */ v(g, { size: 12 }), E("export.templates.summary.available", "{{count}} template{{suffix}} available", {
|
|
77
76
|
count: Q.length,
|
|
78
77
|
suffix: Q.length === 1 ? "" : "s"
|
|
79
78
|
})]
|
|
80
79
|
}),
|
|
81
80
|
children: [
|
|
82
|
-
/* @__PURE__ */ v(
|
|
81
|
+
/* @__PURE__ */ v(S, {
|
|
83
82
|
icon: /* @__PURE__ */ v(f, { size: 16 }),
|
|
84
|
-
children:
|
|
83
|
+
children: E("export.templates.purpose", "Templates save an export setup — the entity types and format you regularly need — so you don't rebuild it each time. Create one here, then start a new export from it in a single click. Handy for recurring backups, periodic audits, or any export you run more than once.")
|
|
85
84
|
}),
|
|
86
|
-
|
|
85
|
+
F && /* @__PURE__ */ y("div", {
|
|
87
86
|
className: "border border-primary/30 rounded-xl p-6 bg-accent-wash space-y-4",
|
|
88
87
|
children: [
|
|
89
88
|
/* @__PURE__ */ y("div", {
|
|
90
89
|
className: "flex items-center justify-between",
|
|
91
90
|
children: [/* @__PURE__ */ v("h3", {
|
|
92
91
|
className: "font-semibold text-lg",
|
|
93
|
-
children:
|
|
92
|
+
children: E("export.templates.form.title", "New Template")
|
|
94
93
|
}), /* @__PURE__ */ v("button", {
|
|
95
94
|
type: "button",
|
|
96
|
-
onClick: () =>
|
|
95
|
+
onClick: () => I(!1),
|
|
97
96
|
className: "p-1.5 rounded-md hover:bg-accent",
|
|
98
97
|
children: /* @__PURE__ */ v(_, { size: 16 })
|
|
99
98
|
})]
|
|
100
99
|
}),
|
|
101
|
-
|
|
100
|
+
G && /* @__PURE__ */ v("p", {
|
|
102
101
|
className: "text-sm text-status-error-text bg-status-error-bg/10 rounded-lg px-3 py-2",
|
|
103
|
-
children:
|
|
102
|
+
children: G
|
|
104
103
|
}),
|
|
105
104
|
/* @__PURE__ */ y("div", {
|
|
106
105
|
className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
|
|
@@ -108,30 +107,30 @@ function T() {
|
|
|
108
107
|
className: "space-y-1",
|
|
109
108
|
children: [/* @__PURE__ */ y("label", {
|
|
110
109
|
className: "text-sm font-medium",
|
|
111
|
-
children: [
|
|
110
|
+
children: [E("export.templates.form.fields.name", "Name"), " *"]
|
|
112
111
|
}), /* @__PURE__ */ v("input", {
|
|
113
112
|
type: "text",
|
|
114
|
-
"aria-label":
|
|
115
|
-
placeholder:
|
|
116
|
-
value:
|
|
117
|
-
onChange: (e) =>
|
|
113
|
+
"aria-label": E("export.templates.form.fields.name", "Name"),
|
|
114
|
+
placeholder: E("export.templates.form.fields.namePlaceholder", "e.g. Weekly Tags Export"),
|
|
115
|
+
value: L,
|
|
116
|
+
onChange: (e) => R(e.target.value),
|
|
118
117
|
className: "w-full px-3 py-2 border-2 border-border-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all"
|
|
119
118
|
})]
|
|
120
119
|
}), /* @__PURE__ */ y("div", {
|
|
121
120
|
className: "space-y-1",
|
|
122
121
|
children: [/* @__PURE__ */ y("label", {
|
|
123
122
|
className: "text-sm font-medium",
|
|
124
|
-
children: [
|
|
123
|
+
children: [E("export.templates.form.fields.format", "Format"), " *"]
|
|
125
124
|
}), /* @__PURE__ */ y("select", {
|
|
126
|
-
value:
|
|
127
|
-
onChange: (e) =>
|
|
125
|
+
value: U,
|
|
126
|
+
onChange: (e) => W(e.target.value),
|
|
128
127
|
className: "w-full px-3 py-2 border-2 border-border-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all",
|
|
129
128
|
children: [/* @__PURE__ */ v("option", {
|
|
130
129
|
value: "CSV",
|
|
131
|
-
children: "CSV"
|
|
130
|
+
children: E("export.format.csv", "CSV")
|
|
132
131
|
}), /* @__PURE__ */ v("option", {
|
|
133
132
|
value: "JSON",
|
|
134
|
-
children: "JSON"
|
|
133
|
+
children: E("export.format.json", "JSON")
|
|
135
134
|
})]
|
|
136
135
|
})]
|
|
137
136
|
})]
|
|
@@ -140,13 +139,13 @@ function T() {
|
|
|
140
139
|
className: "space-y-1",
|
|
141
140
|
children: [/* @__PURE__ */ v("label", {
|
|
142
141
|
className: "text-sm font-medium",
|
|
143
|
-
children:
|
|
142
|
+
children: E("export.templates.form.fields.description", "Description")
|
|
144
143
|
}), /* @__PURE__ */ v("input", {
|
|
145
144
|
type: "text",
|
|
146
|
-
"aria-label":
|
|
147
|
-
placeholder:
|
|
148
|
-
value:
|
|
149
|
-
onChange: (e) =>
|
|
145
|
+
"aria-label": E("export.templates.form.fields.description", "Description"),
|
|
146
|
+
placeholder: E("export.templates.form.fields.descriptionPlaceholder", "Optional description"),
|
|
147
|
+
value: z,
|
|
148
|
+
onChange: (e) => B(e.target.value),
|
|
150
149
|
className: "w-full px-3 py-2 border-2 border-border-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all"
|
|
151
150
|
})]
|
|
152
151
|
}),
|
|
@@ -155,20 +154,20 @@ function T() {
|
|
|
155
154
|
children: [/* @__PURE__ */ y("label", {
|
|
156
155
|
className: "text-sm font-medium",
|
|
157
156
|
children: [
|
|
158
|
-
|
|
157
|
+
E("export.templates.form.fields.entityTypes", "Entity Types"),
|
|
159
158
|
" *",
|
|
160
159
|
" ",
|
|
161
160
|
/* @__PURE__ */ v("span", {
|
|
162
161
|
className: "text-text-secondary font-normal",
|
|
163
|
-
children:
|
|
162
|
+
children: E("export.templates.form.fields.entityTypesHint", "(select at least one)")
|
|
164
163
|
})
|
|
165
164
|
]
|
|
166
165
|
}), /* @__PURE__ */ v("div", {
|
|
167
166
|
className: "flex flex-wrap gap-2",
|
|
168
|
-
children:
|
|
167
|
+
children: M.map((e) => /* @__PURE__ */ v("button", {
|
|
169
168
|
type: "button",
|
|
170
|
-
onClick: () =>
|
|
171
|
-
className:
|
|
169
|
+
onClick: () => Z(e.entityType),
|
|
170
|
+
className: o("px-3 py-1.5 rounded-lg text-sm font-medium border-2 transition-all", V.includes(e.entityType) ? "border-primary bg-action-primary-bg/10 text-primary" : "border-border-subtle bg-bg-surface hover:border-primary/50"),
|
|
172
171
|
children: e.displayName
|
|
173
172
|
}, e.entityType))
|
|
174
173
|
})]
|
|
@@ -177,18 +176,18 @@ function T() {
|
|
|
177
176
|
className: "flex gap-3 pt-2",
|
|
178
177
|
children: [/* @__PURE__ */ y("button", {
|
|
179
178
|
type: "button",
|
|
180
|
-
onClick:
|
|
181
|
-
disabled:
|
|
179
|
+
onClick: X,
|
|
180
|
+
disabled: j,
|
|
182
181
|
className: "inline-flex items-center gap-2 px-5 py-2.5 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium disabled:opacity-50",
|
|
183
|
-
children: [
|
|
182
|
+
children: [j ? /* @__PURE__ */ v(p, {
|
|
184
183
|
size: 16,
|
|
185
184
|
className: "animate-spin"
|
|
186
|
-
}) : /* @__PURE__ */ v(m, { size: 16 }),
|
|
185
|
+
}) : /* @__PURE__ */ v(m, { size: 16 }), E("export.templates.form.actions.create", "Create Template")]
|
|
187
186
|
}), /* @__PURE__ */ v("button", {
|
|
188
187
|
type: "button",
|
|
189
|
-
onClick: () =>
|
|
188
|
+
onClick: () => I(!1),
|
|
190
189
|
className: "px-5 py-2.5 border-2 border-border-subtle rounded-lg hover:bg-accent transition-colors font-medium",
|
|
191
|
-
children:
|
|
190
|
+
children: E("export.templates.form.actions.cancel", "Cancel")
|
|
192
191
|
})]
|
|
193
192
|
})
|
|
194
193
|
]
|
|
@@ -202,21 +201,21 @@ function T() {
|
|
|
202
201
|
}),
|
|
203
202
|
/* @__PURE__ */ v("input", {
|
|
204
203
|
type: "text",
|
|
205
|
-
"aria-label":
|
|
206
|
-
placeholder:
|
|
207
|
-
value:
|
|
208
|
-
onChange: (e) =>
|
|
204
|
+
"aria-label": E("export.templates.search.placeholder", "Search templates"),
|
|
205
|
+
placeholder: E("export.templates.search.placeholder", "Search templates..."),
|
|
206
|
+
value: N,
|
|
207
|
+
onChange: (e) => P(e.target.value),
|
|
209
208
|
className: "w-full pl-[3.25rem] pr-4 py-2.5 border-2 border-border-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all duration-200"
|
|
210
209
|
}),
|
|
211
|
-
|
|
210
|
+
N && /* @__PURE__ */ v("button", {
|
|
212
211
|
type: "button",
|
|
213
|
-
onClick: () =>
|
|
212
|
+
onClick: () => P(""),
|
|
214
213
|
className: "absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-text-secondary hover:text-text-primary hover:bg-accent",
|
|
215
214
|
children: /* @__PURE__ */ v(_, { size: 16 })
|
|
216
215
|
})
|
|
217
216
|
]
|
|
218
217
|
}),
|
|
219
|
-
|
|
218
|
+
O && /* @__PURE__ */ v("div", {
|
|
220
219
|
className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
|
|
221
220
|
children: [...[
|
|
222
221
|
,
|
|
@@ -235,7 +234,7 @@ function T() {
|
|
|
235
234
|
]
|
|
236
235
|
}, t))
|
|
237
236
|
}),
|
|
238
|
-
|
|
237
|
+
O ? null : Q.length > 0 ? /* @__PURE__ */ v("div", {
|
|
239
238
|
className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
|
|
240
239
|
children: Q.map((e, t) => /* @__PURE__ */ y("div", {
|
|
241
240
|
role: "button",
|
|
@@ -244,7 +243,7 @@ function T() {
|
|
|
244
243
|
onKeyDown: (t) => {
|
|
245
244
|
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), $(e.id));
|
|
246
245
|
},
|
|
247
|
-
className:
|
|
246
|
+
className: o("text-left group cursor-pointer border border-border-seam rounded-xl overflow-hidden bg-bg-surface", "hover:border-primary/40 shadow-[var(--shadow-pop)] transition-colors duration-200", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40", "animate-in fade-in-0"),
|
|
248
247
|
style: { animationDelay: `${t * 50}ms` },
|
|
249
248
|
children: [/* @__PURE__ */ v("div", {
|
|
250
249
|
className: "p-4 border-b bg-bg-sunken/30",
|
|
@@ -263,20 +262,20 @@ function T() {
|
|
|
263
262
|
children: e.name
|
|
264
263
|
}), /* @__PURE__ */ y("p", {
|
|
265
264
|
className: "text-xs text-text-secondary",
|
|
266
|
-
children: [
|
|
265
|
+
children: [s(e.format), e.isDefault && /* @__PURE__ */ v("span", {
|
|
267
266
|
className: "ml-1.5 inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-action-primary-bg/10 text-primary font-medium",
|
|
268
|
-
children:
|
|
267
|
+
children: E("export.templates.card.default", "Default")
|
|
269
268
|
})]
|
|
270
269
|
})] })]
|
|
271
270
|
}), /* @__PURE__ */ y("div", {
|
|
272
271
|
className: "flex items-center gap-2",
|
|
273
272
|
children: [/* @__PURE__ */ v("button", {
|
|
274
273
|
type: "button",
|
|
275
|
-
onClick: (t) =>
|
|
274
|
+
onClick: (t) => ne(t, e.id),
|
|
276
275
|
className: "p-1.5 rounded-md text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 transition-colors",
|
|
277
|
-
"aria-label":
|
|
276
|
+
"aria-label": E("export.templates.card.deleteAriaLabel", "Delete {name}", { name: e.name }),
|
|
278
277
|
children: /* @__PURE__ */ v(_, { size: 14 })
|
|
279
|
-
}), /* @__PURE__ */ v(
|
|
278
|
+
}), /* @__PURE__ */ v(te, {
|
|
280
279
|
size: 18,
|
|
281
280
|
className: "text-text-secondary group-hover:text-primary transition-colors mt-1"
|
|
282
281
|
})]
|
|
@@ -294,28 +293,28 @@ function T() {
|
|
|
294
293
|
children: e
|
|
295
294
|
}, e)), e.entityTypes.length > 6 && /* @__PURE__ */ v("span", {
|
|
296
295
|
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-bg-sunken text-text-secondary",
|
|
297
|
-
children:
|
|
296
|
+
children: E("export.templates.card.moreEntities", "+{count} more", { count: e.entityTypes.length - 6 })
|
|
298
297
|
})]
|
|
299
298
|
})]
|
|
300
299
|
})]
|
|
301
300
|
}, e.id))
|
|
302
|
-
}) : /* @__PURE__ */ v(
|
|
303
|
-
illustration:
|
|
304
|
-
title:
|
|
305
|
-
description:
|
|
306
|
-
action:
|
|
301
|
+
}) : /* @__PURE__ */ v(x, {
|
|
302
|
+
illustration: N ? "empty-search" : "empty-data",
|
|
303
|
+
title: N ? E("export.templates.empty.filtered.title", "No matching templates") : E("export.templates.empty.title", "No templates yet"),
|
|
304
|
+
description: N ? E("export.templates.empty.filtered.description", "Try adjusting your search to find what you are looking for.") : E("export.templates.empty.description", "Create a template to save reusable export configurations."),
|
|
305
|
+
action: N ? /* @__PURE__ */ y("button", {
|
|
307
306
|
type: "button",
|
|
308
|
-
onClick: () =>
|
|
307
|
+
onClick: () => P(""),
|
|
309
308
|
className: "inline-flex items-center gap-2 px-4 py-2 border-2 border-border-subtle rounded-lg hover:bg-accent transition-colors font-medium",
|
|
310
|
-
children: [/* @__PURE__ */ v(_, { size: 16 }),
|
|
309
|
+
children: [/* @__PURE__ */ v(_, { size: 16 }), E("export.templates.empty.filtered.actions.clear", "Clear Search")]
|
|
311
310
|
}) : /* @__PURE__ */ y("button", {
|
|
312
311
|
type: "button",
|
|
313
|
-
onClick:
|
|
312
|
+
onClick: Y,
|
|
314
313
|
className: "inline-flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium",
|
|
315
|
-
children: [/* @__PURE__ */ v(m, { size: 16 }),
|
|
314
|
+
children: [/* @__PURE__ */ v(m, { size: 16 }), E("export.templates.empty.actions.newTemplate", "New Template")]
|
|
316
315
|
})
|
|
317
316
|
}),
|
|
318
|
-
|
|
317
|
+
q && /* @__PURE__ */ v("div", {
|
|
319
318
|
role: "dialog",
|
|
320
319
|
"aria-modal": "true",
|
|
321
320
|
"aria-labelledby": "template-list-delete-title",
|
|
@@ -326,24 +325,24 @@ function T() {
|
|
|
326
325
|
/* @__PURE__ */ v("h2", {
|
|
327
326
|
id: "template-list-delete-title",
|
|
328
327
|
className: "text-lg font-bold",
|
|
329
|
-
children:
|
|
328
|
+
children: E("export.templates.delete.title", "Delete template?")
|
|
330
329
|
}),
|
|
331
330
|
/* @__PURE__ */ v("p", {
|
|
332
331
|
className: "mt-2 text-sm text-text-secondary",
|
|
333
|
-
children:
|
|
332
|
+
children: E("export.templates.delete.description", "This removes the saved export configuration. Existing export jobs are not deleted.")
|
|
334
333
|
}),
|
|
335
334
|
/* @__PURE__ */ y("div", {
|
|
336
335
|
className: "mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3",
|
|
337
336
|
children: [/* @__PURE__ */ v("button", {
|
|
338
337
|
type: "button",
|
|
339
|
-
onClick: () =>
|
|
338
|
+
onClick: () => J(null),
|
|
340
339
|
className: "px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium",
|
|
341
|
-
children:
|
|
340
|
+
children: E("export.templates.delete.cancel", "Keep Template")
|
|
342
341
|
}), /* @__PURE__ */ v("button", {
|
|
343
342
|
type: "button",
|
|
344
|
-
onClick:
|
|
343
|
+
onClick: re,
|
|
345
344
|
className: "px-4 py-2 rounded-lg bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90 transition-colors font-medium",
|
|
346
|
-
children:
|
|
345
|
+
children: E("export.templates.delete.confirmAction", "Delete Template")
|
|
347
346
|
})]
|
|
348
347
|
})
|
|
349
348
|
]
|
|
@@ -353,6 +352,6 @@ function T() {
|
|
|
353
352
|
});
|
|
354
353
|
}
|
|
355
354
|
//#endregion
|
|
356
|
-
export {
|
|
355
|
+
export { w as ExportTemplatesPage };
|
|
357
356
|
|
|
358
357
|
//# sourceMappingURL=ExportTemplatesPage.js.map
|
|
@@ -1 +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 {\n CTAOverflowMenu,\n IllustratedEmptyState,\n PagePurpose,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplates,\n useDeleteExportTemplate,\n useCreateExportTemplate,\n useAvailableEntities,\n} from \"../hooks/useExportQueries\";\nimport { PageLayout } 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 <CTAOverflowMenu\n primary={{\n label: tr(\"export.templates.actions.newTemplate\", \"New Template\"),\n icon: <Plus size={16} />,\n onSelect: handleOpenCreate,\n }}\n actions={[\n {\n label: tr(\"export.templates.actions.newExport\", \"New Export\"),\n icon: <Plus size={16} />,\n onSelect: () => navigate?.(joinPath(basePath, \"new\")),\n },\n ]}\n />\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-bg-sunken text-text-secondary\">\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 {/* Page purpose — why templates exist */}\n <PagePurpose icon={<FileBox size={16} />}>\n {tr(\n \"export.templates.purpose\",\n \"Templates save an export setup — the entity types and format you regularly need — so you don't rebuild it each time. Create one here, then start a new export from it in a single click. Handy for recurring backups, periodic audits, or any export you run more than once.\",\n )}\n </PagePurpose>\n\n {/* Create Template Form */}\n {showCreateForm && (\n <div className=\"border border-primary/30 rounded-xl p-6 bg-accent-wash 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 type=\"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-status-error-text bg-status-error-bg/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 aria-label={tr(\"export.templates.form.fields.name\", \"Name\")}\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-subtle rounded-lg bg-bg-surface 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-subtle rounded-lg bg-bg-surface 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 aria-label={tr(\n \"export.templates.form.fields.description\",\n \"Description\",\n )}\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-subtle rounded-lg bg-bg-surface 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-text-secondary 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-action-primary-bg/10 text-primary\"\n : \"border-border-subtle bg-bg-surface 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 type=\"button\"\n onClick={handleCreateTemplate}\n disabled={creating}\n className=\"inline-flex items-center gap-2 px-5 py-2.5 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/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 type=\"button\"\n onClick={() => setShowCreateForm(false)}\n className=\"px-5 py-2.5 border-2 border-border-subtle 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-text-secondary group-focus-within:text-primary transition-colors\"\n />\n <input\n type=\"text\"\n aria-label={tr(\n \"export.templates.search.placeholder\",\n \"Search templates\",\n )}\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-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all duration-200\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-text-secondary hover:text-text-primary 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 key={i} className=\"border border-border-seam rounded-xl p-5\">\n <div className=\"h-5 w-40 bg-bg-sunken rounded mb-3 animate-vc-shimmer\" />\n <div className=\"h-3 w-full bg-bg-sunken rounded mb-2 animate-vc-shimmer\" />\n <div className=\"flex gap-2 mt-4\">\n <div className=\"h-5 w-16 bg-bg-sunken rounded-full animate-vc-shimmer\" />\n <div className=\"h-5 w-16 bg-bg-sunken rounded-full animate-vc-shimmer\" />\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 border-border-seam rounded-xl overflow-hidden bg-bg-surface\",\n \"hover:border-primary/40 shadow-[var(--shadow-pop)] transition-colors duration-200\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40\",\n \"animate-in fade-in-0\",\n )}\n style={{ animationDelay: `${index * 50}ms` }}\n >\n {/* Header */}\n <div className=\"p-4 border-b bg-bg-sunken/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-action-primary-bg/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-text-secondary\">\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-action-primary-bg/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 type=\"button\"\n onClick={(e) => handleDeleteTemplate(e, template.id)}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/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-text-secondary 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-text-secondary 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-bg-sunken text-text-secondary 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-bg-sunken text-text-secondary\">\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 <IllustratedEmptyState\n illustration={searchQuery ? \"empty-search\" : \"empty-data\"}\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 type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"inline-flex items-center gap-2 px-4 py-2 border-2 border-border-subtle 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 type=\"button\"\n onClick={handleOpenCreate}\n className=\"inline-flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/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-bg-surface/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface 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-text-secondary\">\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-subtle 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-status-error-bg text-status-error-text hover:bg-status-error-bg/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":"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"}
|
|
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 {\n CTAOverflowMenu,\n IllustratedEmptyState,\n PagePurpose,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplates,\n useDeleteExportTemplate,\n useCreateExportTemplate,\n useAvailableEntities,\n} from \"../hooks/useExportQueries\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { getFormatDisplayName } from \"../constants/enums\";\nimport { useTr } from \"../hooks/useTr\";\nimport { cn } from \"../utils/cn\";\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 tr = useTr();\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 <CTAOverflowMenu\n primary={{\n label: tr(\"export.templates.actions.newTemplate\", \"New Template\"),\n icon: <Plus size={16} />,\n onSelect: handleOpenCreate,\n }}\n actions={[\n {\n label: tr(\"export.templates.actions.newExport\", \"New Export\"),\n icon: <Plus size={16} />,\n onSelect: () => navigate?.(joinPath(basePath, \"new\")),\n },\n ]}\n />\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-bg-sunken text-text-secondary\">\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 {/* Page purpose — why templates exist */}\n <PagePurpose icon={<FileBox size={16} />}>\n {tr(\n \"export.templates.purpose\",\n \"Templates save an export setup — the entity types and format you regularly need — so you don't rebuild it each time. Create one here, then start a new export from it in a single click. Handy for recurring backups, periodic audits, or any export you run more than once.\",\n )}\n </PagePurpose>\n\n {/* Create Template Form */}\n {showCreateForm && (\n <div className=\"border border-primary/30 rounded-xl p-6 bg-accent-wash 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 type=\"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-status-error-text bg-status-error-bg/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 aria-label={tr(\"export.templates.form.fields.name\", \"Name\")}\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-subtle rounded-lg bg-bg-surface 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-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n >\n <option value=\"CSV\">{tr(\"export.format.csv\", \"CSV\")}</option>\n <option value=\"JSON\">{tr(\"export.format.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 aria-label={tr(\n \"export.templates.form.fields.description\",\n \"Description\",\n )}\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-subtle rounded-lg bg-bg-surface 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-text-secondary 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-action-primary-bg/10 text-primary\"\n : \"border-border-subtle bg-bg-surface 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 type=\"button\"\n onClick={handleCreateTemplate}\n disabled={creating}\n className=\"inline-flex items-center gap-2 px-5 py-2.5 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/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 type=\"button\"\n onClick={() => setShowCreateForm(false)}\n className=\"px-5 py-2.5 border-2 border-border-subtle 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-text-secondary group-focus-within:text-primary transition-colors\"\n />\n <input\n type=\"text\"\n aria-label={tr(\n \"export.templates.search.placeholder\",\n \"Search templates\",\n )}\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-subtle rounded-lg bg-bg-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all duration-200\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-text-secondary hover:text-text-primary 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 key={i} className=\"border border-border-seam rounded-xl p-5\">\n <div className=\"h-5 w-40 bg-bg-sunken rounded mb-3 animate-vc-shimmer\" />\n <div className=\"h-3 w-full bg-bg-sunken rounded mb-2 animate-vc-shimmer\" />\n <div className=\"flex gap-2 mt-4\">\n <div className=\"h-5 w-16 bg-bg-sunken rounded-full animate-vc-shimmer\" />\n <div className=\"h-5 w-16 bg-bg-sunken rounded-full animate-vc-shimmer\" />\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 border-border-seam rounded-xl overflow-hidden bg-bg-surface\",\n \"hover:border-primary/40 shadow-[var(--shadow-pop)] transition-colors duration-200\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40\",\n \"animate-in fade-in-0\",\n )}\n style={{ animationDelay: `${index * 50}ms` }}\n >\n {/* Header */}\n <div className=\"p-4 border-b bg-bg-sunken/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-action-primary-bg/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-text-secondary\">\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-action-primary-bg/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 type=\"button\"\n onClick={(e) => handleDeleteTemplate(e, template.id)}\n className=\"p-1.5 rounded-md text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/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-text-secondary 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-text-secondary 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-bg-sunken text-text-secondary 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-bg-sunken text-text-secondary\">\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 <IllustratedEmptyState\n illustration={searchQuery ? \"empty-search\" : \"empty-data\"}\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 type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"inline-flex items-center gap-2 px-4 py-2 border-2 border-border-subtle 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 type=\"button\"\n onClick={handleOpenCreate}\n className=\"inline-flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/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-bg-surface/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface 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-text-secondary\">\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-subtle 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-status-error-bg text-status-error-text hover:bg-status-error-bg/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":";;;;;;;;;;;;AAgCA,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,IAAK,GAAO,EACZ,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,GAAgB,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,GAAa,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,GAAD;GACE,SAAS;IACP,OAAO,EAAG,wCAAwC,eAAe;IACjE,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IACxB,UAAU;IACX;GACD,SAAS,CACP;IACE,OAAO,EAAG,sCAAsC,aAAa;IAC7D,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IACxB,gBAAgB,IAAW,EAAS,GAAU,MAAM,CAAC;IACtD,CACF;GACD,CAAA;EAEJ,eACE,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EACrB,EACC,sCACA,0CACA;IACE,OAAO,EAAkB;IACzB,QAAQ,EAAkB,WAAW,IAAU,KAAN;IAC1C,CACF,CACI;;YAlCX;GAsCE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;cACrC,EACC,4BACA,+QACD;IACW,CAAA;GAGb,KACC,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,MAAK;OACL,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,cAAY,EAAG,qCAAqC,OAAO;QAC3D,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;mBAAO,EAAG,qBAAqB,MAAM;SAAU,CAAA,EAC7D,kBAAC,UAAD;SAAQ,OAAM;mBAAQ,EAAG,sBAAsB,OAAO;SAAU,CAAA,CACzD;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,cAAY,EACV,4CACA,cACD;OACD,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,wDACA,6DACL;kBAEA,EAAO;QACD,EAXF,EAAO,WAWL,CACT;OACE,CAAA,CACF;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;iBAJZ,CAMG,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,MAAK;OACL,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,cAAY,EACV,uCACA,mBACD;MACD,aAAa,EACX,uCACA,sBACD;MACD,OAAO;MACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;MAC/C,WAAU;MACV,CAAA;KACD,KACC,kBAAC,UAAD;MACE,MAAK;MACL,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;KAAa,WAAU;eAAvB;MACE,kBAAC,OAAD,EAAK,WAAU,yDAA0D,CAAA;MACzE,kBAAC,OAAD,EAAK,WAAU,2DAA4D,CAAA;MAC3E,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAK,WAAU,yDAA0D,CAAA,EACzE,kBAAC,OAAD,EAAK,WAAU,yDAA0D,CAAA,CACrE;;MACF;OAPI,EAOJ,CACN;IACE,CAAA;GAIN,IAkJE,OAjJF,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,qGACA,qFACA,iFACA,uBACD;KACD,OAAO,EAAE,gBAAgB,GAAG,IAAQ,GAAG,KAAK;eAjB9C,CAoBE,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,MAAK;SACL,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;OAvFC,EAAS,GAuFV,CACN;IACE,CAAA,GAEN,kBAAC,GAAD;IACE,cAAc,IAAc,iBAAiB;IAC7C,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,MAAK;KACL,eAAe,EAAe,GAAG;KACjC,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA,EACd,EACC,iDACA,eACD,CACM;SAET,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplateDetailPage.d.ts","sourceRoot":"","sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TemplateDetailPage.d.ts","sourceRoot":"","sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"names":[],"mappings":"AA4CA;;;;GAIG;AACH,wBAAgB,kBAAkB,gCAgTjC"}
|