@burdenoff/microfe-export 2026.623.1 → 2026.624.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/export/ExportRoutes.js +9 -9
- package/dist/export/ExportRoutes.js.map +1 -1
- package/dist/export/components/ExportProgressBar.js +4 -4
- package/dist/export/components/ExportProgressBar.js.map +1 -1
- package/dist/export/components/PageLayout.js +5 -5
- package/dist/export/components/PageLayout.js.map +1 -1
- package/dist/export/pages/CreateExportPage.js +37 -37
- package/dist/export/pages/CreateExportPage.js.map +1 -1
- package/dist/export/pages/ExportDashboardPage.js +15 -15
- package/dist/export/pages/ExportDashboardPage.js.map +1 -1
- package/dist/export/pages/ExportDetailPage.js +18 -18
- package/dist/export/pages/ExportDetailPage.js.map +1 -1
- package/dist/export/pages/ExportHistoryPage.js +22 -22
- package/dist/export/pages/ExportHistoryPage.js.map +1 -1
- package/dist/export/pages/ExportTemplatesPage.js +36 -36
- package/dist/export/pages/ExportTemplatesPage.js.map +1 -1
- package/dist/export/pages/TemplateDetailPage.js +16 -16
- package/dist/export/pages/TemplateDetailPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -63,17 +63,17 @@ function C() {
|
|
|
63
63
|
children: [/* @__PURE__ */ b("button", {
|
|
64
64
|
type: "button",
|
|
65
65
|
onClick: Y,
|
|
66
|
-
className: "inline-flex items-center gap-2 px-3 py-2 sm:px-4 bg-primary text-primary-
|
|
66
|
+
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",
|
|
67
67
|
children: [/* @__PURE__ */ y(h, { size: 16 }), E("export.templates.actions.newTemplate", "New Template")]
|
|
68
68
|
}), /* @__PURE__ */ b("button", {
|
|
69
69
|
type: "button",
|
|
70
70
|
onClick: () => w?.(S(C, "new")),
|
|
71
|
-
className: "inline-flex items-center gap-2 px-3 py-2 sm:px-4 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium text-sm",
|
|
71
|
+
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",
|
|
72
72
|
children: [/* @__PURE__ */ y(h, { size: 16 }), E("export.templates.actions.newExport", "New Export")]
|
|
73
73
|
})]
|
|
74
74
|
}),
|
|
75
75
|
headerContent: /* @__PURE__ */ b("span", {
|
|
76
|
-
className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-
|
|
76
|
+
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
77
|
children: [/* @__PURE__ */ y(_, { size: 12 }), E("export.templates.summary.available", "{count} template{suffix} available", {
|
|
78
78
|
count: Q.length,
|
|
79
79
|
suffix: Q.length === 1 ? "" : "s"
|
|
@@ -81,7 +81,7 @@ function C() {
|
|
|
81
81
|
}),
|
|
82
82
|
children: [
|
|
83
83
|
F && /* @__PURE__ */ b("div", {
|
|
84
|
-
className: "border-2 border-primary/30 rounded-xl p-6 bg-primary/5 space-y-4",
|
|
84
|
+
className: "border-2 border-primary/30 rounded-xl p-6 bg-action-primary-bg/5 space-y-4",
|
|
85
85
|
children: [
|
|
86
86
|
/* @__PURE__ */ b("div", {
|
|
87
87
|
className: "flex items-center justify-between",
|
|
@@ -96,7 +96,7 @@ function C() {
|
|
|
96
96
|
})]
|
|
97
97
|
}),
|
|
98
98
|
G && /* @__PURE__ */ y("p", {
|
|
99
|
-
className: "text-sm text-
|
|
99
|
+
className: "text-sm text-status-error-text bg-status-error-bg/10 rounded-lg px-3 py-2",
|
|
100
100
|
children: G
|
|
101
101
|
}),
|
|
102
102
|
/* @__PURE__ */ b("div", {
|
|
@@ -112,7 +112,7 @@ function C() {
|
|
|
112
112
|
placeholder: E("export.templates.form.fields.namePlaceholder", "e.g. Weekly Tags Export"),
|
|
113
113
|
value: L,
|
|
114
114
|
onChange: (e) => R(e.target.value),
|
|
115
|
-
className: "w-full px-3 py-2 border-2 border-border rounded-lg bg-
|
|
115
|
+
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
116
|
})]
|
|
117
117
|
}), /* @__PURE__ */ b("div", {
|
|
118
118
|
className: "space-y-1",
|
|
@@ -122,7 +122,7 @@ function C() {
|
|
|
122
122
|
}), /* @__PURE__ */ b("select", {
|
|
123
123
|
value: U,
|
|
124
124
|
onChange: (e) => W(e.target.value),
|
|
125
|
-
className: "w-full px-3 py-2 border-2 border-border rounded-lg bg-
|
|
125
|
+
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
126
|
children: [/* @__PURE__ */ y("option", {
|
|
127
127
|
value: "CSV",
|
|
128
128
|
children: "CSV"
|
|
@@ -144,7 +144,7 @@ function C() {
|
|
|
144
144
|
placeholder: E("export.templates.form.fields.descriptionPlaceholder", "Optional description"),
|
|
145
145
|
value: z,
|
|
146
146
|
onChange: (e) => B(e.target.value),
|
|
147
|
-
className: "w-full px-3 py-2 border-2 border-border rounded-lg bg-
|
|
147
|
+
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
148
|
})]
|
|
149
149
|
}),
|
|
150
150
|
/* @__PURE__ */ b("div", {
|
|
@@ -156,7 +156,7 @@ function C() {
|
|
|
156
156
|
" *",
|
|
157
157
|
" ",
|
|
158
158
|
/* @__PURE__ */ y("span", {
|
|
159
|
-
className: "text-
|
|
159
|
+
className: "text-text-secondary font-normal",
|
|
160
160
|
children: E("export.templates.form.fields.entityTypesHint", "(select at least one)")
|
|
161
161
|
})
|
|
162
162
|
]
|
|
@@ -165,7 +165,7 @@ function C() {
|
|
|
165
165
|
children: M.map((e) => /* @__PURE__ */ y("button", {
|
|
166
166
|
type: "button",
|
|
167
167
|
onClick: () => Z(e.entityType),
|
|
168
|
-
className: i("px-3 py-1.5 rounded-lg text-sm font-medium border-2 transition-all", V.includes(e.entityType) ? "border-primary bg-primary/10 text-primary" : "border-border bg-
|
|
168
|
+
className: i("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"),
|
|
169
169
|
children: e.displayName
|
|
170
170
|
}, e.entityType))
|
|
171
171
|
})]
|
|
@@ -176,7 +176,7 @@ function C() {
|
|
|
176
176
|
type: "button",
|
|
177
177
|
onClick: X,
|
|
178
178
|
disabled: j,
|
|
179
|
-
className: "inline-flex items-center gap-2 px-5 py-2.5 bg-primary text-primary-
|
|
179
|
+
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
180
|
children: [j ? /* @__PURE__ */ y(m, {
|
|
181
181
|
size: 16,
|
|
182
182
|
className: "animate-spin"
|
|
@@ -184,7 +184,7 @@ function C() {
|
|
|
184
184
|
}), /* @__PURE__ */ y("button", {
|
|
185
185
|
type: "button",
|
|
186
186
|
onClick: () => I(!1),
|
|
187
|
-
className: "px-5 py-2.5 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium",
|
|
187
|
+
className: "px-5 py-2.5 border-2 border-border-subtle rounded-lg hover:bg-accent transition-colors font-medium",
|
|
188
188
|
children: E("export.templates.form.actions.cancel", "Cancel")
|
|
189
189
|
})]
|
|
190
190
|
})
|
|
@@ -195,7 +195,7 @@ function C() {
|
|
|
195
195
|
children: [
|
|
196
196
|
/* @__PURE__ */ y(g, {
|
|
197
197
|
size: 18,
|
|
198
|
-
className: "absolute left-3 top-1/2 -translate-y-1/2 text-
|
|
198
|
+
className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary group-focus-within:text-primary transition-colors"
|
|
199
199
|
}),
|
|
200
200
|
/* @__PURE__ */ y("input", {
|
|
201
201
|
type: "text",
|
|
@@ -203,12 +203,12 @@ function C() {
|
|
|
203
203
|
placeholder: E("export.templates.search.placeholder", "Search templates..."),
|
|
204
204
|
value: N,
|
|
205
205
|
onChange: (e) => P(e.target.value),
|
|
206
|
-
className: "w-full pl-[3.25rem] pr-4 py-2.5 border-2 border-border rounded-lg bg-
|
|
206
|
+
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
207
|
}),
|
|
208
208
|
N && /* @__PURE__ */ y("button", {
|
|
209
209
|
type: "button",
|
|
210
210
|
onClick: () => P(""),
|
|
211
|
-
className: "absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-
|
|
211
|
+
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
212
|
children: /* @__PURE__ */ y(v, { size: 16 })
|
|
213
213
|
})
|
|
214
214
|
]
|
|
@@ -221,13 +221,13 @@ function C() {
|
|
|
221
221
|
,
|
|
222
222
|
,
|
|
223
223
|
]].map((e, t) => /* @__PURE__ */ b("div", {
|
|
224
|
-
className: "border-2 border-border/50 rounded-xl p-5 animate-pulse",
|
|
224
|
+
className: "border-2 border-border-subtle/50 rounded-xl p-5 animate-pulse",
|
|
225
225
|
children: [
|
|
226
|
-
/* @__PURE__ */ y("div", { className: "h-5 w-40 bg-
|
|
227
|
-
/* @__PURE__ */ y("div", { className: "h-3 w-full bg-
|
|
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" }),
|
|
228
228
|
/* @__PURE__ */ b("div", {
|
|
229
229
|
className: "flex gap-2 mt-4",
|
|
230
|
-
children: [/* @__PURE__ */ y("div", { className: "h-5 w-16 bg-
|
|
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" })]
|
|
231
231
|
})
|
|
232
232
|
]
|
|
233
233
|
}, t))
|
|
@@ -241,16 +241,16 @@ function C() {
|
|
|
241
241
|
onKeyDown: (t) => {
|
|
242
242
|
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), $(e.id));
|
|
243
243
|
},
|
|
244
|
-
className: i("text-left group cursor-pointer border-2 border-border/50 rounded-xl overflow-hidden", "hover:shadow-md hover:border-primary/30 transition-all duration-200", "animate-in fade-in-0 slide-in-from-bottom-2"),
|
|
244
|
+
className: i("text-left group cursor-pointer border-2 border-border-subtle/50 rounded-xl overflow-hidden", "hover:shadow-md hover:border-primary/30 transition-all duration-200", "animate-in fade-in-0 slide-in-from-bottom-2"),
|
|
245
245
|
style: { animationDelay: `${t * 50}ms` },
|
|
246
246
|
children: [/* @__PURE__ */ y("div", {
|
|
247
|
-
className: "p-4 border-b bg-
|
|
247
|
+
className: "p-4 border-b bg-bg-sunken/30",
|
|
248
248
|
children: /* @__PURE__ */ b("div", {
|
|
249
249
|
className: "flex items-start justify-between",
|
|
250
250
|
children: [/* @__PURE__ */ b("div", {
|
|
251
251
|
className: "flex items-center gap-3",
|
|
252
252
|
children: [/* @__PURE__ */ y("div", {
|
|
253
|
-
className: "p-2 rounded-lg bg-primary/10",
|
|
253
|
+
className: "p-2 rounded-lg bg-action-primary-bg/10",
|
|
254
254
|
children: /* @__PURE__ */ y(p, {
|
|
255
255
|
size: 20,
|
|
256
256
|
className: "text-primary"
|
|
@@ -259,9 +259,9 @@ function C() {
|
|
|
259
259
|
className: "font-semibold",
|
|
260
260
|
children: e.name
|
|
261
261
|
}), /* @__PURE__ */ b("p", {
|
|
262
|
-
className: "text-xs text-
|
|
262
|
+
className: "text-xs text-text-secondary",
|
|
263
263
|
children: [a(e.format), e.isDefault && /* @__PURE__ */ y("span", {
|
|
264
|
-
className: "ml-1.5 inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-primary/10 text-primary font-medium",
|
|
264
|
+
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
265
|
children: E("export.templates.card.default", "Default")
|
|
266
266
|
})]
|
|
267
267
|
})] })]
|
|
@@ -270,27 +270,27 @@ function C() {
|
|
|
270
270
|
children: [/* @__PURE__ */ y("button", {
|
|
271
271
|
type: "button",
|
|
272
272
|
onClick: (t) => ne(t, e.id),
|
|
273
|
-
className: "p-1.5 rounded-md text-
|
|
273
|
+
className: "p-1.5 rounded-md text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 transition-colors",
|
|
274
274
|
"aria-label": E("export.templates.card.deleteAriaLabel", "Delete {name}", { name: e.name }),
|
|
275
275
|
children: /* @__PURE__ */ y(v, { size: 14 })
|
|
276
276
|
}), /* @__PURE__ */ y(te, {
|
|
277
277
|
size: 18,
|
|
278
|
-
className: "text-
|
|
278
|
+
className: "text-text-secondary group-hover:text-primary transition-colors mt-1"
|
|
279
279
|
})]
|
|
280
280
|
})]
|
|
281
281
|
})
|
|
282
282
|
}), /* @__PURE__ */ b("div", {
|
|
283
283
|
className: "p-4",
|
|
284
284
|
children: [e.description && /* @__PURE__ */ y("p", {
|
|
285
|
-
className: "text-sm text-
|
|
285
|
+
className: "text-sm text-text-secondary mb-3 line-clamp-2",
|
|
286
286
|
children: e.description
|
|
287
287
|
}), /* @__PURE__ */ b("div", {
|
|
288
288
|
className: "flex flex-wrap gap-1.5 overflow-hidden",
|
|
289
289
|
children: [e.entityTypes.slice(0, 6).map((e) => /* @__PURE__ */ y("span", {
|
|
290
|
-
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-
|
|
290
|
+
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-bg-sunken text-text-secondary max-w-full truncate",
|
|
291
291
|
children: e
|
|
292
292
|
}, e)), e.entityTypes.length > 6 && /* @__PURE__ */ y("span", {
|
|
293
|
-
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-
|
|
293
|
+
className: "inline-flex px-2 py-0.5 rounded text-xs font-medium bg-bg-sunken text-text-secondary",
|
|
294
294
|
children: E("export.templates.card.moreEntities", "+{count} more", { count: e.entityTypes.length - 6 })
|
|
295
295
|
})]
|
|
296
296
|
})]
|
|
@@ -303,12 +303,12 @@ function C() {
|
|
|
303
303
|
action: N ? /* @__PURE__ */ b("button", {
|
|
304
304
|
type: "button",
|
|
305
305
|
onClick: () => P(""),
|
|
306
|
-
className: "inline-flex items-center gap-2 px-4 py-2 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium",
|
|
306
|
+
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
307
|
children: [/* @__PURE__ */ y(v, { size: 16 }), E("export.templates.empty.filtered.actions.clear", "Clear Search")]
|
|
308
308
|
}) : /* @__PURE__ */ b("button", {
|
|
309
309
|
type: "button",
|
|
310
310
|
onClick: Y,
|
|
311
|
-
className: "inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-
|
|
311
|
+
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
312
|
children: [/* @__PURE__ */ y(h, { size: 16 }), E("export.templates.empty.actions.newTemplate", "New Template")]
|
|
313
313
|
})
|
|
314
314
|
}),
|
|
@@ -316,9 +316,9 @@ function C() {
|
|
|
316
316
|
role: "dialog",
|
|
317
317
|
"aria-modal": "true",
|
|
318
318
|
"aria-labelledby": "template-list-delete-title",
|
|
319
|
-
className: "fixed inset-0 z-50 flex items-center justify-center bg-
|
|
319
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-surface/80 backdrop-blur-sm p-4",
|
|
320
320
|
children: /* @__PURE__ */ b("div", {
|
|
321
|
-
className: "w-full max-w-md rounded-xl border-2 border-border bg-
|
|
321
|
+
className: "w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface p-6 shadow-xl",
|
|
322
322
|
children: [
|
|
323
323
|
/* @__PURE__ */ y("h2", {
|
|
324
324
|
id: "template-list-delete-title",
|
|
@@ -326,7 +326,7 @@ function C() {
|
|
|
326
326
|
children: E("export.templates.delete.title", "Delete template?")
|
|
327
327
|
}),
|
|
328
328
|
/* @__PURE__ */ y("p", {
|
|
329
|
-
className: "mt-2 text-sm text-
|
|
329
|
+
className: "mt-2 text-sm text-text-secondary",
|
|
330
330
|
children: E("export.templates.delete.description", "This removes the saved export configuration. Existing export jobs are not deleted.")
|
|
331
331
|
}),
|
|
332
332
|
/* @__PURE__ */ b("div", {
|
|
@@ -334,12 +334,12 @@ function C() {
|
|
|
334
334
|
children: [/* @__PURE__ */ y("button", {
|
|
335
335
|
type: "button",
|
|
336
336
|
onClick: () => J(null),
|
|
337
|
-
className: "px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium",
|
|
337
|
+
className: "px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium",
|
|
338
338
|
children: E("export.templates.delete.cancel", "Keep Template")
|
|
339
339
|
}), /* @__PURE__ */ y("button", {
|
|
340
340
|
type: "button",
|
|
341
341
|
onClick: re,
|
|
342
|
-
className: "px-4 py-2 rounded-lg bg-
|
|
342
|
+
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
343
|
children: E("export.templates.delete.confirmAction", "Delete Template")
|
|
344
344
|
})]
|
|
345
345
|
})
|
|
@@ -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-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium text-sm\"\n >\n <Plus size={16} />\n {tr(\"export.templates.actions.newTemplate\", \"New Template\")}\n </button>\n <button\n 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 rounded-lg hover:bg-accent transition-colors font-medium text-sm\"\n >\n <Plus size={16} />\n {tr(\"export.templates.actions.newExport\", \"New Export\")}\n </button>\n </div>\n }\n headerContent={\n <span className=\"inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground\">\n <Sparkles size={12} />\n {tr(\n \"export.templates.summary.available\",\n \"{count} template{suffix} available\",\n {\n count: filteredTemplates.length,\n suffix: filteredTemplates.length !== 1 ? \"s\" : \"\",\n },\n )}\n </span>\n }\n >\n {/* Create Template Form */}\n {showCreateForm && (\n <div className=\"border-2 border-primary/30 rounded-xl p-6 bg-primary/5 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-semibold text-lg\">\n {tr(\"export.templates.form.title\", \"New Template\")}\n </h3>\n <button\n 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-destructive bg-destructive/10 rounded-lg px-3 py-2\">\n {createError}\n </p>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.name\", \"Name\")} *\n </label>\n <input\n type=\"text\"\n 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 rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n />\n </div>\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.format\", \"Format\")} *\n </label>\n <select\n value={newTemplateFormat}\n onChange={(e) =>\n setNewTemplateFormat(e.target.value as ExportFormat)\n }\n className=\"w-full px-3 py-2 border-2 border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n >\n <option value=\"CSV\">CSV</option>\n <option value=\"JSON\">JSON</option>\n </select>\n </div>\n </div>\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.description\", \"Description\")}\n </label>\n <input\n type=\"text\"\n 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 rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all\"\n />\n </div>\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">\n {tr(\"export.templates.form.fields.entityTypes\", \"Entity Types\")} *{\" \"}\n <span className=\"text-muted-foreground font-normal\">\n {tr(\n \"export.templates.form.fields.entityTypesHint\",\n \"(select at least one)\",\n )}\n </span>\n </label>\n <div className=\"flex flex-wrap gap-2\">\n {availableEntities.map((entity) => (\n <button\n key={entity.entityType}\n type=\"button\"\n onClick={() => toggleEntity(entity.entityType)}\n className={cn(\n \"px-3 py-1.5 rounded-lg text-sm font-medium border-2 transition-all\",\n newTemplateEntityTypes.includes(entity.entityType)\n ? \"border-primary bg-primary/10 text-primary\"\n : \"border-border bg-background hover:border-primary/50\",\n )}\n >\n {entity.displayName}\n </button>\n ))}\n </div>\n </div>\n <div className=\"flex gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={handleCreateTemplate}\n disabled={creating}\n className=\"inline-flex items-center gap-2 px-5 py-2.5 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium disabled:opacity-50\"\n >\n {creating ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : (\n <Plus size={16} />\n )}\n {tr(\"export.templates.form.actions.create\", \"Create Template\")}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(false)}\n className=\"px-5 py-2.5 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium\"\n >\n {tr(\"export.templates.form.actions.cancel\", \"Cancel\")}\n </button>\n </div>\n </div>\n )}\n {/* Search */}\n <div className=\"relative group\">\n <Search\n size={18}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors\"\n />\n <input\n type=\"text\"\n 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 rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all duration-200\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent\"\n >\n <X size={16} />\n </button>\n )}\n </div>\n\n {/* Loading */}\n {isLoading && (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n {[...Array(4)].map((_, i) => (\n <div\n key={i}\n className=\"border-2 border-border/50 rounded-xl p-5 animate-pulse\"\n >\n <div className=\"h-5 w-40 bg-muted rounded mb-3\" />\n <div className=\"h-3 w-full bg-muted rounded mb-2\" />\n <div className=\"flex gap-2 mt-4\">\n <div className=\"h-5 w-16 bg-muted rounded-full\" />\n <div className=\"h-5 w-16 bg-muted rounded-full\" />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Templates Grid */}\n {!isLoading ? (\n filteredTemplates.length > 0 ? (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n {filteredTemplates.map((template, index) => (\n <div\n key={template.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => handleTemplateClick(template.id)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleTemplateClick(template.id);\n }\n }}\n className={cn(\n \"text-left group cursor-pointer border-2 border-border/50 rounded-xl overflow-hidden\",\n \"hover:shadow-md hover:border-primary/30 transition-all duration-200\",\n \"animate-in fade-in-0 slide-in-from-bottom-2\",\n )}\n style={{ animationDelay: `${index * 50}ms` }}\n >\n {/* Header */}\n <div className=\"p-4 border-b bg-muted/30\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-lg bg-primary/10\">\n <FileBox size={20} className=\"text-primary\" />\n </div>\n <div>\n <h3 className=\"font-semibold\">{template.name}</h3>\n <p className=\"text-xs text-muted-foreground\">\n {getFormatDisplayName(template.format)}\n {template.isDefault && (\n <span className=\"ml-1.5 inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-primary/10 text-primary font-medium\">\n {tr(\"export.templates.card.default\", \"Default\")}\n </span>\n )}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={(e) => handleDeleteTemplate(e, template.id)}\n className=\"p-1.5 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors\"\n aria-label={tr(\n \"export.templates.card.deleteAriaLabel\",\n \"Delete {name}\",\n { name: template.name },\n )}\n >\n <X size={14} />\n </button>\n <ChevronRight\n size={18}\n className=\"text-muted-foreground group-hover:text-primary transition-colors mt-1\"\n />\n </div>\n </div>\n </div>\n\n {/* Body */}\n <div className=\"p-4\">\n {template.description && (\n <p className=\"text-sm text-muted-foreground mb-3 line-clamp-2\">\n {template.description}\n </p>\n )}\n <div className=\"flex flex-wrap gap-1.5 overflow-hidden\">\n {template.entityTypes.slice(0, 6).map((entity) => (\n <span\n key={entity}\n className=\"inline-flex px-2 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground max-w-full truncate\"\n >\n {entity}\n </span>\n ))}\n {template.entityTypes.length > 6 && (\n <span className=\"inline-flex px-2 py-0.5 rounded text-xs font-medium bg-muted text-muted-foreground\">\n {tr(\n \"export.templates.card.moreEntities\",\n \"+{count} more\",\n {\n count: template.entityTypes.length - 6,\n },\n )}\n </span>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n icon={searchQuery ? <Search size={48} /> : <FileBox size={48} />}\n title={\n searchQuery\n ? tr(\n \"export.templates.empty.filtered.title\",\n \"No matching templates\",\n )\n : tr(\"export.templates.empty.title\", \"No templates yet\")\n }\n description={\n searchQuery\n ? tr(\n \"export.templates.empty.filtered.description\",\n \"Try adjusting your search to find what you are looking for.\",\n )\n : tr(\n \"export.templates.empty.description\",\n \"Create a template to save reusable export configurations.\",\n )\n }\n action={\n searchQuery ? (\n <button\n type=\"button\"\n onClick={() => setSearchQuery(\"\")}\n className=\"inline-flex items-center gap-2 px-4 py-2 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium\"\n >\n <X size={16} />\n {tr(\n \"export.templates.empty.filtered.actions.clear\",\n \"Clear Search\",\n )}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleOpenCreate}\n className=\"inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium\"\n >\n <Plus size={16} />\n {tr(\n \"export.templates.empty.actions.newTemplate\",\n \"New Template\",\n )}\n </button>\n )\n }\n />\n )\n ) : null}\n {templatePendingDelete && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"template-list-delete-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border bg-card p-6 shadow-xl\">\n <h2 id=\"template-list-delete-title\" className=\"text-lg font-bold\">\n {tr(\"export.templates.delete.title\", \"Delete template?\")}\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {tr(\n \"export.templates.delete.description\",\n \"This removes the saved export configuration. Existing export jobs are not deleted.\",\n )}\n </p>\n <div className=\"mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setTemplatePendingDelete(null)}\n className=\"px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium\"\n >\n {tr(\"export.templates.delete.cancel\", \"Keep Template\")}\n </button>\n <button\n type=\"button\"\n onClick={confirmDeleteTemplate}\n className=\"px-4 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors font-medium\"\n >\n {tr(\"export.templates.delete.confirmAction\", \"Delete Template\")}\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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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 { 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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"}
|
|
@@ -17,14 +17,14 @@ function w() {
|
|
|
17
17
|
size: 48,
|
|
18
18
|
className: "animate-spin text-primary"
|
|
19
19
|
}), /* @__PURE__ */ x("span", {
|
|
20
|
-
className: "mt-6 text-sm font-medium text-
|
|
20
|
+
className: "mt-6 text-sm font-medium text-text-secondary",
|
|
21
21
|
children: "Loading template details…"
|
|
22
22
|
})]
|
|
23
23
|
}) : k ? /* @__PURE__ */ S(a, {
|
|
24
24
|
showHeader: !1,
|
|
25
25
|
children: [
|
|
26
26
|
/* @__PURE__ */ x("div", {
|
|
27
|
-
className: "relative overflow-hidden rounded-xl bg-gradient-to-br from-primary/5 via-primary/3 to-background border border-border/50 p-6 shadow-sm",
|
|
27
|
+
className: "relative overflow-hidden rounded-xl bg-gradient-to-br from-primary/5 via-primary/3 to-background border border-border-subtle/50 p-6 shadow-sm",
|
|
28
28
|
children: /* @__PURE__ */ S("div", {
|
|
29
29
|
className: "relative z-10 flex items-center gap-4",
|
|
30
30
|
children: [/* @__PURE__ */ x("button", {
|
|
@@ -35,7 +35,7 @@ function w() {
|
|
|
35
35
|
}), /* @__PURE__ */ S("div", {
|
|
36
36
|
className: "flex items-center gap-3 flex-1",
|
|
37
37
|
children: [/* @__PURE__ */ x("div", {
|
|
38
|
-
className: "p-2.5 rounded-lg bg-primary/10",
|
|
38
|
+
className: "p-2.5 rounded-lg bg-action-primary-bg/10",
|
|
39
39
|
children: /* @__PURE__ */ x(m, {
|
|
40
40
|
size: 24,
|
|
41
41
|
className: "text-primary"
|
|
@@ -46,7 +46,7 @@ function w() {
|
|
|
46
46
|
className: "text-xl sm:text-2xl font-bold truncate",
|
|
47
47
|
children: k.name
|
|
48
48
|
}), /* @__PURE__ */ x("p", {
|
|
49
|
-
className: "text-sm text-
|
|
49
|
+
className: "text-sm text-text-secondary mt-1",
|
|
50
50
|
children: k.isDefault ? "Default template" : "Custom template"
|
|
51
51
|
})]
|
|
52
52
|
})]
|
|
@@ -54,9 +54,9 @@ function w() {
|
|
|
54
54
|
})
|
|
55
55
|
}),
|
|
56
56
|
k.description && /* @__PURE__ */ x("div", {
|
|
57
|
-
className: "border-2 border-border/50 rounded-xl p-5",
|
|
57
|
+
className: "border-2 border-border-subtle/50 rounded-xl p-5",
|
|
58
58
|
children: /* @__PURE__ */ x("p", {
|
|
59
|
-
className: "text-sm text-
|
|
59
|
+
className: "text-sm text-text-secondary",
|
|
60
60
|
children: k.description
|
|
61
61
|
})
|
|
62
62
|
}),
|
|
@@ -84,9 +84,9 @@ function w() {
|
|
|
84
84
|
value: new Date(k.updatedAt).toLocaleDateString()
|
|
85
85
|
}
|
|
86
86
|
].map((e) => /* @__PURE__ */ S("div", {
|
|
87
|
-
className: "border-2 border-border/50 rounded-xl p-4 bg-gradient-to-br from-card/50 to-background",
|
|
87
|
+
className: "border-2 border-border-subtle/50 rounded-xl p-4 bg-gradient-to-br from-card/50 to-background",
|
|
88
88
|
children: [/* @__PURE__ */ S("div", {
|
|
89
|
-
className: "flex items-center gap-2 text-
|
|
89
|
+
className: "flex items-center gap-2 text-text-secondary mb-1",
|
|
90
90
|
children: [e.icon, /* @__PURE__ */ x("span", {
|
|
91
91
|
className: "text-xs font-medium",
|
|
92
92
|
children: e.label
|
|
@@ -103,7 +103,7 @@ function w() {
|
|
|
103
103
|
children: /* @__PURE__ */ x("div", {
|
|
104
104
|
className: "flex flex-wrap gap-2",
|
|
105
105
|
children: k.entityTypes.map((e) => /* @__PURE__ */ x("span", {
|
|
106
|
-
className: "inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border-2 border-border/50 bg-
|
|
106
|
+
className: "inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border-2 border-border-subtle/50 bg-bg-sunken/30",
|
|
107
107
|
children: e
|
|
108
108
|
}, e))
|
|
109
109
|
})
|
|
@@ -115,7 +115,7 @@ function w() {
|
|
|
115
115
|
onClick: () => {
|
|
116
116
|
E?.(C(T, `new?templateId=${w}`));
|
|
117
117
|
},
|
|
118
|
-
className: "flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 bg-primary text-primary-
|
|
118
|
+
className: "flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-all duration-200 font-bold shadow-lg",
|
|
119
119
|
children: [/* @__PURE__ */ x(v, { size: 18 }), "Use Template"]
|
|
120
120
|
}), !k.isDefault && /* @__PURE__ */ S("button", {
|
|
121
121
|
type: "button",
|
|
@@ -132,9 +132,9 @@ function w() {
|
|
|
132
132
|
role: "dialog",
|
|
133
133
|
"aria-modal": "true",
|
|
134
134
|
"aria-labelledby": "template-delete-title",
|
|
135
|
-
className: "fixed inset-0 z-50 flex items-center justify-center bg-
|
|
135
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-surface/80 backdrop-blur-sm p-4",
|
|
136
136
|
children: /* @__PURE__ */ S("div", {
|
|
137
|
-
className: "w-full max-w-md rounded-xl border-2 border-border bg-
|
|
137
|
+
className: "w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface p-6 shadow-xl",
|
|
138
138
|
children: [
|
|
139
139
|
/* @__PURE__ */ x("h2", {
|
|
140
140
|
id: "template-delete-title",
|
|
@@ -142,7 +142,7 @@ function w() {
|
|
|
142
142
|
children: "Delete template?"
|
|
143
143
|
}),
|
|
144
144
|
/* @__PURE__ */ x("p", {
|
|
145
|
-
className: "mt-2 text-sm text-
|
|
145
|
+
className: "mt-2 text-sm text-text-secondary",
|
|
146
146
|
children: "This removes the saved export configuration. Existing export jobs are not deleted."
|
|
147
147
|
}),
|
|
148
148
|
/* @__PURE__ */ S("div", {
|
|
@@ -150,7 +150,7 @@ function w() {
|
|
|
150
150
|
children: [/* @__PURE__ */ x("button", {
|
|
151
151
|
type: "button",
|
|
152
152
|
onClick: () => O(!1),
|
|
153
|
-
className: "px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium",
|
|
153
|
+
className: "px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium",
|
|
154
154
|
children: "Keep Template"
|
|
155
155
|
}), /* @__PURE__ */ S("button", {
|
|
156
156
|
type: "button",
|
|
@@ -165,7 +165,7 @@ function w() {
|
|
|
165
165
|
}
|
|
166
166
|
},
|
|
167
167
|
disabled: M,
|
|
168
|
-
className: "inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-
|
|
168
|
+
className: "inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90 transition-colors font-medium disabled:opacity-50",
|
|
169
169
|
children: [M ? /* @__PURE__ */ x(_, {
|
|
170
170
|
size: 16,
|
|
171
171
|
className: "animate-spin"
|
|
@@ -207,7 +207,7 @@ function w() {
|
|
|
207
207
|
/* @__PURE__ */ S("button", {
|
|
208
208
|
type: "button",
|
|
209
209
|
onClick: () => E?.(C(T, "templates")),
|
|
210
|
-
className: "inline-flex items-center gap-2 px-6 py-3 bg-primary text-primary-
|
|
210
|
+
className: "inline-flex items-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium",
|
|
211
211
|
children: [/* @__PURE__ */ x(d, { size: 18 }), "Back to Templates"]
|
|
212
212
|
})
|
|
213
213
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { useState } from \"react\";\nimport {\n ArrowLeft,\n FileBox,\n Download,\n Play,\n Trash2,\n AlertCircle,\n Clock,\n FileText,\n HardDrive,\n Loader2,\n} from \"lucide-react\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplate,\n useDeleteExportTemplate,\n} from \"../hooks/useExportQueries\";\nimport {\n getFormatDisplayName,\n getArchiveFormatDisplayName,\n} from \"../constants/enums\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\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 * Template Detail Page\n * View template details and use it to create a new export.\n * Fetches template from the backend via useExportTemplate hook.\n */\nexport function TemplateDetailPage() {\n const { templateId } = useParams<{ templateId: string }>();\n const { basePath = \"/\", navigate } = useExport();\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n\n const { data: template, isLoading } = useExportTemplate(templateId);\n const { deleteTemplate, isPending: deleting } = useDeleteExportTemplate();\n\n // Loading\n if (isLoading) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 size={48} className=\"animate-spin text-primary\" />\n <span className=\"mt-6 text-sm font-medium text-muted-foreground\">\n Loading template details…\n </span>\n </div>\n );\n }\n\n if (!template) {\n return (\n <PageLayout showHeader={false}>\n <div className=\"flex items-center gap-3 mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200\"\n >\n <ArrowLeft size={20} />\n </button>\n <h1 className=\"text-xl font-bold\">Template Not Found</h1>\n </div>\n <div className=\"border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-12 text-center\">\n <AlertCircle\n size={48}\n className=\"text-status-error-text mx-auto mb-4\"\n />\n <h3 className=\"text-lg font-bold text-status-error-text mb-2\">\n Template Not Found\n </h3>\n <p className=\"text-status-error-text mb-6\">\n The template you are looking for does not exist.\n </p>\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"inline-flex items-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium\"\n >\n <ArrowLeft size={18} />\n Back to Templates\n </button>\n </div>\n </PageLayout>\n );\n }\n\n const handleUseTemplate = () => {\n // Navigate to new export page pre-selected with this template\n navigate?.(joinPath(basePath, `new?templateId=${templateId}`));\n };\n\n const handleDelete = async () => {\n if (!templateId) return;\n nativeImpact(\"medium\");\n try {\n await deleteTemplate(templateId);\n nativeNotify(\"success\");\n navigate?.(joinPath(basePath, \"templates\"));\n } catch {\n nativeNotify(\"error\");\n }\n };\n\n return (\n <PageLayout showHeader={false}>\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-xl bg-gradient-to-br from-primary/5 via-primary/3 to-background border border-border/50 p-6 shadow-sm\">\n <div className=\"relative z-10 flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200\"\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex items-center gap-3 flex-1\">\n <div className=\"p-2.5 rounded-lg bg-primary/10\">\n <FileBox size={24} className=\"text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold truncate\">\n {template.name}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {template.isDefault ? \"Default template\" : \"Custom template\"}\n </p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Description */}\n {template.description && (\n <div className=\"border-2 border-border/50 rounded-xl p-5\">\n <p className=\"text-sm text-muted-foreground\">\n {template.description}\n </p>\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n icon: <FileText size={18} />,\n label: \"Format\",\n value: getFormatDisplayName(template.format),\n },\n {\n icon: <HardDrive size={18} />,\n label: \"Archive\",\n // Backend template doesn't carry archiveFormat, default to ZIP\n value: getArchiveFormatDisplayName(\"ZIP\"),\n },\n {\n icon: <Download size={18} />,\n label: \"Entity Types\",\n value: `${template.entityTypes.length} type${template.entityTypes.length !== 1 ? \"s\" : \"\"}`,\n },\n {\n icon: <Clock size={18} />,\n label: \"Last Updated\",\n value: new Date(template.updatedAt).toLocaleDateString(),\n },\n ].map((item) => (\n <div\n key={item.label}\n className=\"border-2 border-border/50 rounded-xl p-4 bg-gradient-to-br from-card/50 to-background\"\n >\n <div className=\"flex items-center gap-2 text-muted-foreground mb-1\">\n {item.icon}\n <span className=\"text-xs font-medium\">{item.label}</span>\n </div>\n <p className=\"font-bold text-lg\">{item.value}</p>\n </div>\n ))}\n </div>\n\n {/* Entity Types */}\n <PageSection\n title=\"Included Entity Types\"\n description=\"Data types that will be exported\"\n >\n <div className=\"flex flex-wrap gap-2\">\n {template.entityTypes.map((entity) => (\n <span\n key={entity}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border-2 border-border/50 bg-muted/30\"\n >\n {entity}\n </span>\n ))}\n </div>\n </PageSection>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={handleUseTemplate}\n className=\"flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-all duration-200 hover:scale-105 font-bold shadow-lg\"\n >\n <Play size={18} />\n Use Template\n </button>\n {!template.isDefault && (\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(true)}\n disabled={deleting}\n className=\"inline-flex items-center justify-center gap-2 px-6 py-3 border-2 border-status-error-bg/50 text-status-error-text rounded-lg hover:bg-status-error-bg-subtle transition-all duration-200 font-medium disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 size={18} className=\"animate-spin\" />\n ) : (\n <Trash2 size={18} />\n )}\n Delete Template\n </button>\n )}\n </div>\n\n {showDeleteConfirm && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"template-delete-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border bg-card p-6 shadow-xl\">\n <h2 id=\"template-delete-title\" className=\"text-lg font-bold\">\n Delete template?\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n This removes the saved export configuration. Existing export jobs\n are not deleted.\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={() => setShowDeleteConfirm(false)}\n className=\"px-4 py-2 rounded-lg border-2 border-border hover:bg-accent transition-colors font-medium\"\n >\n Keep Template\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors font-medium disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : null}\n 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":"TemplateDetailPage.js","names":[],"sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport { useState } from \"react\";\nimport {\n ArrowLeft,\n FileBox,\n Download,\n Play,\n Trash2,\n AlertCircle,\n Clock,\n FileText,\n HardDrive,\n Loader2,\n} from \"lucide-react\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplate,\n useDeleteExportTemplate,\n} from \"../hooks/useExportQueries\";\nimport {\n getFormatDisplayName,\n getArchiveFormatDisplayName,\n} from \"../constants/enums\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\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 * Template Detail Page\n * View template details and use it to create a new export.\n * Fetches template from the backend via useExportTemplate hook.\n */\nexport function TemplateDetailPage() {\n const { templateId } = useParams<{ templateId: string }>();\n const { basePath = \"/\", navigate } = useExport();\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n\n const { data: template, isLoading } = useExportTemplate(templateId);\n const { deleteTemplate, isPending: deleting } = useDeleteExportTemplate();\n\n // Loading\n if (isLoading) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 size={48} className=\"animate-spin text-primary\" />\n <span className=\"mt-6 text-sm font-medium text-text-secondary\">\n Loading template details…\n </span>\n </div>\n );\n }\n\n if (!template) {\n return (\n <PageLayout showHeader={false}>\n <div className=\"flex items-center gap-3 mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200\"\n >\n <ArrowLeft size={20} />\n </button>\n <h1 className=\"text-xl font-bold\">Template Not Found</h1>\n </div>\n <div className=\"border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-12 text-center\">\n <AlertCircle\n size={48}\n className=\"text-status-error-text mx-auto mb-4\"\n />\n <h3 className=\"text-lg font-bold text-status-error-text mb-2\">\n Template Not Found\n </h3>\n <p className=\"text-status-error-text mb-6\">\n The template you are looking for does not exist.\n </p>\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"inline-flex items-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium\"\n >\n <ArrowLeft size={18} />\n Back to Templates\n </button>\n </div>\n </PageLayout>\n );\n }\n\n const handleUseTemplate = () => {\n // Navigate to new export page pre-selected with this template\n navigate?.(joinPath(basePath, `new?templateId=${templateId}`));\n };\n\n const handleDelete = async () => {\n if (!templateId) return;\n nativeImpact(\"medium\");\n try {\n await deleteTemplate(templateId);\n nativeNotify(\"success\");\n navigate?.(joinPath(basePath, \"templates\"));\n } catch {\n nativeNotify(\"error\");\n }\n };\n\n return (\n <PageLayout showHeader={false}>\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-xl bg-gradient-to-br from-primary/5 via-primary/3 to-background border border-border-subtle/50 p-6 shadow-sm\">\n <div className=\"relative z-10 flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200\"\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex items-center gap-3 flex-1\">\n <div className=\"p-2.5 rounded-lg bg-action-primary-bg/10\">\n <FileBox size={24} className=\"text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold truncate\">\n {template.name}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {template.isDefault ? \"Default template\" : \"Custom template\"}\n </p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Description */}\n {template.description && (\n <div className=\"border-2 border-border-subtle/50 rounded-xl p-5\">\n <p className=\"text-sm text-text-secondary\">\n {template.description}\n </p>\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n icon: <FileText size={18} />,\n label: \"Format\",\n value: getFormatDisplayName(template.format),\n },\n {\n icon: <HardDrive size={18} />,\n label: \"Archive\",\n // Backend template doesn't carry archiveFormat, default to ZIP\n value: getArchiveFormatDisplayName(\"ZIP\"),\n },\n {\n icon: <Download size={18} />,\n label: \"Entity Types\",\n value: `${template.entityTypes.length} type${template.entityTypes.length !== 1 ? \"s\" : \"\"}`,\n },\n {\n icon: <Clock size={18} />,\n label: \"Last Updated\",\n value: new Date(template.updatedAt).toLocaleDateString(),\n },\n ].map((item) => (\n <div\n key={item.label}\n className=\"border-2 border-border-subtle/50 rounded-xl p-4 bg-gradient-to-br from-card/50 to-background\"\n >\n <div className=\"flex items-center gap-2 text-text-secondary mb-1\">\n {item.icon}\n <span className=\"text-xs font-medium\">{item.label}</span>\n </div>\n <p className=\"font-bold text-lg\">{item.value}</p>\n </div>\n ))}\n </div>\n\n {/* Entity Types */}\n <PageSection\n title=\"Included Entity Types\"\n description=\"Data types that will be exported\"\n >\n <div className=\"flex flex-wrap gap-2\">\n {template.entityTypes.map((entity) => (\n <span\n key={entity}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border-2 border-border-subtle/50 bg-bg-sunken/30\"\n >\n {entity}\n </span>\n ))}\n </div>\n </PageSection>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={handleUseTemplate}\n className=\"flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-all duration-200 font-bold shadow-lg\"\n >\n <Play size={18} />\n Use Template\n </button>\n {!template.isDefault && (\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(true)}\n disabled={deleting}\n className=\"inline-flex items-center justify-center gap-2 px-6 py-3 border-2 border-status-error-bg/50 text-status-error-text rounded-lg hover:bg-status-error-bg-subtle transition-all duration-200 font-medium disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 size={18} className=\"animate-spin\" />\n ) : (\n <Trash2 size={18} />\n )}\n Delete Template\n </button>\n )}\n </div>\n\n {showDeleteConfirm && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"template-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-delete-title\" className=\"text-lg font-bold\">\n Delete template?\n </h2>\n <p className=\"mt-2 text-sm text-text-secondary\">\n This removes the saved export configuration. Existing export jobs\n are not deleted.\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={() => setShowDeleteConfirm(false)}\n className=\"px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium\"\n >\n Keep Template\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90 transition-colors font-medium disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : null}\n Delete Template\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|