@burdenoff/microfe-export 2026.525.2 → 2026.527.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 +5 -4
- package/dist/export/ExportRoutes.js.map +1 -1
- package/dist/export/components/ExportStatusBadge.js +2 -2
- package/dist/export/components/ExportStatusBadge.js.map +1 -1
- package/dist/export/constants/enums.js +13 -13
- package/dist/export/constants/enums.js.map +1 -1
- package/dist/export/hooks/useExportQueries.js.map +1 -1
- package/dist/export/pages/CreateExportPage.js +10 -1
- package/dist/export/pages/CreateExportPage.js.map +1 -1
- package/dist/export/pages/ExportDashboardPage.js +5 -0
- package/dist/export/pages/ExportDashboardPage.js.map +1 -1
- package/dist/export/pages/ExportDetailPage.js +7 -1
- package/dist/export/pages/ExportDetailPage.js.map +1 -1
- package/dist/export/pages/ExportHistoryPage.js +136 -124
- package/dist/export/pages/ExportHistoryPage.js.map +1 -1
- package/dist/export/pages/ExportTemplatesPage.js +148 -136
- package/dist/export/pages/ExportTemplatesPage.js.map +1 -1
- package/dist/export/pages/TemplateDetailPage.js +6 -1
- package/dist/export/pages/TemplateDetailPage.js.map +1 -1
- package/package.json +1 -1
- package/dist/dev/main.d.ts +0 -2
- package/dist/dev/main.d.ts.map +0 -1
- package/dist/export/ExportRoot.d.ts +0 -31
- package/dist/export/ExportRoot.d.ts.map +0 -1
- package/dist/export/ExportRoutes.d.ts +0 -14
- package/dist/export/ExportRoutes.d.ts.map +0 -1
- package/dist/export/components/ExportProgressBar.d.ts +0 -12
- package/dist/export/components/ExportProgressBar.d.ts.map +0 -1
- package/dist/export/components/ExportStatusBadge.d.ts +0 -12
- package/dist/export/components/ExportStatusBadge.d.ts.map +0 -1
- package/dist/export/components/PageLayout.d.ts +0 -55
- package/dist/export/components/PageLayout.d.ts.map +0 -1
- package/dist/export/components/ui.d.ts +0 -6
- package/dist/export/components/ui.d.ts.map +0 -1
- package/dist/export/constants/enums.d.ts +0 -79
- package/dist/export/constants/enums.d.ts.map +0 -1
- package/dist/export/constants/index.d.ts +0 -7
- package/dist/export/constants/index.d.ts.map +0 -1
- package/dist/export/hooks/useExportQueries.d.ts +0 -224
- package/dist/export/hooks/useExportQueries.d.ts.map +0 -1
- package/dist/export/index.d.ts +0 -29
- package/dist/export/index.d.ts.map +0 -1
- package/dist/export/pages/CreateExportPage.d.ts +0 -2
- package/dist/export/pages/CreateExportPage.d.ts.map +0 -1
- package/dist/export/pages/ExportDashboardPage.d.ts +0 -6
- package/dist/export/pages/ExportDashboardPage.d.ts.map +0 -1
- package/dist/export/pages/ExportDetailPage.d.ts +0 -7
- package/dist/export/pages/ExportDetailPage.d.ts.map +0 -1
- package/dist/export/pages/ExportHistoryPage.d.ts +0 -6
- package/dist/export/pages/ExportHistoryPage.d.ts.map +0 -1
- package/dist/export/pages/ExportLandingPage.d.ts +0 -9
- package/dist/export/pages/ExportLandingPage.d.ts.map +0 -1
- package/dist/export/pages/ExportTemplatesPage.d.ts +0 -6
- package/dist/export/pages/ExportTemplatesPage.d.ts.map +0 -1
- package/dist/export/pages/TemplateDetailPage.d.ts +0 -7
- package/dist/export/pages/TemplateDetailPage.d.ts.map +0 -1
- package/dist/export/providers/ExportProvider.d.ts +0 -13
- package/dist/export/providers/ExportProvider.d.ts.map +0 -1
- package/dist/export/store/exportStore.d.ts +0 -17
- package/dist/export/store/exportStore.d.ts.map +0 -1
- package/dist/export/types/index.d.ts +0 -230
- package/dist/export/types/index.d.ts.map +0 -1
- package/dist/export/utils/cn.d.ts +0 -8
- package/dist/export/utils/cn.d.ts.map +0 -1
- package/dist/export/utils/events.d.ts +0 -7
- package/dist/export/utils/events.d.ts.map +0 -1
- package/dist/export/utils/i18n.d.ts +0 -6
- package/dist/export/utils/i18n.d.ts.map +0 -1
- package/dist/generated/global-operations.d.ts +0 -2
- package/dist/generated/global-operations.d.ts.map +0 -1
- package/dist/generated/global-types.d.ts +0 -23527
- package/dist/generated/global-types.d.ts.map +0 -1
- package/dist/generated/wspace-operations.d.ts +0 -773
- package/dist/generated/wspace-operations.d.ts.map +0 -1
- package/dist/generated/wspace-types.d.ts +0 -57851
- package/dist/generated/wspace-types.d.ts.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
- package/dist/utils/nativeBridge.d.ts +0 -3
- package/dist/utils/nativeBridge.d.ts.map +0 -1
|
@@ -1,132 +1,138 @@
|
|
|
1
1
|
import { useExport as e } from "../providers/ExportProvider.js";
|
|
2
2
|
import { useExportJobs as t } from "../hooks/useExportQueries.js";
|
|
3
3
|
import { cn as n } from "../utils/cn.js";
|
|
4
|
-
import { getAllStatuses as r, getFormatDisplayName as
|
|
5
|
-
import { tWithFallback as
|
|
6
|
-
import { ExportStatusBadge as
|
|
7
|
-
import { EmptyState as
|
|
4
|
+
import { getAllStatuses as r, getFormatDisplayName as ee, getStatusDisplayName as i } from "../constants/enums.js";
|
|
5
|
+
import { tWithFallback as a } from "../utils/i18n.js";
|
|
6
|
+
import { ExportStatusBadge as o } from "../components/ExportStatusBadge.js";
|
|
7
|
+
import { EmptyState as s, PageLayout as c } from "../components/PageLayout.js";
|
|
8
8
|
import { useExportStore as l } from "../store/exportStore.js";
|
|
9
|
-
import { useEffect as u, useMemo as d,
|
|
10
|
-
import { AlertCircle as
|
|
11
|
-
import { useSearchParams as
|
|
12
|
-
import { Fragment as
|
|
13
|
-
import { useI18n as
|
|
9
|
+
import { useEffect as u, useMemo as d, useRef as f, useState as p } from "react";
|
|
10
|
+
import { AlertCircle as te, ChevronLeft as ne, ChevronRight as m, Clock as h, Download as g, Filter as _, RefreshCw as v, Search as y, X as b } from "lucide-react";
|
|
11
|
+
import { useSearchParams as x } from "react-router-dom";
|
|
12
|
+
import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
13
|
+
import { useI18n as T } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
14
14
|
//#region src/export/pages/ExportHistoryPage.tsx
|
|
15
|
-
var
|
|
16
|
-
function
|
|
17
|
-
let { basePath:
|
|
15
|
+
var E = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
16
|
+
function D() {
|
|
17
|
+
let { basePath: D = "/", navigate: O } = e(), { t: k } = T(), A = (e, t, n) => a(k, e, t, n), { jobFilter: j, updateFilter: M, resetFilter: N, pagination: P, setPagination: F } = l(), [I] = x(), L = f(!1);
|
|
18
18
|
u(() => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
if (L.current) return;
|
|
20
|
+
L.current = !0;
|
|
21
|
+
let e = I.get("status");
|
|
22
|
+
e && M({ status: e });
|
|
23
|
+
}, [I, M]);
|
|
24
|
+
let [R, z] = p(!1), [B, V] = p([null]), [H, U] = p(0), W = B[H] ?? null, { data: G, totalCount: K, pageInfo: q, isLoading: J, error: Y, refetch: X } = t(j, {
|
|
25
|
+
first: P.pageSize,
|
|
24
26
|
after: W ?? void 0
|
|
25
27
|
}), Z = d(() => {
|
|
26
|
-
if (!
|
|
27
|
-
let e =
|
|
28
|
+
if (!j.search) return G;
|
|
29
|
+
let e = j.search.toLowerCase();
|
|
28
30
|
return G.filter((t) => t.name.toLowerCase().includes(e) || t.entityTypes.some((t) => t.toLowerCase().includes(e)));
|
|
29
|
-
}, [G,
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
!q?.hasNextPage || !q.endCursor || (V([...B.slice(0, H + 1), q.endCursor]), U(
|
|
33
|
-
page:
|
|
31
|
+
}, [G, j.search]), Q = j.search || j.status !== "all" || j.format !== "all", re = (e) => {
|
|
32
|
+
O?.(E(D, e));
|
|
33
|
+
}, ie = () => {
|
|
34
|
+
!q?.hasNextPage || !q.endCursor || (V([...B.slice(0, H + 1), q.endCursor]), U((e) => e + 1), F({
|
|
35
|
+
page: P.page + 1,
|
|
34
36
|
cursor: q.endCursor
|
|
35
37
|
}));
|
|
36
|
-
},
|
|
38
|
+
}, ae = () => {
|
|
37
39
|
if (H === 0) return;
|
|
38
40
|
let e = H - 1;
|
|
39
|
-
U(e),
|
|
40
|
-
page:
|
|
41
|
+
U(e), F({
|
|
42
|
+
page: P.page - 1,
|
|
41
43
|
cursor: B[e] ?? null
|
|
42
44
|
});
|
|
43
45
|
}, $ = () => {
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
return /* @__PURE__ */
|
|
47
|
-
title:
|
|
48
|
-
subtitle:
|
|
49
|
-
icon: /* @__PURE__ */
|
|
50
|
-
headerActions: /* @__PURE__ */
|
|
51
|
-
|
|
46
|
+
N(), V([null]), U(0);
|
|
47
|
+
}, oe = H > 0, se = q?.hasNextPage ?? !1, ce = H * P.pageSize + 1, le = Math.min((H + 1) * P.pageSize, K), ue = (e) => A(`export.shared.status.${e.toLowerCase()}`, i(e));
|
|
48
|
+
return /* @__PURE__ */ w(c, {
|
|
49
|
+
title: A("export.history.title", "Export History"),
|
|
50
|
+
subtitle: A("export.history.subtitle", "View and manage all your past export jobs"),
|
|
51
|
+
icon: /* @__PURE__ */ C(h, { size: 24 }),
|
|
52
|
+
headerActions: /* @__PURE__ */ w("button", {
|
|
53
|
+
type: "button",
|
|
54
|
+
onClick: () => O?.(E(D, "new")),
|
|
52
55
|
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",
|
|
53
|
-
children: [/* @__PURE__ */
|
|
56
|
+
children: [/* @__PURE__ */ C(g, { size: 16 }), A("export.history.actions.newExport", "New Export")]
|
|
54
57
|
}),
|
|
55
58
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ w("div", {
|
|
57
60
|
className: "space-y-4",
|
|
58
|
-
children: [/* @__PURE__ */
|
|
61
|
+
children: [/* @__PURE__ */ w("div", {
|
|
59
62
|
className: "flex flex-col sm:flex-row gap-3",
|
|
60
|
-
children: [/* @__PURE__ */
|
|
63
|
+
children: [/* @__PURE__ */ w("div", {
|
|
61
64
|
className: "flex-1 relative group",
|
|
62
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ C(y, {
|
|
63
66
|
size: 18,
|
|
64
67
|
className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors"
|
|
65
|
-
}), /* @__PURE__ */
|
|
68
|
+
}), /* @__PURE__ */ C("input", {
|
|
66
69
|
type: "text",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
"aria-label": A("export.history.search.placeholder", "Search exports by name or entity type"),
|
|
71
|
+
placeholder: A("export.history.search.placeholder", "Search exports by name or entity type..."),
|
|
72
|
+
value: j.search,
|
|
73
|
+
onChange: (e) => M({ search: e.target.value }),
|
|
70
74
|
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"
|
|
71
75
|
})]
|
|
72
|
-
}), /* @__PURE__ */
|
|
76
|
+
}), /* @__PURE__ */ w("button", {
|
|
77
|
+
type: "button",
|
|
73
78
|
onClick: () => z(!R),
|
|
74
79
|
className: n("inline-flex items-center gap-2 px-4 py-2.5 border-2 rounded-lg transition-all duration-200 font-medium", R || Q ? "bg-primary text-primary-foreground border-primary" : "border-border/50 hover:border-primary/30"),
|
|
75
|
-
children: [/* @__PURE__ */
|
|
80
|
+
children: [/* @__PURE__ */ C(_, { size: 16 }), R ? A("export.history.filters.hide", "Hide") : A("export.history.filters.show", "Filter")]
|
|
76
81
|
})]
|
|
77
|
-
}), R && /* @__PURE__ */
|
|
82
|
+
}), R && /* @__PURE__ */ w("div", {
|
|
78
83
|
className: "border-2 border-border/50 rounded-xl p-4 animate-in slide-in-from-top-2 duration-200",
|
|
79
|
-
children: [/* @__PURE__ */
|
|
84
|
+
children: [/* @__PURE__ */ w("div", {
|
|
80
85
|
className: "flex items-center justify-between mb-4",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ w("h3", {
|
|
82
87
|
className: "font-semibold flex items-center gap-2",
|
|
83
|
-
children: [/* @__PURE__ */
|
|
88
|
+
children: [/* @__PURE__ */ C(_, {
|
|
84
89
|
size: 16,
|
|
85
90
|
className: "text-primary"
|
|
86
|
-
}),
|
|
87
|
-
}), Q && /* @__PURE__ */
|
|
91
|
+
}), A("export.history.filters.title", "Filters")]
|
|
92
|
+
}), Q && /* @__PURE__ */ w("button", {
|
|
93
|
+
type: "button",
|
|
88
94
|
onClick: $,
|
|
89
95
|
className: "text-sm text-muted-foreground hover:text-destructive flex items-center gap-1",
|
|
90
|
-
children: [/* @__PURE__ */
|
|
96
|
+
children: [/* @__PURE__ */ C(b, { size: 14 }), A("export.history.filters.clearAll", "Clear All")]
|
|
91
97
|
})]
|
|
92
|
-
}), /* @__PURE__ */
|
|
98
|
+
}), /* @__PURE__ */ w("div", {
|
|
93
99
|
className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
100
|
+
children: [/* @__PURE__ */ w("div", {
|
|
95
101
|
className: "space-y-2",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ C("label", {
|
|
97
103
|
className: "block text-sm font-medium",
|
|
98
|
-
children:
|
|
99
|
-
}), /* @__PURE__ */
|
|
100
|
-
value:
|
|
101
|
-
onChange: (e) =>
|
|
104
|
+
children: A("export.history.filters.status.label", "Status")
|
|
105
|
+
}), /* @__PURE__ */ w("select", {
|
|
106
|
+
value: j.status,
|
|
107
|
+
onChange: (e) => M({ status: e.target.value }),
|
|
102
108
|
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",
|
|
103
|
-
children: [/* @__PURE__ */
|
|
109
|
+
children: [/* @__PURE__ */ C("option", {
|
|
104
110
|
value: "all",
|
|
105
|
-
children:
|
|
106
|
-
}), r().map((e) => /* @__PURE__ */
|
|
111
|
+
children: A("export.history.filters.status.all", "All Statuses")
|
|
112
|
+
}), r().map((e) => /* @__PURE__ */ C("option", {
|
|
107
113
|
value: e,
|
|
108
|
-
children:
|
|
114
|
+
children: ue(e)
|
|
109
115
|
}, e))]
|
|
110
116
|
})]
|
|
111
|
-
}), /* @__PURE__ */
|
|
117
|
+
}), /* @__PURE__ */ w("div", {
|
|
112
118
|
className: "space-y-2",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
119
|
+
children: [/* @__PURE__ */ C("label", {
|
|
114
120
|
className: "block text-sm font-medium",
|
|
115
|
-
children:
|
|
116
|
-
}), /* @__PURE__ */
|
|
117
|
-
value:
|
|
118
|
-
onChange: (e) =>
|
|
121
|
+
children: A("export.history.filters.format.label", "Format")
|
|
122
|
+
}), /* @__PURE__ */ w("select", {
|
|
123
|
+
value: j.format,
|
|
124
|
+
onChange: (e) => M({ format: e.target.value }),
|
|
119
125
|
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",
|
|
120
126
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
127
|
+
/* @__PURE__ */ C("option", {
|
|
122
128
|
value: "all",
|
|
123
|
-
children:
|
|
129
|
+
children: A("export.history.filters.format.all", "All Formats")
|
|
124
130
|
}),
|
|
125
|
-
/* @__PURE__ */
|
|
131
|
+
/* @__PURE__ */ C("option", {
|
|
126
132
|
value: "CSV",
|
|
127
133
|
children: "CSV"
|
|
128
134
|
}),
|
|
129
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ C("option", {
|
|
130
136
|
value: "JSON",
|
|
131
137
|
children: "JSON"
|
|
132
138
|
})
|
|
@@ -136,28 +142,29 @@ function O() {
|
|
|
136
142
|
})]
|
|
137
143
|
})]
|
|
138
144
|
}),
|
|
139
|
-
Y && /* @__PURE__ */
|
|
145
|
+
Y && /* @__PURE__ */ w("div", {
|
|
140
146
|
className: "border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-6 flex items-start gap-4",
|
|
141
|
-
children: [/* @__PURE__ */
|
|
147
|
+
children: [/* @__PURE__ */ C(te, {
|
|
142
148
|
size: 24,
|
|
143
149
|
className: "text-status-error-text flex-shrink-0"
|
|
144
|
-
}), /* @__PURE__ */
|
|
145
|
-
/* @__PURE__ */
|
|
150
|
+
}), /* @__PURE__ */ w("div", { children: [
|
|
151
|
+
/* @__PURE__ */ C("h3", {
|
|
146
152
|
className: "font-semibold text-status-error-text mb-1",
|
|
147
|
-
children:
|
|
153
|
+
children: A("export.history.error.title", "Error loading exports")
|
|
148
154
|
}),
|
|
149
|
-
/* @__PURE__ */
|
|
155
|
+
/* @__PURE__ */ C("p", {
|
|
150
156
|
className: "text-sm text-status-error-text mb-3",
|
|
151
157
|
children: Y.message
|
|
152
158
|
}),
|
|
153
|
-
/* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ w("button", {
|
|
160
|
+
type: "button",
|
|
154
161
|
onClick: () => X(),
|
|
155
162
|
className: "inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-destructive text-destructive-foreground rounded-lg hover:bg-destructive/90 transition-colors",
|
|
156
|
-
children: [/* @__PURE__ */
|
|
163
|
+
children: [/* @__PURE__ */ C(v, { size: 14 }), A("export.history.error.retry", "Try Again")]
|
|
157
164
|
})
|
|
158
165
|
] })]
|
|
159
166
|
}),
|
|
160
|
-
J && /* @__PURE__ */
|
|
167
|
+
J && /* @__PURE__ */ C("div", {
|
|
161
168
|
className: "space-y-3",
|
|
162
169
|
children: [...[
|
|
163
170
|
,
|
|
@@ -165,97 +172,102 @@ function O() {
|
|
|
165
172
|
,
|
|
166
173
|
,
|
|
167
174
|
,
|
|
168
|
-
]].map((e, t) => /* @__PURE__ */
|
|
175
|
+
]].map((e, t) => /* @__PURE__ */ C("div", {
|
|
169
176
|
className: "border-2 border-border/50 rounded-xl p-4 animate-pulse",
|
|
170
|
-
children: /* @__PURE__ */
|
|
177
|
+
children: /* @__PURE__ */ w("div", {
|
|
171
178
|
className: "flex items-center justify-between",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
179
|
+
children: [/* @__PURE__ */ w("div", {
|
|
173
180
|
className: "space-y-2",
|
|
174
|
-
children: [/* @__PURE__ */
|
|
175
|
-
}), /* @__PURE__ */
|
|
181
|
+
children: [/* @__PURE__ */ C("div", { className: "h-4 w-48 bg-muted rounded" }), /* @__PURE__ */ C("div", { className: "h-3 w-32 bg-muted rounded" })]
|
|
182
|
+
}), /* @__PURE__ */ C("div", { className: "h-5 w-20 bg-muted rounded-full" })]
|
|
176
183
|
})
|
|
177
184
|
}, t))
|
|
178
185
|
}),
|
|
179
|
-
!J && !Y && (Z.length > 0 ? /* @__PURE__ */
|
|
186
|
+
!J && !Y && (Z.length > 0 ? /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C("div", {
|
|
180
187
|
className: "space-y-2",
|
|
181
|
-
children: Z.map((e, t) => /* @__PURE__ */
|
|
182
|
-
|
|
188
|
+
children: Z.map((e, t) => /* @__PURE__ */ C("button", {
|
|
189
|
+
type: "button",
|
|
190
|
+
onClick: () => re(e.id),
|
|
183
191
|
className: n("w-full text-left border-2 border-border/50 rounded-xl p-4", "hover:shadow-md hover:border-primary/30 transition-all duration-200", "group animate-in fade-in-0 slide-in-from-bottom-2"),
|
|
184
192
|
style: { animationDelay: `${t * 30}ms` },
|
|
185
|
-
children: /* @__PURE__ */
|
|
193
|
+
children: /* @__PURE__ */ w("div", {
|
|
186
194
|
className: "flex items-start sm:items-center justify-between gap-3",
|
|
187
|
-
children: [/* @__PURE__ */
|
|
195
|
+
children: [/* @__PURE__ */ w("div", {
|
|
188
196
|
className: "flex-1 min-w-0",
|
|
189
|
-
children: [/* @__PURE__ */
|
|
197
|
+
children: [/* @__PURE__ */ C("h4", {
|
|
190
198
|
className: "font-semibold text-foreground truncate",
|
|
191
199
|
children: e.name
|
|
192
|
-
}), /* @__PURE__ */
|
|
200
|
+
}), /* @__PURE__ */ w("p", {
|
|
193
201
|
className: "text-sm text-muted-foreground mt-1 line-clamp-2",
|
|
194
202
|
children: [
|
|
195
203
|
e.entityTypes.join(", "),
|
|
196
204
|
" ·",
|
|
197
205
|
" ",
|
|
198
|
-
|
|
206
|
+
ee(e.format),
|
|
199
207
|
e.createdAt && ` · ${new Date(e.createdAt).toLocaleDateString()}`
|
|
200
208
|
]
|
|
201
209
|
})]
|
|
202
|
-
}), /* @__PURE__ */
|
|
210
|
+
}), /* @__PURE__ */ w("div", {
|
|
203
211
|
className: "flex items-center gap-2 flex-shrink-0",
|
|
204
|
-
children: [/* @__PURE__ */
|
|
212
|
+
children: [/* @__PURE__ */ C(o, { status: e.status }), /* @__PURE__ */ C(m, {
|
|
205
213
|
size: 18,
|
|
206
214
|
className: "text-muted-foreground group-hover:text-primary transition-colors hidden sm:block"
|
|
207
215
|
})]
|
|
208
216
|
})]
|
|
209
217
|
})
|
|
210
218
|
}, e.id))
|
|
211
|
-
}), K >
|
|
219
|
+
}), K > P.pageSize && /* @__PURE__ */ w("div", {
|
|
212
220
|
className: "flex flex-col sm:flex-row items-center justify-between gap-3 pt-2",
|
|
213
|
-
children: [/* @__PURE__ */
|
|
221
|
+
children: [/* @__PURE__ */ C("p", {
|
|
214
222
|
className: "text-sm text-muted-foreground",
|
|
215
|
-
children:
|
|
216
|
-
start:
|
|
217
|
-
end:
|
|
223
|
+
children: A("export.history.pagination.showing", "Showing {start}-{end} of {total}", {
|
|
224
|
+
start: ce,
|
|
225
|
+
end: le,
|
|
218
226
|
total: K
|
|
219
227
|
})
|
|
220
|
-
}), /* @__PURE__ */
|
|
228
|
+
}), /* @__PURE__ */ w("div", {
|
|
221
229
|
className: "flex items-center gap-2",
|
|
222
230
|
children: [
|
|
223
|
-
/* @__PURE__ */
|
|
224
|
-
|
|
225
|
-
|
|
231
|
+
/* @__PURE__ */ w("button", {
|
|
232
|
+
type: "button",
|
|
233
|
+
onClick: ae,
|
|
234
|
+
disabled: !oe,
|
|
226
235
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border-2 border-border/50 rounded-lg hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed transition-colors font-medium",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
236
|
+
children: [/* @__PURE__ */ C(ne, { size: 16 }), A("export.history.pagination.prev", "Prev")]
|
|
228
237
|
}),
|
|
229
|
-
/* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ C("span", {
|
|
230
239
|
className: "text-sm text-muted-foreground px-2",
|
|
231
|
-
children:
|
|
240
|
+
children: A("export.history.pagination.page", "Page {page}", { page: P.page })
|
|
232
241
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
|
|
235
|
-
|
|
242
|
+
/* @__PURE__ */ w("button", {
|
|
243
|
+
type: "button",
|
|
244
|
+
onClick: ie,
|
|
245
|
+
disabled: !se,
|
|
236
246
|
className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border-2 border-border/50 rounded-lg hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed transition-colors font-medium",
|
|
237
|
-
children: [
|
|
247
|
+
children: [A("export.history.pagination.next", "Next"), /* @__PURE__ */ C(m, { size: 16 })]
|
|
238
248
|
})
|
|
239
249
|
]
|
|
240
250
|
})]
|
|
241
|
-
})] }) : /* @__PURE__ */
|
|
242
|
-
icon:
|
|
243
|
-
title: Q ?
|
|
244
|
-
description: Q ?
|
|
245
|
-
action: Q ? /* @__PURE__ */
|
|
251
|
+
})] }) : /* @__PURE__ */ C(s, {
|
|
252
|
+
icon: C(Q ? y : h, { size: 48 }),
|
|
253
|
+
title: Q ? A("export.history.empty.filtered.title", "No matching exports") : A("export.history.empty.title", "No exports yet"),
|
|
254
|
+
description: Q ? A("export.history.empty.filtered.description", "Try adjusting your search or filters.") : A("export.history.empty.description", "Create your first export to see it here."),
|
|
255
|
+
action: Q ? /* @__PURE__ */ w("button", {
|
|
256
|
+
type: "button",
|
|
246
257
|
onClick: $,
|
|
247
258
|
className: "inline-flex items-center gap-2 px-4 py-2 border-2 border-border rounded-lg hover:bg-accent transition-colors font-medium",
|
|
248
|
-
children: [/* @__PURE__ */
|
|
249
|
-
}) : /* @__PURE__ */
|
|
250
|
-
|
|
259
|
+
children: [/* @__PURE__ */ C(b, { size: 16 }), A("export.history.empty.filtered.actions.clear", "Clear Filters")]
|
|
260
|
+
}) : /* @__PURE__ */ w("button", {
|
|
261
|
+
type: "button",
|
|
262
|
+
onClick: () => O?.(E(D, "new")),
|
|
251
263
|
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",
|
|
252
|
-
children: [/* @__PURE__ */
|
|
264
|
+
children: [/* @__PURE__ */ C(g, { size: 16 }), A("export.history.empty.actions.createFirst", "Create First Export")]
|
|
253
265
|
})
|
|
254
266
|
}))
|
|
255
267
|
]
|
|
256
268
|
});
|
|
257
269
|
}
|
|
258
270
|
//#endregion
|
|
259
|
-
export {
|
|
271
|
+
export { D as ExportHistoryPage };
|
|
260
272
|
|
|
261
273
|
//# sourceMappingURL=ExportHistoryPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportHistoryPage.js","names":[],"sources":["../../../src/export/pages/ExportHistoryPage.tsx"],"sourcesContent":["import { useState, useMemo, useEffect } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport {\n Search,\n X,\n Clock,\n Download,\n ChevronRight,\n ChevronLeft,\n RefreshCw,\n AlertCircle,\n Filter,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport { useExportJobs } from \"../hooks/useExportQueries\";\nimport { useExportStore } from \"../store/exportStore\";\nimport { ExportStatusBadge } from \"../components/ExportStatusBadge\";\nimport { PageLayout, EmptyState } from \"../components/PageLayout\";\nimport { cn } from \"../utils/cn\";\nimport {\n getFormatDisplayName,\n getAllStatuses,\n getStatusDisplayName,\n} from \"../constants/enums\";\nimport type { ExportJobStatus, ExportFormat } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\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 History Page\n * Table/list of all past exports with filters and cursor-based pagination\n */\nexport function ExportHistoryPage() {\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 { jobFilter, updateFilter, resetFilter, pagination, setPagination } =\n useExportStore();\n const [searchParams] = useSearchParams();\n\n // Initialize status filter from URL search params (e.g. ?status=RUNNING)\n useEffect(() => {\n const statusParam = searchParams.get(\"status\");\n if (statusParam) {\n updateFilter({\n status: statusParam as ExportJobStatus,\n });\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const [showFilters, setShowFilters] = useState(false);\n // Cursor stack: index 0 = first page (no cursor), subsequent entries are \"after\" cursors\n const [cursorStack, setCursorStack] = useState<(string | null)[]>([null]);\n const [currentPageIndex, setCurrentPageIndex] = useState(0);\n\n const currentCursor = cursorStack[currentPageIndex] ?? null;\n\n const {\n data: jobs,\n totalCount,\n pageInfo,\n isLoading,\n error,\n refetch,\n } = useExportJobs(jobFilter, {\n first: pagination.pageSize,\n after: currentCursor ?? undefined,\n });\n\n // Client-side search filter (server-side filtering handles status/format)\n const filteredJobs = useMemo(() => {\n if (!jobFilter.search) return jobs;\n const q = jobFilter.search.toLowerCase();\n return jobs.filter(\n (job) =>\n job.name.toLowerCase().includes(q) ||\n job.entityTypes.some((e) => e.toLowerCase().includes(q)),\n );\n }, [jobs, jobFilter.search]);\n\n const hasActiveFilters =\n jobFilter.search ||\n jobFilter.status !== \"all\" ||\n jobFilter.format !== \"all\";\n\n const handleJobClick = (jobId: string) => {\n navigate?.(joinPath(basePath, jobId));\n };\n\n const handleNextPage = () => {\n if (!pageInfo?.hasNextPage || !pageInfo.endCursor) return;\n const newStack = [\n ...cursorStack.slice(0, currentPageIndex + 1),\n pageInfo.endCursor,\n ];\n setCursorStack(newStack);\n setCurrentPageIndex(currentPageIndex + 1);\n setPagination({ page: pagination.page + 1, cursor: pageInfo.endCursor });\n };\n\n const handlePrevPage = () => {\n if (currentPageIndex === 0) return;\n const prevIndex = currentPageIndex - 1;\n setCurrentPageIndex(prevIndex);\n setPagination({\n page: pagination.page - 1,\n cursor: cursorStack[prevIndex] ?? null,\n });\n };\n\n const handleResetFilters = () => {\n resetFilter();\n setCursorStack([null]);\n setCurrentPageIndex(0);\n };\n\n const hasPrevPage = currentPageIndex > 0;\n const hasNextPage = pageInfo?.hasNextPage ?? false;\n const pageStart = currentPageIndex * pagination.pageSize + 1;\n const pageEnd = Math.min(\n (currentPageIndex + 1) * pagination.pageSize,\n totalCount,\n );\n const getStatusLabel = (status: ExportJobStatus) =>\n tr(\n `export.shared.status.${status.toLowerCase()}`,\n getStatusDisplayName(status),\n );\n\n return (\n <PageLayout\n title={tr(\"export.history.title\", \"Export History\")}\n subtitle={tr(\n \"export.history.subtitle\",\n \"View and manage all your past export jobs\",\n )}\n icon={<Clock size={24} />}\n headerActions={\n <button\n onClick={() => navigate?.(joinPath(basePath, \"new\"))}\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 <Download size={16} />\n {tr(\"export.history.actions.newExport\", \"New Export\")}\n </button>\n }\n >\n {/* Search & Filters */}\n <div className=\"space-y-4\">\n <div className=\"flex flex-col sm:flex-row gap-3\">\n {/* Search */}\n <div className=\"flex-1 relative group\">\n <Search\n size={18}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground group-focus-within:text-primary transition-colors\"\n />\n <input\n type=\"text\"\n placeholder={tr(\n \"export.history.search.placeholder\",\n \"Search exports by name or entity type...\",\n )}\n value={jobFilter.search}\n onChange={(e) => updateFilter({ search: 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 </div>\n\n {/* Filter Toggle */}\n <button\n onClick={() => setShowFilters(!showFilters)}\n className={cn(\n \"inline-flex items-center gap-2 px-4 py-2.5 border-2 rounded-lg transition-all duration-200 font-medium\",\n showFilters || hasActiveFilters\n ? \"bg-primary text-primary-foreground border-primary\"\n : \"border-border/50 hover:border-primary/30\",\n )}\n >\n <Filter size={16} />\n {showFilters\n ? tr(\"export.history.filters.hide\", \"Hide\")\n : tr(\"export.history.filters.show\", \"Filter\")}\n </button>\n </div>\n\n {/* Filter Panel */}\n {showFilters && (\n <div className=\"border-2 border-border/50 rounded-xl p-4 animate-in slide-in-from-top-2 duration-200\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"font-semibold flex items-center gap-2\">\n <Filter size={16} className=\"text-primary\" />\n {tr(\"export.history.filters.title\", \"Filters\")}\n </h3>\n {hasActiveFilters && (\n <button\n onClick={handleResetFilters}\n className=\"text-sm text-muted-foreground hover:text-destructive flex items-center gap-1\"\n >\n <X size={14} />\n {tr(\"export.history.filters.clearAll\", \"Clear All\")}\n </button>\n )}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {/* Status Filter */}\n <div className=\"space-y-2\">\n <label className=\"block text-sm font-medium\">\n {tr(\"export.history.filters.status.label\", \"Status\")}\n </label>\n <select\n value={jobFilter.status}\n onChange={(e) =>\n updateFilter({\n status: e.target.value as ExportJobStatus | \"all\",\n })\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\"\n >\n <option value=\"all\">\n {tr(\"export.history.filters.status.all\", \"All Statuses\")}\n </option>\n {getAllStatuses().map((status) => (\n <option key={status} value={status}>\n {getStatusLabel(status)}\n </option>\n ))}\n </select>\n </div>\n\n {/* Format Filter — only CSV and JSON are supported */}\n <div className=\"space-y-2\">\n <label className=\"block text-sm font-medium\">\n {tr(\"export.history.filters.format.label\", \"Format\")}\n </label>\n <select\n value={jobFilter.format}\n onChange={(e) =>\n updateFilter({\n format: e.target.value as ExportFormat | \"all\",\n })\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\"\n >\n <option value=\"all\">\n {tr(\"export.history.filters.format.all\", \"All Formats\")}\n </option>\n <option value=\"CSV\">CSV</option>\n <option value=\"JSON\">JSON</option>\n </select>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Error State */}\n {error && (\n <div className=\"border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-6 flex items-start gap-4\">\n <AlertCircle\n size={24}\n className=\"text-status-error-text flex-shrink-0\"\n />\n <div>\n <h3 className=\"font-semibold text-status-error-text mb-1\">\n {tr(\"export.history.error.title\", \"Error loading exports\")}\n </h3>\n <p className=\"text-sm text-status-error-text mb-3\">\n {error.message}\n </p>\n <button\n onClick={() => refetch()}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-destructive text-destructive-foreground rounded-lg hover:bg-destructive/90 transition-colors\"\n >\n <RefreshCw size={14} />\n {tr(\"export.history.error.retry\", \"Try Again\")}\n </button>\n </div>\n </div>\n )}\n\n {/* Loading */}\n {isLoading && (\n <div className=\"space-y-3\">\n {[...Array(5)].map((_, i) => (\n <div\n key={i}\n className=\"border-2 border-border/50 rounded-xl p-4 animate-pulse\"\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-2\">\n <div className=\"h-4 w-48 bg-muted rounded\" />\n <div className=\"h-3 w-32 bg-muted rounded\" />\n </div>\n <div className=\"h-5 w-20 bg-muted rounded-full\" />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Jobs List */}\n {!isLoading &&\n !error &&\n (filteredJobs.length > 0 ? (\n <>\n <div className=\"space-y-2\">\n {filteredJobs.map((job, index) => (\n <button\n key={job.id}\n onClick={() => handleJobClick(job.id)}\n className={cn(\n \"w-full text-left border-2 border-border/50 rounded-xl p-4\",\n \"hover:shadow-md hover:border-primary/30 transition-all duration-200\",\n \"group animate-in fade-in-0 slide-in-from-bottom-2\",\n )}\n style={{ animationDelay: `${index * 30}ms` }}\n >\n <div className=\"flex items-start sm:items-center justify-between gap-3\">\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-semibold text-foreground truncate\">\n {job.name}\n </h4>\n <p className=\"text-sm text-muted-foreground mt-1 line-clamp-2\">\n {job.entityTypes.join(\", \")} ·{\" \"}\n {getFormatDisplayName(job.format)}\n {job.createdAt &&\n ` · ${new Date(job.createdAt).toLocaleDateString()}`}\n </p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <ExportStatusBadge status={job.status} />\n <ChevronRight\n size={18}\n className=\"text-muted-foreground group-hover:text-primary transition-colors hidden sm:block\"\n />\n </div>\n </div>\n </button>\n ))}\n </div>\n\n {/* Pagination */}\n {totalCount > pagination.pageSize && (\n <div className=\"flex flex-col sm:flex-row items-center justify-between gap-3 pt-2\">\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n \"export.history.pagination.showing\",\n \"Showing {start}-{end} of {total}\",\n {\n start: pageStart,\n end: pageEnd,\n total: totalCount,\n },\n )}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handlePrevPage}\n disabled={!hasPrevPage}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border-2 border-border/50 rounded-lg hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n <ChevronLeft size={16} />\n {tr(\"export.history.pagination.prev\", \"Prev\")}\n </button>\n <span className=\"text-sm text-muted-foreground px-2\">\n {tr(\"export.history.pagination.page\", \"Page {page}\", {\n page: pagination.page,\n })}\n </span>\n <button\n onClick={handleNextPage}\n disabled={!hasNextPage}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border-2 border-border/50 rounded-lg hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {tr(\"export.history.pagination.next\", \"Next\")}\n <ChevronRight size={16} />\n </button>\n </div>\n </div>\n )}\n </>\n ) : (\n <EmptyState\n icon={hasActiveFilters ? <Search size={48} /> : <Clock size={48} />}\n title={\n hasActiveFilters\n ? tr(\n \"export.history.empty.filtered.title\",\n \"No matching exports\",\n )\n : tr(\"export.history.empty.title\", \"No exports yet\")\n }\n description={\n hasActiveFilters\n ? tr(\n \"export.history.empty.filtered.description\",\n \"Try adjusting your search or filters.\",\n )\n : tr(\n \"export.history.empty.description\",\n \"Create your first export to see it here.\",\n )\n }\n action={\n hasActiveFilters ? (\n <button\n onClick={handleResetFilters}\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.history.empty.filtered.actions.clear\",\n \"Clear Filters\",\n )}\n </button>\n ) : (\n <button\n onClick={() => navigate?.(joinPath(basePath, \"new\"))}\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 <Download size={16} />\n {tr(\n \"export.history.empty.actions.createFirst\",\n \"Create First Export\",\n )}\n </button>\n )\n }\n />\n ))}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ExportHistoryPage.js","names":[],"sources":["../../../src/export/pages/ExportHistoryPage.tsx"],"sourcesContent":["import { useState, useMemo, useEffect, useRef } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport {\n Search,\n X,\n Clock,\n Download,\n ChevronRight,\n ChevronLeft,\n RefreshCw,\n AlertCircle,\n Filter,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport { useExportJobs } from \"../hooks/useExportQueries\";\nimport { useExportStore } from \"../store/exportStore\";\nimport { ExportStatusBadge } from \"../components/ExportStatusBadge\";\nimport { PageLayout, EmptyState } from \"../components/PageLayout\";\nimport { cn } from \"../utils/cn\";\nimport {\n getFormatDisplayName,\n getAllStatuses,\n getStatusDisplayName,\n} from \"../constants/enums\";\nimport type { ExportJobStatus, ExportFormat } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\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 History Page\n * Table/list of all past exports with filters and cursor-based pagination\n */\nexport function ExportHistoryPage() {\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 { jobFilter, updateFilter, resetFilter, pagination, setPagination } =\n useExportStore();\n const [searchParams] = useSearchParams();\n\n // Initialize status filter from URL search params (e.g. ?status=RUNNING) — once\n const didInitFromUrlRef = useRef(false);\n useEffect(() => {\n if (didInitFromUrlRef.current) return;\n didInitFromUrlRef.current = true;\n const statusParam = searchParams.get(\"status\");\n if (statusParam) {\n updateFilter({\n status: statusParam as ExportJobStatus,\n });\n }\n }, [searchParams, updateFilter]);\n\n const [showFilters, setShowFilters] = useState(false);\n // Cursor stack: index 0 = first page (no cursor), subsequent entries are \"after\" cursors\n const [cursorStack, setCursorStack] = useState<(string | null)[]>([null]);\n const [currentPageIndex, setCurrentPageIndex] = useState(0);\n\n const currentCursor = cursorStack[currentPageIndex] ?? null;\n\n const {\n data: jobs,\n totalCount,\n pageInfo,\n isLoading,\n error,\n refetch,\n } = useExportJobs(jobFilter, {\n first: pagination.pageSize,\n after: currentCursor ?? undefined,\n });\n\n // Client-side search filter (server-side filtering handles status/format)\n const filteredJobs = useMemo(() => {\n if (!jobFilter.search) return jobs;\n const q = jobFilter.search.toLowerCase();\n return jobs.filter(\n (job) =>\n job.name.toLowerCase().includes(q) ||\n job.entityTypes.some((e) => e.toLowerCase().includes(q)),\n );\n }, [jobs, jobFilter.search]);\n\n const hasActiveFilters =\n jobFilter.search ||\n jobFilter.status !== \"all\" ||\n jobFilter.format !== \"all\";\n\n const handleJobClick = (jobId: string) => {\n navigate?.(joinPath(basePath, jobId));\n };\n\n const handleNextPage = () => {\n if (!pageInfo?.hasNextPage || !pageInfo.endCursor) return;\n const newStack = [\n ...cursorStack.slice(0, currentPageIndex + 1),\n pageInfo.endCursor,\n ];\n setCursorStack(newStack);\n setCurrentPageIndex((prev) => prev + 1);\n setPagination({ page: pagination.page + 1, cursor: pageInfo.endCursor });\n };\n\n const handlePrevPage = () => {\n if (currentPageIndex === 0) return;\n const prevIndex = currentPageIndex - 1;\n setCurrentPageIndex(prevIndex);\n setPagination({\n page: pagination.page - 1,\n cursor: cursorStack[prevIndex] ?? null,\n });\n };\n\n const handleResetFilters = () => {\n resetFilter();\n setCursorStack([null]);\n setCurrentPageIndex(0);\n };\n\n const hasPrevPage = currentPageIndex > 0;\n const hasNextPage = pageInfo?.hasNextPage ?? false;\n const pageStart = currentPageIndex * pagination.pageSize + 1;\n const pageEnd = Math.min(\n (currentPageIndex + 1) * pagination.pageSize,\n totalCount,\n );\n const getStatusLabel = (status: ExportJobStatus) =>\n tr(\n `export.shared.status.${status.toLowerCase()}`,\n getStatusDisplayName(status),\n );\n\n return (\n <PageLayout\n title={tr(\"export.history.title\", \"Export History\")}\n subtitle={tr(\n \"export.history.subtitle\",\n \"View and manage all your past export jobs\",\n )}\n icon={<Clock size={24} />}\n headerActions={\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"new\"))}\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 <Download size={16} />\n {tr(\"export.history.actions.newExport\", \"New Export\")}\n </button>\n }\n >\n {/* Search & Filters */}\n <div className=\"space-y-4\">\n <div className=\"flex flex-col sm:flex-row gap-3\">\n {/* Search */}\n <div className=\"flex-1 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.history.search.placeholder\",\n \"Search exports by name or entity type\",\n )}\n placeholder={tr(\n \"export.history.search.placeholder\",\n \"Search exports by name or entity type...\",\n )}\n value={jobFilter.search}\n onChange={(e) => updateFilter({ search: 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 </div>\n\n {/* Filter Toggle */}\n <button\n type=\"button\"\n onClick={() => setShowFilters(!showFilters)}\n className={cn(\n \"inline-flex items-center gap-2 px-4 py-2.5 border-2 rounded-lg transition-all duration-200 font-medium\",\n showFilters || hasActiveFilters\n ? \"bg-primary text-primary-foreground border-primary\"\n : \"border-border/50 hover:border-primary/30\",\n )}\n >\n <Filter size={16} />\n {showFilters\n ? tr(\"export.history.filters.hide\", \"Hide\")\n : tr(\"export.history.filters.show\", \"Filter\")}\n </button>\n </div>\n\n {/* Filter Panel */}\n {showFilters && (\n <div className=\"border-2 border-border/50 rounded-xl p-4 animate-in slide-in-from-top-2 duration-200\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"font-semibold flex items-center gap-2\">\n <Filter size={16} className=\"text-primary\" />\n {tr(\"export.history.filters.title\", \"Filters\")}\n </h3>\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={handleResetFilters}\n className=\"text-sm text-muted-foreground hover:text-destructive flex items-center gap-1\"\n >\n <X size={14} />\n {tr(\"export.history.filters.clearAll\", \"Clear All\")}\n </button>\n )}\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {/* Status Filter */}\n <div className=\"space-y-2\">\n <label className=\"block text-sm font-medium\">\n {tr(\"export.history.filters.status.label\", \"Status\")}\n </label>\n <select\n value={jobFilter.status}\n onChange={(e) =>\n updateFilter({\n status: e.target.value as ExportJobStatus | \"all\",\n })\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\"\n >\n <option value=\"all\">\n {tr(\"export.history.filters.status.all\", \"All Statuses\")}\n </option>\n {getAllStatuses().map((status) => (\n <option key={status} value={status}>\n {getStatusLabel(status)}\n </option>\n ))}\n </select>\n </div>\n\n {/* Format Filter — only CSV and JSON are supported */}\n <div className=\"space-y-2\">\n <label className=\"block text-sm font-medium\">\n {tr(\"export.history.filters.format.label\", \"Format\")}\n </label>\n <select\n value={jobFilter.format}\n onChange={(e) =>\n updateFilter({\n format: e.target.value as ExportFormat | \"all\",\n })\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\"\n >\n <option value=\"all\">\n {tr(\"export.history.filters.format.all\", \"All Formats\")}\n </option>\n <option value=\"CSV\">CSV</option>\n <option value=\"JSON\">JSON</option>\n </select>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Error State */}\n {error && (\n <div className=\"border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-6 flex items-start gap-4\">\n <AlertCircle\n size={24}\n className=\"text-status-error-text flex-shrink-0\"\n />\n <div>\n <h3 className=\"font-semibold text-status-error-text mb-1\">\n {tr(\"export.history.error.title\", \"Error loading exports\")}\n </h3>\n <p className=\"text-sm text-status-error-text mb-3\">\n {error.message}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"inline-flex items-center gap-2 px-3 py-1.5 text-sm bg-destructive text-destructive-foreground rounded-lg hover:bg-destructive/90 transition-colors\"\n >\n <RefreshCw size={14} />\n {tr(\"export.history.error.retry\", \"Try Again\")}\n </button>\n </div>\n </div>\n )}\n\n {/* Loading */}\n {isLoading && (\n <div className=\"space-y-3\">\n {[...Array(5)].map((_, i) => (\n <div\n key={i}\n className=\"border-2 border-border/50 rounded-xl p-4 animate-pulse\"\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-2\">\n <div className=\"h-4 w-48 bg-muted rounded\" />\n <div className=\"h-3 w-32 bg-muted rounded\" />\n </div>\n <div className=\"h-5 w-20 bg-muted rounded-full\" />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Jobs List */}\n {!isLoading &&\n !error &&\n (filteredJobs.length > 0 ? (\n <>\n <div className=\"space-y-2\">\n {filteredJobs.map((job, index) => (\n <button\n type=\"button\"\n key={job.id}\n onClick={() => handleJobClick(job.id)}\n className={cn(\n \"w-full text-left border-2 border-border/50 rounded-xl p-4\",\n \"hover:shadow-md hover:border-primary/30 transition-all duration-200\",\n \"group animate-in fade-in-0 slide-in-from-bottom-2\",\n )}\n style={{ animationDelay: `${index * 30}ms` }}\n >\n <div className=\"flex items-start sm:items-center justify-between gap-3\">\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-semibold text-foreground truncate\">\n {job.name}\n </h4>\n <p className=\"text-sm text-muted-foreground mt-1 line-clamp-2\">\n {job.entityTypes.join(\", \")} ·{\" \"}\n {getFormatDisplayName(job.format)}\n {job.createdAt &&\n ` · ${new Date(job.createdAt).toLocaleDateString()}`}\n </p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <ExportStatusBadge status={job.status} />\n <ChevronRight\n size={18}\n className=\"text-muted-foreground group-hover:text-primary transition-colors hidden sm:block\"\n />\n </div>\n </div>\n </button>\n ))}\n </div>\n\n {/* Pagination */}\n {totalCount > pagination.pageSize && (\n <div className=\"flex flex-col sm:flex-row items-center justify-between gap-3 pt-2\">\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n \"export.history.pagination.showing\",\n \"Showing {start}-{end} of {total}\",\n {\n start: pageStart,\n end: pageEnd,\n total: totalCount,\n },\n )}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handlePrevPage}\n disabled={!hasPrevPage}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border-2 border-border/50 rounded-lg hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n <ChevronLeft size={16} />\n {tr(\"export.history.pagination.prev\", \"Prev\")}\n </button>\n <span className=\"text-sm text-muted-foreground px-2\">\n {tr(\"export.history.pagination.page\", \"Page {page}\", {\n page: pagination.page,\n })}\n </span>\n <button\n type=\"button\"\n onClick={handleNextPage}\n disabled={!hasNextPage}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border-2 border-border/50 rounded-lg hover:border-primary/30 disabled:opacity-40 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {tr(\"export.history.pagination.next\", \"Next\")}\n <ChevronRight size={16} />\n </button>\n </div>\n </div>\n )}\n </>\n ) : (\n <EmptyState\n icon={hasActiveFilters ? <Search size={48} /> : <Clock size={48} />}\n title={\n hasActiveFilters\n ? tr(\n \"export.history.empty.filtered.title\",\n \"No matching exports\",\n )\n : tr(\"export.history.empty.title\", \"No exports yet\")\n }\n description={\n hasActiveFilters\n ? tr(\n \"export.history.empty.filtered.description\",\n \"Try adjusting your search or filters.\",\n )\n : tr(\n \"export.history.empty.description\",\n \"Create your first export to see it here.\",\n )\n }\n action={\n hasActiveFilters ? (\n <button\n type=\"button\"\n onClick={handleResetFilters}\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.history.empty.filtered.actions.clear\",\n \"Clear Filters\",\n )}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"new\"))}\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 <Download size={16} />\n {tr(\n \"export.history.empty.actions.createFirst\",\n \"Create First Export\",\n )}\n </button>\n )\n }\n />\n ))}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|