@burdenoff/microfe-export 2026.624.1 → 2026.625.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/components/ExportStatusBadge.js +1 -1
- package/dist/export/components/ExportStatusBadge.js.map +1 -1
- package/dist/export/components/PageLayout.js +7 -18
- package/dist/export/components/PageLayout.js.map +1 -1
- package/dist/export/pages/CreateExportPage.js +256 -249
- package/dist/export/pages/CreateExportPage.js.map +1 -1
- package/dist/export/pages/ExportDashboardPage.js +119 -53
- package/dist/export/pages/ExportDashboardPage.js.map +1 -1
- package/dist/export/pages/ExportDetailPage.js +214 -198
- package/dist/export/pages/ExportDetailPage.js.map +1 -1
- package/dist/export/pages/ExportHistoryPage.js +28 -27
- package/dist/export/pages/ExportHistoryPage.js.map +1 -1
- package/dist/export/pages/ExportTemplatesPage.js +122 -123
- package/dist/export/pages/ExportTemplatesPage.js.map +1 -1
- package/dist/export/pages/TemplateDetailPage.js +127 -116
- package/dist/export/pages/TemplateDetailPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,103 +1,102 @@
|
|
|
1
1
|
import { useExport as e } from "../providers/ExportProvider.js";
|
|
2
|
-
import { useAvailableEntities as t, useCreateExportTemplate as n, useDeleteExportTemplate as r, useExportTemplates as
|
|
3
|
-
import { cn as
|
|
4
|
-
import { getFormatDisplayName as
|
|
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 ee } from "../constants/enums.js";
|
|
5
5
|
import { tWithFallback as o } from "../utils/i18n.js";
|
|
6
|
-
import {
|
|
7
|
-
import { nativeImpact as
|
|
8
|
-
import { useMemo as
|
|
9
|
-
import { ChevronRight as
|
|
6
|
+
import { PageLayout as s } from "../components/PageLayout.js";
|
|
7
|
+
import { nativeImpact as c, nativeNotify as l } from "../../utils/nativeBridge.js";
|
|
8
|
+
import { useMemo as u, useState as d } from "react";
|
|
9
|
+
import { ChevronRight as f, FileBox as p, Loader2 as m, Plus as h, Search as g, Sparkles as _, X as v } from "lucide-react";
|
|
10
10
|
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
11
|
-
import { useI18n as
|
|
11
|
+
import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
12
|
+
import { CTAOverflowMenu as x, IllustratedEmptyState as S } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/export/pages/ExportTemplatesPage.tsx
|
|
13
|
-
var
|
|
14
|
-
function
|
|
15
|
-
let { basePath:
|
|
16
|
-
|
|
17
|
-
},
|
|
18
|
-
if (!
|
|
19
|
-
|
|
14
|
+
var C = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
15
|
+
function w() {
|
|
16
|
+
let { basePath: w = "/", navigate: T } = e(), { t: E } = te(), D = (e, t, n) => o(E, e, t, n), { data: O, isLoading: k } = i(), { deleteTemplate: A } = r(), { createTemplate: j, isPending: M } = n(), { data: N } = t(), [P, F] = d(""), [I, L] = d(!1), [R, z] = d(""), [B, V] = d(""), [H, U] = d([]), [W, G] = d("CSV"), [K, q] = d(null), [J, Y] = d(null), X = () => {
|
|
17
|
+
z(""), V(""), U([]), G("CSV"), q(null), L(!0);
|
|
18
|
+
}, Z = async () => {
|
|
19
|
+
if (!R.trim()) {
|
|
20
|
+
q(D("export.templates.form.errors.nameRequired", "Template name is required."));
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
|
-
if (
|
|
23
|
-
|
|
23
|
+
if (H.length === 0) {
|
|
24
|
+
q(D("export.templates.form.errors.entityRequired", "Select at least one entity type."));
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
q(null);
|
|
27
28
|
try {
|
|
28
|
-
await
|
|
29
|
-
name:
|
|
30
|
-
description:
|
|
31
|
-
entityTypes:
|
|
32
|
-
format:
|
|
33
|
-
}),
|
|
29
|
+
await j({
|
|
30
|
+
name: R.trim(),
|
|
31
|
+
description: B.trim() || void 0,
|
|
32
|
+
entityTypes: H,
|
|
33
|
+
format: W
|
|
34
|
+
}), L(!1);
|
|
34
35
|
} catch (e) {
|
|
35
|
-
|
|
36
|
+
q(e instanceof Error ? e.message : D("export.templates.form.errors.createFailed", "Failed to create template."));
|
|
36
37
|
}
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
}, Q =
|
|
40
|
-
if (!
|
|
41
|
-
let e =
|
|
42
|
-
return
|
|
43
|
-
}, [
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
e.stopPropagation(),
|
|
47
|
-
},
|
|
48
|
-
if (
|
|
38
|
+
}, ne = (e) => {
|
|
39
|
+
U((t) => t.includes(e) ? t.filter((t) => t !== e) : [...t, e]);
|
|
40
|
+
}, Q = u(() => {
|
|
41
|
+
if (!P) return O;
|
|
42
|
+
let e = P.toLowerCase();
|
|
43
|
+
return O.filter((t) => t.name.toLowerCase().includes(e) || t.entityTypes.some((t) => t.toLowerCase().includes(e)));
|
|
44
|
+
}, [O, P]), $ = (e) => {
|
|
45
|
+
T?.(C(w, `templates/${e}`));
|
|
46
|
+
}, re = async (e, t) => {
|
|
47
|
+
e.stopPropagation(), c("medium"), Y(t);
|
|
48
|
+
}, ie = async () => {
|
|
49
|
+
if (J) {
|
|
49
50
|
try {
|
|
50
|
-
await
|
|
51
|
+
await A(J), l("success");
|
|
51
52
|
} catch {
|
|
52
|
-
|
|
53
|
+
l("error");
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
+
Y(null);
|
|
55
56
|
}
|
|
56
57
|
};
|
|
57
|
-
return /* @__PURE__ */ b(
|
|
58
|
-
title:
|
|
59
|
-
subtitle:
|
|
58
|
+
return /* @__PURE__ */ b(s, {
|
|
59
|
+
title: D("export.templates.title", "Export Templates"),
|
|
60
|
+
subtitle: D("export.templates.subtitle", "Reusable export configurations for common data exports"),
|
|
60
61
|
icon: /* @__PURE__ */ y(p, { size: 24 }),
|
|
61
|
-
headerActions: /* @__PURE__ */
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
children: [/* @__PURE__ */ y(h, { size: 16 }), E("export.templates.actions.newExport", "New Export")]
|
|
73
|
-
})]
|
|
62
|
+
headerActions: /* @__PURE__ */ y(x, {
|
|
63
|
+
primary: {
|
|
64
|
+
label: D("export.templates.actions.newTemplate", "New Template"),
|
|
65
|
+
icon: /* @__PURE__ */ y(h, { size: 16 }),
|
|
66
|
+
onSelect: X
|
|
67
|
+
},
|
|
68
|
+
actions: [{
|
|
69
|
+
label: D("export.templates.actions.newExport", "New Export"),
|
|
70
|
+
icon: /* @__PURE__ */ y(h, { size: 16 }),
|
|
71
|
+
onSelect: () => T?.(C(w, "new"))
|
|
72
|
+
}]
|
|
74
73
|
}),
|
|
75
74
|
headerContent: /* @__PURE__ */ b("span", {
|
|
76
75
|
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",
|
|
77
|
-
children: [/* @__PURE__ */ y(_, { size: 12 }),
|
|
76
|
+
children: [/* @__PURE__ */ y(_, { size: 12 }), D("export.templates.summary.available", "{count} template{suffix} available", {
|
|
78
77
|
count: Q.length,
|
|
79
78
|
suffix: Q.length === 1 ? "" : "s"
|
|
80
79
|
})]
|
|
81
80
|
}),
|
|
82
81
|
children: [
|
|
83
|
-
|
|
84
|
-
className: "border
|
|
82
|
+
I && /* @__PURE__ */ b("div", {
|
|
83
|
+
className: "border border-primary/30 rounded-xl p-6 bg-accent-wash space-y-4",
|
|
85
84
|
children: [
|
|
86
85
|
/* @__PURE__ */ b("div", {
|
|
87
86
|
className: "flex items-center justify-between",
|
|
88
87
|
children: [/* @__PURE__ */ y("h3", {
|
|
89
88
|
className: "font-semibold text-lg",
|
|
90
|
-
children:
|
|
89
|
+
children: D("export.templates.form.title", "New Template")
|
|
91
90
|
}), /* @__PURE__ */ y("button", {
|
|
92
91
|
type: "button",
|
|
93
|
-
onClick: () =>
|
|
92
|
+
onClick: () => L(!1),
|
|
94
93
|
className: "p-1.5 rounded-md hover:bg-accent",
|
|
95
94
|
children: /* @__PURE__ */ y(v, { size: 16 })
|
|
96
95
|
})]
|
|
97
96
|
}),
|
|
98
|
-
|
|
97
|
+
K && /* @__PURE__ */ y("p", {
|
|
99
98
|
className: "text-sm text-status-error-text bg-status-error-bg/10 rounded-lg px-3 py-2",
|
|
100
|
-
children:
|
|
99
|
+
children: K
|
|
101
100
|
}),
|
|
102
101
|
/* @__PURE__ */ b("div", {
|
|
103
102
|
className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
|
|
@@ -105,23 +104,23 @@ function C() {
|
|
|
105
104
|
className: "space-y-1",
|
|
106
105
|
children: [/* @__PURE__ */ b("label", {
|
|
107
106
|
className: "text-sm font-medium",
|
|
108
|
-
children: [
|
|
107
|
+
children: [D("export.templates.form.fields.name", "Name"), " *"]
|
|
109
108
|
}), /* @__PURE__ */ y("input", {
|
|
110
109
|
type: "text",
|
|
111
|
-
"aria-label":
|
|
112
|
-
placeholder:
|
|
113
|
-
value:
|
|
114
|
-
onChange: (e) =>
|
|
110
|
+
"aria-label": D("export.templates.form.fields.name", "Name"),
|
|
111
|
+
placeholder: D("export.templates.form.fields.namePlaceholder", "e.g. Weekly Tags Export"),
|
|
112
|
+
value: R,
|
|
113
|
+
onChange: (e) => z(e.target.value),
|
|
115
114
|
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"
|
|
116
115
|
})]
|
|
117
116
|
}), /* @__PURE__ */ b("div", {
|
|
118
117
|
className: "space-y-1",
|
|
119
118
|
children: [/* @__PURE__ */ b("label", {
|
|
120
119
|
className: "text-sm font-medium",
|
|
121
|
-
children: [
|
|
120
|
+
children: [D("export.templates.form.fields.format", "Format"), " *"]
|
|
122
121
|
}), /* @__PURE__ */ b("select", {
|
|
123
|
-
value:
|
|
124
|
-
onChange: (e) =>
|
|
122
|
+
value: W,
|
|
123
|
+
onChange: (e) => G(e.target.value),
|
|
125
124
|
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",
|
|
126
125
|
children: [/* @__PURE__ */ y("option", {
|
|
127
126
|
value: "CSV",
|
|
@@ -137,13 +136,13 @@ function C() {
|
|
|
137
136
|
className: "space-y-1",
|
|
138
137
|
children: [/* @__PURE__ */ y("label", {
|
|
139
138
|
className: "text-sm font-medium",
|
|
140
|
-
children:
|
|
139
|
+
children: D("export.templates.form.fields.description", "Description")
|
|
141
140
|
}), /* @__PURE__ */ y("input", {
|
|
142
141
|
type: "text",
|
|
143
|
-
"aria-label":
|
|
144
|
-
placeholder:
|
|
145
|
-
value:
|
|
146
|
-
onChange: (e) =>
|
|
142
|
+
"aria-label": D("export.templates.form.fields.description", "Description"),
|
|
143
|
+
placeholder: D("export.templates.form.fields.descriptionPlaceholder", "Optional description"),
|
|
144
|
+
value: B,
|
|
145
|
+
onChange: (e) => V(e.target.value),
|
|
147
146
|
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"
|
|
148
147
|
})]
|
|
149
148
|
}),
|
|
@@ -152,20 +151,20 @@ function C() {
|
|
|
152
151
|
children: [/* @__PURE__ */ b("label", {
|
|
153
152
|
className: "text-sm font-medium",
|
|
154
153
|
children: [
|
|
155
|
-
|
|
154
|
+
D("export.templates.form.fields.entityTypes", "Entity Types"),
|
|
156
155
|
" *",
|
|
157
156
|
" ",
|
|
158
157
|
/* @__PURE__ */ y("span", {
|
|
159
158
|
className: "text-text-secondary font-normal",
|
|
160
|
-
children:
|
|
159
|
+
children: D("export.templates.form.fields.entityTypesHint", "(select at least one)")
|
|
161
160
|
})
|
|
162
161
|
]
|
|
163
162
|
}), /* @__PURE__ */ y("div", {
|
|
164
163
|
className: "flex flex-wrap gap-2",
|
|
165
|
-
children:
|
|
164
|
+
children: N.map((e) => /* @__PURE__ */ y("button", {
|
|
166
165
|
type: "button",
|
|
167
|
-
onClick: () =>
|
|
168
|
-
className:
|
|
166
|
+
onClick: () => ne(e.entityType),
|
|
167
|
+
className: a("px-3 py-1.5 rounded-lg text-sm font-medium border-2 transition-all", H.includes(e.entityType) ? "border-primary bg-action-primary-bg/10 text-primary" : "border-border-subtle bg-bg-surface hover:border-primary/50"),
|
|
169
168
|
children: e.displayName
|
|
170
169
|
}, e.entityType))
|
|
171
170
|
})]
|
|
@@ -174,18 +173,18 @@ function C() {
|
|
|
174
173
|
className: "flex gap-3 pt-2",
|
|
175
174
|
children: [/* @__PURE__ */ b("button", {
|
|
176
175
|
type: "button",
|
|
177
|
-
onClick:
|
|
178
|
-
disabled:
|
|
176
|
+
onClick: Z,
|
|
177
|
+
disabled: M,
|
|
179
178
|
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",
|
|
180
|
-
children: [
|
|
179
|
+
children: [M ? /* @__PURE__ */ y(m, {
|
|
181
180
|
size: 16,
|
|
182
181
|
className: "animate-spin"
|
|
183
|
-
}) : /* @__PURE__ */ y(h, { size: 16 }),
|
|
182
|
+
}) : /* @__PURE__ */ y(h, { size: 16 }), D("export.templates.form.actions.create", "Create Template")]
|
|
184
183
|
}), /* @__PURE__ */ y("button", {
|
|
185
184
|
type: "button",
|
|
186
|
-
onClick: () =>
|
|
185
|
+
onClick: () => L(!1),
|
|
187
186
|
className: "px-5 py-2.5 border-2 border-border-subtle rounded-lg hover:bg-accent transition-colors font-medium",
|
|
188
|
-
children:
|
|
187
|
+
children: D("export.templates.form.actions.cancel", "Cancel")
|
|
189
188
|
})]
|
|
190
189
|
})
|
|
191
190
|
]
|
|
@@ -199,21 +198,21 @@ function C() {
|
|
|
199
198
|
}),
|
|
200
199
|
/* @__PURE__ */ y("input", {
|
|
201
200
|
type: "text",
|
|
202
|
-
"aria-label":
|
|
203
|
-
placeholder:
|
|
204
|
-
value:
|
|
205
|
-
onChange: (e) =>
|
|
201
|
+
"aria-label": D("export.templates.search.placeholder", "Search templates"),
|
|
202
|
+
placeholder: D("export.templates.search.placeholder", "Search templates..."),
|
|
203
|
+
value: P,
|
|
204
|
+
onChange: (e) => F(e.target.value),
|
|
206
205
|
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"
|
|
207
206
|
}),
|
|
208
|
-
|
|
207
|
+
P && /* @__PURE__ */ y("button", {
|
|
209
208
|
type: "button",
|
|
210
|
-
onClick: () =>
|
|
209
|
+
onClick: () => F(""),
|
|
211
210
|
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",
|
|
212
211
|
children: /* @__PURE__ */ y(v, { size: 16 })
|
|
213
212
|
})
|
|
214
213
|
]
|
|
215
214
|
}),
|
|
216
|
-
|
|
215
|
+
k && /* @__PURE__ */ y("div", {
|
|
217
216
|
className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
|
|
218
217
|
children: [...[
|
|
219
218
|
,
|
|
@@ -221,18 +220,18 @@ function C() {
|
|
|
221
220
|
,
|
|
222
221
|
,
|
|
223
222
|
]].map((e, t) => /* @__PURE__ */ b("div", {
|
|
224
|
-
className: "border
|
|
223
|
+
className: "border border-border-seam rounded-xl p-5",
|
|
225
224
|
children: [
|
|
226
|
-
/* @__PURE__ */ y("div", { className: "h-5 w-40 bg-bg-sunken rounded mb-3" }),
|
|
227
|
-
/* @__PURE__ */ y("div", { className: "h-3 w-full bg-bg-sunken rounded mb-2" }),
|
|
225
|
+
/* @__PURE__ */ y("div", { className: "h-5 w-40 bg-bg-sunken rounded mb-3 animate-vc-shimmer" }),
|
|
226
|
+
/* @__PURE__ */ y("div", { className: "h-3 w-full bg-bg-sunken rounded mb-2 animate-vc-shimmer" }),
|
|
228
227
|
/* @__PURE__ */ b("div", {
|
|
229
228
|
className: "flex gap-2 mt-4",
|
|
230
|
-
children: [/* @__PURE__ */ y("div", { className: "h-5 w-16 bg-bg-sunken rounded-full" }), /* @__PURE__ */ y("div", { className: "h-5 w-16 bg-bg-sunken rounded-full" })]
|
|
229
|
+
children: [/* @__PURE__ */ y("div", { className: "h-5 w-16 bg-bg-sunken rounded-full animate-vc-shimmer" }), /* @__PURE__ */ y("div", { className: "h-5 w-16 bg-bg-sunken rounded-full animate-vc-shimmer" })]
|
|
231
230
|
})
|
|
232
231
|
]
|
|
233
232
|
}, t))
|
|
234
233
|
}),
|
|
235
|
-
|
|
234
|
+
k ? null : Q.length > 0 ? /* @__PURE__ */ y("div", {
|
|
236
235
|
className: "grid grid-cols-1 lg:grid-cols-2 gap-4",
|
|
237
236
|
children: Q.map((e, t) => /* @__PURE__ */ b("div", {
|
|
238
237
|
role: "button",
|
|
@@ -241,7 +240,7 @@ function C() {
|
|
|
241
240
|
onKeyDown: (t) => {
|
|
242
241
|
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), $(e.id));
|
|
243
242
|
},
|
|
244
|
-
className:
|
|
243
|
+
className: a("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"),
|
|
245
244
|
style: { animationDelay: `${t * 50}ms` },
|
|
246
245
|
children: [/* @__PURE__ */ y("div", {
|
|
247
246
|
className: "p-4 border-b bg-bg-sunken/30",
|
|
@@ -260,20 +259,20 @@ function C() {
|
|
|
260
259
|
children: e.name
|
|
261
260
|
}), /* @__PURE__ */ b("p", {
|
|
262
261
|
className: "text-xs text-text-secondary",
|
|
263
|
-
children: [
|
|
262
|
+
children: [ee(e.format), e.isDefault && /* @__PURE__ */ y("span", {
|
|
264
263
|
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",
|
|
265
|
-
children:
|
|
264
|
+
children: D("export.templates.card.default", "Default")
|
|
266
265
|
})]
|
|
267
266
|
})] })]
|
|
268
267
|
}), /* @__PURE__ */ b("div", {
|
|
269
268
|
className: "flex items-center gap-2",
|
|
270
269
|
children: [/* @__PURE__ */ y("button", {
|
|
271
270
|
type: "button",
|
|
272
|
-
onClick: (t) =>
|
|
271
|
+
onClick: (t) => re(t, e.id),
|
|
273
272
|
className: "p-1.5 rounded-md text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 transition-colors",
|
|
274
|
-
"aria-label":
|
|
273
|
+
"aria-label": D("export.templates.card.deleteAriaLabel", "Delete {name}", { name: e.name }),
|
|
275
274
|
children: /* @__PURE__ */ y(v, { size: 14 })
|
|
276
|
-
}), /* @__PURE__ */ y(
|
|
275
|
+
}), /* @__PURE__ */ y(f, {
|
|
277
276
|
size: 18,
|
|
278
277
|
className: "text-text-secondary group-hover:text-primary transition-colors mt-1"
|
|
279
278
|
})]
|
|
@@ -291,28 +290,28 @@ function C() {
|
|
|
291
290
|
children: e
|
|
292
291
|
}, e)), e.entityTypes.length > 6 && /* @__PURE__ */ y("span", {
|
|
293
292
|
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-bg-sunken text-text-secondary",
|
|
294
|
-
children:
|
|
293
|
+
children: D("export.templates.card.moreEntities", "+{count} more", { count: e.entityTypes.length - 6 })
|
|
295
294
|
})]
|
|
296
295
|
})]
|
|
297
296
|
})]
|
|
298
297
|
}, e.id))
|
|
299
|
-
}) : /* @__PURE__ */ y(
|
|
300
|
-
|
|
301
|
-
title:
|
|
302
|
-
description:
|
|
303
|
-
action:
|
|
298
|
+
}) : /* @__PURE__ */ y(S, {
|
|
299
|
+
illustration: P ? "empty-search" : "empty-data",
|
|
300
|
+
title: P ? D("export.templates.empty.filtered.title", "No matching templates") : D("export.templates.empty.title", "No templates yet"),
|
|
301
|
+
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."),
|
|
302
|
+
action: P ? /* @__PURE__ */ b("button", {
|
|
304
303
|
type: "button",
|
|
305
|
-
onClick: () =>
|
|
304
|
+
onClick: () => F(""),
|
|
306
305
|
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",
|
|
307
|
-
children: [/* @__PURE__ */ y(v, { size: 16 }),
|
|
306
|
+
children: [/* @__PURE__ */ y(v, { size: 16 }), D("export.templates.empty.filtered.actions.clear", "Clear Search")]
|
|
308
307
|
}) : /* @__PURE__ */ b("button", {
|
|
309
308
|
type: "button",
|
|
310
|
-
onClick:
|
|
309
|
+
onClick: X,
|
|
311
310
|
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",
|
|
312
|
-
children: [/* @__PURE__ */ y(h, { size: 16 }),
|
|
311
|
+
children: [/* @__PURE__ */ y(h, { size: 16 }), D("export.templates.empty.actions.newTemplate", "New Template")]
|
|
313
312
|
})
|
|
314
313
|
}),
|
|
315
|
-
|
|
314
|
+
J && /* @__PURE__ */ y("div", {
|
|
316
315
|
role: "dialog",
|
|
317
316
|
"aria-modal": "true",
|
|
318
317
|
"aria-labelledby": "template-list-delete-title",
|
|
@@ -323,24 +322,24 @@ function C() {
|
|
|
323
322
|
/* @__PURE__ */ y("h2", {
|
|
324
323
|
id: "template-list-delete-title",
|
|
325
324
|
className: "text-lg font-bold",
|
|
326
|
-
children:
|
|
325
|
+
children: D("export.templates.delete.title", "Delete template?")
|
|
327
326
|
}),
|
|
328
327
|
/* @__PURE__ */ y("p", {
|
|
329
328
|
className: "mt-2 text-sm text-text-secondary",
|
|
330
|
-
children:
|
|
329
|
+
children: D("export.templates.delete.description", "This removes the saved export configuration. Existing export jobs are not deleted.")
|
|
331
330
|
}),
|
|
332
331
|
/* @__PURE__ */ b("div", {
|
|
333
332
|
className: "mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3",
|
|
334
333
|
children: [/* @__PURE__ */ y("button", {
|
|
335
334
|
type: "button",
|
|
336
|
-
onClick: () =>
|
|
335
|
+
onClick: () => Y(null),
|
|
337
336
|
className: "px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium",
|
|
338
|
-
children:
|
|
337
|
+
children: D("export.templates.delete.cancel", "Keep Template")
|
|
339
338
|
}), /* @__PURE__ */ y("button", {
|
|
340
339
|
type: "button",
|
|
341
|
-
onClick:
|
|
340
|
+
onClick: ie,
|
|
342
341
|
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",
|
|
343
|
-
children:
|
|
342
|
+
children: D("export.templates.delete.confirmAction", "Delete Template")
|
|
344
343
|
})]
|
|
345
344
|
})
|
|
346
345
|
]
|
|
@@ -350,6 +349,6 @@ function C() {
|
|
|
350
349
|
});
|
|
351
350
|
}
|
|
352
351
|
//#endregion
|
|
353
|
-
export {
|
|
352
|
+
export { w as ExportTemplatesPage };
|
|
354
353
|
|
|
355
354
|
//# 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 { 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 type=\"button\"\n onClick={handleOpenCreate}\n className=\"inline-flex items-center gap-2 px-3 py-2 sm:px-4 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/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 type=\"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-subtle 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-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 {/* Create Template Form */}\n {showCreateForm && (\n <div className=\"border-2 border-primary/30 rounded-xl p-6 bg-action-primary-bg/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 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\n key={i}\n className=\"border-2 border-border-subtle/50 rounded-xl p-5 animate-pulse\"\n >\n <div className=\"h-5 w-40 bg-bg-sunken rounded mb-3\" />\n <div className=\"h-3 w-full bg-bg-sunken rounded mb-2\" />\n <div className=\"flex gap-2 mt-4\">\n <div className=\"h-5 w-16 bg-bg-sunken rounded-full\" />\n <div className=\"h-5 w-16 bg-bg-sunken 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-subtle/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-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 <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 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 { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { CTAOverflowMenu, IllustratedEmptyState } 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 {/* 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\n key={i}\n className=\"border border-border-seam rounded-xl p-5\"\n >\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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"}
|