@burdenoff/microfe-export 2026.801.1 → 2026.830.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/export/ExportRoot.d.ts.map +1 -1
  2. package/dist/export/ExportRoot.js +41 -36
  3. package/dist/export/ExportRoot.js.map +1 -1
  4. package/dist/export/ExportRoutes.d.ts.map +1 -1
  5. package/dist/export/ExportRoutes.js +75 -66
  6. package/dist/export/ExportRoutes.js.map +1 -1
  7. package/dist/export/components/ExportProgressBar.d.ts.map +1 -1
  8. package/dist/export/components/ExportProgressBar.js +19 -18
  9. package/dist/export/components/ExportProgressBar.js.map +1 -1
  10. package/dist/export/components/ExportStatusBadge.d.ts.map +1 -1
  11. package/dist/export/components/ExportStatusBadge.js +19 -20
  12. package/dist/export/components/ExportStatusBadge.js.map +1 -1
  13. package/dist/export/hooks/useTr.d.ts +19 -0
  14. package/dist/export/hooks/useTr.d.ts.map +1 -0
  15. package/dist/export/hooks/useTr.js +15 -0
  16. package/dist/export/hooks/useTr.js.map +1 -0
  17. package/dist/export/index.d.ts +1 -0
  18. package/dist/export/index.d.ts.map +1 -1
  19. package/dist/export/index.js +1 -0
  20. package/dist/export/pages/CreateExportPage.d.ts +4 -0
  21. package/dist/export/pages/CreateExportPage.d.ts.map +1 -1
  22. package/dist/export/pages/CreateExportPage.js +271 -283
  23. package/dist/export/pages/CreateExportPage.js.map +1 -1
  24. package/dist/export/pages/ExportDashboardPage.d.ts.map +1 -1
  25. package/dist/export/pages/ExportDashboardPage.js +71 -72
  26. package/dist/export/pages/ExportDashboardPage.js.map +1 -1
  27. package/dist/export/pages/ExportDetailPage.d.ts.map +1 -1
  28. package/dist/export/pages/ExportDetailPage.js +171 -173
  29. package/dist/export/pages/ExportDetailPage.js.map +1 -1
  30. package/dist/export/pages/ExportHistoryPage.d.ts.map +1 -1
  31. package/dist/export/pages/ExportHistoryPage.js +110 -111
  32. package/dist/export/pages/ExportHistoryPage.js.map +1 -1
  33. package/dist/export/pages/ExportLandingPage.d.ts.map +1 -1
  34. package/dist/export/pages/ExportLandingPage.js +28 -27
  35. package/dist/export/pages/ExportLandingPage.js.map +1 -1
  36. package/dist/export/pages/ExportTemplatesPage.d.ts.map +1 -1
  37. package/dist/export/pages/ExportTemplatesPage.js +115 -116
  38. package/dist/export/pages/ExportTemplatesPage.js.map +1 -1
  39. package/dist/export/pages/TemplateDetailPage.d.ts.map +1 -1
  40. package/dist/export/pages/TemplateDetailPage.js +122 -118
  41. package/dist/export/pages/TemplateDetailPage.js.map +1 -1
  42. package/dist/export/utils/i18n.d.ts +18 -2
  43. package/dist/export/utils/i18n.d.ts.map +1 -1
  44. package/dist/export/utils/i18n.js +2 -9
  45. package/dist/export/utils/i18n.js.map +1 -1
  46. package/dist/index.js +2 -1
  47. package/dist/translations/en.d.ts +454 -0
  48. package/dist/translations/en.d.ts.map +1 -0
  49. package/dist/translations/en.js +417 -0
  50. package/dist/translations/en.js.map +1 -0
  51. package/package.json +2 -2
@@ -1,228 +1,232 @@
1
1
  import { useExport as e } from "../providers/ExportProvider.js";
2
- import { useDeleteExportTemplate as t, useExportTemplate as n } from "../hooks/useExportQueries.js";
3
- import { getArchiveFormatDisplayName as r, getFormatDisplayName as i } from "../constants/enums.js";
4
- import { PageLayout as a, PageSection as o } from "../components/PageLayout.js";
5
- import { nativeImpact as s, nativeNotify as c } from "../../utils/nativeBridge.js";
6
- import { useState as l } from "react";
7
- import { ArrowLeft as u, Clock as d, Download as f, FileBox as p, FileText as m, HardDrive as h, Loader2 as g, Play as _, Trash2 as v } from "lucide-react";
8
- import { useParams as y } from "react-router";
9
- import { jsx as b, jsxs as x } from "react/jsx-runtime";
10
- import { CTAOverflowMenu as S, GlassCard as C, IllustratedEmptyState as w, NextSteps as T, ShowMore as E } from "@burdenoff/fe-libs/ui";
2
+ import { useTr as t } from "../hooks/useTr.js";
3
+ import { useDeleteExportTemplate as n, useExportTemplate as r } from "../hooks/useExportQueries.js";
4
+ import { getArchiveFormatDisplayName as i, getFormatDisplayName as a } from "../constants/enums.js";
5
+ import { PageLayout as o, PageSection as s } from "../components/PageLayout.js";
6
+ import { nativeImpact as c, nativeNotify as l } from "../../utils/nativeBridge.js";
7
+ import { useState as u } from "react";
8
+ import { ArrowLeft as d, Clock as f, Download as p, FileBox as m, FileText as h, HardDrive as g, Loader2 as _, Play as v, Trash2 as y } from "lucide-react";
9
+ import { useParams as b } from "react-router";
10
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
11
+ import { CTAOverflowMenu as C, GlassCard as w, IllustratedEmptyState as T, NextSteps as E, ShowMore as D } from "@burdenoff/fe-libs/ui";
11
12
  //#region src/export/pages/TemplateDetailPage.tsx
12
- var D = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
13
- function O() {
14
- let { templateId: O } = y(), { basePath: k = "/", navigate: A } = e(), [j, M] = l(!1), { data: N, isLoading: P } = n(O), { deleteTemplate: F, isPending: I } = t();
15
- if (P) return /* @__PURE__ */ x("div", {
13
+ var O = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
14
+ function k() {
15
+ let { templateId: k } = b(), { basePath: A = "/", navigate: j } = e(), M = t(), [N, P] = u(!1), { data: F, isLoading: I } = r(k), { deleteTemplate: L, isPending: R } = n();
16
+ if (I) return /* @__PURE__ */ S("div", {
16
17
  className: "flex flex-col items-center justify-center py-20",
17
- children: [/* @__PURE__ */ b(g, {
18
+ children: [/* @__PURE__ */ x(_, {
18
19
  size: 48,
19
20
  className: "animate-spin text-primary"
20
- }), /* @__PURE__ */ b("span", {
21
+ }), /* @__PURE__ */ x("span", {
21
22
  className: "mt-6 text-sm font-medium text-text-secondary",
22
- children: "Loading template details…"
23
+ children: M("export.templateDetail.loading", "Loading template details…")
23
24
  })]
24
25
  });
25
- if (!N) return /* @__PURE__ */ x(a, {
26
+ if (!F) return /* @__PURE__ */ S(o, {
26
27
  showHeader: !1,
27
- children: [/* @__PURE__ */ x("div", {
28
+ children: [/* @__PURE__ */ S("div", {
28
29
  className: "flex items-center gap-3 mb-6",
29
- children: [/* @__PURE__ */ b("button", {
30
+ children: [/* @__PURE__ */ x("button", {
30
31
  type: "button",
31
- onClick: () => A?.(D(k, "templates")),
32
- "aria-label": "Back to templates",
32
+ onClick: () => j?.(O(A, "templates")),
33
+ "aria-label": M("export.templateDetail.actions.backToTemplates", "Back to templates"),
33
34
  className: "p-2.5 hover:bg-accent rounded-lg transition-all duration-200",
34
- children: /* @__PURE__ */ b(u, { size: 20 })
35
- }), /* @__PURE__ */ b("h1", {
35
+ children: /* @__PURE__ */ x(d, { size: 20 })
36
+ }), /* @__PURE__ */ x("h1", {
36
37
  className: "text-xl font-bold",
37
- children: "Template Not Found"
38
+ children: M("export.templateDetail.notFound.title", "Template Not Found")
38
39
  })]
39
- }), /* @__PURE__ */ b(w, {
40
+ }), /* @__PURE__ */ x(T, {
40
41
  illustration: "empty-data",
41
- title: "Template not found",
42
- description: "The template you are looking for does not exist.",
43
- action: /* @__PURE__ */ x("button", {
42
+ title: M("export.templateDetail.notFound.heading", "Template not found"),
43
+ description: M("export.templateDetail.notFound.description", "The template you are looking for does not exist."),
44
+ action: /* @__PURE__ */ S("button", {
44
45
  type: "button",
45
- onClick: () => A?.(D(k, "templates")),
46
+ onClick: () => j?.(O(A, "templates")),
46
47
  className: "inline-flex items-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium",
47
- children: [/* @__PURE__ */ b(u, { size: 18 }), "Back to Templates"]
48
+ children: [/* @__PURE__ */ x(d, { size: 18 }), M("export.templateDetail.actions.backToTemplates", "Back to Templates")]
48
49
  })
49
50
  })]
50
51
  });
51
- let L = () => {
52
- A?.(D(k, `new?templateId=${O}`));
53
- }, R = async () => {
54
- if (O) {
55
- s("medium");
52
+ let z = () => {
53
+ j?.(O(A, `new?templateId=${k}`));
54
+ }, B = async () => {
55
+ if (k) {
56
+ c("medium");
56
57
  try {
57
- await F(O), c("success"), A?.(D(k, "templates"));
58
+ await L(k), l("success"), j?.(O(A, "templates"));
58
59
  } catch {
59
- c("error");
60
+ l("error");
60
61
  }
61
62
  }
62
- }, z = N.isDefault ? [] : [{
63
- label: "Delete Template",
64
- icon: /* @__PURE__ */ b(v, { size: 16 }),
63
+ }, V = F.isDefault ? [] : [{
64
+ label: M("export.templateDetail.actions.delete", "Delete Template"),
65
+ icon: /* @__PURE__ */ x(y, { size: 16 }),
65
66
  intent: "destructive",
66
- disabled: I,
67
- onSelect: () => M(!0)
68
- }], B = [{
67
+ disabled: R,
68
+ onSelect: () => P(!0)
69
+ }], H = [{
69
70
  id: "use",
70
- label: "Start an export from this template",
71
- description: "Pre-fills a new export with this template's entity types and format.",
72
- icon: /* @__PURE__ */ b(_, { size: 16 }),
73
- onClick: L
71
+ label: M("export.templateDetail.nextSteps.use.label", "Start an export from this template"),
72
+ description: M("export.templateDetail.nextSteps.use.description", "Pre-fills a new export with this template's entity types and format."),
73
+ icon: /* @__PURE__ */ x(v, { size: 16 }),
74
+ onClick: z
74
75
  }, {
75
76
  id: "browse",
76
- label: "Browse all templates",
77
- description: "Manage your other saved export configurations.",
78
- icon: /* @__PURE__ */ b(p, { size: 16 }),
79
- onClick: () => A?.(D(k, "templates"))
77
+ label: M("export.templateDetail.nextSteps.browse.label", "Browse all templates"),
78
+ description: M("export.templateDetail.nextSteps.browse.description", "Manage your other saved export configurations."),
79
+ icon: /* @__PURE__ */ x(m, { size: 16 }),
80
+ onClick: () => j?.(O(A, "templates"))
80
81
  }];
81
- return /* @__PURE__ */ x(a, {
82
+ return /* @__PURE__ */ S(o, {
82
83
  showHeader: !1,
83
84
  children: [
84
- /* @__PURE__ */ b("div", {
85
+ /* @__PURE__ */ x("div", {
85
86
  className: "relative overflow-hidden rounded-xl bg-accent-wash border border-border-seam p-6 shadow-[var(--shadow-pop)]",
86
- children: /* @__PURE__ */ x("div", {
87
+ children: /* @__PURE__ */ S("div", {
87
88
  className: "relative z-10 flex flex-col sm:flex-row sm:items-center gap-4",
88
89
  children: [
89
- /* @__PURE__ */ b("button", {
90
+ /* @__PURE__ */ x("button", {
90
91
  type: "button",
91
- onClick: () => A?.(D(k, "templates")),
92
- "aria-label": "Back to templates",
92
+ onClick: () => j?.(O(A, "templates")),
93
+ "aria-label": M("export.templateDetail.actions.backToTemplates", "Back to templates"),
93
94
  className: "p-2.5 hover:bg-accent rounded-lg transition-colors duration-200 self-start focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
94
- children: /* @__PURE__ */ b(u, { size: 20 })
95
+ children: /* @__PURE__ */ x(d, { size: 20 })
95
96
  }),
96
- /* @__PURE__ */ x("div", {
97
+ /* @__PURE__ */ S("div", {
97
98
  className: "flex items-center gap-3 flex-1 min-w-0",
98
- children: [/* @__PURE__ */ b("div", {
99
+ children: [/* @__PURE__ */ x("div", {
99
100
  className: "p-2.5 rounded-lg bg-action-primary-bg/10",
100
- children: /* @__PURE__ */ b(p, {
101
+ children: /* @__PURE__ */ x(m, {
101
102
  size: 24,
102
103
  className: "text-primary"
103
104
  })
104
- }), /* @__PURE__ */ x("div", {
105
+ }), /* @__PURE__ */ S("div", {
105
106
  className: "flex-1 min-w-0",
106
- children: [/* @__PURE__ */ b("h1", {
107
+ children: [/* @__PURE__ */ x("h1", {
107
108
  className: "text-xl sm:text-2xl font-bold truncate",
108
- children: N.name
109
- }), /* @__PURE__ */ b("p", {
109
+ children: F.name
110
+ }), /* @__PURE__ */ x("p", {
110
111
  className: "text-sm text-text-secondary mt-1",
111
- children: N.isDefault ? "Default template" : "Custom template"
112
+ children: F.isDefault ? M("export.templateDetail.badge.default", "Default template") : M("export.templateDetail.badge.custom", "Custom template")
112
113
  })]
113
114
  })]
114
115
  }),
115
- /* @__PURE__ */ b("div", {
116
+ /* @__PURE__ */ x("div", {
116
117
  className: "flex-shrink-0",
117
- children: /* @__PURE__ */ b(S, {
118
+ children: /* @__PURE__ */ x(C, {
118
119
  primary: {
119
- label: "Use Template",
120
- icon: /* @__PURE__ */ b(_, { size: 16 }),
121
- onSelect: L
120
+ label: M("export.templateDetail.actions.use", "Use Template"),
121
+ icon: /* @__PURE__ */ x(v, { size: 16 }),
122
+ onSelect: z
122
123
  },
123
- actions: z
124
+ actions: V
124
125
  })
125
126
  })
126
127
  ]
127
128
  })
128
129
  }),
129
- N.description && /* @__PURE__ */ b("div", {
130
+ F.description && /* @__PURE__ */ x("div", {
130
131
  className: "border border-border-seam rounded-xl p-5 bg-bg-surface",
131
- children: /* @__PURE__ */ b("p", {
132
+ children: /* @__PURE__ */ x("p", {
132
133
  className: "text-sm text-text-secondary",
133
- children: N.description
134
+ children: F.description
134
135
  })
135
136
  }),
136
- /* @__PURE__ */ b("div", {
137
+ /* @__PURE__ */ x("div", {
137
138
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",
138
139
  children: [
139
140
  {
140
- icon: /* @__PURE__ */ b(m, { size: 18 }),
141
- label: "Format",
142
- value: i(N.format)
141
+ icon: /* @__PURE__ */ x(h, { size: 18 }),
142
+ label: M("export.templateDetail.info.format", "Format"),
143
+ value: a(F.format)
143
144
  },
144
145
  {
145
- icon: /* @__PURE__ */ b(h, { size: 18 }),
146
- label: "Archive",
147
- value: r("ZIP")
146
+ icon: /* @__PURE__ */ x(g, { size: 18 }),
147
+ label: M("export.templateDetail.info.archive", "Archive"),
148
+ value: i("ZIP")
148
149
  },
149
150
  {
150
- icon: /* @__PURE__ */ b(f, { size: 18 }),
151
- label: "Entity Types",
152
- value: `${N.entityTypes.length} type${N.entityTypes.length === 1 ? "" : "s"}`
151
+ icon: /* @__PURE__ */ x(p, { size: 18 }),
152
+ label: M("export.templateDetail.info.entityTypes", "Entity Types"),
153
+ value: M("export.templateDetail.info.entityTypesCount", "{{count}} type{{suffix}}", {
154
+ count: F.entityTypes.length,
155
+ suffix: F.entityTypes.length === 1 ? "" : "s"
156
+ })
153
157
  },
154
158
  {
155
- icon: /* @__PURE__ */ b(d, { size: 18 }),
156
- label: "Last Updated",
157
- value: new Date(N.updatedAt).toLocaleDateString()
159
+ icon: /* @__PURE__ */ x(f, { size: 18 }),
160
+ label: M("export.templateDetail.info.lastUpdated", "Last Updated"),
161
+ value: new Date(F.updatedAt).toLocaleDateString()
158
162
  }
159
- ].map((e) => /* @__PURE__ */ x(C, {
163
+ ].map((e) => /* @__PURE__ */ S(w, {
160
164
  treatment: "metric",
161
165
  className: "p-4 border-border-seam",
162
- children: [/* @__PURE__ */ x("div", {
166
+ children: [/* @__PURE__ */ S("div", {
163
167
  className: "flex items-center gap-2 text-text-secondary mb-1",
164
- children: [e.icon, /* @__PURE__ */ b("span", {
168
+ children: [e.icon, /* @__PURE__ */ x("span", {
165
169
  className: "text-xs font-medium",
166
170
  children: e.label
167
171
  })]
168
- }), /* @__PURE__ */ b("p", {
172
+ }), /* @__PURE__ */ x("p", {
169
173
  className: "font-bold text-lg tabular-nums",
170
174
  children: e.value
171
175
  })]
172
176
  }, e.label))
173
177
  }),
174
- /* @__PURE__ */ b(T, {
175
- storageKey: `export-template-${N.id}`,
176
- title: "What's next",
177
- steps: B
178
+ /* @__PURE__ */ x(E, {
179
+ storageKey: `export-template-${F.id}`,
180
+ title: M("export.templateDetail.nextSteps.title", "What's next"),
181
+ steps: H
178
182
  }),
179
- /* @__PURE__ */ b(o, {
180
- title: "Included Entity Types",
181
- description: "Data types that will be exported",
182
- children: /* @__PURE__ */ b(E, {
183
+ /* @__PURE__ */ x(s, {
184
+ title: M("export.templateDetail.entityTypes.title", "Included Entity Types"),
185
+ description: M("export.templateDetail.entityTypes.description", "Data types that will be exported"),
186
+ children: /* @__PURE__ */ x(D, {
183
187
  collapsedHeight: 96,
184
- children: /* @__PURE__ */ b("div", {
188
+ children: /* @__PURE__ */ x("div", {
185
189
  className: "flex flex-wrap gap-2",
186
- children: N.entityTypes.map((e) => /* @__PURE__ */ b("span", {
190
+ children: F.entityTypes.map((e) => /* @__PURE__ */ x("span", {
187
191
  className: "inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border border-border-seam bg-bg-sunken/30",
188
192
  children: e
189
193
  }, e))
190
194
  })
191
195
  })
192
196
  }),
193
- j && /* @__PURE__ */ b("div", {
197
+ N && /* @__PURE__ */ x("div", {
194
198
  role: "dialog",
195
199
  "aria-modal": "true",
196
200
  "aria-labelledby": "template-delete-title",
197
201
  className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-surface/80 backdrop-blur-sm p-4",
198
- children: /* @__PURE__ */ x("div", {
202
+ children: /* @__PURE__ */ S("div", {
199
203
  className: "w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface p-6 shadow-xl",
200
204
  children: [
201
- /* @__PURE__ */ b("h2", {
205
+ /* @__PURE__ */ x("h2", {
202
206
  id: "template-delete-title",
203
207
  className: "text-lg font-bold",
204
- children: "Delete template?"
208
+ children: M("export.templateDetail.delete.title", "Delete template?")
205
209
  }),
206
- /* @__PURE__ */ b("p", {
210
+ /* @__PURE__ */ x("p", {
207
211
  className: "mt-2 text-sm text-text-secondary",
208
- children: "This removes the saved export configuration. Existing export jobs are not deleted."
212
+ children: M("export.templateDetail.delete.description", "This removes the saved export configuration. Existing export jobs are not deleted.")
209
213
  }),
210
- /* @__PURE__ */ x("div", {
214
+ /* @__PURE__ */ S("div", {
211
215
  className: "mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3",
212
- children: [/* @__PURE__ */ b("button", {
216
+ children: [/* @__PURE__ */ x("button", {
213
217
  type: "button",
214
- onClick: () => M(!1),
218
+ onClick: () => P(!1),
215
219
  className: "px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium",
216
- children: "Keep Template"
217
- }), /* @__PURE__ */ x("button", {
220
+ children: M("export.templateDetail.delete.keep", "Keep Template")
221
+ }), /* @__PURE__ */ S("button", {
218
222
  type: "button",
219
- onClick: R,
220
- disabled: I,
223
+ onClick: B,
224
+ disabled: R,
221
225
  className: "inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90 transition-colors font-medium disabled:opacity-50",
222
- children: [I ? /* @__PURE__ */ b(g, {
226
+ children: [R ? /* @__PURE__ */ x(_, {
223
227
  size: 16,
224
228
  className: "animate-spin"
225
- }) : null, "Delete Template"]
229
+ }) : null, M("export.templateDetail.delete.confirmAction", "Delete Template")]
226
230
  })]
227
231
  })
228
232
  ]
@@ -232,6 +236,6 @@ function O() {
232
236
  });
233
237
  }
234
238
  //#endregion
235
- export { O as TemplateDetailPage };
239
+ export { k as TemplateDetailPage };
236
240
 
237
241
  //# sourceMappingURL=TemplateDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router\";\nimport { useState } from \"react\";\nimport {\n ArrowLeft,\n FileBox,\n Download,\n Play,\n Trash2,\n Clock,\n FileText,\n HardDrive,\n Loader2,\n} from \"lucide-react\";\nimport {\n GlassCard,\n CTAOverflowMenu,\n ShowMore,\n IllustratedEmptyState,\n NextSteps,\n type NextStep,\n} from \"@burdenoff/fe-libs/ui\";\nimport type { CTAAction } from \"@burdenoff/fe-libs/ui\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplate,\n useDeleteExportTemplate,\n} from \"../hooks/useExportQueries\";\nimport {\n getFormatDisplayName,\n getArchiveFormatDisplayName,\n} from \"../constants/enums\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/**\n * Helper to join paths correctly\n */\nconst joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n};\n\n/**\n * Template Detail Page\n * View template details and use it to create a new export.\n * Fetches template from the backend via useExportTemplate hook.\n */\nexport function TemplateDetailPage() {\n const { templateId } = useParams<{ templateId: string }>();\n const { basePath = \"/\", navigate } = useExport();\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n\n const { data: template, isLoading } = useExportTemplate(templateId);\n const { deleteTemplate, isPending: deleting } = useDeleteExportTemplate();\n\n // Loading\n if (isLoading) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 size={48} className=\"animate-spin text-primary\" />\n <span className=\"mt-6 text-sm font-medium text-text-secondary\">\n Loading template details…\n </span>\n </div>\n );\n }\n\n if (!template) {\n return (\n <PageLayout showHeader={false}>\n <div className=\"flex items-center gap-3 mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n aria-label=\"Back to templates\"\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200\"\n >\n <ArrowLeft size={20} />\n </button>\n <h1 className=\"text-xl font-bold\">Template Not Found</h1>\n </div>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Template not found\"\n description=\"The template you are looking for does not exist.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"inline-flex items-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium\"\n >\n <ArrowLeft size={18} />\n Back to Templates\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const handleUseTemplate = () => {\n // Navigate to new export page pre-selected with this template\n navigate?.(joinPath(basePath, `new?templateId=${templateId}`));\n };\n\n const handleDelete = async () => {\n if (!templateId) return;\n nativeImpact(\"medium\");\n try {\n await deleteTemplate(templateId);\n nativeNotify(\"success\");\n navigate?.(joinPath(basePath, \"templates\"));\n } catch {\n nativeNotify(\"error\");\n }\n };\n\n const overflowActions: CTAAction[] = template.isDefault\n ? []\n : [\n {\n label: \"Delete Template\",\n icon: <Trash2 size={16} />,\n intent: \"destructive\",\n disabled: deleting,\n onSelect: () => setShowDeleteConfirm(true),\n },\n ];\n\n // Next-step guidance — what a new user would do with a saved template.\n const nextSteps: NextStep[] = [\n {\n id: \"use\",\n label: \"Start an export from this template\",\n description:\n \"Pre-fills a new export with this template's entity types and format.\",\n icon: <Play size={16} />,\n onClick: handleUseTemplate,\n },\n {\n id: \"browse\",\n label: \"Browse all templates\",\n description: \"Manage your other saved export configurations.\",\n icon: <FileBox size={16} />,\n onClick: () => navigate?.(joinPath(basePath, \"templates\")),\n },\n ];\n\n return (\n <PageLayout showHeader={false}>\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-xl bg-accent-wash border border-border-seam p-6 shadow-[var(--shadow-pop)]\">\n <div className=\"relative z-10 flex flex-col sm:flex-row sm:items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n aria-label=\"Back to templates\"\n className=\"p-2.5 hover:bg-accent rounded-lg transition-colors duration-200 self-start focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40\"\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex items-center gap-3 flex-1 min-w-0\">\n <div className=\"p-2.5 rounded-lg bg-action-primary-bg/10\">\n <FileBox size={24} className=\"text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold truncate\">\n {template.name}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {template.isDefault ? \"Default template\" : \"Custom template\"}\n </p>\n </div>\n </div>\n <div className=\"flex-shrink-0\">\n <CTAOverflowMenu\n primary={{\n label: \"Use Template\",\n icon: <Play size={16} />,\n onSelect: handleUseTemplate,\n }}\n actions={overflowActions}\n />\n </div>\n </div>\n </div>\n\n {/* Description */}\n {template.description && (\n <div className=\"border border-border-seam rounded-xl p-5 bg-bg-surface\">\n <p className=\"text-sm text-text-secondary\">{template.description}</p>\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n icon: <FileText size={18} />,\n label: \"Format\",\n value: getFormatDisplayName(template.format),\n },\n {\n icon: <HardDrive size={18} />,\n label: \"Archive\",\n // Backend template doesn't carry archiveFormat, default to ZIP\n value: getArchiveFormatDisplayName(\"ZIP\"),\n },\n {\n icon: <Download size={18} />,\n label: \"Entity Types\",\n value: `${template.entityTypes.length} type${template.entityTypes.length !== 1 ? \"s\" : \"\"}`,\n },\n {\n icon: <Clock size={18} />,\n label: \"Last Updated\",\n value: new Date(template.updatedAt).toLocaleDateString(),\n },\n ].map((item) => (\n <GlassCard\n key={item.label}\n treatment=\"metric\"\n className=\"p-4 border-border-seam\"\n >\n <div className=\"flex items-center gap-2 text-text-secondary mb-1\">\n {item.icon}\n <span className=\"text-xs font-medium\">{item.label}</span>\n </div>\n <p className=\"font-bold text-lg tabular-nums\">{item.value}</p>\n </GlassCard>\n ))}\n </div>\n\n {/* Next-step guidance (dismissable, per-template) */}\n <NextSteps\n storageKey={`export-template-${template.id}`}\n title=\"What's next\"\n steps={nextSteps}\n />\n\n {/* Entity Types */}\n <PageSection\n title=\"Included Entity Types\"\n description=\"Data types that will be exported\"\n >\n <ShowMore collapsedHeight={96}>\n <div className=\"flex flex-wrap gap-2\">\n {template.entityTypes.map((entity) => (\n <span\n key={entity}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border border-border-seam bg-bg-sunken/30\"\n >\n {entity}\n </span>\n ))}\n </div>\n </ShowMore>\n </PageSection>\n\n {showDeleteConfirm && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"template-delete-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-surface/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface p-6 shadow-xl\">\n <h2 id=\"template-delete-title\" className=\"text-lg font-bold\">\n Delete template?\n </h2>\n <p className=\"mt-2 text-sm text-text-secondary\">\n This removes the saved export configuration. Existing export jobs\n are not deleted.\n </p>\n <div className=\"mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(false)}\n className=\"px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium\"\n >\n Keep Template\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90 transition-colors font-medium disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : null}\n Delete Template\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../../src/export/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useParams } from \"react-router\";\nimport { useState } from \"react\";\nimport {\n ArrowLeft,\n FileBox,\n Download,\n Play,\n Trash2,\n Clock,\n FileText,\n HardDrive,\n Loader2,\n} from \"lucide-react\";\nimport {\n GlassCard,\n CTAOverflowMenu,\n ShowMore,\n IllustratedEmptyState,\n NextSteps,\n type NextStep,\n} from \"@burdenoff/fe-libs/ui\";\nimport type { CTAAction } from \"@burdenoff/fe-libs/ui\";\nimport { useExport } from \"../providers/ExportProvider\";\nimport {\n useExportTemplate,\n useDeleteExportTemplate,\n} from \"../hooks/useExportQueries\";\nimport {\n getFormatDisplayName,\n getArchiveFormatDisplayName,\n} from \"../constants/enums\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { useTr } from \"../hooks/useTr\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/**\n * Helper to join paths correctly\n */\nconst joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n};\n\n/**\n * Template Detail Page\n * View template details and use it to create a new export.\n * Fetches template from the backend via useExportTemplate hook.\n */\nexport function TemplateDetailPage() {\n const { templateId } = useParams<{ templateId: string }>();\n const { basePath = \"/\", navigate } = useExport();\n const tr = useTr();\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n\n const { data: template, isLoading } = useExportTemplate(templateId);\n const { deleteTemplate, isPending: deleting } = useDeleteExportTemplate();\n\n // Loading\n if (isLoading) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 size={48} className=\"animate-spin text-primary\" />\n <span className=\"mt-6 text-sm font-medium text-text-secondary\">\n {tr(\"export.templateDetail.loading\", \"Loading template details…\")}\n </span>\n </div>\n );\n }\n\n if (!template) {\n return (\n <PageLayout showHeader={false}>\n <div className=\"flex items-center gap-3 mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n aria-label={tr(\n \"export.templateDetail.actions.backToTemplates\",\n \"Back to templates\",\n )}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-all duration-200\"\n >\n <ArrowLeft size={20} />\n </button>\n <h1 className=\"text-xl font-bold\">\n {tr(\"export.templateDetail.notFound.title\", \"Template Not Found\")}\n </h1>\n </div>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr(\n \"export.templateDetail.notFound.heading\",\n \"Template not found\",\n )}\n description={tr(\n \"export.templateDetail.notFound.description\",\n \"The template you are looking for does not exist.\",\n )}\n action={\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n className=\"inline-flex items-center gap-2 px-6 py-3 bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bg/90 transition-colors font-medium\"\n >\n <ArrowLeft size={18} />\n {tr(\n \"export.templateDetail.actions.backToTemplates\",\n \"Back to Templates\",\n )}\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const handleUseTemplate = () => {\n // Navigate to new export page pre-selected with this template\n navigate?.(joinPath(basePath, `new?templateId=${templateId}`));\n };\n\n const handleDelete = async () => {\n if (!templateId) return;\n nativeImpact(\"medium\");\n try {\n await deleteTemplate(templateId);\n nativeNotify(\"success\");\n navigate?.(joinPath(basePath, \"templates\"));\n } catch {\n nativeNotify(\"error\");\n }\n };\n\n const overflowActions: CTAAction[] = template.isDefault\n ? []\n : [\n {\n label: tr(\"export.templateDetail.actions.delete\", \"Delete Template\"),\n icon: <Trash2 size={16} />,\n intent: \"destructive\",\n disabled: deleting,\n onSelect: () => setShowDeleteConfirm(true),\n },\n ];\n\n // Next-step guidance — what a new user would do with a saved template.\n const nextSteps: NextStep[] = [\n {\n id: \"use\",\n label: tr(\n \"export.templateDetail.nextSteps.use.label\",\n \"Start an export from this template\",\n ),\n description: tr(\n \"export.templateDetail.nextSteps.use.description\",\n \"Pre-fills a new export with this template's entity types and format.\",\n ),\n icon: <Play size={16} />,\n onClick: handleUseTemplate,\n },\n {\n id: \"browse\",\n label: tr(\n \"export.templateDetail.nextSteps.browse.label\",\n \"Browse all templates\",\n ),\n description: tr(\n \"export.templateDetail.nextSteps.browse.description\",\n \"Manage your other saved export configurations.\",\n ),\n icon: <FileBox size={16} />,\n onClick: () => navigate?.(joinPath(basePath, \"templates\")),\n },\n ];\n\n return (\n <PageLayout showHeader={false}>\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-xl bg-accent-wash border border-border-seam p-6 shadow-[var(--shadow-pop)]\">\n <div className=\"relative z-10 flex flex-col sm:flex-row sm:items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate?.(joinPath(basePath, \"templates\"))}\n aria-label={tr(\n \"export.templateDetail.actions.backToTemplates\",\n \"Back to templates\",\n )}\n className=\"p-2.5 hover:bg-accent rounded-lg transition-colors duration-200 self-start focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40\"\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex items-center gap-3 flex-1 min-w-0\">\n <div className=\"p-2.5 rounded-lg bg-action-primary-bg/10\">\n <FileBox size={24} className=\"text-primary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold truncate\">\n {template.name}\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {template.isDefault\n ? tr(\n \"export.templateDetail.badge.default\",\n \"Default template\",\n )\n : tr(\"export.templateDetail.badge.custom\", \"Custom template\")}\n </p>\n </div>\n </div>\n <div className=\"flex-shrink-0\">\n <CTAOverflowMenu\n primary={{\n label: tr(\"export.templateDetail.actions.use\", \"Use Template\"),\n icon: <Play size={16} />,\n onSelect: handleUseTemplate,\n }}\n actions={overflowActions}\n />\n </div>\n </div>\n </div>\n\n {/* Description */}\n {template.description && (\n <div className=\"border border-border-seam rounded-xl p-5 bg-bg-surface\">\n <p className=\"text-sm text-text-secondary\">{template.description}</p>\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n icon: <FileText size={18} />,\n label: tr(\"export.templateDetail.info.format\", \"Format\"),\n value: getFormatDisplayName(template.format),\n },\n {\n icon: <HardDrive size={18} />,\n label: tr(\"export.templateDetail.info.archive\", \"Archive\"),\n // Backend template doesn't carry archiveFormat, default to ZIP\n value: getArchiveFormatDisplayName(\"ZIP\"),\n },\n {\n icon: <Download size={18} />,\n label: tr(\"export.templateDetail.info.entityTypes\", \"Entity Types\"),\n value: tr(\n \"export.templateDetail.info.entityTypesCount\",\n \"{{count}} type{{suffix}}\",\n {\n count: template.entityTypes.length,\n suffix: template.entityTypes.length !== 1 ? \"s\" : \"\",\n },\n ),\n },\n {\n icon: <Clock size={18} />,\n label: tr(\"export.templateDetail.info.lastUpdated\", \"Last Updated\"),\n value: new Date(template.updatedAt).toLocaleDateString(),\n },\n ].map((item) => (\n <GlassCard\n key={item.label}\n treatment=\"metric\"\n className=\"p-4 border-border-seam\"\n >\n <div className=\"flex items-center gap-2 text-text-secondary mb-1\">\n {item.icon}\n <span className=\"text-xs font-medium\">{item.label}</span>\n </div>\n <p className=\"font-bold text-lg tabular-nums\">{item.value}</p>\n </GlassCard>\n ))}\n </div>\n\n {/* Next-step guidance (dismissable, per-template) */}\n <NextSteps\n storageKey={`export-template-${template.id}`}\n title={tr(\"export.templateDetail.nextSteps.title\", \"What's next\")}\n steps={nextSteps}\n />\n\n {/* Entity Types */}\n <PageSection\n title={tr(\n \"export.templateDetail.entityTypes.title\",\n \"Included Entity Types\",\n )}\n description={tr(\n \"export.templateDetail.entityTypes.description\",\n \"Data types that will be exported\",\n )}\n >\n <ShowMore collapsedHeight={96}>\n <div className=\"flex flex-wrap gap-2\">\n {template.entityTypes.map((entity) => (\n <span\n key={entity}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium border border-border-seam bg-bg-sunken/30\"\n >\n {entity}\n </span>\n ))}\n </div>\n </ShowMore>\n </PageSection>\n\n {showDeleteConfirm && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"template-delete-title\"\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-surface/80 backdrop-blur-sm p-4\"\n >\n <div className=\"w-full max-w-md rounded-xl border-2 border-border-subtle bg-bg-surface p-6 shadow-xl\">\n <h2 id=\"template-delete-title\" className=\"text-lg font-bold\">\n {tr(\"export.templateDetail.delete.title\", \"Delete template?\")}\n </h2>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {tr(\n \"export.templateDetail.delete.description\",\n \"This removes the saved export configuration. Existing export jobs are not deleted.\",\n )}\n </p>\n <div className=\"mt-6 flex flex-col-reverse sm:flex-row sm:justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(false)}\n className=\"px-4 py-2 rounded-lg border-2 border-border-subtle hover:bg-accent transition-colors font-medium\"\n >\n {tr(\"export.templateDetail.delete.keep\", \"Keep Template\")}\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={deleting}\n className=\"inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90 transition-colors font-medium disabled:opacity-50\"\n >\n {deleting ? (\n <Loader2 size={16} className=\"animate-spin\" />\n ) : null}\n {tr(\n \"export.templateDetail.delete.confirmAction\",\n \"Delete Template\",\n )}\n </button>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,22 @@
1
1
  import { I18nContextValue } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';
2
2
 
3
- type TranslationParams = Record<string, string | number>;
3
+ export type TranslationParams = Record<string, string | number>;
4
+ /**
5
+ * Interpolate `{{key}}` placeholders in a fallback string.
6
+ *
7
+ * Matches the VibeControls `useTr` interpolation convention so call sites
8
+ * across the platform stay consistent.
9
+ */
10
+ export declare function interpolateFallback(fallback: string, params?: TranslationParams): string;
11
+ /**
12
+ * Translate a key using the provided `t` function, falling back to the
13
+ * caller's English string when the key is missing.
14
+ *
15
+ * Supports optional interpolation with `{{key}}` placeholders.
16
+ *
17
+ * Prefer the `useTr()` hook in components; this helper is useful when a
18
+ * translation is needed outside a hook context (e.g. inside a callback that
19
+ * already has access to `t`).
20
+ */
4
21
  export declare function tWithFallback(t: I18nContextValue["t"], key: string, fallback: string, params?: TranslationParams): string;
5
- export {};
6
22
  //# sourceMappingURL=i18n.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../src/export/utils/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAC;AAE/F,KAAK,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAgBzD,wBAAgB,aAAa,CAC3B,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,EACxB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,iBAAiB,GACzB,MAAM,CAKR"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../src/export/utils/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAC;AAE/F,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,iBAAiB,GACzB,MAAM,CAUR;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,EACxB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,iBAAiB,GACzB,MAAM,CAIR"}
@@ -1,15 +1,8 @@
1
1
  //#region src/export/utils/i18n.ts
2
2
  function e(e, t) {
3
- return t ? e.replace(/\{(\w+)\}/g, (e, n) => {
4
- let r = t[n];
5
- return r == null ? e : String(r);
6
- }) : e;
7
- }
8
- function t(t, n, r, i) {
9
- let a = t(n, i);
10
- return a === n ? e(r, i) : a;
3
+ return t ? Object.entries(t).reduce((e, [t, n]) => e.replace(RegExp(`\\{\\{${t}\\}\\}`, "g"), String(n)), e) : e;
11
4
  }
12
5
  //#endregion
13
- export { t as tWithFallback };
6
+ export { e as interpolateFallback };
14
7
 
15
8
  //# sourceMappingURL=i18n.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","names":[],"sources":["../../../src/export/utils/i18n.ts"],"sourcesContent":["import type { I18nContextValue } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\ntype TranslationParams = Record<string, string | number>;\n\nfunction interpolateFallback(\n fallback: string,\n params?: TranslationParams,\n): string {\n if (!params) {\n return fallback;\n }\n\n return fallback.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n const value = params[key];\n return value == null ? match : String(value);\n });\n}\n\nexport function tWithFallback(\n t: I18nContextValue[\"t\"],\n key: string,\n fallback: string,\n params?: TranslationParams,\n): string {\n const translated = t(key, params);\n return translated === key\n ? interpolateFallback(fallback, params)\n : translated;\n}\n"],"mappings":";AAIA,SAAS,EACP,GACA,GACQ;AAKR,QAJK,IAIE,EAAS,QAAQ,eAAe,GAAO,MAAgB;EAC5D,IAAM,IAAQ,EAAO;AACrB,SAAO,KAAS,OAAO,IAAQ,OAAO,EAAM;GAC5C,GANO;;AASX,SAAgB,EACd,GACA,GACA,GACA,GACQ;CACR,IAAM,IAAa,EAAE,GAAK,EAAO;AACjC,QAAO,MAAe,IAClB,EAAoB,GAAU,EAAO,GACrC"}
1
+ {"version":3,"file":"i18n.js","names":[],"sources":["../../../src/export/utils/i18n.ts"],"sourcesContent":["import type { I18nContextValue } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nexport type TranslationParams = Record<string, string | number>;\n\n/**\n * Interpolate `{{key}}` placeholders in a fallback string.\n *\n * Matches the VibeControls `useTr` interpolation convention so call sites\n * across the platform stay consistent.\n */\nexport function interpolateFallback(\n fallback: string,\n params?: TranslationParams,\n): string {\n if (!params) {\n return fallback;\n }\n\n return Object.entries(params).reduce(\n (str, [key, value]) =>\n str.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, \"g\"), String(value)),\n fallback,\n );\n}\n\n/**\n * Translate a key using the provided `t` function, falling back to the\n * caller's English string when the key is missing.\n *\n * Supports optional interpolation with `{{key}}` placeholders.\n *\n * Prefer the `useTr()` hook in components; this helper is useful when a\n * translation is needed outside a hook context (e.g. inside a callback that\n * already has access to `t`).\n */\nexport function tWithFallback(\n t: I18nContextValue[\"t\"],\n key: string,\n fallback: string,\n params?: TranslationParams,\n): string {\n const translated = t(key);\n const raw = translated === key ? fallback : translated;\n return interpolateFallback(raw, params);\n}\n"],"mappings":";AAUA,SAAgB,EACd,GACA,GACQ;AAKR,QAJK,IAIE,OAAO,QAAQ,EAAO,CAAC,QAC3B,GAAK,CAAC,GAAK,OACV,EAAI,QAAY,OAAO,SAAS,EAAI,SAAS,IAAI,EAAE,OAAO,EAAM,CAAC,EACnE,EACD,GAPQ"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useExport as e } from "./export/providers/ExportProvider.js";
2
2
  import { useExportStore as t } from "./export/store/exportStore.js";
3
3
  import { ExportRoot as n } from "./export/ExportRoot.js";
4
+ import { enTranslations as r } from "./translations/en.js";
4
5
  import "./export/index.js";
5
- export { n as ExportRoot, e as useExport, t as useExportStore };
6
+ export { n as ExportRoot, r as enTranslations, e as useExport, t as useExportStore };