@burdenoff/microfe-export 2026.527.1 → 2026.530.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/components/PageLayout.js +17 -24
- package/dist/export/components/PageLayout.js.map +1 -1
- package/dist/export/pages/CreateExportPage.js +215 -189
- package/dist/export/pages/CreateExportPage.js.map +1 -1
- package/dist/export/pages/ExportDashboardPage.js +3 -0
- package/dist/export/pages/ExportDashboardPage.js.map +1 -1
- package/dist/export/utils/disabledModules.js +23 -0
- package/dist/export/utils/disabledModules.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
2
|
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { EmptyState as r } from "@burdenoff/fe-libs/ui";
|
|
3
4
|
//#region src/export/components/PageLayout.tsx
|
|
4
|
-
var
|
|
5
|
+
var i = {
|
|
5
6
|
sm: "max-w-screen-sm",
|
|
6
7
|
md: "max-w-screen-md",
|
|
7
8
|
lg: "max-w-screen-lg",
|
|
@@ -10,11 +11,11 @@ var r = {
|
|
|
10
11
|
"3xl": "max-w-[1600px]",
|
|
11
12
|
full: "max-w-full"
|
|
12
13
|
};
|
|
13
|
-
function
|
|
14
|
+
function a({ children: r, title: a, subtitle: o, icon: s, headerActions: c, headerContent: l, maxWidth: u = "xl", showHeader: d = !0, className: f, contentClassName: p }) {
|
|
14
15
|
return /* @__PURE__ */ t("div", {
|
|
15
16
|
className: e("min-h-full w-full", "px-4 sm:px-6 lg:px-8 xl:px-12", "py-6 sm:py-8 lg:py-10", "animate-in fade-in-0 slide-in-from-bottom-4 duration-500", f),
|
|
16
17
|
children: /* @__PURE__ */ n("div", {
|
|
17
|
-
className: e("mx-auto w-full",
|
|
18
|
+
className: e("mx-auto w-full", i[u], p),
|
|
18
19
|
children: [d && (a || c) && /* @__PURE__ */ n("header", {
|
|
19
20
|
className: "mb-6 sm:mb-8",
|
|
20
21
|
children: [/* @__PURE__ */ n("div", {
|
|
@@ -44,14 +45,15 @@ function i({ children: i, title: a, subtitle: o, icon: s, headerActions: c, head
|
|
|
44
45
|
})]
|
|
45
46
|
}), /* @__PURE__ */ t("div", {
|
|
46
47
|
className: "space-y-6",
|
|
47
|
-
children:
|
|
48
|
+
children: r
|
|
48
49
|
})]
|
|
49
50
|
})
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
|
-
function
|
|
53
|
+
function o({ children: r, title: i, description: a, actions: o, className: s, ...c }) {
|
|
53
54
|
return /* @__PURE__ */ n("section", {
|
|
54
55
|
className: e("space-y-4", s),
|
|
56
|
+
...c,
|
|
55
57
|
children: [(i || o) && /* @__PURE__ */ n("div", {
|
|
56
58
|
className: "flex items-center justify-between",
|
|
57
59
|
children: [/* @__PURE__ */ n("div", { children: [i && /* @__PURE__ */ t("h2", {
|
|
@@ -67,27 +69,18 @@ function a({ children: r, title: i, description: a, actions: o, className: s })
|
|
|
67
69
|
}), r]
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
|
-
function
|
|
71
|
-
return /* @__PURE__ */
|
|
72
|
-
className:
|
|
73
|
-
children:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
className: "text-lg font-semibold text-foreground mb-1",
|
|
80
|
-
children: i
|
|
81
|
-
}),
|
|
82
|
-
a && /* @__PURE__ */ t("p", {
|
|
83
|
-
className: "text-sm text-muted-foreground max-w-sm mb-4",
|
|
84
|
-
children: a
|
|
85
|
-
}),
|
|
86
|
-
o
|
|
87
|
-
]
|
|
72
|
+
function s({ icon: e, title: n, description: i, action: a, className: o }) {
|
|
73
|
+
return /* @__PURE__ */ t("div", {
|
|
74
|
+
className: o,
|
|
75
|
+
children: /* @__PURE__ */ t(r, {
|
|
76
|
+
icon: e,
|
|
77
|
+
title: n,
|
|
78
|
+
description: i,
|
|
79
|
+
action: a
|
|
80
|
+
})
|
|
88
81
|
});
|
|
89
82
|
}
|
|
90
83
|
//#endregion
|
|
91
|
-
export {
|
|
84
|
+
export { s as EmptyState, a as PageLayout, o as PageSection };
|
|
92
85
|
|
|
93
86
|
//# sourceMappingURL=PageLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/export/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\ninterface PageLayoutProps {\n children: ReactNode;\n /** Page title */\n title?: string;\n /** Page subtitle/description */\n subtitle?: string;\n /** Icon component to display next to title */\n icon?: ReactNode;\n /** Actions to display in header (buttons, etc.) */\n headerActions?: ReactNode;\n /** Additional header content below title */\n headerContent?: ReactNode;\n /** Maximum width of content: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full' */\n maxWidth?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"full\";\n /** Whether to show the header section */\n showHeader?: boolean;\n /** Additional CSS classes for the container */\n className?: string;\n /** Additional CSS classes for the content area */\n contentClassName?: string;\n}\n\nconst maxWidthClasses = {\n sm: \"max-w-screen-sm\", // 640px\n md: \"max-w-screen-md\", // 768px\n lg: \"max-w-screen-lg\", // 1024px\n xl: \"max-w-screen-xl\", // 1280px\n \"2xl\": \"max-w-screen-2xl\", // 1536px\n \"3xl\": \"max-w-[1600px]\",\n full: \"max-w-full\",\n};\n\n/**\n * PageLayout - A consistent page wrapper with responsive design\n *\n * Features:\n * - Proper max-width constraints\n * - Responsive padding\n * - Animated entrance\n * - Consistent header styling\n */\nexport function PageLayout({\n children,\n title,\n subtitle,\n icon,\n headerActions,\n headerContent,\n maxWidth = \"xl\",\n showHeader = true,\n className,\n contentClassName,\n}: PageLayoutProps) {\n return (\n <div\n className={cn(\n // Base styles - full width container with padding\n \"min-h-full w-full\",\n // Responsive horizontal padding\n \"px-4 sm:px-6 lg:px-8 xl:px-12\",\n // Responsive vertical padding\n \"py-6 sm:py-8 lg:py-10\",\n // Entrance animation\n \"animate-in fade-in-0 slide-in-from-bottom-4 duration-500\",\n className,\n )}\n >\n {/* Max-width constrained content container */}\n <div\n className={cn(\n \"mx-auto w-full\",\n maxWidthClasses[maxWidth],\n contentClassName,\n )}\n >\n {/* Page Header */}\n {showHeader && (title || headerActions) && (\n <header className=\"mb-6 sm:mb-8\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n {/* Title Section */}\n <div className=\"space-y-1\">\n {(title || icon) && (\n <div className=\"flex items-center gap-3\">\n {icon && (\n <div className=\"flex-shrink-0 p-2.5 rounded-xl bg-primary/10 text-primary\">\n {icon}\n </div>\n )}\n {title && (\n <h1 className=\"text-2xl sm:text-3xl font-bold tracking-tight text-foreground\">\n {title}\n </h1>\n )}\n </div>\n )}\n {subtitle && (\n <p className=\"text-sm sm:text-base text-muted-foreground max-w-2xl\">\n {subtitle}\n </p>\n )}\n </div>\n\n {/* Header Actions */}\n {headerActions && (\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {headerActions}\n </div>\n )}\n </div>\n\n {/* Additional Header Content */}\n {headerContent && <div className=\"mt-4\">{headerContent}</div>}\n </header>\n )}\n\n {/* Page content — must not use a <main> landmark here because the\n host shell (AppShellLayout) already renders one; nesting <main>\n elements is invalid HTML and an a11y violation. */}\n <div className=\"space-y-6\">{children}</div>\n </div>\n </div>\n );\n}\n\n/**\n * PageSection - A section within a page with consistent spacing\n */\nexport function PageSection({\n children,\n title,\n description,\n actions,\n className,\n}: {\n children: ReactNode;\n title?: string;\n description?: string;\n actions?: ReactNode;\n className?: string;\n}) {\n return (\n <section className={cn(\"space-y-4\", className)}>\n {(title || actions) && (\n <div className=\"flex items-center justify-between\">\n <div>\n {title && (\n <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>\n )}\n {description && (\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n {actions && <div className=\"flex items-center gap-2\">{actions}</div>}\n </div>\n )}\n {children}\n </section>\n );\n}\n\n/**\n * EmptyState -
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/export/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { EmptyState as CanonicalEmptyState } from \"@burdenoff/fe-libs/ui\";\nimport { cn } from \"../utils/cn\";\n\ninterface PageLayoutProps {\n children: ReactNode;\n /** Page title */\n title?: string;\n /** Page subtitle/description */\n subtitle?: string;\n /** Icon component to display next to title */\n icon?: ReactNode;\n /** Actions to display in header (buttons, etc.) */\n headerActions?: ReactNode;\n /** Additional header content below title */\n headerContent?: ReactNode;\n /** Maximum width of content: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full' */\n maxWidth?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\" | \"full\";\n /** Whether to show the header section */\n showHeader?: boolean;\n /** Additional CSS classes for the container */\n className?: string;\n /** Additional CSS classes for the content area */\n contentClassName?: string;\n}\n\nconst maxWidthClasses = {\n sm: \"max-w-screen-sm\", // 640px\n md: \"max-w-screen-md\", // 768px\n lg: \"max-w-screen-lg\", // 1024px\n xl: \"max-w-screen-xl\", // 1280px\n \"2xl\": \"max-w-screen-2xl\", // 1536px\n \"3xl\": \"max-w-[1600px]\",\n full: \"max-w-full\",\n};\n\n/**\n * PageLayout - A consistent page wrapper with responsive design\n *\n * Features:\n * - Proper max-width constraints\n * - Responsive padding\n * - Animated entrance\n * - Consistent header styling\n */\nexport function PageLayout({\n children,\n title,\n subtitle,\n icon,\n headerActions,\n headerContent,\n maxWidth = \"xl\",\n showHeader = true,\n className,\n contentClassName,\n}: PageLayoutProps) {\n return (\n <div\n className={cn(\n // Base styles - full width container with padding\n \"min-h-full w-full\",\n // Responsive horizontal padding\n \"px-4 sm:px-6 lg:px-8 xl:px-12\",\n // Responsive vertical padding\n \"py-6 sm:py-8 lg:py-10\",\n // Entrance animation\n \"animate-in fade-in-0 slide-in-from-bottom-4 duration-500\",\n className,\n )}\n >\n {/* Max-width constrained content container */}\n <div\n className={cn(\n \"mx-auto w-full\",\n maxWidthClasses[maxWidth],\n contentClassName,\n )}\n >\n {/* Page Header */}\n {showHeader && (title || headerActions) && (\n <header className=\"mb-6 sm:mb-8\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n {/* Title Section */}\n <div className=\"space-y-1\">\n {(title || icon) && (\n <div className=\"flex items-center gap-3\">\n {icon && (\n <div className=\"flex-shrink-0 p-2.5 rounded-xl bg-primary/10 text-primary\">\n {icon}\n </div>\n )}\n {title && (\n <h1 className=\"text-2xl sm:text-3xl font-bold tracking-tight text-foreground\">\n {title}\n </h1>\n )}\n </div>\n )}\n {subtitle && (\n <p className=\"text-sm sm:text-base text-muted-foreground max-w-2xl\">\n {subtitle}\n </p>\n )}\n </div>\n\n {/* Header Actions */}\n {headerActions && (\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {headerActions}\n </div>\n )}\n </div>\n\n {/* Additional Header Content */}\n {headerContent && <div className=\"mt-4\">{headerContent}</div>}\n </header>\n )}\n\n {/* Page content — must not use a <main> landmark here because the\n host shell (AppShellLayout) already renders one; nesting <main>\n elements is invalid HTML and an a11y violation. */}\n <div className=\"space-y-6\">{children}</div>\n </div>\n </div>\n );\n}\n\n/**\n * PageSection - A section within a page with consistent spacing\n */\nexport function PageSection({\n children,\n title,\n description,\n actions,\n className,\n ...rest\n}: {\n children: ReactNode;\n title?: string;\n description?: string;\n actions?: ReactNode;\n className?: string;\n} & React.HTMLAttributes<HTMLElement>) {\n return (\n <section className={cn(\"space-y-4\", className)} {...rest}>\n {(title || actions) && (\n <div className=\"flex items-center justify-between\">\n <div>\n {title && (\n <h2 className=\"text-lg font-semibold text-foreground\">{title}</h2>\n )}\n {description && (\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n {actions && <div className=\"flex items-center gap-2\">{actions}</div>}\n </div>\n )}\n {children}\n </section>\n );\n}\n\n/**\n * Local wrapper around the canonical `EmptyState` from `@burdenoff/fe-libs/ui`.\n * Keeps the existing microfe-export call sites stable while routing all\n * empty-state rendering through the shared primitive.\n */\nexport function EmptyState({\n icon,\n title,\n description,\n action,\n className,\n}: {\n icon?: ReactNode;\n title: string;\n description?: string;\n action?: ReactNode;\n className?: string;\n}) {\n return (\n <div className={className}>\n <CanonicalEmptyState\n icon={icon}\n title={title}\n description={description}\n action={action}\n />\n </div>\n );\n}\n"],"mappings":";;;;AA0BA,IAAM,IAAkB;CACtB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,MAAM;CACP;AAWD,SAAgB,EAAW,EACzB,aACA,UACA,aACA,SACA,kBACA,kBACA,cAAW,MACX,gBAAa,IACb,cACA,uBACkB;AAClB,QACE,kBAAC,OAAD;EACE,WAAW,EAET,qBAEA,iCAEA,yBAEA,4DACA,EACD;YAGD,kBAAC,OAAD;GACE,WAAW,EACT,kBACA,EAAgB,IAChB,EACD;aALH,CAQG,MAAe,KAAS,MACvB,kBAAC,UAAD;IAAQ,WAAU;cAAlB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,EACI,KAAS,MACT,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,KACC,kBAAC,OAAD;QAAK,WAAU;kBACZ;QACG,CAAA,EAEP,KACC,kBAAC,MAAD;QAAI,WAAU;kBACX;QACE,CAAA,CAEH;UAEP,KACC,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA,CAEF;SAGL,KACC,kBAAC,OAAD;MAAK,WAAU;gBACZ;MACG,CAAA,CAEJ;QAGL,KAAiB,kBAAC,OAAD;KAAK,WAAU;eAAQ;KAAoB,CAAA,CACtD;OAMX,kBAAC,OAAD;IAAK,WAAU;IAAa;IAAe,CAAA,CACvC;;EACF,CAAA;;AAOV,SAAgB,EAAY,EAC1B,aACA,UACA,gBACA,YACA,cACA,GAAG,KAOkC;AACrC,QACE,kBAAC,WAAD;EAAS,WAAW,EAAG,aAAa,EAAU;EAAE,GAAI;YAApD,EACI,KAAS,MACT,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACG,KACC,kBAAC,MAAD;IAAI,WAAU;cAAyC;IAAW,CAAA,EAEnE,KACC,kBAAC,KAAD;IAAG,WAAU;cAAiC;IAAgB,CAAA,CAE5D,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;IAAK,WAAU;cAA2B;IAAc,CAAA,CAChE;MAEP,EACO;;;AASd,SAAgB,EAAW,EACzB,SACA,UACA,gBACA,WACA,gBAOC;AACD,QACE,kBAAC,OAAD;EAAgB;YACd,kBAAC,GAAD;GACQ;GACC;GACM;GACL;GACR,CAAA;EACE,CAAA"}
|
|
@@ -2,19 +2,20 @@ import { useExport as e } from "../providers/ExportProvider.js";
|
|
|
2
2
|
import { useAvailableEntities as t, useCreateExportJob as n, useExportTemplates as r } from "../hooks/useExportQueries.js";
|
|
3
3
|
import { cn as i } from "../utils/cn.js";
|
|
4
4
|
import { getFormatDisplayName as a } from "../constants/enums.js";
|
|
5
|
-
import { tWithFallback as
|
|
6
|
-
import { PageLayout as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { tWithFallback as ee } from "../utils/i18n.js";
|
|
6
|
+
import { PageLayout as te } from "../components/PageLayout.js";
|
|
7
|
+
import { isExportEntityDisabled as o } from "../utils/disabledModules.js";
|
|
8
|
+
import { useEffect as ne, useRef as s, useState as c } from "react";
|
|
9
|
+
import { ArrowLeft as l, ArrowRight as u, Building2 as d, CalendarRange as re, CheckCircle2 as f, Download as p, FileJson as m, FileSpreadsheet as h, Loader2 as g, Lock as _, Package as ie, User as v, Users as y } from "lucide-react";
|
|
9
10
|
import { usePermission as b } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
10
|
-
import { useSearchParams as
|
|
11
|
-
import { jsx as
|
|
12
|
-
import { useI18n as
|
|
11
|
+
import { useSearchParams as ae } from "react-router-dom";
|
|
12
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
13
|
+
import { useI18n as oe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
14
|
//#region src/export/pages/CreateExportPage.tsx
|
|
14
|
-
var
|
|
15
|
+
var C = ["CSV", "JSON"], se = {
|
|
15
16
|
CSV: h,
|
|
16
17
|
JSON: m
|
|
17
|
-
},
|
|
18
|
+
}, w = [
|
|
18
19
|
{
|
|
19
20
|
value: "INDIVIDUAL",
|
|
20
21
|
label: "My Data Only",
|
|
@@ -34,65 +35,65 @@ var T = ["CSV", "JSON"], ie = {
|
|
|
34
35
|
Icon: d
|
|
35
36
|
}
|
|
36
37
|
];
|
|
37
|
-
function
|
|
38
|
-
let { basePath: d = "/", navigate: m, organizationId: h } = e(), { t: v } =
|
|
39
|
-
|
|
40
|
-
let e =
|
|
41
|
-
if (!e || !
|
|
42
|
-
let t =
|
|
38
|
+
function T() {
|
|
39
|
+
let { basePath: d = "/", navigate: m, organizationId: h } = e(), { t: v } = oe(), y = (e, t, n) => ee(v, e, t, n), { createJob: T, isPending: E } = n(), { data: ce } = t(), { data: D } = r(), [O] = ae(), k = b("export:workspace:admin"), le = b("export:org:admin"), [A, j] = c("entities"), [M, N] = c([]), [P, F] = c(""), [I, L] = c("CSV"), [R, z] = c("INDIVIDUAL"), [B, V] = c(O.get("templateId") ?? void 0), [H, U] = c(""), [W, ue] = c(""), [G, K] = c(null), q = s(null);
|
|
40
|
+
ne(() => {
|
|
41
|
+
let e = O.get("templateId");
|
|
42
|
+
if (!e || !D?.length || q.current === e) return;
|
|
43
|
+
let t = D.find((t) => t.id === e);
|
|
43
44
|
if (!t) return;
|
|
44
|
-
|
|
45
|
+
q.current = e, V(t.id), N((t.entityTypes ?? []).filter((e) => !o(e)));
|
|
45
46
|
let n = t.format?.toUpperCase();
|
|
46
|
-
n &&
|
|
47
|
-
}, [
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
if (
|
|
52
|
-
if (
|
|
53
|
-
|
|
47
|
+
n && C.includes(n) && L(n), t.name && F(t.name);
|
|
48
|
+
}, [O, D]);
|
|
49
|
+
let de = () => {
|
|
50
|
+
A === "options" ? j("entities") : A === "review" ? j("options") : m?.(d);
|
|
51
|
+
}, fe = () => {
|
|
52
|
+
if (A === "entities") {
|
|
53
|
+
if (M.length === 0) {
|
|
54
|
+
K(y("export.create.errors.selectEntity", "Please select at least one entity type to export"));
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
-
} else if (
|
|
58
|
-
if (!
|
|
59
|
-
|
|
57
|
+
K(null), j("options");
|
|
58
|
+
} else if (A === "options") {
|
|
59
|
+
if (!P.trim()) {
|
|
60
|
+
K(y("export.create.errors.exportNameRequired", "Please provide a name for this export"));
|
|
60
61
|
return;
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
+
K(null), j("review");
|
|
63
64
|
}
|
|
64
|
-
},
|
|
65
|
+
}, pe = async () => {
|
|
65
66
|
try {
|
|
66
|
-
|
|
67
|
-
name:
|
|
68
|
-
entityTypes:
|
|
69
|
-
format:
|
|
70
|
-
exportScope:
|
|
71
|
-
organizationId:
|
|
72
|
-
templateId:
|
|
73
|
-
dateRangeStart:
|
|
74
|
-
dateRangeEnd:
|
|
67
|
+
K(null), await T({
|
|
68
|
+
name: P,
|
|
69
|
+
entityTypes: M,
|
|
70
|
+
format: I,
|
|
71
|
+
exportScope: R,
|
|
72
|
+
organizationId: R === "ORG_ADMIN" ? h : void 0,
|
|
73
|
+
templateId: B,
|
|
74
|
+
dateRangeStart: H ? new Date(H).toISOString() : null,
|
|
75
|
+
dateRangeEnd: W ? new Date(W).toISOString() : null
|
|
75
76
|
}), m?.(d);
|
|
76
77
|
} catch (e) {
|
|
77
|
-
|
|
78
|
+
K(e instanceof Error ? e.message : y("export.create.errors.createFailed", "Failed to create export"));
|
|
78
79
|
}
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
},
|
|
80
|
+
}, me = (e) => {
|
|
81
|
+
o(e) || N((t) => t.includes(e) ? t.filter((t) => t !== e) : [...t, e]);
|
|
82
|
+
}, J = (e) => {
|
|
82
83
|
switch (e) {
|
|
83
84
|
case "INDIVIDUAL": return y("export.create.scope.individual.label", "My Data Only");
|
|
84
85
|
case "WORKSPACE_ADMIN": return y("export.create.scope.workspaceAdmin.label", "All Workspace Data");
|
|
85
86
|
case "ORG_ADMIN": return y("export.create.scope.orgAdmin.label", "All Organization Data");
|
|
86
87
|
default: return e;
|
|
87
88
|
}
|
|
88
|
-
},
|
|
89
|
+
}, he = (e) => {
|
|
89
90
|
switch (e) {
|
|
90
91
|
case "INDIVIDUAL": return y("export.create.scope.individual.description", "Export data you personally created across selected modules.");
|
|
91
92
|
case "WORKSPACE_ADMIN": return y("export.create.scope.workspaceAdmin.description", "Export all data in this workspace regardless of who created it. Requires workspace admin access.");
|
|
92
93
|
case "ORG_ADMIN": return y("export.create.scope.orgAdmin.description", "Export data from every workspace in your organization. Requires org-admin access.");
|
|
93
94
|
default: return "";
|
|
94
95
|
}
|
|
95
|
-
},
|
|
96
|
+
}, Y = {
|
|
96
97
|
users: {
|
|
97
98
|
label: y("export.create.entities.users.label", "Users"),
|
|
98
99
|
description: y("export.create.entities.users.description", "Workspace members and profiles")
|
|
@@ -125,300 +126,325 @@ function E() {
|
|
|
125
126
|
label: y("export.create.entities.calendar.label", "Calendar Events"),
|
|
126
127
|
description: y("export.create.entities.calendar.description", "Events and bookings")
|
|
127
128
|
}
|
|
128
|
-
},
|
|
129
|
-
|
|
129
|
+
}, X = ce.filter((e) => e.enabled !== !1), Z = X.filter((e) => !o(e.entityType)), Q = X.filter((e) => o(e.entityType)), $ = (e, t) => {
|
|
130
|
+
let n = !t && M.includes(e.entityType);
|
|
131
|
+
return /* @__PURE__ */ S("button", {
|
|
132
|
+
type: "button",
|
|
133
|
+
onClick: () => me(e.entityType),
|
|
134
|
+
disabled: t,
|
|
135
|
+
"aria-disabled": t,
|
|
136
|
+
title: t ? y("export.create.select.unavailableHint", "Export for this product isn't available yet") : void 0,
|
|
137
|
+
className: i("text-left border-2 rounded-xl p-4 transition-all duration-200", t ? "border-border/30 opacity-50 cursor-not-allowed" : n ? "border-primary bg-primary/5 shadow-md" : "border-border/50 hover:border-primary/30 hover:shadow-sm"),
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ S("div", {
|
|
140
|
+
className: "flex items-center justify-between mb-2 gap-2",
|
|
141
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
142
|
+
className: "font-semibold",
|
|
143
|
+
children: Y[e.entityType]?.label ?? e.displayName
|
|
144
|
+
}), t ? /* @__PURE__ */ x("span", {
|
|
145
|
+
className: "shrink-0 text-xs font-medium px-2 py-0.5 rounded-full bg-muted text-muted-foreground",
|
|
146
|
+
children: y("export.create.select.unavailable", "Unavailable")
|
|
147
|
+
}) : n && /* @__PURE__ */ x(f, {
|
|
148
|
+
size: 18,
|
|
149
|
+
className: "text-primary"
|
|
150
|
+
})]
|
|
151
|
+
}),
|
|
152
|
+
/* @__PURE__ */ x("p", {
|
|
153
|
+
className: "text-sm text-muted-foreground",
|
|
154
|
+
children: Y[e.entityType]?.description ?? e.description
|
|
155
|
+
}),
|
|
156
|
+
e.estimatedSize && /* @__PURE__ */ S("p", {
|
|
157
|
+
className: "text-xs text-muted-foreground mt-2",
|
|
158
|
+
children: ["~", e.estimatedSize]
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
}, e.entityType);
|
|
162
|
+
};
|
|
163
|
+
return /* @__PURE__ */ S(te, {
|
|
130
164
|
showHeader: !1,
|
|
131
165
|
children: [
|
|
132
|
-
/* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ x("div", {
|
|
133
167
|
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",
|
|
134
|
-
children: /* @__PURE__ */
|
|
168
|
+
children: /* @__PURE__ */ S("div", {
|
|
135
169
|
className: "relative z-10 flex items-center gap-4",
|
|
136
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ x("button", {
|
|
137
171
|
type: "button",
|
|
138
172
|
onClick: () => m?.(d),
|
|
139
173
|
className: "p-2.5 hover:bg-accent rounded-lg transition-all duration-200 hover:scale-110",
|
|
140
174
|
"aria-label": y("export.create.actions.backToDashboard", "Back to export dashboard"),
|
|
141
|
-
children: /* @__PURE__ */
|
|
142
|
-
}), /* @__PURE__ */
|
|
175
|
+
children: /* @__PURE__ */ x(l, { size: 20 })
|
|
176
|
+
}), /* @__PURE__ */ S("div", {
|
|
143
177
|
className: "flex items-center gap-3",
|
|
144
|
-
children: [/* @__PURE__ */
|
|
178
|
+
children: [/* @__PURE__ */ x("div", {
|
|
145
179
|
className: "p-2.5 rounded-lg bg-primary/10 backdrop-blur-sm",
|
|
146
|
-
children: /* @__PURE__ */
|
|
180
|
+
children: /* @__PURE__ */ x(p, {
|
|
147
181
|
size: 24,
|
|
148
182
|
className: "text-primary"
|
|
149
183
|
})
|
|
150
|
-
}), /* @__PURE__ */
|
|
184
|
+
}), /* @__PURE__ */ x("h1", {
|
|
151
185
|
className: "text-2xl sm:text-3xl font-bold",
|
|
152
186
|
children: y("export.create.title", "New Export")
|
|
153
187
|
})]
|
|
154
188
|
})]
|
|
155
189
|
})
|
|
156
190
|
}),
|
|
157
|
-
/* @__PURE__ */
|
|
191
|
+
/* @__PURE__ */ x("div", {
|
|
158
192
|
className: "flex items-center gap-2 justify-center flex-wrap",
|
|
159
193
|
children: [
|
|
160
194
|
"entities",
|
|
161
195
|
"options",
|
|
162
196
|
"review"
|
|
163
|
-
].map((e, t) => /* @__PURE__ */
|
|
197
|
+
].map((e, t) => /* @__PURE__ */ S("div", {
|
|
164
198
|
className: "flex items-center gap-2",
|
|
165
|
-
children: [t > 0 && /* @__PURE__ */
|
|
199
|
+
children: [t > 0 && /* @__PURE__ */ x(u, {
|
|
166
200
|
size: 18,
|
|
167
201
|
className: "text-muted-foreground"
|
|
168
|
-
}), /* @__PURE__ */
|
|
169
|
-
className: i("flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium border-2 transition-all duration-200",
|
|
170
|
-
children: [/* @__PURE__ */
|
|
202
|
+
}), /* @__PURE__ */ S("div", {
|
|
203
|
+
className: i("flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium border-2 transition-all duration-200", A === e ? "bg-primary text-primary-foreground border-primary shadow-md scale-105" : "bg-muted/50 text-muted-foreground border-border/50"),
|
|
204
|
+
children: [/* @__PURE__ */ x("div", {
|
|
171
205
|
className: "flex items-center justify-center size-6 rounded-full bg-background/20 font-bold",
|
|
172
206
|
children: t + 1
|
|
173
|
-
}), /* @__PURE__ */
|
|
207
|
+
}), /* @__PURE__ */ x("span", {
|
|
174
208
|
className: "hidden sm:inline capitalize",
|
|
175
209
|
children: e === "entities" ? y("export.create.steps.entities", "Select Data") : e === "options" ? y("export.create.steps.options", "Options") : y("export.create.steps.review", "Review")
|
|
176
210
|
})]
|
|
177
211
|
})]
|
|
178
212
|
}, e))
|
|
179
213
|
}),
|
|
180
|
-
|
|
214
|
+
G && /* @__PURE__ */ x("div", {
|
|
181
215
|
className: "border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-4 text-status-error-text text-sm",
|
|
182
|
-
children:
|
|
216
|
+
children: G
|
|
183
217
|
}),
|
|
184
|
-
|
|
218
|
+
A === "entities" && /* @__PURE__ */ S("div", {
|
|
185
219
|
className: "space-y-4",
|
|
186
220
|
children: [
|
|
187
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ x("h2", {
|
|
188
222
|
className: "text-lg font-bold",
|
|
189
223
|
children: y("export.create.select.title", "Select data to export")
|
|
190
224
|
}),
|
|
191
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ x("p", {
|
|
192
226
|
className: "text-sm text-muted-foreground",
|
|
193
227
|
children: y("export.create.select.description", "Choose which entity types to include in your export.")
|
|
194
228
|
}),
|
|
195
|
-
|
|
229
|
+
X.length === 0 ? /* @__PURE__ */ x("div", {
|
|
196
230
|
className: "border-2 border-border rounded-xl bg-muted/30 p-6 text-sm text-muted-foreground",
|
|
197
231
|
children: y("export.create.select.empty", "No exportable entity types are configured for this workspace yet.")
|
|
198
232
|
}) : /* @__PURE__ */ S("div", {
|
|
199
|
-
className: "
|
|
200
|
-
children:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
233
|
+
className: "space-y-6",
|
|
234
|
+
children: [Z.length > 0 && /* @__PURE__ */ x("div", {
|
|
235
|
+
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",
|
|
236
|
+
children: Z.map((e) => $(e, !1))
|
|
237
|
+
}), Q.length > 0 && /* @__PURE__ */ S("div", {
|
|
238
|
+
className: "space-y-3",
|
|
239
|
+
children: [/* @__PURE__ */ S("div", {
|
|
240
|
+
className: "flex items-center gap-3",
|
|
206
241
|
children: [
|
|
207
|
-
/* @__PURE__ */
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
children: Q[e.entityType]?.label ?? e.displayName
|
|
212
|
-
}), t && /* @__PURE__ */ S(f, {
|
|
213
|
-
size: 18,
|
|
214
|
-
className: "text-primary"
|
|
215
|
-
})]
|
|
216
|
-
}),
|
|
217
|
-
/* @__PURE__ */ S("p", {
|
|
218
|
-
className: "text-sm text-muted-foreground",
|
|
219
|
-
children: Q[e.entityType]?.description ?? e.description
|
|
242
|
+
/* @__PURE__ */ x("div", { className: "h-px flex-1 bg-border/60" }),
|
|
243
|
+
/* @__PURE__ */ x("span", {
|
|
244
|
+
className: "text-xs font-medium uppercase tracking-wide text-muted-foreground",
|
|
245
|
+
children: y("export.create.select.unavailableSection", "Not available yet")
|
|
220
246
|
}),
|
|
221
|
-
|
|
222
|
-
className: "text-xs text-muted-foreground mt-2",
|
|
223
|
-
children: ["~", e.estimatedSize]
|
|
224
|
-
})
|
|
247
|
+
/* @__PURE__ */ x("div", { className: "h-px flex-1 bg-border/60" })
|
|
225
248
|
]
|
|
226
|
-
},
|
|
227
|
-
|
|
249
|
+
}), /* @__PURE__ */ x("div", {
|
|
250
|
+
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3",
|
|
251
|
+
children: Q.map((e) => $(e, !0))
|
|
252
|
+
})]
|
|
253
|
+
})]
|
|
228
254
|
})
|
|
229
255
|
]
|
|
230
256
|
}),
|
|
231
|
-
|
|
257
|
+
A === "options" && /* @__PURE__ */ x("div", {
|
|
232
258
|
className: "space-y-6",
|
|
233
|
-
children: /* @__PURE__ */
|
|
259
|
+
children: /* @__PURE__ */ S("div", {
|
|
234
260
|
className: "border-2 border-border/50 rounded-xl p-6 space-y-5",
|
|
235
261
|
children: [
|
|
236
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ x("h2", {
|
|
237
263
|
className: "text-lg font-bold",
|
|
238
264
|
children: y("export.create.options.title", "Export Options")
|
|
239
265
|
}),
|
|
240
|
-
/* @__PURE__ */
|
|
266
|
+
/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("label", {
|
|
241
267
|
className: "block text-sm font-semibold mb-2",
|
|
242
268
|
children: y("export.create.options.scope", "Export Scope")
|
|
243
|
-
}), /* @__PURE__ */
|
|
269
|
+
}), /* @__PURE__ */ x("div", {
|
|
244
270
|
className: "grid grid-cols-1 sm:grid-cols-3 gap-3",
|
|
245
|
-
children:
|
|
246
|
-
let n = e === "WORKSPACE_ADMIN" && !
|
|
247
|
-
return /* @__PURE__ */
|
|
271
|
+
children: w.map(({ value: e, Icon: t }) => {
|
|
272
|
+
let n = e === "WORKSPACE_ADMIN" && !k || e === "ORG_ADMIN" && !le;
|
|
273
|
+
return /* @__PURE__ */ S("button", {
|
|
248
274
|
type: "button",
|
|
249
|
-
onClick: () => !n &&
|
|
275
|
+
onClick: () => !n && z(e),
|
|
250
276
|
disabled: n,
|
|
251
|
-
className: i("text-left border-2 rounded-xl p-4 transition-all duration-200",
|
|
277
|
+
className: i("text-left border-2 rounded-xl p-4 transition-all duration-200", R === e ? "border-primary bg-primary/5 shadow-md" : n ? "border-border/30 opacity-50 cursor-not-allowed" : "border-border/50 hover:border-primary/30 hover:shadow-sm"),
|
|
252
278
|
title: n ? e === "WORKSPACE_ADMIN" ? "Workspace admin access required" : "Organization admin access required" : void 0,
|
|
253
279
|
children: [
|
|
254
|
-
/* @__PURE__ */
|
|
280
|
+
/* @__PURE__ */ S("div", {
|
|
255
281
|
className: "flex items-center justify-between mb-2",
|
|
256
282
|
children: [
|
|
257
|
-
/* @__PURE__ */
|
|
283
|
+
/* @__PURE__ */ x(t, {
|
|
258
284
|
size: 18,
|
|
259
|
-
className:
|
|
285
|
+
className: R === e ? "text-primary" : n ? "text-muted-foreground/50" : "text-muted-foreground"
|
|
260
286
|
}),
|
|
261
|
-
|
|
287
|
+
R === e && /* @__PURE__ */ x(f, {
|
|
262
288
|
size: 16,
|
|
263
289
|
className: "text-primary"
|
|
264
290
|
}),
|
|
265
|
-
n && /* @__PURE__ */
|
|
291
|
+
n && /* @__PURE__ */ x(_, {
|
|
266
292
|
size: 14,
|
|
267
293
|
className: "text-muted-foreground/50"
|
|
268
294
|
})
|
|
269
295
|
]
|
|
270
296
|
}),
|
|
271
|
-
/* @__PURE__ */
|
|
297
|
+
/* @__PURE__ */ x("p", {
|
|
272
298
|
className: "font-semibold text-sm",
|
|
273
|
-
children:
|
|
299
|
+
children: J(e)
|
|
274
300
|
}),
|
|
275
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ x("p", {
|
|
276
302
|
className: "text-xs text-muted-foreground mt-1",
|
|
277
|
-
children:
|
|
303
|
+
children: he(e)
|
|
278
304
|
})
|
|
279
305
|
]
|
|
280
306
|
}, e);
|
|
281
307
|
})
|
|
282
308
|
})] }),
|
|
283
|
-
/* @__PURE__ */
|
|
309
|
+
/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ S("label", {
|
|
284
310
|
htmlFor: "exportName",
|
|
285
311
|
className: "block text-sm font-semibold mb-2",
|
|
286
312
|
children: [
|
|
287
313
|
y("export.create.options.name", "Export Name"),
|
|
288
314
|
" ",
|
|
289
|
-
/* @__PURE__ */
|
|
315
|
+
/* @__PURE__ */ x("span", {
|
|
290
316
|
className: "text-status-error-text",
|
|
291
317
|
children: "*"
|
|
292
318
|
})
|
|
293
319
|
]
|
|
294
|
-
}), /* @__PURE__ */
|
|
320
|
+
}), /* @__PURE__ */ x("input", {
|
|
295
321
|
type: "text",
|
|
296
322
|
id: "exportName",
|
|
297
323
|
"aria-label": y("export.create.options.name", "Export Name"),
|
|
298
|
-
value:
|
|
299
|
-
onChange: (e) =>
|
|
324
|
+
value: P,
|
|
325
|
+
onChange: (e) => F(e.target.value),
|
|
300
326
|
className: "w-full px-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",
|
|
301
327
|
placeholder: y("export.create.options.namePlaceholder", "e.g., Monthly User Export")
|
|
302
328
|
})] }),
|
|
303
|
-
/* @__PURE__ */
|
|
329
|
+
/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("label", {
|
|
304
330
|
className: "block text-sm font-semibold mb-2",
|
|
305
331
|
children: y("export.create.options.outputFormat", "Output Format")
|
|
306
|
-
}), /* @__PURE__ */
|
|
332
|
+
}), /* @__PURE__ */ x("div", {
|
|
307
333
|
className: "grid grid-cols-2 gap-3",
|
|
308
|
-
children:
|
|
309
|
-
let t =
|
|
310
|
-
return /* @__PURE__ */
|
|
334
|
+
children: C.map((e) => {
|
|
335
|
+
let t = se[e];
|
|
336
|
+
return /* @__PURE__ */ S("button", {
|
|
311
337
|
type: "button",
|
|
312
|
-
onClick: () =>
|
|
313
|
-
className: i("flex items-center gap-3 border-2 rounded-lg p-3 transition-all duration-200",
|
|
314
|
-
children: [/* @__PURE__ */
|
|
338
|
+
onClick: () => L(e),
|
|
339
|
+
className: i("flex items-center gap-3 border-2 rounded-lg p-3 transition-all duration-200", I === e ? "border-primary bg-primary/5" : "border-border/50 hover:border-primary/30"),
|
|
340
|
+
children: [/* @__PURE__ */ x(t, {
|
|
315
341
|
size: 18,
|
|
316
|
-
className:
|
|
317
|
-
}), /* @__PURE__ */
|
|
342
|
+
className: I === e ? "text-primary" : "text-muted-foreground"
|
|
343
|
+
}), /* @__PURE__ */ x("span", {
|
|
318
344
|
className: "font-medium text-sm",
|
|
319
345
|
children: a(e)
|
|
320
346
|
})]
|
|
321
347
|
}, e);
|
|
322
348
|
})
|
|
323
349
|
})] }),
|
|
324
|
-
/* @__PURE__ */
|
|
325
|
-
/* @__PURE__ */
|
|
350
|
+
/* @__PURE__ */ S("div", { children: [
|
|
351
|
+
/* @__PURE__ */ x("label", {
|
|
326
352
|
className: "block text-sm font-semibold mb-2",
|
|
327
353
|
children: y("export.create.options.archiveFormat", "Archive Format")
|
|
328
354
|
}),
|
|
329
|
-
/* @__PURE__ */
|
|
355
|
+
/* @__PURE__ */ x("div", {
|
|
330
356
|
className: "flex gap-3",
|
|
331
|
-
children: /* @__PURE__ */
|
|
357
|
+
children: /* @__PURE__ */ S("div", {
|
|
332
358
|
className: i("flex items-center gap-2 border-2 rounded-lg px-4 py-2 border-primary bg-primary/5"),
|
|
333
|
-
children: [/* @__PURE__ */
|
|
359
|
+
children: [/* @__PURE__ */ x(ie, {
|
|
334
360
|
size: 16,
|
|
335
361
|
className: "text-primary"
|
|
336
|
-
}), /* @__PURE__ */
|
|
362
|
+
}), /* @__PURE__ */ x("span", {
|
|
337
363
|
className: "font-medium text-sm",
|
|
338
364
|
children: "ZIP"
|
|
339
365
|
})]
|
|
340
366
|
})
|
|
341
367
|
}),
|
|
342
|
-
/* @__PURE__ */
|
|
368
|
+
/* @__PURE__ */ x("p", {
|
|
343
369
|
className: "text-xs text-muted-foreground mt-1.5",
|
|
344
370
|
children: y("export.create.options.archiveDescription", "Exports are packaged as ZIP archives.")
|
|
345
371
|
})
|
|
346
372
|
] }),
|
|
347
|
-
/* @__PURE__ */
|
|
348
|
-
/* @__PURE__ */
|
|
373
|
+
/* @__PURE__ */ S("div", { children: [
|
|
374
|
+
/* @__PURE__ */ x("label", {
|
|
349
375
|
className: "block text-sm font-semibold mb-2",
|
|
350
|
-
children: /* @__PURE__ */
|
|
376
|
+
children: /* @__PURE__ */ S("span", {
|
|
351
377
|
className: "inline-flex items-center gap-1.5",
|
|
352
378
|
children: [
|
|
353
|
-
/* @__PURE__ */
|
|
379
|
+
/* @__PURE__ */ x(re, { size: 15 }),
|
|
354
380
|
y("export.create.options.dateRange", "Date Range"),
|
|
355
381
|
" ",
|
|
356
|
-
/* @__PURE__ */
|
|
382
|
+
/* @__PURE__ */ x("span", {
|
|
357
383
|
className: "font-normal text-muted-foreground",
|
|
358
384
|
children: y("export.create.shared.optional", "(optional)")
|
|
359
385
|
})
|
|
360
386
|
]
|
|
361
387
|
})
|
|
362
388
|
}),
|
|
363
|
-
/* @__PURE__ */
|
|
389
|
+
/* @__PURE__ */ S("div", {
|
|
364
390
|
className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
|
|
365
|
-
children: [/* @__PURE__ */
|
|
391
|
+
children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("label", {
|
|
366
392
|
htmlFor: "dateFrom",
|
|
367
393
|
className: "block text-xs text-muted-foreground mb-1",
|
|
368
394
|
children: y("export.create.options.dateFrom", "From")
|
|
369
|
-
}), /* @__PURE__ */
|
|
395
|
+
}), /* @__PURE__ */ x("input", {
|
|
370
396
|
type: "date",
|
|
371
397
|
id: "dateFrom",
|
|
372
398
|
"aria-label": y("export.create.options.dateFrom", "From"),
|
|
373
|
-
value:
|
|
374
|
-
onChange: (e) =>
|
|
399
|
+
value: H,
|
|
400
|
+
onChange: (e) => U(e.target.value),
|
|
375
401
|
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 text-sm"
|
|
376
|
-
})] }), /* @__PURE__ */
|
|
402
|
+
})] }), /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("label", {
|
|
377
403
|
htmlFor: "dateTo",
|
|
378
404
|
className: "block text-xs text-muted-foreground mb-1",
|
|
379
405
|
children: y("export.create.options.dateTo", "To")
|
|
380
|
-
}), /* @__PURE__ */
|
|
406
|
+
}), /* @__PURE__ */ x("input", {
|
|
381
407
|
type: "date",
|
|
382
408
|
id: "dateTo",
|
|
383
409
|
"aria-label": y("export.create.options.dateTo", "To"),
|
|
384
|
-
value:
|
|
385
|
-
min:
|
|
386
|
-
onChange: (e) =>
|
|
410
|
+
value: W,
|
|
411
|
+
min: H,
|
|
412
|
+
onChange: (e) => ue(e.target.value),
|
|
387
413
|
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 text-sm"
|
|
388
414
|
})] })]
|
|
389
415
|
}),
|
|
390
|
-
/* @__PURE__ */
|
|
416
|
+
/* @__PURE__ */ x("p", {
|
|
391
417
|
className: "text-xs text-muted-foreground mt-1.5",
|
|
392
418
|
children: y("export.create.options.dateRangeDescription", "Leave blank to export all data regardless of date.")
|
|
393
419
|
})
|
|
394
420
|
] }),
|
|
395
|
-
/* @__PURE__ */
|
|
421
|
+
/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ S("label", {
|
|
396
422
|
className: "block text-sm font-semibold mb-2",
|
|
397
423
|
children: [
|
|
398
424
|
y("export.create.options.basedOnTemplate", "Based on Template"),
|
|
399
425
|
" ",
|
|
400
|
-
/* @__PURE__ */
|
|
426
|
+
/* @__PURE__ */ x("span", {
|
|
401
427
|
className: "font-normal text-muted-foreground",
|
|
402
428
|
children: y("export.create.shared.optional", "(optional)")
|
|
403
429
|
})
|
|
404
430
|
]
|
|
405
|
-
}),
|
|
406
|
-
value:
|
|
407
|
-
onChange: (e) =>
|
|
431
|
+
}), D.length > 0 ? /* @__PURE__ */ S("select", {
|
|
432
|
+
value: B ?? "",
|
|
433
|
+
onChange: (e) => V(e.target.value || void 0),
|
|
408
434
|
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",
|
|
409
|
-
children: [/* @__PURE__ */
|
|
435
|
+
children: [/* @__PURE__ */ x("option", {
|
|
410
436
|
value: "",
|
|
411
437
|
children: y("export.create.options.noTemplate", "No template")
|
|
412
|
-
}),
|
|
438
|
+
}), D.map((e) => /* @__PURE__ */ x("option", {
|
|
413
439
|
value: e.id,
|
|
414
440
|
children: e.name
|
|
415
441
|
}, e.id))]
|
|
416
|
-
}) : /* @__PURE__ */
|
|
442
|
+
}) : /* @__PURE__ */ S("p", {
|
|
417
443
|
className: "text-sm text-muted-foreground",
|
|
418
444
|
children: [
|
|
419
445
|
y("export.create.options.noTemplates", "No templates yet."),
|
|
420
446
|
" ",
|
|
421
|
-
/* @__PURE__ */
|
|
447
|
+
/* @__PURE__ */ x("button", {
|
|
422
448
|
type: "button",
|
|
423
449
|
onClick: () => m?.(d + "/templates"),
|
|
424
450
|
className: "text-primary underline-offset-2 hover:underline",
|
|
@@ -431,53 +457,53 @@ function E() {
|
|
|
431
457
|
]
|
|
432
458
|
})
|
|
433
459
|
}),
|
|
434
|
-
|
|
460
|
+
A === "review" && /* @__PURE__ */ x("div", {
|
|
435
461
|
className: "space-y-6",
|
|
436
|
-
children: /* @__PURE__ */
|
|
462
|
+
children: /* @__PURE__ */ S("div", {
|
|
437
463
|
className: "border-2 border-primary/50 rounded-xl bg-primary/5 p-6 space-y-4",
|
|
438
|
-
children: [/* @__PURE__ */
|
|
464
|
+
children: [/* @__PURE__ */ S("h2", {
|
|
439
465
|
className: "text-lg font-bold flex items-center gap-2",
|
|
440
|
-
children: [/* @__PURE__ */
|
|
466
|
+
children: [/* @__PURE__ */ x(f, {
|
|
441
467
|
size: 20,
|
|
442
468
|
className: "text-primary"
|
|
443
469
|
}), y("export.create.review.title", "Review Export")]
|
|
444
|
-
}), /* @__PURE__ */
|
|
470
|
+
}), /* @__PURE__ */ x("dl", {
|
|
445
471
|
className: "space-y-3 text-sm",
|
|
446
472
|
children: [
|
|
447
473
|
{
|
|
448
474
|
label: y("export.create.review.fields.name", "Name"),
|
|
449
|
-
value:
|
|
475
|
+
value: P
|
|
450
476
|
},
|
|
451
477
|
{
|
|
452
478
|
label: y("export.create.review.fields.scope", "Scope"),
|
|
453
|
-
value:
|
|
479
|
+
value: J(R)
|
|
454
480
|
},
|
|
455
481
|
{
|
|
456
482
|
label: y("export.create.review.fields.entityTypes", "Entity Types"),
|
|
457
|
-
value:
|
|
483
|
+
value: M.join(", ")
|
|
458
484
|
},
|
|
459
485
|
{
|
|
460
486
|
label: y("export.create.review.fields.format", "Format"),
|
|
461
|
-
value: a(
|
|
487
|
+
value: a(I)
|
|
462
488
|
},
|
|
463
489
|
{
|
|
464
490
|
label: y("export.create.review.fields.archive", "Archive"),
|
|
465
491
|
value: "ZIP"
|
|
466
492
|
},
|
|
467
|
-
...
|
|
493
|
+
...H || W ? [{
|
|
468
494
|
label: y("export.create.review.fields.dateRange", "Date Range"),
|
|
469
|
-
value: [
|
|
495
|
+
value: [H || "—", W || "—"].join(" → ")
|
|
470
496
|
}] : [],
|
|
471
|
-
...
|
|
497
|
+
...B ? [{
|
|
472
498
|
label: y("export.create.review.fields.template", "Template"),
|
|
473
|
-
value:
|
|
499
|
+
value: D.find((e) => e.id === B)?.name ?? B
|
|
474
500
|
}] : []
|
|
475
|
-
].map(({ label: e, value: t }) => /* @__PURE__ */
|
|
501
|
+
].map(({ label: e, value: t }) => /* @__PURE__ */ S("div", {
|
|
476
502
|
className: "flex flex-col sm:flex-row sm:justify-between gap-1",
|
|
477
|
-
children: [/* @__PURE__ */
|
|
503
|
+
children: [/* @__PURE__ */ x("dt", {
|
|
478
504
|
className: "text-muted-foreground",
|
|
479
505
|
children: e
|
|
480
|
-
}), /* @__PURE__ */
|
|
506
|
+
}), /* @__PURE__ */ x("dd", {
|
|
481
507
|
className: "font-semibold sm:text-right break-words",
|
|
482
508
|
children: t
|
|
483
509
|
})]
|
|
@@ -485,33 +511,33 @@ function E() {
|
|
|
485
511
|
})]
|
|
486
512
|
})
|
|
487
513
|
}),
|
|
488
|
-
/* @__PURE__ */
|
|
514
|
+
/* @__PURE__ */ S("div", {
|
|
489
515
|
className: "flex flex-col sm:flex-row gap-3 pt-2",
|
|
490
|
-
children: [/* @__PURE__ */
|
|
516
|
+
children: [/* @__PURE__ */ x("button", {
|
|
491
517
|
type: "button",
|
|
492
|
-
onClick:
|
|
518
|
+
onClick: de,
|
|
493
519
|
className: "px-6 py-3 border-2 border-border/50 rounded-lg hover:bg-accent hover:border-primary/30 transition-all duration-200 font-medium",
|
|
494
|
-
children:
|
|
495
|
-
}),
|
|
520
|
+
children: A === "entities" ? y("export.create.actions.cancel", "Cancel") : y("export.create.actions.back", "Back")
|
|
521
|
+
}), A === "review" ? /* @__PURE__ */ S("button", {
|
|
496
522
|
type: "button",
|
|
497
|
-
onClick:
|
|
498
|
-
disabled:
|
|
523
|
+
onClick: pe,
|
|
524
|
+
disabled: E,
|
|
499
525
|
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 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
500
|
-
children: [
|
|
526
|
+
children: [E ? /* @__PURE__ */ x(g, {
|
|
501
527
|
size: 18,
|
|
502
528
|
className: "animate-spin"
|
|
503
|
-
}) : /* @__PURE__ */
|
|
504
|
-
}) : /* @__PURE__ */
|
|
529
|
+
}) : /* @__PURE__ */ x(p, { size: 18 }), y("export.create.actions.startExport", "Start Export")]
|
|
530
|
+
}) : /* @__PURE__ */ S("button", {
|
|
505
531
|
type: "button",
|
|
506
|
-
onClick:
|
|
532
|
+
onClick: fe,
|
|
507
533
|
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",
|
|
508
|
-
children: [y("export.create.actions.next", "Next"), /* @__PURE__ */
|
|
534
|
+
children: [y("export.create.actions.next", "Next"), /* @__PURE__ */ x(u, { size: 18 })]
|
|
509
535
|
})]
|
|
510
536
|
})
|
|
511
537
|
]
|
|
512
538
|
});
|
|
513
539
|
}
|
|
514
540
|
//#endregion
|
|
515
|
-
export {
|
|
541
|
+
export { T as CreateExportPage };
|
|
516
542
|
|
|
517
543
|
//# sourceMappingURL=CreateExportPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateExportPage.js","names":[],"sources":["../../../src/export/pages/CreateExportPage.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport {\n ArrowLeft,\n ArrowRight,\n Download,\n CheckCircle2,\n Loader2,\n FileSpreadsheet,\n FileJson,\n Package,\n CalendarRange,\n User,\n Users,\n Building2,\n Lock,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { usePermission } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useCreateExportJob,\n useAvailableEntities,\n useExportTemplates,\n} from \"../hooks/useExportQueries\";\nimport { getFormatDisplayName } from \"../constants/enums\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { cn } from \"../utils/cn\";\nimport type { ExportFormat, ExportScope } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ntype WizardStep = \"entities\" | \"options\" | \"review\";\n\n// Backend only supports CSV and JSON\nconst SUPPORTED_FORMATS: ExportFormat[] = [\"CSV\", \"JSON\"];\n\nconst FORMAT_ICONS: Record<ExportFormat, typeof FileJson> = {\n CSV: FileSpreadsheet,\n JSON: FileJson,\n};\n\n/**\n * Create Export Page\n * Multi-step form: select entities, configure options, review & start\n */\nconst SCOPE_OPTIONS: Array<{\n value: ExportScope;\n label: string;\n description: string;\n Icon: typeof User;\n}> = [\n {\n value: \"INDIVIDUAL\",\n label: \"My Data Only\",\n description: \"Export data you personally created across selected modules.\",\n Icon: User,\n },\n {\n value: \"WORKSPACE_ADMIN\",\n label: \"All Workspace Data\",\n description:\n \"Export all data in this workspace regardless of who created it. Requires workspace admin access.\",\n Icon: Users,\n },\n {\n value: \"ORG_ADMIN\",\n label: \"All Organization Data\",\n description:\n \"Export data from every workspace in your organization. Requires org-admin access.\",\n Icon: Building2,\n },\n];\n\nexport function CreateExportPage() {\n const { basePath = \"/\", navigate, organizationId } = 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 { createJob, isPending } = useCreateExportJob();\n const { data: availableEntities } = useAvailableEntities();\n const { data: templates } = useExportTemplates();\n const [searchParams] = useSearchParams();\n\n // RBAC: Check permissions for admin scopes\n const canExportWorkspaceAdmin = usePermission(\"export:workspace:admin\");\n const canExportOrgAdmin = usePermission(\"export:org:admin\");\n\n const [step, setStep] = useState<WizardStep>(\"entities\");\n const [selectedEntities, setSelectedEntities] = useState<string[]>([]);\n const [exportName, setExportName] = useState(\"\");\n const [selectedFormat, setSelectedFormat] = useState<ExportFormat>(\"CSV\");\n const [selectedScope, setSelectedScope] = useState<ExportScope>(\"INDIVIDUAL\");\n const [selectedTemplateId, setSelectedTemplateId] = useState<\n string | undefined\n >(searchParams.get(\"templateId\") ?? undefined);\n const [dateRangeStart, setDateRangeStart] = useState(\"\");\n const [dateRangeEnd, setDateRangeEnd] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n // Pre-populate form when arriving from \"Use Template\" — apply only once per templateId\n const appliedTemplateRef = useRef<string | null>(null);\n useEffect(() => {\n const paramTemplateId = searchParams.get(\"templateId\");\n if (!paramTemplateId || !templates?.length) return;\n if (appliedTemplateRef.current === paramTemplateId) return;\n const tpl = templates.find((t) => t.id === paramTemplateId);\n if (!tpl) return;\n appliedTemplateRef.current = paramTemplateId;\n setSelectedTemplateId(tpl.id);\n setSelectedEntities(tpl.entityTypes ?? []);\n const normalizedFormat = tpl.format?.toUpperCase() as\n | ExportFormat\n | undefined;\n if (normalizedFormat && SUPPORTED_FORMATS.includes(normalizedFormat)) {\n setSelectedFormat(normalizedFormat);\n }\n if (tpl.name) setExportName(tpl.name);\n }, [searchParams, templates]);\n\n const handleBack = () => {\n if (step === \"options\") setStep(\"entities\");\n else if (step === \"review\") setStep(\"options\");\n else navigate?.(basePath);\n };\n\n const handleNext = () => {\n if (step === \"entities\") {\n if (selectedEntities.length === 0) {\n setError(\n tr(\n \"export.create.errors.selectEntity\",\n \"Please select at least one entity type to export\",\n ),\n );\n return;\n }\n setError(null);\n setStep(\"options\");\n } else if (step === \"options\") {\n if (!exportName.trim()) {\n setError(\n tr(\n \"export.create.errors.exportNameRequired\",\n \"Please provide a name for this export\",\n ),\n );\n return;\n }\n setError(null);\n setStep(\"review\");\n }\n };\n\n const handleSubmit = async () => {\n try {\n setError(null);\n await createJob({\n name: exportName,\n entityTypes: selectedEntities,\n format: selectedFormat,\n exportScope: selectedScope,\n organizationId:\n selectedScope === \"ORG_ADMIN\" ? organizationId : undefined,\n templateId: selectedTemplateId,\n dateRangeStart: dateRangeStart\n ? new Date(dateRangeStart).toISOString()\n : null,\n dateRangeEnd: dateRangeEnd\n ? new Date(dateRangeEnd).toISOString()\n : null,\n });\n navigate?.(basePath);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\"export.create.errors.createFailed\", \"Failed to create export\"),\n );\n }\n };\n\n const toggleEntity = (entityType: string) => {\n setSelectedEntities((prev) =>\n prev.includes(entityType)\n ? prev.filter((e) => e !== entityType)\n : [...prev, entityType],\n );\n };\n\n const getScopeLabel = (scope: ExportScope) => {\n switch (scope) {\n case \"INDIVIDUAL\":\n return tr(\"export.create.scope.individual.label\", \"My Data Only\");\n case \"WORKSPACE_ADMIN\":\n return tr(\n \"export.create.scope.workspaceAdmin.label\",\n \"All Workspace Data\",\n );\n case \"ORG_ADMIN\":\n return tr(\n \"export.create.scope.orgAdmin.label\",\n \"All Organization Data\",\n );\n default:\n return scope;\n }\n };\n\n const getScopeDescription = (scope: ExportScope) => {\n switch (scope) {\n case \"INDIVIDUAL\":\n return tr(\n \"export.create.scope.individual.description\",\n \"Export data you personally created across selected modules.\",\n );\n case \"WORKSPACE_ADMIN\":\n return tr(\n \"export.create.scope.workspaceAdmin.description\",\n \"Export all data in this workspace regardless of who created it. Requires workspace admin access.\",\n );\n case \"ORG_ADMIN\":\n return tr(\n \"export.create.scope.orgAdmin.description\",\n \"Export data from every workspace in your organization. Requires org-admin access.\",\n );\n default:\n return \"\";\n }\n };\n\n const fallbackEntityCopy: Record<\n string,\n { label: string; description: string }\n > = {\n users: {\n label: tr(\"export.create.entities.users.label\", \"Users\"),\n description: tr(\n \"export.create.entities.users.description\",\n \"Workspace members and profiles\",\n ),\n },\n projects: {\n label: tr(\"export.create.entities.projects.label\", \"Projects\"),\n description: tr(\n \"export.create.entities.projects.description\",\n \"Projects and their metadata\",\n ),\n },\n tasks: {\n label: tr(\"export.create.entities.tasks.label\", \"Tasks\"),\n description: tr(\n \"export.create.entities.tasks.description\",\n \"Task items and assignments\",\n ),\n },\n files: {\n label: tr(\"export.create.entities.files.label\", \"Files\"),\n description: tr(\n \"export.create.entities.files.description\",\n \"File metadata and references\",\n ),\n },\n conversations: {\n label: tr(\"export.create.entities.conversations.label\", \"Conversations\"),\n description: tr(\n \"export.create.entities.conversations.description\",\n \"Chat messages and threads\",\n ),\n },\n tags: {\n label: tr(\"export.create.entities.tags.label\", \"Tags\"),\n description: tr(\n \"export.create.entities.tags.description\",\n \"Tags and vocabularies\",\n ),\n },\n activity: {\n label: tr(\"export.create.entities.activity.label\", \"Activity Logs\"),\n description: tr(\n \"export.create.entities.activity.description\",\n \"Audit trail and activity history\",\n ),\n },\n calendar: {\n label: tr(\"export.create.entities.calendar.label\", \"Calendar Events\"),\n description: tr(\n \"export.create.entities.calendar.description\",\n \"Events and bookings\",\n ),\n },\n };\n\n // Only show enabled entities\n const enabledEntities = availableEntities.filter((e) => e.enabled !== false);\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?.(basePath)}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200 hover:scale-110\"\n aria-label={tr(\n \"export.create.actions.backToDashboard\",\n \"Back to export dashboard\",\n )}\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2.5 rounded-lg bg-primary/10 backdrop-blur-sm\">\n <Download size={24} className=\"text-primary\" />\n </div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tr(\"export.create.title\", \"New Export\")}\n </h1>\n </div>\n </div>\n </div>\n\n {/* Step Indicator */}\n <div className=\"flex items-center gap-2 justify-center flex-wrap\">\n {([\"entities\", \"options\", \"review\"] as WizardStep[]).map((s, i) => (\n <div key={s} className=\"flex items-center gap-2\">\n {i > 0 && (\n <ArrowRight size={18} className=\"text-muted-foreground\" />\n )}\n <div\n className={cn(\n \"flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium border-2 transition-all duration-200\",\n step === s\n ? \"bg-primary text-primary-foreground border-primary shadow-md scale-105\"\n : \"bg-muted/50 text-muted-foreground border-border/50\",\n )}\n >\n <div className=\"flex items-center justify-center size-6 rounded-full bg-background/20 font-bold\">\n {i + 1}\n </div>\n <span className=\"hidden sm:inline capitalize\">\n {s === \"entities\"\n ? tr(\"export.create.steps.entities\", \"Select Data\")\n : s === \"options\"\n ? tr(\"export.create.steps.options\", \"Options\")\n : tr(\"export.create.steps.review\", \"Review\")}\n </span>\n </div>\n </div>\n ))}\n </div>\n\n {/* Error */}\n {error && (\n <div className=\"border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-4 text-status-error-text text-sm\">\n {error}\n </div>\n )}\n\n {/* Step 1: Select Entities */}\n {step === \"entities\" && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-bold\">\n {tr(\"export.create.select.title\", \"Select data to export\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n \"export.create.select.description\",\n \"Choose which entity types to include in your export.\",\n )}\n </p>\n {enabledEntities.length === 0 ? (\n <div className=\"border-2 border-border rounded-xl bg-muted/30 p-6 text-sm text-muted-foreground\">\n {tr(\n \"export.create.select.empty\",\n \"No exportable entity types are configured for this workspace yet.\",\n )}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {enabledEntities.map((entity) => {\n const isSelected = selectedEntities.includes(entity.entityType);\n return (\n <button\n type=\"button\"\n key={entity.entityType}\n onClick={() => toggleEntity(entity.entityType)}\n className={cn(\n \"text-left border-2 rounded-xl p-4 transition-all duration-200\",\n isSelected\n ? \"border-primary bg-primary/5 shadow-md\"\n : \"border-border/50 hover:border-primary/30 hover:shadow-sm\",\n )}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"font-semibold\">\n {fallbackEntityCopy[entity.entityType]?.label ??\n entity.displayName}\n </h3>\n {isSelected && (\n <CheckCircle2 size={18} className=\"text-primary\" />\n )}\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {fallbackEntityCopy[entity.entityType]?.description ??\n entity.description}\n </p>\n {entity.estimatedSize && (\n <p className=\"text-xs text-muted-foreground mt-2\">\n ~{entity.estimatedSize}\n </p>\n )}\n </button>\n );\n })}\n </div>\n )}\n </div>\n )}\n\n {/* Step 2: Configure Options */}\n {step === \"options\" && (\n <div className=\"space-y-6\">\n <div className=\"border-2 border-border/50 rounded-xl p-6 space-y-5\">\n <h2 className=\"text-lg font-bold\">\n {tr(\"export.create.options.title\", \"Export Options\")}\n </h2>\n\n {/* Scope selection */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\"export.create.options.scope\", \"Export Scope\")}\n </label>\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-3\">\n {SCOPE_OPTIONS.map(({ value, Icon }) => {\n // RBAC: Disable admin scopes if user lacks permission\n const isDisabled =\n (value === \"WORKSPACE_ADMIN\" && !canExportWorkspaceAdmin) ||\n (value === \"ORG_ADMIN\" && !canExportOrgAdmin);\n\n return (\n <button\n key={value}\n type=\"button\"\n onClick={() => !isDisabled && setSelectedScope(value)}\n disabled={isDisabled}\n className={cn(\n \"text-left border-2 rounded-xl p-4 transition-all duration-200\",\n selectedScope === value\n ? \"border-primary bg-primary/5 shadow-md\"\n : isDisabled\n ? \"border-border/30 opacity-50 cursor-not-allowed\"\n : \"border-border/50 hover:border-primary/30 hover:shadow-sm\",\n )}\n title={\n isDisabled\n ? value === \"WORKSPACE_ADMIN\"\n ? \"Workspace admin access required\"\n : \"Organization admin access required\"\n : undefined\n }\n >\n <div className=\"flex items-center justify-between mb-2\">\n <Icon\n size={18}\n className={\n selectedScope === value\n ? \"text-primary\"\n : isDisabled\n ? \"text-muted-foreground/50\"\n : \"text-muted-foreground\"\n }\n />\n {selectedScope === value && (\n <CheckCircle2 size={16} className=\"text-primary\" />\n )}\n {isDisabled && (\n <Lock\n size={14}\n className=\"text-muted-foreground/50\"\n />\n )}\n </div>\n <p className=\"font-semibold text-sm\">\n {getScopeLabel(value)}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {getScopeDescription(value)}\n </p>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Name */}\n <div>\n <label\n htmlFor=\"exportName\"\n className=\"block text-sm font-semibold mb-2\"\n >\n {tr(\"export.create.options.name\", \"Export Name\")}{\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"exportName\"\n aria-label={tr(\"export.create.options.name\", \"Export Name\")}\n value={exportName}\n onChange={(e) => setExportName(e.target.value)}\n className=\"w-full px-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 placeholder={tr(\n \"export.create.options.namePlaceholder\",\n \"e.g., Monthly User Export\",\n )}\n />\n </div>\n\n {/* Format — only CSV and JSON are supported */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\"export.create.options.outputFormat\", \"Output Format\")}\n </label>\n <div className=\"grid grid-cols-2 gap-3\">\n {SUPPORTED_FORMATS.map((format) => {\n const Icon = FORMAT_ICONS[format];\n return (\n <button\n type=\"button\"\n key={format}\n onClick={() => setSelectedFormat(format)}\n className={cn(\n \"flex items-center gap-3 border-2 rounded-lg p-3 transition-all duration-200\",\n selectedFormat === format\n ? \"border-primary bg-primary/5\"\n : \"border-border/50 hover:border-primary/30\",\n )}\n >\n <Icon\n size={18}\n className={\n selectedFormat === format\n ? \"text-primary\"\n : \"text-muted-foreground\"\n }\n />\n <span className=\"font-medium text-sm\">\n {getFormatDisplayName(format)}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Archive format — only ZIP is supported */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\"export.create.options.archiveFormat\", \"Archive Format\")}\n </label>\n <div className=\"flex gap-3\">\n <div\n className={cn(\n \"flex items-center gap-2 border-2 rounded-lg px-4 py-2 border-primary bg-primary/5\",\n )}\n >\n <Package size={16} className=\"text-primary\" />\n <span className=\"font-medium text-sm\">ZIP</span>\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground mt-1.5\">\n {tr(\n \"export.create.options.archiveDescription\",\n \"Exports are packaged as ZIP archives.\",\n )}\n </p>\n </div>\n\n {/* Date range filter — always visible */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n <span className=\"inline-flex items-center gap-1.5\">\n <CalendarRange size={15} />\n {tr(\"export.create.options.dateRange\", \"Date Range\")}{\" \"}\n <span className=\"font-normal text-muted-foreground\">\n {tr(\"export.create.shared.optional\", \"(optional)\")}\n </span>\n </span>\n </label>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"dateFrom\"\n className=\"block text-xs text-muted-foreground mb-1\"\n >\n {tr(\"export.create.options.dateFrom\", \"From\")}\n </label>\n <input\n type=\"date\"\n id=\"dateFrom\"\n aria-label={tr(\"export.create.options.dateFrom\", \"From\")}\n value={dateRangeStart}\n onChange={(e) => setDateRangeStart(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 text-sm\"\n />\n </div>\n <div>\n <label\n htmlFor=\"dateTo\"\n className=\"block text-xs text-muted-foreground mb-1\"\n >\n {tr(\"export.create.options.dateTo\", \"To\")}\n </label>\n <input\n type=\"date\"\n id=\"dateTo\"\n aria-label={tr(\"export.create.options.dateTo\", \"To\")}\n value={dateRangeEnd}\n min={dateRangeStart}\n onChange={(e) => setDateRangeEnd(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 text-sm\"\n />\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground mt-1.5\">\n {tr(\n \"export.create.options.dateRangeDescription\",\n \"Leave blank to export all data regardless of date.\",\n )}\n </p>\n </div>\n\n {/* Optional: Use a template */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\n \"export.create.options.basedOnTemplate\",\n \"Based on Template\",\n )}{\" \"}\n <span className=\"font-normal text-muted-foreground\">\n {tr(\"export.create.shared.optional\", \"(optional)\")}\n </span>\n </label>\n {templates.length > 0 ? (\n <select\n value={selectedTemplateId ?? \"\"}\n onChange={(e) =>\n setSelectedTemplateId(e.target.value || undefined)\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=\"\">\n {tr(\"export.create.options.noTemplate\", \"No template\")}\n </option>\n {templates.map((t) => (\n <option key={t.id} value={t.id}>\n {t.name}\n </option>\n ))}\n </select>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\"export.create.options.noTemplates\", \"No templates yet.\")}{\" \"}\n <button\n type=\"button\"\n onClick={() => navigate?.(basePath + \"/templates\")}\n className=\"text-primary underline-offset-2 hover:underline\"\n >\n {tr(\n \"export.create.options.createTemplateLink\",\n \"Create one\",\n )}\n </button>{\" \"}\n {tr(\n \"export.create.options.reuseTemplateHint\",\n \"to reuse export configurations.\",\n )}\n </p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Step 3: Review */}\n {step === \"review\" && (\n <div className=\"space-y-6\">\n <div className=\"border-2 border-primary/50 rounded-xl bg-primary/5 p-6 space-y-4\">\n <h2 className=\"text-lg font-bold flex items-center gap-2\">\n <CheckCircle2 size={20} className=\"text-primary\" />\n {tr(\"export.create.review.title\", \"Review Export\")}\n </h2>\n <dl className=\"space-y-3 text-sm\">\n {[\n {\n label: tr(\"export.create.review.fields.name\", \"Name\"),\n value: exportName,\n },\n {\n label: tr(\"export.create.review.fields.scope\", \"Scope\"),\n value: getScopeLabel(selectedScope),\n },\n {\n label: tr(\n \"export.create.review.fields.entityTypes\",\n \"Entity Types\",\n ),\n value: selectedEntities.join(\", \"),\n },\n {\n label: tr(\"export.create.review.fields.format\", \"Format\"),\n value: getFormatDisplayName(selectedFormat),\n },\n {\n label: tr(\"export.create.review.fields.archive\", \"Archive\"),\n value: \"ZIP\",\n },\n ...(dateRangeStart || dateRangeEnd\n ? [\n {\n label: tr(\n \"export.create.review.fields.dateRange\",\n \"Date Range\",\n ),\n value: [\n dateRangeStart || \"—\",\n dateRangeEnd || \"—\",\n ].join(\" → \"),\n },\n ]\n : []),\n ...(selectedTemplateId\n ? [\n {\n label: tr(\n \"export.create.review.fields.template\",\n \"Template\",\n ),\n value:\n templates.find((t) => t.id === selectedTemplateId)\n ?.name ?? selectedTemplateId,\n },\n ]\n : []),\n ].map(({ label, value }) => (\n <div\n key={label}\n className=\"flex flex-col sm:flex-row sm:justify-between gap-1\"\n >\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className=\"font-semibold sm:text-right break-words\">\n {value}\n </dd>\n </div>\n ))}\n </dl>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"px-6 py-3 border-2 border-border/50 rounded-lg hover:bg-accent hover:border-primary/30 transition-all duration-200 font-medium\"\n >\n {step === \"entities\"\n ? tr(\"export.create.actions.cancel\", \"Cancel\")\n : tr(\"export.create.actions.back\", \"Back\")}\n </button>\n {step === \"review\" ? (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isPending}\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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isPending ? (\n <Loader2 size={18} className=\"animate-spin\" />\n ) : (\n <Download size={18} />\n )}\n {tr(\"export.create.actions.startExport\", \"Start Export\")}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleNext}\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 {tr(\"export.create.actions.next\", \"Next\")}\n <ArrowRight size={18} />\n </button>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateExportPage.js","names":[],"sources":["../../../src/export/pages/CreateExportPage.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport {\n ArrowLeft,\n ArrowRight,\n Download,\n CheckCircle2,\n Loader2,\n FileSpreadsheet,\n FileJson,\n Package,\n CalendarRange,\n User,\n Users,\n Building2,\n Lock,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { usePermission } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useCreateExportJob,\n useAvailableEntities,\n useExportTemplates,\n} from \"../hooks/useExportQueries\";\nimport { getFormatDisplayName } from \"../constants/enums\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { cn } from \"../utils/cn\";\nimport { isExportEntityDisabled } from \"../utils/disabledModules\";\nimport type {\n ExportAvailableEntity,\n ExportFormat,\n ExportScope,\n} from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ntype WizardStep = \"entities\" | \"options\" | \"review\";\n\n// Backend only supports CSV and JSON\nconst SUPPORTED_FORMATS: ExportFormat[] = [\"CSV\", \"JSON\"];\n\nconst FORMAT_ICONS: Record<ExportFormat, typeof FileJson> = {\n CSV: FileSpreadsheet,\n JSON: FileJson,\n};\n\n/**\n * Create Export Page\n * Multi-step form: select entities, configure options, review & start\n */\nconst SCOPE_OPTIONS: Array<{\n value: ExportScope;\n label: string;\n description: string;\n Icon: typeof User;\n}> = [\n {\n value: \"INDIVIDUAL\",\n label: \"My Data Only\",\n description: \"Export data you personally created across selected modules.\",\n Icon: User,\n },\n {\n value: \"WORKSPACE_ADMIN\",\n label: \"All Workspace Data\",\n description:\n \"Export all data in this workspace regardless of who created it. Requires workspace admin access.\",\n Icon: Users,\n },\n {\n value: \"ORG_ADMIN\",\n label: \"All Organization Data\",\n description:\n \"Export data from every workspace in your organization. Requires org-admin access.\",\n Icon: Building2,\n },\n];\n\nexport function CreateExportPage() {\n const { basePath = \"/\", navigate, organizationId } = 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 { createJob, isPending } = useCreateExportJob();\n const { data: availableEntities } = useAvailableEntities();\n const { data: templates } = useExportTemplates();\n const [searchParams] = useSearchParams();\n\n // RBAC: Check permissions for admin scopes\n const canExportWorkspaceAdmin = usePermission(\"export:workspace:admin\");\n const canExportOrgAdmin = usePermission(\"export:org:admin\");\n\n const [step, setStep] = useState<WizardStep>(\"entities\");\n const [selectedEntities, setSelectedEntities] = useState<string[]>([]);\n const [exportName, setExportName] = useState(\"\");\n const [selectedFormat, setSelectedFormat] = useState<ExportFormat>(\"CSV\");\n const [selectedScope, setSelectedScope] = useState<ExportScope>(\"INDIVIDUAL\");\n const [selectedTemplateId, setSelectedTemplateId] = useState<\n string | undefined\n >(searchParams.get(\"templateId\") ?? undefined);\n const [dateRangeStart, setDateRangeStart] = useState(\"\");\n const [dateRangeEnd, setDateRangeEnd] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n // Pre-populate form when arriving from \"Use Template\" — apply only once per templateId\n const appliedTemplateRef = useRef<string | null>(null);\n useEffect(() => {\n const paramTemplateId = searchParams.get(\"templateId\");\n if (!paramTemplateId || !templates?.length) return;\n if (appliedTemplateRef.current === paramTemplateId) return;\n const tpl = templates.find((t) => t.id === paramTemplateId);\n if (!tpl) return;\n appliedTemplateRef.current = paramTemplateId;\n setSelectedTemplateId(tpl.id);\n // Skip entity types whose product export is currently disabled.\n setSelectedEntities(\n (tpl.entityTypes ?? []).filter((et) => !isExportEntityDisabled(et)),\n );\n const normalizedFormat = tpl.format?.toUpperCase() as\n | ExportFormat\n | undefined;\n if (normalizedFormat && SUPPORTED_FORMATS.includes(normalizedFormat)) {\n setSelectedFormat(normalizedFormat);\n }\n if (tpl.name) setExportName(tpl.name);\n }, [searchParams, templates]);\n\n const handleBack = () => {\n if (step === \"options\") setStep(\"entities\");\n else if (step === \"review\") setStep(\"options\");\n else navigate?.(basePath);\n };\n\n const handleNext = () => {\n if (step === \"entities\") {\n if (selectedEntities.length === 0) {\n setError(\n tr(\n \"export.create.errors.selectEntity\",\n \"Please select at least one entity type to export\",\n ),\n );\n return;\n }\n setError(null);\n setStep(\"options\");\n } else if (step === \"options\") {\n if (!exportName.trim()) {\n setError(\n tr(\n \"export.create.errors.exportNameRequired\",\n \"Please provide a name for this export\",\n ),\n );\n return;\n }\n setError(null);\n setStep(\"review\");\n }\n };\n\n const handleSubmit = async () => {\n try {\n setError(null);\n await createJob({\n name: exportName,\n entityTypes: selectedEntities,\n format: selectedFormat,\n exportScope: selectedScope,\n organizationId:\n selectedScope === \"ORG_ADMIN\" ? organizationId : undefined,\n templateId: selectedTemplateId,\n dateRangeStart: dateRangeStart\n ? new Date(dateRangeStart).toISOString()\n : null,\n dateRangeEnd: dateRangeEnd\n ? new Date(dateRangeEnd).toISOString()\n : null,\n });\n navigate?.(basePath);\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\"export.create.errors.createFailed\", \"Failed to create export\"),\n );\n }\n };\n\n const toggleEntity = (entityType: string) => {\n // Disabled-product entities can't be selected.\n if (isExportEntityDisabled(entityType)) return;\n setSelectedEntities((prev) =>\n prev.includes(entityType)\n ? prev.filter((e) => e !== entityType)\n : [...prev, entityType],\n );\n };\n\n const getScopeLabel = (scope: ExportScope) => {\n switch (scope) {\n case \"INDIVIDUAL\":\n return tr(\"export.create.scope.individual.label\", \"My Data Only\");\n case \"WORKSPACE_ADMIN\":\n return tr(\n \"export.create.scope.workspaceAdmin.label\",\n \"All Workspace Data\",\n );\n case \"ORG_ADMIN\":\n return tr(\n \"export.create.scope.orgAdmin.label\",\n \"All Organization Data\",\n );\n default:\n return scope;\n }\n };\n\n const getScopeDescription = (scope: ExportScope) => {\n switch (scope) {\n case \"INDIVIDUAL\":\n return tr(\n \"export.create.scope.individual.description\",\n \"Export data you personally created across selected modules.\",\n );\n case \"WORKSPACE_ADMIN\":\n return tr(\n \"export.create.scope.workspaceAdmin.description\",\n \"Export all data in this workspace regardless of who created it. Requires workspace admin access.\",\n );\n case \"ORG_ADMIN\":\n return tr(\n \"export.create.scope.orgAdmin.description\",\n \"Export data from every workspace in your organization. Requires org-admin access.\",\n );\n default:\n return \"\";\n }\n };\n\n const fallbackEntityCopy: Record<\n string,\n { label: string; description: string }\n > = {\n users: {\n label: tr(\"export.create.entities.users.label\", \"Users\"),\n description: tr(\n \"export.create.entities.users.description\",\n \"Workspace members and profiles\",\n ),\n },\n projects: {\n label: tr(\"export.create.entities.projects.label\", \"Projects\"),\n description: tr(\n \"export.create.entities.projects.description\",\n \"Projects and their metadata\",\n ),\n },\n tasks: {\n label: tr(\"export.create.entities.tasks.label\", \"Tasks\"),\n description: tr(\n \"export.create.entities.tasks.description\",\n \"Task items and assignments\",\n ),\n },\n files: {\n label: tr(\"export.create.entities.files.label\", \"Files\"),\n description: tr(\n \"export.create.entities.files.description\",\n \"File metadata and references\",\n ),\n },\n conversations: {\n label: tr(\"export.create.entities.conversations.label\", \"Conversations\"),\n description: tr(\n \"export.create.entities.conversations.description\",\n \"Chat messages and threads\",\n ),\n },\n tags: {\n label: tr(\"export.create.entities.tags.label\", \"Tags\"),\n description: tr(\n \"export.create.entities.tags.description\",\n \"Tags and vocabularies\",\n ),\n },\n activity: {\n label: tr(\"export.create.entities.activity.label\", \"Activity Logs\"),\n description: tr(\n \"export.create.entities.activity.description\",\n \"Audit trail and activity history\",\n ),\n },\n calendar: {\n label: tr(\"export.create.entities.calendar.label\", \"Calendar Events\"),\n description: tr(\n \"export.create.entities.calendar.description\",\n \"Events and bookings\",\n ),\n },\n };\n\n // Entities the backend marks available, split so the ones a user can export\n // show first and disabled-product entities are listed (still visible) below.\n const visibleEntities = availableEntities.filter((e) => e.enabled !== false);\n const availableForExport = visibleEntities.filter(\n (e) => !isExportEntityDisabled(e.entityType),\n );\n const unavailableForExport = visibleEntities.filter((e) =>\n isExportEntityDisabled(e.entityType),\n );\n\n const renderEntityCard = (\n entity: ExportAvailableEntity,\n isDisabled: boolean,\n ) => {\n const isSelected =\n !isDisabled && selectedEntities.includes(entity.entityType);\n return (\n <button\n type=\"button\"\n key={entity.entityType}\n onClick={() => toggleEntity(entity.entityType)}\n disabled={isDisabled}\n aria-disabled={isDisabled}\n title={\n isDisabled\n ? tr(\n \"export.create.select.unavailableHint\",\n \"Export for this product isn't available yet\",\n )\n : undefined\n }\n className={cn(\n \"text-left border-2 rounded-xl p-4 transition-all duration-200\",\n isDisabled\n ? \"border-border/30 opacity-50 cursor-not-allowed\"\n : isSelected\n ? \"border-primary bg-primary/5 shadow-md\"\n : \"border-border/50 hover:border-primary/30 hover:shadow-sm\",\n )}\n >\n <div className=\"flex items-center justify-between mb-2 gap-2\">\n <h3 className=\"font-semibold\">\n {fallbackEntityCopy[entity.entityType]?.label ?? entity.displayName}\n </h3>\n {isDisabled ? (\n <span className=\"shrink-0 text-xs font-medium px-2 py-0.5 rounded-full bg-muted text-muted-foreground\">\n {tr(\"export.create.select.unavailable\", \"Unavailable\")}\n </span>\n ) : (\n isSelected && <CheckCircle2 size={18} className=\"text-primary\" />\n )}\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {fallbackEntityCopy[entity.entityType]?.description ??\n entity.description}\n </p>\n {entity.estimatedSize && (\n <p className=\"text-xs text-muted-foreground mt-2\">\n ~{entity.estimatedSize}\n </p>\n )}\n </button>\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?.(basePath)}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200 hover:scale-110\"\n aria-label={tr(\n \"export.create.actions.backToDashboard\",\n \"Back to export dashboard\",\n )}\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2.5 rounded-lg bg-primary/10 backdrop-blur-sm\">\n <Download size={24} className=\"text-primary\" />\n </div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tr(\"export.create.title\", \"New Export\")}\n </h1>\n </div>\n </div>\n </div>\n\n {/* Step Indicator */}\n <div className=\"flex items-center gap-2 justify-center flex-wrap\">\n {([\"entities\", \"options\", \"review\"] as WizardStep[]).map((s, i) => (\n <div key={s} className=\"flex items-center gap-2\">\n {i > 0 && (\n <ArrowRight size={18} className=\"text-muted-foreground\" />\n )}\n <div\n className={cn(\n \"flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium border-2 transition-all duration-200\",\n step === s\n ? \"bg-primary text-primary-foreground border-primary shadow-md scale-105\"\n : \"bg-muted/50 text-muted-foreground border-border/50\",\n )}\n >\n <div className=\"flex items-center justify-center size-6 rounded-full bg-background/20 font-bold\">\n {i + 1}\n </div>\n <span className=\"hidden sm:inline capitalize\">\n {s === \"entities\"\n ? tr(\"export.create.steps.entities\", \"Select Data\")\n : s === \"options\"\n ? tr(\"export.create.steps.options\", \"Options\")\n : tr(\"export.create.steps.review\", \"Review\")}\n </span>\n </div>\n </div>\n ))}\n </div>\n\n {/* Error */}\n {error && (\n <div className=\"border-2 border-border-default rounded-xl bg-status-error-bg-subtle p-4 text-status-error-text text-sm\">\n {error}\n </div>\n )}\n\n {/* Step 1: Select Entities */}\n {step === \"entities\" && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-bold\">\n {tr(\"export.create.select.title\", \"Select data to export\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n \"export.create.select.description\",\n \"Choose which entity types to include in your export.\",\n )}\n </p>\n {visibleEntities.length === 0 ? (\n <div className=\"border-2 border-border rounded-xl bg-muted/30 p-6 text-sm text-muted-foreground\">\n {tr(\n \"export.create.select.empty\",\n \"No exportable entity types are configured for this workspace yet.\",\n )}\n </div>\n ) : (\n <div className=\"space-y-6\">\n {availableForExport.length > 0 && (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {availableForExport.map((entity) =>\n renderEntityCard(entity, false),\n )}\n </div>\n )}\n {unavailableForExport.length > 0 && (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"h-px flex-1 bg-border/60\" />\n <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {tr(\n \"export.create.select.unavailableSection\",\n \"Not available yet\",\n )}\n </span>\n <div className=\"h-px flex-1 bg-border/60\" />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {unavailableForExport.map((entity) =>\n renderEntityCard(entity, true),\n )}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n\n {/* Step 2: Configure Options */}\n {step === \"options\" && (\n <div className=\"space-y-6\">\n <div className=\"border-2 border-border/50 rounded-xl p-6 space-y-5\">\n <h2 className=\"text-lg font-bold\">\n {tr(\"export.create.options.title\", \"Export Options\")}\n </h2>\n\n {/* Scope selection */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\"export.create.options.scope\", \"Export Scope\")}\n </label>\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-3\">\n {SCOPE_OPTIONS.map(({ value, Icon }) => {\n // RBAC: Disable admin scopes if user lacks permission\n const isDisabled =\n (value === \"WORKSPACE_ADMIN\" && !canExportWorkspaceAdmin) ||\n (value === \"ORG_ADMIN\" && !canExportOrgAdmin);\n\n return (\n <button\n key={value}\n type=\"button\"\n onClick={() => !isDisabled && setSelectedScope(value)}\n disabled={isDisabled}\n className={cn(\n \"text-left border-2 rounded-xl p-4 transition-all duration-200\",\n selectedScope === value\n ? \"border-primary bg-primary/5 shadow-md\"\n : isDisabled\n ? \"border-border/30 opacity-50 cursor-not-allowed\"\n : \"border-border/50 hover:border-primary/30 hover:shadow-sm\",\n )}\n title={\n isDisabled\n ? value === \"WORKSPACE_ADMIN\"\n ? \"Workspace admin access required\"\n : \"Organization admin access required\"\n : undefined\n }\n >\n <div className=\"flex items-center justify-between mb-2\">\n <Icon\n size={18}\n className={\n selectedScope === value\n ? \"text-primary\"\n : isDisabled\n ? \"text-muted-foreground/50\"\n : \"text-muted-foreground\"\n }\n />\n {selectedScope === value && (\n <CheckCircle2 size={16} className=\"text-primary\" />\n )}\n {isDisabled && (\n <Lock\n size={14}\n className=\"text-muted-foreground/50\"\n />\n )}\n </div>\n <p className=\"font-semibold text-sm\">\n {getScopeLabel(value)}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {getScopeDescription(value)}\n </p>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Name */}\n <div>\n <label\n htmlFor=\"exportName\"\n className=\"block text-sm font-semibold mb-2\"\n >\n {tr(\"export.create.options.name\", \"Export Name\")}{\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n id=\"exportName\"\n aria-label={tr(\"export.create.options.name\", \"Export Name\")}\n value={exportName}\n onChange={(e) => setExportName(e.target.value)}\n className=\"w-full px-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 placeholder={tr(\n \"export.create.options.namePlaceholder\",\n \"e.g., Monthly User Export\",\n )}\n />\n </div>\n\n {/* Format — only CSV and JSON are supported */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\"export.create.options.outputFormat\", \"Output Format\")}\n </label>\n <div className=\"grid grid-cols-2 gap-3\">\n {SUPPORTED_FORMATS.map((format) => {\n const Icon = FORMAT_ICONS[format];\n return (\n <button\n type=\"button\"\n key={format}\n onClick={() => setSelectedFormat(format)}\n className={cn(\n \"flex items-center gap-3 border-2 rounded-lg p-3 transition-all duration-200\",\n selectedFormat === format\n ? \"border-primary bg-primary/5\"\n : \"border-border/50 hover:border-primary/30\",\n )}\n >\n <Icon\n size={18}\n className={\n selectedFormat === format\n ? \"text-primary\"\n : \"text-muted-foreground\"\n }\n />\n <span className=\"font-medium text-sm\">\n {getFormatDisplayName(format)}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Archive format — only ZIP is supported */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\"export.create.options.archiveFormat\", \"Archive Format\")}\n </label>\n <div className=\"flex gap-3\">\n <div\n className={cn(\n \"flex items-center gap-2 border-2 rounded-lg px-4 py-2 border-primary bg-primary/5\",\n )}\n >\n <Package size={16} className=\"text-primary\" />\n <span className=\"font-medium text-sm\">ZIP</span>\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground mt-1.5\">\n {tr(\n \"export.create.options.archiveDescription\",\n \"Exports are packaged as ZIP archives.\",\n )}\n </p>\n </div>\n\n {/* Date range filter — always visible */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n <span className=\"inline-flex items-center gap-1.5\">\n <CalendarRange size={15} />\n {tr(\"export.create.options.dateRange\", \"Date Range\")}{\" \"}\n <span className=\"font-normal text-muted-foreground\">\n {tr(\"export.create.shared.optional\", \"(optional)\")}\n </span>\n </span>\n </label>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"dateFrom\"\n className=\"block text-xs text-muted-foreground mb-1\"\n >\n {tr(\"export.create.options.dateFrom\", \"From\")}\n </label>\n <input\n type=\"date\"\n id=\"dateFrom\"\n aria-label={tr(\"export.create.options.dateFrom\", \"From\")}\n value={dateRangeStart}\n onChange={(e) => setDateRangeStart(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 text-sm\"\n />\n </div>\n <div>\n <label\n htmlFor=\"dateTo\"\n className=\"block text-xs text-muted-foreground mb-1\"\n >\n {tr(\"export.create.options.dateTo\", \"To\")}\n </label>\n <input\n type=\"date\"\n id=\"dateTo\"\n aria-label={tr(\"export.create.options.dateTo\", \"To\")}\n value={dateRangeEnd}\n min={dateRangeStart}\n onChange={(e) => setDateRangeEnd(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 text-sm\"\n />\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground mt-1.5\">\n {tr(\n \"export.create.options.dateRangeDescription\",\n \"Leave blank to export all data regardless of date.\",\n )}\n </p>\n </div>\n\n {/* Optional: Use a template */}\n <div>\n <label className=\"block text-sm font-semibold mb-2\">\n {tr(\n \"export.create.options.basedOnTemplate\",\n \"Based on Template\",\n )}{\" \"}\n <span className=\"font-normal text-muted-foreground\">\n {tr(\"export.create.shared.optional\", \"(optional)\")}\n </span>\n </label>\n {templates.length > 0 ? (\n <select\n value={selectedTemplateId ?? \"\"}\n onChange={(e) =>\n setSelectedTemplateId(e.target.value || undefined)\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=\"\">\n {tr(\"export.create.options.noTemplate\", \"No template\")}\n </option>\n {templates.map((t) => (\n <option key={t.id} value={t.id}>\n {t.name}\n </option>\n ))}\n </select>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\"export.create.options.noTemplates\", \"No templates yet.\")}{\" \"}\n <button\n type=\"button\"\n onClick={() => navigate?.(basePath + \"/templates\")}\n className=\"text-primary underline-offset-2 hover:underline\"\n >\n {tr(\n \"export.create.options.createTemplateLink\",\n \"Create one\",\n )}\n </button>{\" \"}\n {tr(\n \"export.create.options.reuseTemplateHint\",\n \"to reuse export configurations.\",\n )}\n </p>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Step 3: Review */}\n {step === \"review\" && (\n <div className=\"space-y-6\">\n <div className=\"border-2 border-primary/50 rounded-xl bg-primary/5 p-6 space-y-4\">\n <h2 className=\"text-lg font-bold flex items-center gap-2\">\n <CheckCircle2 size={20} className=\"text-primary\" />\n {tr(\"export.create.review.title\", \"Review Export\")}\n </h2>\n <dl className=\"space-y-3 text-sm\">\n {[\n {\n label: tr(\"export.create.review.fields.name\", \"Name\"),\n value: exportName,\n },\n {\n label: tr(\"export.create.review.fields.scope\", \"Scope\"),\n value: getScopeLabel(selectedScope),\n },\n {\n label: tr(\n \"export.create.review.fields.entityTypes\",\n \"Entity Types\",\n ),\n value: selectedEntities.join(\", \"),\n },\n {\n label: tr(\"export.create.review.fields.format\", \"Format\"),\n value: getFormatDisplayName(selectedFormat),\n },\n {\n label: tr(\"export.create.review.fields.archive\", \"Archive\"),\n value: \"ZIP\",\n },\n ...(dateRangeStart || dateRangeEnd\n ? [\n {\n label: tr(\n \"export.create.review.fields.dateRange\",\n \"Date Range\",\n ),\n value: [\n dateRangeStart || \"—\",\n dateRangeEnd || \"—\",\n ].join(\" → \"),\n },\n ]\n : []),\n ...(selectedTemplateId\n ? [\n {\n label: tr(\n \"export.create.review.fields.template\",\n \"Template\",\n ),\n value:\n templates.find((t) => t.id === selectedTemplateId)\n ?.name ?? selectedTemplateId,\n },\n ]\n : []),\n ].map(({ label, value }) => (\n <div\n key={label}\n className=\"flex flex-col sm:flex-row sm:justify-between gap-1\"\n >\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className=\"font-semibold sm:text-right break-words\">\n {value}\n </dd>\n </div>\n ))}\n </dl>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"px-6 py-3 border-2 border-border/50 rounded-lg hover:bg-accent hover:border-primary/30 transition-all duration-200 font-medium\"\n >\n {step === \"entities\"\n ? tr(\"export.create.actions.cancel\", \"Cancel\")\n : tr(\"export.create.actions.back\", \"Back\")}\n </button>\n {step === \"review\" ? (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isPending}\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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isPending ? (\n <Loader2 size={18} className=\"animate-spin\" />\n ) : (\n <Download size={18} />\n )}\n {tr(\"export.create.actions.startExport\", \"Start Export\")}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleNext}\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 {tr(\"export.create.actions.next\", \"Next\")}\n <ArrowRight size={18} />\n </button>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAuCA,IAAM,IAAoC,CAAC,OAAO,OAAO,EAEnD,KAAsD;CAC1D,KAAK;CACL,MAAM;CACP,EAMK,IAKD;CACH;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,MAAM;EACP;CACD;EACE,OAAO;EACP,OAAO;EACP,aACE;EACF,MAAM;EACP;CACD;EACE,OAAO;EACP,OAAO;EACP,aACE;EACF,MAAM;EACP;CACF;AAED,SAAgB,IAAmB;CACjC,IAAM,EAAE,cAAW,KAAK,aAAU,sBAAmB,GAAW,EAC1D,EAAE,SAAM,IAAS,EACjB,KACJ,GACA,GACA,MACG,GAAc,GAAG,GAAK,GAAU,EAAO,EACtC,EAAE,cAAW,iBAAc,GAAoB,EAC/C,EAAE,MAAM,OAAsB,GAAsB,EACpD,EAAE,MAAM,MAAc,GAAoB,EAC1C,CAAC,KAAgB,IAAiB,EAGlC,IAA0B,EAAc,yBAAyB,EACjE,KAAoB,EAAc,mBAAmB,EAErD,CAAC,GAAM,KAAW,EAAqB,WAAW,EAClD,CAAC,GAAkB,KAAuB,EAAmB,EAAE,CAAC,EAChE,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAgB,KAAqB,EAAuB,MAAM,EACnE,CAAC,GAAe,KAAoB,EAAsB,aAAa,EACvE,CAAC,GAAoB,KAAyB,EAElD,EAAa,IAAI,aAAa,IAAI,KAAA,EAAU,EACxC,CAAC,GAAgB,KAAqB,EAAS,GAAG,EAClD,CAAC,GAAc,MAAmB,EAAS,GAAG,EAC9C,CAAC,GAAO,KAAY,EAAwB,KAAK,EAGjD,IAAqB,EAAsB,KAAK;AACtD,UAAgB;EACd,IAAM,IAAkB,EAAa,IAAI,aAAa;AAEtD,MADI,CAAC,KAAmB,CAAC,GAAW,UAChC,EAAmB,YAAY,EAAiB;EACpD,IAAM,IAAM,EAAU,MAAM,MAAM,EAAE,OAAO,EAAgB;AAC3D,MAAI,CAAC,EAAK;AAIV,EAHA,EAAmB,UAAU,GAC7B,EAAsB,EAAI,GAAG,EAE7B,GACG,EAAI,eAAe,EAAE,EAAE,QAAQ,MAAO,CAAC,EAAuB,EAAG,CAAC,CACpE;EACD,IAAM,IAAmB,EAAI,QAAQ,aAAa;AAMlD,EAHI,KAAoB,EAAkB,SAAS,EAAiB,IAClE,EAAkB,EAAiB,EAEjC,EAAI,QAAM,EAAc,EAAI,KAAK;IACpC,CAAC,GAAc,EAAU,CAAC;CAE7B,IAAM,WAAmB;AACvB,EAAI,MAAS,YAAW,EAAQ,WAAW,GAClC,MAAS,WAAU,EAAQ,UAAU,GACzC,IAAW,EAAS;IAGrB,WAAmB;AACvB,MAAI,MAAS,YAAY;AACvB,OAAI,EAAiB,WAAW,GAAG;AACjC,MACE,EACE,qCACA,mDACD,CACF;AACD;;AAGF,GADA,EAAS,KAAK,EACd,EAAQ,UAAU;aACT,MAAS,WAAW;AAC7B,OAAI,CAAC,EAAW,MAAM,EAAE;AACtB,MACE,EACE,2CACA,wCACD,CACF;AACD;;AAGF,GADA,EAAS,KAAK,EACd,EAAQ,SAAS;;IAIf,KAAe,YAAY;AAC/B,MAAI;AAiBF,GAhBA,EAAS,KAAK,EACd,MAAM,EAAU;IACd,MAAM;IACN,aAAa;IACb,QAAQ;IACR,aAAa;IACb,gBACE,MAAkB,cAAc,IAAiB,KAAA;IACnD,YAAY;IACZ,gBAAgB,IACZ,IAAI,KAAK,EAAe,CAAC,aAAa,GACtC;IACJ,cAAc,IACV,IAAI,KAAK,EAAa,CAAC,aAAa,GACpC;IACL,CAAC,EACF,IAAW,EAAS;WACb,GAAK;AACZ,KACE,aAAe,QACX,EAAI,UACJ,EAAG,qCAAqC,0BAA0B,CACvE;;IAIC,MAAgB,MAAuB;AAEvC,IAAuB,EAAW,IACtC,GAAqB,MACnB,EAAK,SAAS,EAAW,GACrB,EAAK,QAAQ,MAAM,MAAM,EAAW,GACpC,CAAC,GAAG,GAAM,EAAW,CAC1B;IAGG,KAAiB,MAAuB;AAC5C,UAAQ,GAAR;GACE,KAAK,aACH,QAAO,EAAG,wCAAwC,eAAe;GACnE,KAAK,kBACH,QAAO,EACL,4CACA,qBACD;GACH,KAAK,YACH,QAAO,EACL,sCACA,wBACD;GACH,QACE,QAAO;;IAIP,MAAuB,MAAuB;AAClD,UAAQ,GAAR;GACE,KAAK,aACH,QAAO,EACL,8CACA,8DACD;GACH,KAAK,kBACH,QAAO,EACL,kDACA,mGACD;GACH,KAAK,YACH,QAAO,EACL,4CACA,oFACD;GACH,QACE,QAAO;;IAIP,IAGF;EACF,OAAO;GACL,OAAO,EAAG,sCAAsC,QAAQ;GACxD,aAAa,EACX,4CACA,iCACD;GACF;EACD,UAAU;GACR,OAAO,EAAG,yCAAyC,WAAW;GAC9D,aAAa,EACX,+CACA,8BACD;GACF;EACD,OAAO;GACL,OAAO,EAAG,sCAAsC,QAAQ;GACxD,aAAa,EACX,4CACA,6BACD;GACF;EACD,OAAO;GACL,OAAO,EAAG,sCAAsC,QAAQ;GACxD,aAAa,EACX,4CACA,+BACD;GACF;EACD,eAAe;GACb,OAAO,EAAG,8CAA8C,gBAAgB;GACxE,aAAa,EACX,oDACA,4BACD;GACF;EACD,MAAM;GACJ,OAAO,EAAG,qCAAqC,OAAO;GACtD,aAAa,EACX,2CACA,wBACD;GACF;EACD,UAAU;GACR,OAAO,EAAG,yCAAyC,gBAAgB;GACnE,aAAa,EACX,+CACA,mCACD;GACF;EACD,UAAU;GACR,OAAO,EAAG,yCAAyC,kBAAkB;GACrE,aAAa,EACX,+CACA,sBACD;GACF;EACF,EAIK,IAAkB,GAAkB,QAAQ,MAAM,EAAE,YAAY,GAAM,EACtE,IAAqB,EAAgB,QACxC,MAAM,CAAC,EAAuB,EAAE,WAAW,CAC7C,EACK,IAAuB,EAAgB,QAAQ,MACnD,EAAuB,EAAE,WAAW,CACrC,EAEK,KACJ,GACA,MACG;EACH,IAAM,IACJ,CAAC,KAAc,EAAiB,SAAS,EAAO,WAAW;AAC7D,SACE,kBAAC,UAAD;GACE,MAAK;GAEL,eAAe,GAAa,EAAO,WAAW;GAC9C,UAAU;GACV,iBAAe;GACf,OACE,IACI,EACE,wCACA,8CACD,GACD,KAAA;GAEN,WAAW,EACT,iEACA,IACI,mDACA,IACE,0CACA,2DACP;aArBH;IAuBE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAmB,EAAO,aAAa,SAAS,EAAO;MACrD,CAAA,EACJ,IACC,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,oCAAoC,cAAc;MACjD,CAAA,GAEP,KAAc,kBAAC,GAAD;MAAc,MAAM;MAAI,WAAU;MAAiB,CAAA,CAE/D;;IACN,kBAAC,KAAD;KAAG,WAAU;eACV,EAAmB,EAAO,aAAa,eACtC,EAAO;KACP,CAAA;IACH,EAAO,iBACN,kBAAC,KAAD;KAAG,WAAU;eAAb,CAAkD,KAC9C,EAAO,cACP;;IAEC;KA1CF,EAAO,WA0CL;;AAIb,QACE,kBAAC,IAAD;EAAY,YAAY;YAAxB;GAEE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,IAAW,EAAS;MACnC,WAAU;MACV,cAAY,EACV,yCACA,2BACD;gBAED,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;MAChB,CAAA,EACT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAU,MAAM;QAAI,WAAU;QAAiB,CAAA;OAC3C,CAAA,EACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,uBAAuB,aAAa;OACrC,CAAA,CACD;QACF;;IACF,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cACX;KAAC;KAAY;KAAW;KAAS,CAAkB,KAAK,GAAG,MAC3D,kBAAC,OAAD;KAAa,WAAU;eAAvB,CACG,IAAI,KACH,kBAAC,GAAD;MAAY,MAAM;MAAI,WAAU;MAA0B,CAAA,EAE5D,kBAAC,OAAD;MACE,WAAW,EACT,2GACA,MAAS,IACL,0EACA,qDACL;gBANH,CAQE,kBAAC,OAAD;OAAK,WAAU;iBACZ,IAAI;OACD,CAAA,EACN,kBAAC,QAAD;OAAM,WAAU;iBACb,MAAM,aACH,EAAG,gCAAgC,cAAc,GACjD,MAAM,YACJ,EAAG,+BAA+B,UAAU,GAC5C,EAAG,8BAA8B,SAAS;OAC3C,CAAA,CACH;QACF;OAvBI,EAuBJ,CACN;IACE,CAAA;GAGL,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ;IACG,CAAA;GAIP,MAAS,cACR,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAG,8BAA8B,wBAAwB;MACvD,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,oCACA,uDACD;MACC,CAAA;KACH,EAAgB,WAAW,IAC1B,kBAAC,OAAD;MAAK,WAAU;gBACZ,EACC,8BACA,oEACD;MACG,CAAA,GAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAmB,SAAS,KAC3B,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAmB,KAAK,MACvB,EAAiB,GAAQ,GAAM,CAChC;OACG,CAAA,EAEP,EAAqB,SAAS,KAC7B,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD,EAAK,WAAU,4BAA6B,CAAA;SAC5C,kBAAC,QAAD;UAAM,WAAU;oBACb,EACC,2CACA,oBACD;UACI,CAAA;SACP,kBAAC,OAAD,EAAK,WAAU,4BAA6B,CAAA;SACxC;WACN,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAqB,KAAK,MACzB,EAAiB,GAAQ,GAAK,CAC/B;QACG,CAAA,CACF;SAEJ;;KAEJ;;GAIP,MAAS,aACR,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,+BAA+B,iBAAiB;OACjD,CAAA;MAGL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAU;iBACd,EAAG,+BAA+B,eAAe;OAC5C,CAAA,EACR,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAc,KAAK,EAAE,UAAO,cAAW;QAEtC,IAAM,IACH,MAAU,qBAAqB,CAAC,KAChC,MAAU,eAAe,CAAC;AAE7B,eACE,kBAAC,UAAD;SAEE,MAAK;SACL,eAAe,CAAC,KAAc,EAAiB,EAAM;SACrD,UAAU;SACV,WAAW,EACT,iEACA,MAAkB,IACd,0CACA,IACE,mDACA,2DACP;SACD,OACE,IACI,MAAU,oBACR,oCACA,uCACF,KAAA;mBAlBR;UAqBE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,GAAD;aACE,MAAM;aACN,WACE,MAAkB,IACd,iBACA,IACE,6BACA;aAER,CAAA;YACD,MAAkB,KACjB,kBAAC,GAAD;aAAc,MAAM;aAAI,WAAU;aAAiB,CAAA;YAEpD,KACC,kBAAC,GAAD;aACE,MAAM;aACN,WAAU;aACV,CAAA;YAEA;;UACN,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAc,EAAM;WACnB,CAAA;UACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,GAAoB,EAAM;WACzB,CAAA;UACG;WA/CF,EA+CE;SAEX;OACE,CAAA,CACF,EAAA,CAAA;MAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OACE,SAAQ;OACR,WAAU;iBAFZ;QAIG,EAAG,8BAA8B,cAAc;QAAE;QAClD,kBAAC,QAAD;SAAM,WAAU;mBAAyB;SAAQ,CAAA;QAC3C;UACR,kBAAC,SAAD;OACE,MAAK;OACL,IAAG;OACH,cAAY,EAAG,8BAA8B,cAAc;OAC3D,OAAO;OACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;OAC9C,WAAU;OACV,aAAa,EACX,yCACA,4BACD;OACD,CAAA,CACE,EAAA,CAAA;MAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAU;iBACd,EAAG,sCAAsC,gBAAgB;OACpD,CAAA,EACR,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAkB,KAAK,MAAW;QACjC,IAAM,IAAO,GAAa;AAC1B,eACE,kBAAC,UAAD;SACE,MAAK;SAEL,eAAe,EAAkB,EAAO;SACxC,WAAW,EACT,+EACA,MAAmB,IACf,gCACA,2CACL;mBATH,CAWE,kBAAC,GAAD;UACE,MAAM;UACN,WACE,MAAmB,IACf,iBACA;UAEN,CAAA,EACF,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAqB,EAAO;UACxB,CAAA,CACA;WApBF,EAoBE;SAEX;OACE,CAAA,CACF,EAAA,CAAA;MAGN,kBAAC,OAAD,EAAA,UAAA;OACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,uCAAuC,iBAAiB;QACtD,CAAA;OACR,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SACE,WAAW,EACT,oFACD;mBAHH,CAKE,kBAAC,IAAD;UAAS,MAAM;UAAI,WAAU;UAAiB,CAAA,EAC9C,kBAAC,QAAD;UAAM,WAAU;oBAAsB;UAAU,CAAA,CAC5C;;QACF,CAAA;OACN,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,4CACA,wCACD;QACC,CAAA;OACA,EAAA,CAAA;MAGN,kBAAC,OAAD,EAAA,UAAA;OACE,kBAAC,SAAD;QAAO,WAAU;kBACf,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACE,kBAAC,IAAD,EAAe,MAAM,IAAM,CAAA;UAC1B,EAAG,mCAAmC,aAAa;UAAE;UACtD,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAG,iCAAiC,aAAa;WAC7C,CAAA;UACF;;QACD,CAAA;OACR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBAET,EAAG,kCAAkC,OAAO;SACvC,CAAA,EACR,kBAAC,SAAD;SACE,MAAK;SACL,IAAG;SACH,cAAY,EAAG,kCAAkC,OAAO;SACxD,OAAO;SACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAM;SAClD,WAAU;SACV,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBAET,EAAG,gCAAgC,KAAK;SACnC,CAAA,EACR,kBAAC,SAAD;SACE,MAAK;SACL,IAAG;SACH,cAAY,EAAG,gCAAgC,KAAK;SACpD,OAAO;SACP,KAAK;SACL,WAAW,MAAM,GAAgB,EAAE,OAAO,MAAM;SAChD,WAAU;SACV,CAAA,CACE,EAAA,CAAA,CACF;;OACN,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,8CACA,qDACD;QACC,CAAA;OACA,EAAA,CAAA;MAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAU;iBAAjB;QACG,EACC,yCACA,oBACD;QAAE;QACH,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,iCAAiC,aAAa;SAC7C,CAAA;QACD;UACP,EAAU,SAAS,IAClB,kBAAC,UAAD;OACE,OAAO,KAAsB;OAC7B,WAAW,MACT,EAAsB,EAAE,OAAO,SAAS,KAAA,EAAU;OAEpD,WAAU;iBALZ,CAOE,kBAAC,UAAD;QAAQ,OAAM;kBACX,EAAG,oCAAoC,cAAc;QAC/C,CAAA,EACR,EAAU,KAAK,MACd,kBAAC,UAAD;QAAmB,OAAO,EAAE;kBACzB,EAAE;QACI,EAFI,EAAE,GAEN,CACT,CACK;WAET,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAG,qCAAqC,oBAAoB;QAAE;QAC/D,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,IAAW,IAAW,aAAa;SAClD,WAAU;mBAET,EACC,4CACA,aACD;SACM,CAAA;QAAC;QACT,EACC,2CACA,kCACD;QACC;SAEF,EAAA,CAAA;MACF;;IACF,CAAA;GAIP,MAAS,YACR,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD;OAAc,MAAM;OAAI,WAAU;OAAiB,CAAA,EAClD,EAAG,8BAA8B,gBAAgB,CAC/C;SACL,kBAAC,MAAD;MAAI,WAAU;gBACX;OACC;QACE,OAAO,EAAG,oCAAoC,OAAO;QACrD,OAAO;QACR;OACD;QACE,OAAO,EAAG,qCAAqC,QAAQ;QACvD,OAAO,EAAc,EAAc;QACpC;OACD;QACE,OAAO,EACL,2CACA,eACD;QACD,OAAO,EAAiB,KAAK,KAAK;QACnC;OACD;QACE,OAAO,EAAG,sCAAsC,SAAS;QACzD,OAAO,EAAqB,EAAe;QAC5C;OACD;QACE,OAAO,EAAG,uCAAuC,UAAU;QAC3D,OAAO;QACR;OACD,GAAI,KAAkB,IAClB,CACE;QACE,OAAO,EACL,yCACA,aACD;QACD,OAAO,CACL,KAAkB,KAClB,KAAgB,IACjB,CAAC,KAAK,MAAM;QACd,CACF,GACD,EAAE;OACN,GAAI,IACA,CACE;QACE,OAAO,EACL,wCACA,WACD;QACD,OACE,EAAU,MAAM,MAAM,EAAE,OAAO,EAAmB,EAC9C,QAAQ;QACf,CACF,GACD,EAAE;OACP,CAAC,KAAK,EAAE,UAAO,eACd,kBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,MAAD;QAAI,WAAU;kBAAyB;QAAW,CAAA,EAClD,kBAAC,MAAD;QAAI,WAAU;kBACX;QACE,CAAA,CACD;SAPC,EAOD,CACN;MACC,CAAA,CACD;;IACF,CAAA;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,MAAS,aACN,EAAG,gCAAgC,SAAS,GAC5C,EAAG,8BAA8B,OAAO;KACrC,CAAA,EACR,MAAS,WACR,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,UAAU;KACV,WAAU;eAJZ,CAMG,IACC,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAiB,CAAA,GAE9C,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EAEvB,EAAG,qCAAqC,eAAe,CACjD;SAET,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKG,EAAG,8BAA8B,OAAO,EACzC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;OAEP;;GACK"}
|
|
@@ -21,6 +21,7 @@ function y() {
|
|
|
21
21
|
children: [
|
|
22
22
|
/* @__PURE__ */ h("div", {
|
|
23
23
|
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",
|
|
24
|
+
"data-tour": "export-dashboard-quick-actions",
|
|
24
25
|
children: [
|
|
25
26
|
{
|
|
26
27
|
icon: /* @__PURE__ */ h(m, { size: 20 }),
|
|
@@ -75,6 +76,7 @@ function y() {
|
|
|
75
76
|
}, e.path + e.label))
|
|
76
77
|
}),
|
|
77
78
|
E.length > 0 && /* @__PURE__ */ h(c, {
|
|
79
|
+
"data-tour": "export-dashboard-active-section",
|
|
78
80
|
title: S("export.dashboard.active.title", "Active Exports"),
|
|
79
81
|
description: S("export.dashboard.active.description", "Currently running export jobs"),
|
|
80
82
|
children: /* @__PURE__ */ h("div", {
|
|
@@ -97,6 +99,7 @@ function y() {
|
|
|
97
99
|
})
|
|
98
100
|
}),
|
|
99
101
|
/* @__PURE__ */ h(c, {
|
|
102
|
+
"data-tour": "export-dashboard-recent-section",
|
|
100
103
|
title: S("export.dashboard.recent.title", "Recent Exports"),
|
|
101
104
|
description: S("export.dashboard.recent.description", "Your latest export jobs"),
|
|
102
105
|
actions: T.length > 0 ? /* @__PURE__ */ h("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportDashboardPage.js","names":[],"sources":["../../../src/export/pages/ExportDashboardPage.tsx"],"sourcesContent":["import {\n Plus,\n Download,\n Clock,\n FileBox,\n ArrowRight,\n Activity,\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 { ExportStatusBadge } from \"../components/ExportStatusBadge\";\nimport { ExportProgressBar } from \"../components/ExportProgressBar\";\nimport { PageLayout, PageSection, EmptyState } from \"../components/PageLayout\";\nimport { cn } from \"../utils/cn\";\nimport { tWithFallback } from \"../utils/i18n\";\n\n/**\n * Helper to join paths correctly, avoiding double slashes\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 Dashboard Page\n * Overview with recent exports, quick actions, and status summary\n */\nexport function ExportDashboardPage() {\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: jobs, isLoading } = useExportJobs({});\n\n const recentJobs = jobs.slice(0, 5);\n const activeJobs = jobs.filter(\n (j) => j.status === \"RUNNING\" || j.status === \"QUEUED\",\n );\n\n const handleNavigate = (path: string) => {\n if (!navigate) return;\n navigate(joinPath(basePath, path));\n };\n\n return (\n <PageLayout\n title={tr(\"export.dashboard.title\", \"Export\")}\n subtitle={tr(\n \"export.dashboard.subtitle\",\n \"Export your workspace data in multiple formats\",\n )}\n icon={<Download size={24} />}\n >\n {/* Quick Actions */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n icon: <Plus size={20} />,\n label: tr(\"export.dashboard.actions.newExport.label\", \"New Export\"),\n description: tr(\n \"export.dashboard.actions.newExport.description\",\n \"Create a new data export\",\n ),\n path: \"new\",\n color: \"bg-primary/10 text-primary\",\n },\n {\n icon: <Clock size={20} />,\n label: tr(\"export.dashboard.actions.history.label\", \"History\"),\n description: tr(\n \"export.dashboard.actions.history.description\",\n \"View past exports\",\n ),\n path: \"history\",\n color: \"bg-status-info-bg-subtle text-status-info-text\",\n },\n {\n icon: <FileBox size={20} />,\n label: tr(\"export.dashboard.actions.templates.label\", \"Templates\"),\n description: tr(\n \"export.dashboard.actions.templates.description\",\n \"Reusable export configs\",\n ),\n path: \"templates\",\n color: \"bg-accent-purple-subtle text-accent-purple\",\n },\n {\n icon: <Activity size={20} />,\n label: tr(\n \"export.dashboard.actions.activeJobs.label\",\n \"Active Jobs\",\n ),\n description: tr(\n \"export.dashboard.actions.activeJobs.description\",\n \"{count} running\",\n { count: activeJobs.length },\n ),\n path: \"history?status=RUNNING\",\n color: \"bg-status-success-bg-subtle text-status-success-text\",\n },\n ].map((action) => (\n <button\n type=\"button\"\n key={action.path + action.label}\n onClick={() => handleNavigate(action.path)}\n className={cn(\n \"group relative overflow-hidden border-2 border-border/50 rounded-xl p-5\",\n \"bg-gradient-to-br from-card/50 to-background shadow-sm\",\n \"hover:shadow-md hover:border-primary/30 transition-all duration-200\",\n \"text-left\",\n )}\n >\n <div className={cn(\"p-2.5 rounded-lg w-fit mb-3\", action.color)}>\n {action.icon}\n </div>\n <h3 className=\"font-semibold text-foreground mb-1\">\n {action.label}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n {action.description}\n </p>\n <ArrowRight\n size={16}\n className=\"absolute top-5 right-5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n </button>\n ))}\n </div>\n\n {/* Active Exports */}\n {activeJobs.length > 0 && (\n <PageSection\n title={tr(\"export.dashboard.active.title\", \"Active Exports\")}\n description={tr(\n \"export.dashboard.active.description\",\n \"Currently running export jobs\",\n )}\n >\n <div className=\"space-y-3\">\n {activeJobs.map((job) => (\n <button\n type=\"button\"\n key={job.id}\n onClick={() => handleNavigate(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 \"bg-gradient-to-r from-card/50 to-background\",\n )}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h4 className=\"font-semibold text-foreground\">{job.name}</h4>\n <ExportStatusBadge status={job.status} />\n </div>\n <ExportProgressBar progress={job.progress} size=\"sm\" />\n </button>\n ))}\n </div>\n </PageSection>\n )}\n\n {/* Recent Exports */}\n <PageSection\n title={tr(\"export.dashboard.recent.title\", \"Recent Exports\")}\n description={tr(\n \"export.dashboard.recent.description\",\n \"Your latest export jobs\",\n )}\n actions={\n recentJobs.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"history\")}\n className=\"text-sm text-primary hover:underline font-medium\"\n >\n {tr(\"export.dashboard.recent.actions.viewAll\", \"View All\")}\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"space-y-3\">\n {[...Array(3)].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 mb-2\">\n <div className=\"h-4 w-48 bg-muted rounded\" />\n <div className=\"h-5 w-20 bg-muted rounded-full\" />\n </div>\n <div className=\"h-3 w-32 bg-muted rounded mt-2\" />\n </div>\n ))}\n </div>\n ) : recentJobs.length > 0 ? (\n <div className=\"space-y-3\">\n {recentJobs.map((job, index) => (\n <button\n type=\"button\"\n key={job.id}\n onClick={() => handleNavigate(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 \"bg-gradient-to-r from-card/50 to-background\",\n \"animate-in fade-in-0 slide-in-from-bottom-2\",\n )}\n style={{ animationDelay: `${index * 50}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(\", \")} · {job.format}\n {job.totalEntities != null &&\n ` · ${tr(\n \"export.dashboard.recent.entitiesCount\",\n \"{count} entities\",\n { count: job.totalEntities.toLocaleString() },\n )}`}\n </p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <ExportStatusBadge status={job.status} />\n <ArrowRight\n size={16}\n className=\"text-muted-foreground hidden sm:block\"\n />\n </div>\n </div>\n </button>\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<Download size={48} />}\n title={tr(\"export.dashboard.empty.title\", \"No exports yet\")}\n description={tr(\n \"export.dashboard.empty.description\",\n \"Create your first export to start downloading your workspace data.\",\n )}\n action={\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"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 <Plus size={16} />\n {tr(\n \"export.dashboard.empty.actions.createFirst\",\n \"Create First Export\",\n )}\n </button>\n }\n />\n )}\n </PageSection>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ExportDashboardPage.js","names":[],"sources":["../../../src/export/pages/ExportDashboardPage.tsx"],"sourcesContent":["import {\n Plus,\n Download,\n Clock,\n FileBox,\n ArrowRight,\n Activity,\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 { ExportStatusBadge } from \"../components/ExportStatusBadge\";\nimport { ExportProgressBar } from \"../components/ExportProgressBar\";\nimport { PageLayout, PageSection, EmptyState } from \"../components/PageLayout\";\nimport { cn } from \"../utils/cn\";\nimport { tWithFallback } from \"../utils/i18n\";\n\n/**\n * Helper to join paths correctly, avoiding double slashes\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 Dashboard Page\n * Overview with recent exports, quick actions, and status summary\n */\nexport function ExportDashboardPage() {\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: jobs, isLoading } = useExportJobs({});\n\n const recentJobs = jobs.slice(0, 5);\n const activeJobs = jobs.filter(\n (j) => j.status === \"RUNNING\" || j.status === \"QUEUED\",\n );\n\n const handleNavigate = (path: string) => {\n if (!navigate) return;\n navigate(joinPath(basePath, path));\n };\n\n return (\n <PageLayout\n title={tr(\"export.dashboard.title\", \"Export\")}\n subtitle={tr(\n \"export.dashboard.subtitle\",\n \"Export your workspace data in multiple formats\",\n )}\n icon={<Download size={24} />}\n >\n {/* Quick Actions */}\n <div\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\"\n data-tour=\"export-dashboard-quick-actions\"\n >\n {[\n {\n icon: <Plus size={20} />,\n label: tr(\"export.dashboard.actions.newExport.label\", \"New Export\"),\n description: tr(\n \"export.dashboard.actions.newExport.description\",\n \"Create a new data export\",\n ),\n path: \"new\",\n color: \"bg-primary/10 text-primary\",\n },\n {\n icon: <Clock size={20} />,\n label: tr(\"export.dashboard.actions.history.label\", \"History\"),\n description: tr(\n \"export.dashboard.actions.history.description\",\n \"View past exports\",\n ),\n path: \"history\",\n color: \"bg-status-info-bg-subtle text-status-info-text\",\n },\n {\n icon: <FileBox size={20} />,\n label: tr(\"export.dashboard.actions.templates.label\", \"Templates\"),\n description: tr(\n \"export.dashboard.actions.templates.description\",\n \"Reusable export configs\",\n ),\n path: \"templates\",\n color: \"bg-accent-purple-subtle text-accent-purple\",\n },\n {\n icon: <Activity size={20} />,\n label: tr(\n \"export.dashboard.actions.activeJobs.label\",\n \"Active Jobs\",\n ),\n description: tr(\n \"export.dashboard.actions.activeJobs.description\",\n \"{count} running\",\n { count: activeJobs.length },\n ),\n path: \"history?status=RUNNING\",\n color: \"bg-status-success-bg-subtle text-status-success-text\",\n },\n ].map((action) => (\n <button\n type=\"button\"\n key={action.path + action.label}\n onClick={() => handleNavigate(action.path)}\n className={cn(\n \"group relative overflow-hidden border-2 border-border/50 rounded-xl p-5\",\n \"bg-gradient-to-br from-card/50 to-background shadow-sm\",\n \"hover:shadow-md hover:border-primary/30 transition-all duration-200\",\n \"text-left\",\n )}\n >\n <div className={cn(\"p-2.5 rounded-lg w-fit mb-3\", action.color)}>\n {action.icon}\n </div>\n <h3 className=\"font-semibold text-foreground mb-1\">\n {action.label}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n {action.description}\n </p>\n <ArrowRight\n size={16}\n className=\"absolute top-5 right-5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n </button>\n ))}\n </div>\n\n {/* Active Exports */}\n {activeJobs.length > 0 && (\n <PageSection\n data-tour=\"export-dashboard-active-section\"\n title={tr(\"export.dashboard.active.title\", \"Active Exports\")}\n description={tr(\n \"export.dashboard.active.description\",\n \"Currently running export jobs\",\n )}\n >\n <div className=\"space-y-3\">\n {activeJobs.map((job) => (\n <button\n type=\"button\"\n key={job.id}\n onClick={() => handleNavigate(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 \"bg-gradient-to-r from-card/50 to-background\",\n )}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h4 className=\"font-semibold text-foreground\">{job.name}</h4>\n <ExportStatusBadge status={job.status} />\n </div>\n <ExportProgressBar progress={job.progress} size=\"sm\" />\n </button>\n ))}\n </div>\n </PageSection>\n )}\n\n {/* Recent Exports */}\n <PageSection\n data-tour=\"export-dashboard-recent-section\"\n title={tr(\"export.dashboard.recent.title\", \"Recent Exports\")}\n description={tr(\n \"export.dashboard.recent.description\",\n \"Your latest export jobs\",\n )}\n actions={\n recentJobs.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"history\")}\n className=\"text-sm text-primary hover:underline font-medium\"\n >\n {tr(\"export.dashboard.recent.actions.viewAll\", \"View All\")}\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"space-y-3\">\n {[...Array(3)].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 mb-2\">\n <div className=\"h-4 w-48 bg-muted rounded\" />\n <div className=\"h-5 w-20 bg-muted rounded-full\" />\n </div>\n <div className=\"h-3 w-32 bg-muted rounded mt-2\" />\n </div>\n ))}\n </div>\n ) : recentJobs.length > 0 ? (\n <div className=\"space-y-3\">\n {recentJobs.map((job, index) => (\n <button\n type=\"button\"\n key={job.id}\n onClick={() => handleNavigate(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 \"bg-gradient-to-r from-card/50 to-background\",\n \"animate-in fade-in-0 slide-in-from-bottom-2\",\n )}\n style={{ animationDelay: `${index * 50}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(\", \")} · {job.format}\n {job.totalEntities != null &&\n ` · ${tr(\n \"export.dashboard.recent.entitiesCount\",\n \"{count} entities\",\n { count: job.totalEntities.toLocaleString() },\n )}`}\n </p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <ExportStatusBadge status={job.status} />\n <ArrowRight\n size={16}\n className=\"text-muted-foreground hidden sm:block\"\n />\n </div>\n </div>\n </button>\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<Download size={48} />}\n title={tr(\"export.dashboard.empty.title\", \"No exports yet\")}\n description={tr(\n \"export.dashboard.empty.description\",\n \"Create your first export to start downloading your workspace data.\",\n )}\n action={\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"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 <Plus size={16} />\n {tr(\n \"export.dashboard.empty.actions.createFirst\",\n \"Create First Export\",\n )}\n </button>\n }\n />\n )}\n </PageSection>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/export/utils/disabledModules.ts
|
|
2
|
+
var e = [
|
|
3
|
+
"bigconsole",
|
|
4
|
+
"botlit",
|
|
5
|
+
"healthybowl",
|
|
6
|
+
"crewfoundry",
|
|
7
|
+
"movethewheels",
|
|
8
|
+
"eventfullymanaged",
|
|
9
|
+
"ggnomad",
|
|
10
|
+
"headshotmarketing",
|
|
11
|
+
"kadaikodi",
|
|
12
|
+
"timecampus",
|
|
13
|
+
"assethandler",
|
|
14
|
+
"housingvista",
|
|
15
|
+
"planmagnet"
|
|
16
|
+
];
|
|
17
|
+
function t(t) {
|
|
18
|
+
return e.some((e) => t === e || t.startsWith(`${e}_`));
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { t as isExportEntityDisabled };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=disabledModules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disabledModules.js","names":[],"sources":["../../../src/export/utils/disabledModules.ts"],"sourcesContent":["/**\n * Export entity availability.\n *\n * For now, export is limited to the **workspaces app** and **vibecontrols**.\n * Entity types belonging to any other product are still shown in the export\n * wizard but rendered **disabled** (greyed out, non-selectable) rather than\n * hidden — so users can see the capability exists without selecting it.\n *\n * Matching is by entity-type module prefix (entity types are seeded as\n * `<module>_<thing>`, e.g. `bigconsole_dashboards`). Workspaces-app/core\n * modules and `vibecontrols_*` are intentionally absent from this list and\n * stay enabled.\n *\n * Reversible: remove a prefix here to re-enable that product's export entities.\n *\n * NOTE: this is a denylist, so a *new* product seeded on the backend is enabled\n * by default until its prefix is added here. That's deliberate — workspaces-core\n * modules (the allowed set) change far more often than products are added, and an\n * allowlist would silently hide new core entity types. Adding a new product's\n * export entities should include adding its prefix here.\n */\nexport const DISABLED_EXPORT_MODULE_PREFIXES = [\n \"bigconsole\",\n \"botlit\",\n \"healthybowl\",\n \"crewfoundry\",\n \"movethewheels\",\n \"eventfullymanaged\",\n \"ggnomad\",\n \"headshotmarketing\",\n \"kadaikodi\",\n \"timecampus\",\n \"assethandler\",\n \"housingvista\",\n \"planmagnet\",\n] as const;\n\n/**\n * True if the entity type belongs to a product whose export is disabled for now\n * (i.e. not part of the workspaces app or vibecontrols).\n */\nexport function isExportEntityDisabled(entityType: string): boolean {\n return DISABLED_EXPORT_MODULE_PREFIXES.some(\n (prefix) => entityType === prefix || entityType.startsWith(`${prefix}_`),\n );\n}\n"],"mappings":";AAqBA,IAAa,IAAkC;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAMD,SAAgB,EAAuB,GAA6B;AAClE,QAAO,EAAgC,MACpC,MAAW,MAAe,KAAU,EAAW,WAAW,GAAG,EAAO,GAAG,CACzE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-export",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.530.1",
|
|
4
4
|
"description": "Burdenoff Export Microfrontend - Data export management interface",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@apollo/client": "^4.0.11",
|
|
43
|
-
"@burdenoff/fe-libs": "2026.
|
|
43
|
+
"@burdenoff/fe-libs": "2026.529.2",
|
|
44
44
|
"@tanstack/react-query": "^5.90.16",
|
|
45
45
|
"clsx": "^2.1.1",
|
|
46
46
|
"graphql": "^16.10.0",
|