@burdenoff/microfe-tours 2026.830.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 (47) hide show
  1. package/dist/tours/ToursAdminRoot.js +28 -27
  2. package/dist/tours/ToursAdminRoot.js.map +1 -1
  3. package/dist/tours/ToursAdminRoutes.js +56 -55
  4. package/dist/tours/ToursAdminRoutes.js.map +1 -1
  5. package/dist/tours/ToursUserRoot.js +34 -29
  6. package/dist/tours/ToursUserRoot.js.map +1 -1
  7. package/dist/tours/ToursUserRoutes.js +33 -33
  8. package/dist/tours/ToursUserRoutes.js.map +1 -1
  9. package/dist/tours/components/MarkdownEditor.js +85 -80
  10. package/dist/tours/components/MarkdownEditor.js.map +1 -1
  11. package/dist/tours/components/RouteHelpers.js +47 -42
  12. package/dist/tours/components/RouteHelpers.js.map +1 -1
  13. package/dist/tours/components/ToursAdminNavRegistrar.js +32 -27
  14. package/dist/tours/components/ToursAdminNavRegistrar.js.map +1 -1
  15. package/dist/tours/hooks/useStepSequencer.js +45 -36
  16. package/dist/tours/hooks/useStepSequencer.js.map +1 -1
  17. package/dist/tours/hooks/useTourBuilder.js +106 -93
  18. package/dist/tours/hooks/useTourBuilder.js.map +1 -1
  19. package/dist/tours/hooks/useTourPreview.js +51 -41
  20. package/dist/tours/hooks/useTourPreview.js.map +1 -1
  21. package/dist/tours/hooks/useToursList.js +28 -27
  22. package/dist/tours/hooks/useToursList.js.map +1 -1
  23. package/dist/tours/i18n.js +187 -5
  24. package/dist/tours/i18n.js.map +1 -1
  25. package/dist/tours/pages/AnalyticsDashboardPage.js +108 -109
  26. package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
  27. package/dist/tours/pages/MediaLibraryPage.js +58 -61
  28. package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
  29. package/dist/tours/pages/MyToursPage.js +5 -5
  30. package/dist/tours/pages/MyToursProgressPage.js +5 -5
  31. package/dist/tours/pages/SchedulerPage.js +78 -81
  32. package/dist/tours/pages/SchedulerPage.js.map +1 -1
  33. package/dist/tours/pages/StepEditorPage.js +32 -32
  34. package/dist/tours/pages/StepEditorPage.js.map +1 -1
  35. package/dist/tours/pages/SystemAnalyticsPage.js +75 -79
  36. package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
  37. package/dist/tours/pages/TemplateLibraryPage.js +8 -8
  38. package/dist/tours/pages/TourBuilderPage.js +279 -279
  39. package/dist/tours/pages/TourBuilderPage.js.map +1 -1
  40. package/dist/tours/pages/TourPreviewPage.js +6 -6
  41. package/dist/tours/pages/ToursListPage.js +155 -155
  42. package/dist/tours/pages/ToursListPage.js.map +1 -1
  43. package/dist/tours/store/toursStore.js +11 -7
  44. package/dist/tours/store/toursStore.js.map +1 -1
  45. package/dist/tours/utils/index.js +32 -19
  46. package/dist/tours/utils/index.js.map +1 -1
  47. package/package.json +1 -1
@@ -1,40 +1,37 @@
1
- import { useToursContext as e } from "../providers/ToursProvider.js";
2
- import { useToursPermissionAccess as t } from "../hooks/useToursPermissionAccess.js";
3
- import { TOUR_PERMISSIONS as n } from "../../constants/permissions.js";
4
- import { emitToursApiFailed as r } from "../telemetry.js";
5
- import { formatFileSize as i } from "../utils/index.js";
6
- import { useMediaLibrary as a } from "../hooks/useMediaLibrary.js";
7
- import { useCallback as o, useEffect as s, useState as c } from "react";
1
+ import { useToursI18n as e } from "../i18n.js";
2
+ import { useToursContext as t } from "../providers/ToursProvider.js";
3
+ import { useToursPermissionAccess as n } from "../hooks/useToursPermissionAccess.js";
4
+ import { TOUR_PERMISSIONS as r } from "../../constants/permissions.js";
5
+ import { emitToursApiFailed as i } from "../telemetry.js";
6
+ import { useToursFormatters as a } from "../utils/index.js";
7
+ import { useMediaLibrary as o } from "../hooks/useMediaLibrary.js";
8
+ import { useEffect as s, useState as c } from "react";
8
9
  import { PermissionGate as l } from "@burdenoff/fe-libs/shared/components";
9
10
  import { FileText as u, Image as d, Music as f, Search as p, Trash2 as m, Upload as h, Video as g } from "lucide-react";
10
11
  import { jsx as _, jsxs as v } from "react/jsx-runtime";
11
12
  import { GlassCard as y, IllustratedEmptyState as b, PagePurpose as x } from "@burdenoff/fe-libs/ui";
12
- import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
13
- import { useEventBus as C } from "@burdenoff/fe-libs/shared/events";
13
+ import { useEventBus as S } from "@burdenoff/fe-libs/shared/events";
14
14
  //#region src/tours/pages/MediaLibraryPage.tsx
15
- function w() {
16
- let { t: w } = S(), T = o((e, t) => {
17
- let n = w(e);
18
- return n === e ? t : n;
19
- }, [w]), E = e(), D = C(), { hasPermission: O, isLoading: k } = t(), A = O(n.TOUR_MEDIA_LIST), j = O(n.TOUR_MEDIA_DELETE), { media: M, fetchMedia: N, uploadMedia: P, deleteMedia: F, uploadProgress: I } = a(), [L, R] = c(""), [z, B] = c("all");
15
+ function C() {
16
+ let { tr: C } = e(), w = t(), T = S(), { hasPermission: E, isLoading: D } = n(), O = E(r.TOUR_MEDIA_LIST), k = E(r.TOUR_MEDIA_DELETE), { media: A, fetchMedia: j, uploadMedia: M, deleteMedia: N, uploadProgress: P } = o(), { formatFileSize: F } = a(), [I, L] = c(""), [R, z] = c("all");
20
17
  s(() => {
21
- k || !A || N(z === "all" ? void 0 : z, L);
18
+ D || !O || j(R === "all" ? void 0 : R, I);
22
19
  }, [
23
- N,
24
- z,
25
- L,
26
- k,
27
- A
20
+ j,
21
+ R,
22
+ I,
23
+ D,
24
+ O
28
25
  ]);
29
- let V = async (e) => {
26
+ let B = async (e) => {
30
27
  e && (await Promise.all(Array.from(e).map(async (e) => {
31
28
  try {
32
- await P(e);
29
+ await M(e);
33
30
  } catch {
34
- r(D, "media.upload", E);
31
+ i(T, "media.upload", w);
35
32
  }
36
- })), N(z === "all" ? void 0 : z, L));
37
- }, H = (e) => {
33
+ })), j(R === "all" ? void 0 : R, I));
34
+ }, V = (e) => {
38
35
  switch (e) {
39
36
  case "IMAGE": return /* @__PURE__ */ _(d, { size: 20 });
40
37
  case "VIDEO": return /* @__PURE__ */ _(g, { size: 20 });
@@ -49,30 +46,30 @@ function w() {
49
46
  className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",
50
47
  children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h1", {
51
48
  className: "text-xl sm:text-2xl font-bold",
52
- children: T("mediaLibrary.title", "Media Library")
49
+ children: C("mediaLibrary.title", "Media Library")
53
50
  }), /* @__PURE__ */ _("p", {
54
51
  className: "text-sm text-text-secondary",
55
- children: T("mediaLibrary.subtitle", "Upload and manage media for tour steps")
52
+ children: C("mediaLibrary.subtitle", "Upload and manage media for tour steps")
56
53
  })] }), /* @__PURE__ */ _(l, {
57
- permission: n.TOUR_MEDIA_CREATE,
54
+ permission: r.TOUR_MEDIA_CREATE,
58
55
  children: /* @__PURE__ */ v("label", {
59
56
  className: "w-full sm:w-auto px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer flex items-center justify-center gap-2",
60
57
  children: [
61
58
  /* @__PURE__ */ _(h, { size: 20 }),
62
- T("mediaLibrary.uploadMedia", "Upload Media"),
59
+ C("mediaLibrary.uploadMedia", "Upload Media"),
63
60
  /* @__PURE__ */ _("input", {
64
61
  type: "file",
65
62
  multiple: !0,
66
63
  accept: "image/*,video/*,audio/*,.pdf,.doc,.docx",
67
- onChange: (e) => V(e.target.files),
64
+ onChange: (e) => B(e.target.files),
68
65
  className: "hidden",
69
- "aria-label": T("mediaLibrary.uploadMedia", "Upload Media")
66
+ "aria-label": C("mediaLibrary.uploadMedia", "Upload Media")
70
67
  })
71
68
  ]
72
69
  })
73
70
  })]
74
71
  }),
75
- /* @__PURE__ */ _(x, { children: T("mediaLibrary.purpose", "Store and organise the images, videos, audio, and documents you attach to tour steps. Upload an asset here once, then reference it from any step — handy for product screenshots, short demo clips, or callout graphics that make a walkthrough clearer.") }),
72
+ /* @__PURE__ */ _(x, { children: C("mediaLibrary.purpose", "Store and organise the images, videos, audio, and documents you attach to tour steps. Upload an asset here once, then reference it from any step — handy for product screenshots, short demo clips, or callout graphics that make a walkthrough clearer.") }),
76
73
  /* @__PURE__ */ v("div", {
77
74
  className: "flex flex-col sm:flex-row gap-3",
78
75
  children: [/* @__PURE__ */ v("div", {
@@ -82,50 +79,50 @@ function w() {
82
79
  size: 20
83
80
  }), /* @__PURE__ */ _("input", {
84
81
  type: "text",
85
- placeholder: T("mediaLibrary.searchPlaceholder", "Search media..."),
86
- value: L,
87
- onChange: (e) => R(e.target.value),
82
+ placeholder: C("mediaLibrary.searchPlaceholder", "Search media..."),
83
+ value: I,
84
+ onChange: (e) => L(e.target.value),
88
85
  className: "w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
89
- "aria-label": T("mediaLibrary.searchPlaceholder", "Search media...")
86
+ "aria-label": C("mediaLibrary.searchPlaceholder", "Search media...")
90
87
  })]
91
88
  }), /* @__PURE__ */ v("select", {
92
- value: z,
93
- onChange: (e) => B(e.target.value),
89
+ value: R,
90
+ onChange: (e) => z(e.target.value),
94
91
  className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
95
- "aria-label": T("mediaLibrary.filterByType", "Filter by media type"),
92
+ "aria-label": C("mediaLibrary.filterByType", "Filter by media type"),
96
93
  children: [
97
94
  /* @__PURE__ */ _("option", {
98
95
  value: "all",
99
- children: T("mediaLibrary.allTypes", "All Types")
96
+ children: C("mediaLibrary.allTypes", "All Types")
100
97
  }),
101
98
  /* @__PURE__ */ _("option", {
102
99
  value: "IMAGE",
103
- children: T("mediaLibrary.typeImages", "Images")
100
+ children: C("mediaLibrary.typeImages", "Images")
104
101
  }),
105
102
  /* @__PURE__ */ _("option", {
106
103
  value: "VIDEO",
107
- children: T("mediaLibrary.typeVideos", "Videos")
104
+ children: C("mediaLibrary.typeVideos", "Videos")
108
105
  }),
109
106
  /* @__PURE__ */ _("option", {
110
107
  value: "AUDIO",
111
- children: T("mediaLibrary.typeAudio", "Audio")
108
+ children: C("mediaLibrary.typeAudio", "Audio")
112
109
  }),
113
110
  /* @__PURE__ */ _("option", {
114
111
  value: "DOCUMENT",
115
- children: T("mediaLibrary.typeDocuments", "Documents")
112
+ children: C("mediaLibrary.typeDocuments", "Documents")
116
113
  })
117
114
  ]
118
115
  })]
119
116
  }),
120
- Object.keys(I).length > 0 && /* @__PURE__ */ _("div", {
117
+ Object.keys(P).length > 0 && /* @__PURE__ */ _("div", {
121
118
  className: "space-y-2",
122
- children: Object.entries(I).map(([e, t]) => /* @__PURE__ */ v("div", {
119
+ children: Object.entries(P).map(([e, t]) => /* @__PURE__ */ v("div", {
123
120
  className: "border border-border-subtle rounded-md p-3",
124
121
  children: [/* @__PURE__ */ v("div", {
125
122
  className: "flex items-center justify-between mb-2",
126
123
  children: [/* @__PURE__ */ _("span", {
127
124
  className: "text-sm",
128
- children: T("mediaLibrary.uploading", "Uploading...")
125
+ children: C("mediaLibrary.uploading", "Uploading...")
129
126
  }), /* @__PURE__ */ v("span", {
130
127
  className: "text-sm text-text-secondary",
131
128
  children: [t, "%"]
@@ -139,9 +136,9 @@ function w() {
139
136
  })]
140
137
  }, e))
141
138
  }),
142
- M.length > 0 ? /* @__PURE__ */ _("div", {
139
+ A.length > 0 ? /* @__PURE__ */ _("div", {
143
140
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4",
144
- children: M.map((e) => /* @__PURE__ */ v(y, {
141
+ children: A.map((e) => /* @__PURE__ */ v(y, {
145
142
  treatment: "plain",
146
143
  className: "overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]",
147
144
  children: [/* @__PURE__ */ _("div", {
@@ -152,7 +149,7 @@ function w() {
152
149
  className: "w-full h-full object-cover"
153
150
  }) : /* @__PURE__ */ _("div", {
154
151
  className: "text-text-secondary",
155
- children: H(e.type)
152
+ children: V(e.type)
156
153
  })
157
154
  }), /* @__PURE__ */ v("div", {
158
155
  className: "p-3",
@@ -163,37 +160,37 @@ function w() {
163
160
  }),
164
161
  /* @__PURE__ */ v("div", {
165
162
  className: "flex items-center justify-between mt-2 text-xs text-text-secondary",
166
- children: [/* @__PURE__ */ _("span", { children: e.type }), /* @__PURE__ */ _("span", { children: i(e.size) })]
163
+ children: [/* @__PURE__ */ _("span", { children: e.type }), /* @__PURE__ */ _("span", { children: F(e.size) })]
167
164
  }),
168
- j && /* @__PURE__ */ v("button", {
165
+ k && /* @__PURE__ */ v("button", {
169
166
  onClick: () => {
170
- window.confirm(T("mediaLibrary.deleteConfirm", "Delete this media?")) && F(e.id);
167
+ window.confirm(C("mediaLibrary.deleteConfirm", "Delete this media?")) && N(e.id);
171
168
  },
172
169
  type: "button",
173
170
  className: "mt-2 w-full p-2 min-h-[44px] text-xs text-status-error-text border border-status-error-border/30 rounded hover:bg-status-error-bg/10 flex items-center justify-center gap-1",
174
- children: [/* @__PURE__ */ _(m, { size: 12 }), T("mediaLibrary.delete", "Delete")]
171
+ children: [/* @__PURE__ */ _(m, { size: 12 }), C("mediaLibrary.delete", "Delete")]
175
172
  })
176
173
  ]
177
174
  })]
178
175
  }, e.id))
179
176
  }) : /* @__PURE__ */ _(b, {
180
177
  illustration: "empty-files",
181
- title: T("mediaLibrary.noMedia", "No media files"),
182
- description: T("mediaLibrary.uploadFirstMedia", "Upload your first media file to get started"),
178
+ title: C("mediaLibrary.noMedia", "No media files"),
179
+ description: C("mediaLibrary.uploadFirstMedia", "Upload your first media file to get started"),
183
180
  action: /* @__PURE__ */ _(l, {
184
- permission: n.TOUR_MEDIA_CREATE,
181
+ permission: r.TOUR_MEDIA_CREATE,
185
182
  children: /* @__PURE__ */ v("label", {
186
183
  className: "px-4 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer inline-flex items-center justify-center gap-2",
187
184
  children: [
188
185
  /* @__PURE__ */ _(h, { size: 20 }),
189
- T("mediaLibrary.uploadMedia", "Upload Media"),
186
+ C("mediaLibrary.uploadMedia", "Upload Media"),
190
187
  /* @__PURE__ */ _("input", {
191
188
  type: "file",
192
189
  multiple: !0,
193
190
  accept: "image/*,video/*,audio/*,.pdf,.doc,.docx",
194
- onChange: (e) => V(e.target.files),
191
+ onChange: (e) => B(e.target.files),
195
192
  className: "hidden",
196
- "aria-label": T("mediaLibrary.uploadMedia", "Upload Media")
193
+ "aria-label": C("mediaLibrary.uploadMedia", "Upload Media")
197
194
  })
198
195
  ]
199
196
  })
@@ -203,6 +200,6 @@ function w() {
203
200
  });
204
201
  }
205
202
  //#endregion
206
- export { w as MediaLibraryPage };
203
+ export { C as MediaLibraryPage };
207
204
 
208
205
  //# sourceMappingURL=MediaLibraryPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MediaLibraryPage.js","names":[],"sources":["../../../src/tours/pages/MediaLibraryPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { Upload, Search, Image as ImageIcon, Video, Music, FileText, Trash2 } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useMediaLibrary } from '../hooks/useMediaLibrary';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { formatFileSize } from '../utils';\nimport type { TourMediaType } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\n\n/**\n * Media library page\n * Browse, upload, and manage tour media\n */\nexport function MediaLibraryPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const toursContext = useToursContext();\n const eventBus = useEventBus();\n\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canListMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_LIST);\n const canDeleteMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_DELETE);\n\n const { media, fetchMedia, uploadMedia, deleteMedia, uploadProgress } = useMediaLibrary();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState<TourMediaType | 'all'>('all');\n\n useEffect(() => {\n if (permissionsLoading || !canListMedia) return;\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n }, [fetchMedia, typeFilter, searchQuery, permissionsLoading, canListMedia]);\n\n const handleFileUpload = async (files: FileList | null) => {\n if (!files) return;\n\n await Promise.all(\n Array.from(files).map(async (file) => {\n try {\n await uploadMedia(file);\n } catch {\n emitToursApiFailed(eventBus, 'media.upload', toursContext);\n }\n })\n );\n\n // Refresh media list\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n };\n\n const getMediaIcon = (type: TourMediaType) => {\n switch (type) {\n case 'IMAGE':\n return <ImageIcon size={20} />;\n case 'VIDEO':\n return <Video size={20} />;\n case 'AUDIO':\n return <Music size={20} />;\n case 'DOCUMENT':\n return <FileText size={20} />;\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('mediaLibrary.title', 'Media Library')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('mediaLibrary.subtitle', 'Upload and manage media for tour steps')}\n </p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"w-full sm:w-auto px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n </div>\n\n <PagePurpose>\n {tr(\n 'mediaLibrary.purpose',\n 'Store and organise the images, videos, audio, and documents you attach to tour steps. Upload an asset here once, then reference it from any step — handy for product screenshots, short demo clips, or callout graphics that make a walkthrough clearer.'\n )}\n </PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n />\n </div>\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value as TourMediaType | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.filterByType', 'Filter by media type')}\n >\n <option value=\"all\">{tr('mediaLibrary.allTypes', 'All Types')}</option>\n <option value=\"IMAGE\">{tr('mediaLibrary.typeImages', 'Images')}</option>\n <option value=\"VIDEO\">{tr('mediaLibrary.typeVideos', 'Videos')}</option>\n <option value=\"AUDIO\">{tr('mediaLibrary.typeAudio', 'Audio')}</option>\n <option value=\"DOCUMENT\">{tr('mediaLibrary.typeDocuments', 'Documents')}</option>\n </select>\n </div>\n\n {/* Upload Progress */}\n {Object.keys(uploadProgress).length > 0 && (\n <div className=\"space-y-2\">\n {Object.entries(uploadProgress).map(([fileId, progress]) => (\n <div key={fileId} className=\"border border-border-subtle rounded-md p-3\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm\">{tr('mediaLibrary.uploading', 'Uploading...')}</span>\n <span className=\"text-sm text-text-secondary\">{progress}%</span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Media Grid */}\n {media.length > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">\n {media.map((item) => (\n <GlassCard\n key={item.id}\n treatment=\"plain\"\n className=\"overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n {/* Thumbnail */}\n <div className=\"aspect-video bg-bg-sunken flex items-center justify-center\">\n {item.type === 'IMAGE' ? (\n <img\n src={item.cdnUrl}\n alt={item.altText || item.name}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"text-text-secondary\">{getMediaIcon(item.type)}</div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"p-3\">\n <h3 className=\"font-medium text-sm truncate\">{item.name}</h3>\n <div className=\"flex items-center justify-between mt-2 text-xs text-text-secondary\">\n <span>{item.type}</span>\n <span>{formatFileSize(item.size)}</span>\n </div>\n {canDeleteMedia && (\n <button\n onClick={() => {\n if (window.confirm(tr('mediaLibrary.deleteConfirm', 'Delete this media?')))\n deleteMedia(item.id);\n }}\n type=\"button\"\n className=\"mt-2 w-full p-2 min-h-[44px] text-xs text-status-error-text border border-status-error-border/30 rounded hover:bg-status-error-bg/10 flex items-center justify-center gap-1\"\n >\n <Trash2 size={12} />\n {tr('mediaLibrary.delete', 'Delete')}\n </button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title={tr('mediaLibrary.noMedia', 'No media files')}\n description={tr(\n 'mediaLibrary.uploadFirstMedia',\n 'Upload your first media file to get started'\n )}\n action={\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"px-4 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer inline-flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n }\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MediaLibraryPage.js","names":[],"sources":["../../../src/tours/pages/MediaLibraryPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Upload, Search, Image as ImageIcon, Video, Music, FileText, Trash2 } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursI18n } from '../i18n';\nimport { useMediaLibrary } from '../hooks/useMediaLibrary';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursFormatters } from '../utils';\nimport type { TourMediaType } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\n\n/**\n * Media library page\n * Browse, upload, and manage tour media\n */\nexport function MediaLibraryPage() {\n const { tr } = useToursI18n();\n\n const toursContext = useToursContext();\n const eventBus = useEventBus();\n\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canListMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_LIST);\n const canDeleteMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_DELETE);\n\n const { media, fetchMedia, uploadMedia, deleteMedia, uploadProgress } = useMediaLibrary();\n const { formatFileSize } = useToursFormatters();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState<TourMediaType | 'all'>('all');\n\n useEffect(() => {\n if (permissionsLoading || !canListMedia) return;\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n }, [fetchMedia, typeFilter, searchQuery, permissionsLoading, canListMedia]);\n\n const handleFileUpload = async (files: FileList | null) => {\n if (!files) return;\n\n await Promise.all(\n Array.from(files).map(async (file) => {\n try {\n await uploadMedia(file);\n } catch {\n emitToursApiFailed(eventBus, 'media.upload', toursContext);\n }\n })\n );\n\n // Refresh media list\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n };\n\n const getMediaIcon = (type: TourMediaType) => {\n switch (type) {\n case 'IMAGE':\n return <ImageIcon size={20} />;\n case 'VIDEO':\n return <Video size={20} />;\n case 'AUDIO':\n return <Music size={20} />;\n case 'DOCUMENT':\n return <FileText size={20} />;\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('mediaLibrary.title', 'Media Library')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('mediaLibrary.subtitle', 'Upload and manage media for tour steps')}\n </p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"w-full sm:w-auto px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n </div>\n\n <PagePurpose>\n {tr(\n 'mediaLibrary.purpose',\n 'Store and organise the images, videos, audio, and documents you attach to tour steps. Upload an asset here once, then reference it from any step — handy for product screenshots, short demo clips, or callout graphics that make a walkthrough clearer.'\n )}\n </PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n />\n </div>\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value as TourMediaType | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.filterByType', 'Filter by media type')}\n >\n <option value=\"all\">{tr('mediaLibrary.allTypes', 'All Types')}</option>\n <option value=\"IMAGE\">{tr('mediaLibrary.typeImages', 'Images')}</option>\n <option value=\"VIDEO\">{tr('mediaLibrary.typeVideos', 'Videos')}</option>\n <option value=\"AUDIO\">{tr('mediaLibrary.typeAudio', 'Audio')}</option>\n <option value=\"DOCUMENT\">{tr('mediaLibrary.typeDocuments', 'Documents')}</option>\n </select>\n </div>\n\n {/* Upload Progress */}\n {Object.keys(uploadProgress).length > 0 && (\n <div className=\"space-y-2\">\n {Object.entries(uploadProgress).map(([fileId, progress]) => (\n <div key={fileId} className=\"border border-border-subtle rounded-md p-3\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm\">{tr('mediaLibrary.uploading', 'Uploading...')}</span>\n <span className=\"text-sm text-text-secondary\">{progress}%</span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Media Grid */}\n {media.length > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">\n {media.map((item) => (\n <GlassCard\n key={item.id}\n treatment=\"plain\"\n className=\"overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n {/* Thumbnail */}\n <div className=\"aspect-video bg-bg-sunken flex items-center justify-center\">\n {item.type === 'IMAGE' ? (\n <img\n src={item.cdnUrl}\n alt={item.altText || item.name}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"text-text-secondary\">{getMediaIcon(item.type)}</div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"p-3\">\n <h3 className=\"font-medium text-sm truncate\">{item.name}</h3>\n <div className=\"flex items-center justify-between mt-2 text-xs text-text-secondary\">\n <span>{item.type}</span>\n <span>{formatFileSize(item.size)}</span>\n </div>\n {canDeleteMedia && (\n <button\n onClick={() => {\n if (window.confirm(tr('mediaLibrary.deleteConfirm', 'Delete this media?')))\n deleteMedia(item.id);\n }}\n type=\"button\"\n className=\"mt-2 w-full p-2 min-h-[44px] text-xs text-status-error-text border border-status-error-border/30 rounded hover:bg-status-error-bg/10 flex items-center justify-center gap-1\"\n >\n <Trash2 size={12} />\n {tr('mediaLibrary.delete', 'Delete')}\n </button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title={tr('mediaLibrary.noMedia', 'No media files')}\n description={tr(\n 'mediaLibrary.uploadFirstMedia',\n 'Upload your first media file to get started'\n )}\n action={\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"px-4 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer inline-flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n }\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { useToursContext as e } from "../providers/ToursProvider.js";
2
- import { useGetToursForContextQuery as t } from "../../generated/global-operations.js";
3
- import { useToursI18n as n } from "../i18n.js";
1
+ import { useToursI18n as e } from "../i18n.js";
2
+ import { useToursContext as t } from "../providers/ToursProvider.js";
3
+ import { useGetToursForContextQuery as n } from "../../generated/global-operations.js";
4
4
  import { useEffect as r, useRef as i } from "react";
5
5
  import { Markdown as a } from "@burdenoff/fe-libs/shared/components";
6
6
  import { CheckCircle2 as o, Circle as s, MinusCircle as c, Play as l } from "lucide-react";
@@ -17,7 +17,7 @@ function S(e, t) {
17
17
  return e && b.test(e) ? e : t;
18
18
  }
19
19
  function C() {
20
- let { tr: o } = n(), { productId: s, moduleId: c, pageId: l, workspaceId: y } = e(), [b] = f(), C = v(), T = i(!1);
20
+ let { tr: o } = e(), { productId: s, moduleId: c, pageId: l, workspaceId: y } = t(), [b] = f(), C = v(), T = i(!1);
21
21
  r(() => {
22
22
  T.current || (T.current = !0, C.emit("tours.page.viewed", {
23
23
  page: "/tours",
@@ -33,7 +33,7 @@ function C() {
33
33
  l,
34
34
  y
35
35
  ]);
36
- let E = x(s, void 0) ?? x(b.get("productId"), void 0), D = x(c, void 0) ?? x(b.get("moduleId"), void 0), O = S(l, void 0) ?? S(b.get("pageId"), void 0), { data: k, loading: A } = t({
36
+ let E = x(s, void 0) ?? x(b.get("productId"), void 0), D = x(c, void 0) ?? x(b.get("moduleId"), void 0), O = S(l, void 0) ?? S(b.get("pageId"), void 0), { data: k, loading: A } = n({
37
37
  variables: E ? { context: {
38
38
  productId: E,
39
39
  moduleId: D,
@@ -1,6 +1,6 @@
1
- import { useToursContext as e } from "../providers/ToursProvider.js";
2
- import { useGetUserTourProgressesQuery as t } from "../../generated/global-operations.js";
3
- import { useToursI18n as n } from "../i18n.js";
1
+ import { useToursI18n as e } from "../i18n.js";
2
+ import { useToursContext as t } from "../providers/ToursProvider.js";
3
+ import { useGetUserTourProgressesQuery as n } from "../../generated/global-operations.js";
4
4
  import { useEffect as r, useMemo as i, useRef as a } from "react";
5
5
  import { CheckCircle2 as o, HelpCircle as s, MinusCircle as c, Play as l } from "lucide-react";
6
6
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
@@ -8,7 +8,7 @@ import { EmphasisPanel as f, IllustratedEmptyState as p, PagePurpose as m } from
8
8
  import { useEventBus as h } from "@burdenoff/fe-libs/shared/events";
9
9
  //#region src/tours/pages/MyToursProgressPage.tsx
10
10
  function g() {
11
- let { tr: g } = n(), { currentUser: v, productId: y, moduleId: b, pageId: x, workspaceId: S } = e(), C = h(), w = a(!1);
11
+ let { tr: g } = e(), { currentUser: v, productId: y, moduleId: b, pageId: x, workspaceId: S } = t(), C = h(), w = a(!1);
12
12
  r(() => {
13
13
  w.current || (w.current = !0, C.emit("tours.page.viewed", {
14
14
  page: "/tours/progress",
@@ -24,7 +24,7 @@ function g() {
24
24
  x,
25
25
  S
26
26
  ]);
27
- let { data: T, loading: E } = t({
27
+ let { data: T, loading: E } = n({
28
28
  variables: {
29
29
  userId: v?.id,
30
30
  pagination: {