@burdenoff/microfe-tours 2026.830.1 → 2026.918.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.
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 +295 -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 +1 @@
1
- {"version":3,"file":"toursStore.js","names":[],"sources":["../../../src/tours/store/toursStore.ts"],"sourcesContent":["import { create } from 'zustand';\nimport type {\n Tour,\n TourStep,\n TourMedia,\n TourFilters,\n ValidationError,\n ToursViewMode,\n DeviceType,\n} from '../types';\n\ninterface ToursState {\n // View state\n selectedTour: Tour | null;\n selectedStep: TourStep | null;\n viewMode: ToursViewMode;\n devicePreview: DeviceType;\n\n // Filters\n tourFilter: TourFilters;\n toursListRevision: number;\n\n // Builder state\n stepSequence: TourStep[];\n isDirty: boolean;\n validationErrors: ValidationError[];\n\n // Media state\n selectedMedia: TourMedia[];\n uploadProgress: Record<string, number>;\n\n // UI state\n sidebarOpen: boolean;\n\n // Lifecycle\n resetTransientState: () => void;\n\n // Actions - View\n setSelectedTour: (tour: Tour | null) => void;\n setSelectedStep: (step: TourStep | null) => void;\n setViewMode: (mode: ToursViewMode) => void;\n setDevicePreview: (device: DeviceType) => void;\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n\n // Actions - Filters\n updateTourFilter: (filter: Partial<TourFilters>) => void;\n resetTourFilter: () => void;\n notifyToursListChanged: () => void;\n\n // Actions - Steps\n updateStepSequence: (steps: TourStep[]) => void;\n reorderSteps: (startIndex: number, endIndex: number) => void;\n addStep: (step: Partial<TourStep>) => void;\n removeStep: (stepId: string) => void;\n updateStep: (stepId: string, updates: Partial<TourStep>) => void;\n\n // Actions - Validation\n setDirty: (dirty: boolean) => void;\n validateTour: () => ValidationError[];\n clearValidationErrors: () => void;\n\n // Actions - Media\n selectMedia: (media: TourMedia) => void;\n unselectMedia: (mediaId: string) => void;\n clearSelectedMedia: () => void;\n updateUploadProgress: (fileId: string, progress: number) => void;\n removeUploadProgress: (fileId: string) => void;\n}\n\nconst defaultFilter: TourFilters = {\n search: '',\n status: 'all',\n productId: undefined,\n moduleId: undefined,\n pageId: undefined,\n dateRange: { start: null, end: null },\n createdBy: undefined,\n};\n\nexport const useToursStore = create<ToursState>((set, get) => ({\n // Initial state\n selectedTour: null,\n selectedStep: null,\n viewMode: 'grid',\n devicePreview: 'desktop',\n tourFilter: defaultFilter,\n toursListRevision: 0,\n stepSequence: [],\n isDirty: false,\n validationErrors: [],\n selectedMedia: [],\n uploadProgress: {},\n sidebarOpen: true,\n\n resetTransientState: () =>\n set({\n selectedTour: null,\n selectedStep: null,\n viewMode: 'grid',\n devicePreview: 'desktop',\n tourFilter: defaultFilter,\n toursListRevision: 0,\n stepSequence: [],\n isDirty: false,\n validationErrors: [],\n selectedMedia: [],\n uploadProgress: {},\n sidebarOpen: true,\n }),\n\n // View actions\n setSelectedTour: (tour) =>\n set({\n selectedTour: tour,\n stepSequence: tour?.steps || [],\n isDirty: false,\n validationErrors: [],\n }),\n\n setSelectedStep: (step) => set({ selectedStep: step }),\n\n setViewMode: (mode) => set({ viewMode: mode }),\n\n setDevicePreview: (device) => set({ devicePreview: device }),\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n\n setSidebarOpen: (open) => set({ sidebarOpen: open }),\n\n // Filter actions\n updateTourFilter: (filter) =>\n set((state) => ({\n tourFilter: { ...state.tourFilter, ...filter },\n })),\n\n resetTourFilter: () => set({ tourFilter: defaultFilter }),\n\n notifyToursListChanged: () =>\n set((state) => ({ toursListRevision: state.toursListRevision + 1 })),\n\n // Step actions\n updateStepSequence: (steps) => set({ stepSequence: steps, isDirty: true }),\n\n reorderSteps: (startIndex, endIndex) => {\n const steps = [...get().stepSequence];\n const [removed] = steps.splice(startIndex, 1);\n steps.splice(endIndex, 0, removed);\n const reorderedSteps = steps.map((step, index) => ({\n ...step,\n stepOrder: index + 1,\n }));\n set({ stepSequence: reorderedSteps, isDirty: true });\n },\n\n addStep: (step) => {\n const steps = [...get().stepSequence];\n const newStep: TourStep = {\n id: `temp_${Date.now()}`,\n tourId: get().selectedTour?.id || '',\n title: step.title || 'New Step',\n content: step.content || '',\n stepOrder: steps.length + 1,\n targetSelector: step.targetSelector || '',\n placement: step.placement || 'AUTO',\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n ...step,\n } as TourStep;\n steps.push(newStep);\n set({ stepSequence: steps, isDirty: true });\n },\n\n removeStep: (stepId) => {\n const steps = get().stepSequence.filter((s) => s.id !== stepId);\n const reorderedSteps = steps.map((step, index) => ({\n ...step,\n stepOrder: index + 1,\n }));\n set({ stepSequence: reorderedSteps, isDirty: true });\n },\n\n updateStep: (stepId, updates) => {\n const steps = get().stepSequence.map((step) =>\n step.id === stepId ? { ...step, ...updates } : step\n );\n set({ stepSequence: steps, isDirty: true });\n },\n\n // Validation actions\n setDirty: (dirty) => set({ isDirty: dirty }),\n\n validateTour: () => {\n const errors: ValidationError[] = [];\n const { selectedTour, stepSequence } = get();\n\n if (!selectedTour) {\n errors.push({ field: 'tour', message: 'No tour selected' });\n set({ validationErrors: errors });\n return errors;\n }\n\n if (!selectedTour.name || selectedTour.name.trim() === '') {\n errors.push({ field: 'name', message: 'Tour name is required' });\n }\n\n if (!selectedTour.productId) {\n errors.push({ field: 'productId', message: 'Product ID is required' });\n }\n\n if (stepSequence.length === 0) {\n errors.push({ field: 'steps', message: 'At least one step is required' });\n }\n\n stepSequence.forEach((step, index) => {\n if (!step.title || step.title.trim() === '') {\n errors.push({\n field: `step[${index}].title`,\n message: `Step ${index + 1} title is required`,\n });\n }\n if (!step.content || step.content.trim() === '') {\n errors.push({\n field: `step[${index}].content`,\n message: `Step ${index + 1} content is required`,\n });\n }\n if (\n step.placement !== 'CENTER' &&\n (!step.targetSelector || step.targetSelector.trim() === '')\n ) {\n errors.push({\n field: `step[${index}].targetSelector`,\n message: `Step ${index + 1} selector is required`,\n });\n }\n });\n\n set({ validationErrors: errors });\n return errors;\n },\n\n clearValidationErrors: () => set({ validationErrors: [] }),\n\n // Media actions\n selectMedia: (media) => {\n const selected = [...get().selectedMedia];\n if (!selected.find((m) => m.id === media.id)) {\n selected.push(media);\n set({ selectedMedia: selected });\n }\n },\n\n unselectMedia: (mediaId) => {\n set({\n selectedMedia: get().selectedMedia.filter((m) => m.id !== mediaId),\n });\n },\n\n clearSelectedMedia: () => set({ selectedMedia: [] }),\n\n updateUploadProgress: (fileId, progress) => {\n set({\n uploadProgress: { ...get().uploadProgress, [fileId]: progress },\n });\n },\n\n removeUploadProgress: (fileId) => {\n const progress = { ...get().uploadProgress };\n delete progress[fileId];\n set({ uploadProgress: progress });\n },\n}));\n"],"mappings":"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"}
1
+ {"version":3,"file":"toursStore.js","names":[],"sources":["../../../src/tours/store/toursStore.ts"],"sourcesContent":["import { create } from 'zustand';\nimport type {\n Tour,\n TourStep,\n TourMedia,\n TourFilters,\n ValidationError,\n ToursViewMode,\n DeviceType,\n} from '../types';\n\ninterface ToursState {\n // View state\n selectedTour: Tour | null;\n selectedStep: TourStep | null;\n viewMode: ToursViewMode;\n devicePreview: DeviceType;\n\n // Filters\n tourFilter: TourFilters;\n toursListRevision: number;\n\n // Builder state\n stepSequence: TourStep[];\n isDirty: boolean;\n validationErrors: ValidationError[];\n\n // Media state\n selectedMedia: TourMedia[];\n uploadProgress: Record<string, number>;\n\n // UI state\n sidebarOpen: boolean;\n\n // Lifecycle\n resetTransientState: () => void;\n\n // Actions - View\n setSelectedTour: (tour: Tour | null) => void;\n setSelectedStep: (step: TourStep | null) => void;\n setViewMode: (mode: ToursViewMode) => void;\n setDevicePreview: (device: DeviceType) => void;\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n\n // Actions - Filters\n updateTourFilter: (filter: Partial<TourFilters>) => void;\n resetTourFilter: () => void;\n notifyToursListChanged: () => void;\n\n // Actions - Steps\n updateStepSequence: (steps: TourStep[]) => void;\n reorderSteps: (startIndex: number, endIndex: number) => void;\n addStep: (step: Partial<TourStep>) => void;\n removeStep: (stepId: string) => void;\n updateStep: (stepId: string, updates: Partial<TourStep>) => void;\n\n // Actions - Validation\n setDirty: (dirty: boolean) => void;\n validateTour: () => ValidationError[];\n clearValidationErrors: () => void;\n\n // Actions - Media\n selectMedia: (media: TourMedia) => void;\n unselectMedia: (mediaId: string) => void;\n clearSelectedMedia: () => void;\n updateUploadProgress: (fileId: string, progress: number) => void;\n removeUploadProgress: (fileId: string) => void;\n}\n\nconst defaultFilter: TourFilters = {\n search: '',\n status: 'all',\n productId: undefined,\n moduleId: undefined,\n pageId: undefined,\n dateRange: { start: null, end: null },\n createdBy: undefined,\n};\n\nexport const useToursStore = create<ToursState>((set, get) => ({\n // Initial state\n selectedTour: null,\n selectedStep: null,\n viewMode: 'grid',\n devicePreview: 'desktop',\n tourFilter: defaultFilter,\n toursListRevision: 0,\n stepSequence: [],\n isDirty: false,\n validationErrors: [],\n selectedMedia: [],\n uploadProgress: {},\n sidebarOpen: true,\n\n resetTransientState: () =>\n set({\n selectedTour: null,\n selectedStep: null,\n viewMode: 'grid',\n devicePreview: 'desktop',\n tourFilter: defaultFilter,\n toursListRevision: 0,\n stepSequence: [],\n isDirty: false,\n validationErrors: [],\n selectedMedia: [],\n uploadProgress: {},\n sidebarOpen: true,\n }),\n\n // View actions\n setSelectedTour: (tour) =>\n set({\n selectedTour: tour,\n stepSequence: tour?.steps || [],\n isDirty: false,\n validationErrors: [],\n }),\n\n setSelectedStep: (step) => set({ selectedStep: step }),\n\n setViewMode: (mode) => set({ viewMode: mode }),\n\n setDevicePreview: (device) => set({ devicePreview: device }),\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n\n setSidebarOpen: (open) => set({ sidebarOpen: open }),\n\n // Filter actions\n updateTourFilter: (filter) =>\n set((state) => ({\n tourFilter: { ...state.tourFilter, ...filter },\n })),\n\n resetTourFilter: () => set({ tourFilter: defaultFilter }),\n\n notifyToursListChanged: () =>\n set((state) => ({ toursListRevision: state.toursListRevision + 1 })),\n\n // Step actions\n updateStepSequence: (steps) => set({ stepSequence: steps, isDirty: true }),\n\n reorderSteps: (startIndex, endIndex) => {\n const steps = [...get().stepSequence];\n const [removed] = steps.splice(startIndex, 1);\n steps.splice(endIndex, 0, removed);\n const reorderedSteps = steps.map((step, index) => ({\n ...step,\n stepOrder: index + 1,\n }));\n set({ stepSequence: reorderedSteps, isDirty: true });\n },\n\n addStep: (step) => {\n const steps = [...get().stepSequence];\n const newStep: TourStep = {\n id: `temp_${Date.now()}`,\n tourId: get().selectedTour?.id || '',\n title: step.title || 'New Step',\n content: step.content || '',\n stepOrder: steps.length + 1,\n targetSelector: step.targetSelector || '',\n placement: step.placement || 'AUTO',\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n ...step,\n } as TourStep;\n steps.push(newStep);\n set({ stepSequence: steps, isDirty: true });\n },\n\n removeStep: (stepId) => {\n const steps = get().stepSequence.filter((s) => s.id !== stepId);\n const reorderedSteps = steps.map((step, index) => ({\n ...step,\n stepOrder: index + 1,\n }));\n set({ stepSequence: reorderedSteps, isDirty: true });\n },\n\n updateStep: (stepId, updates) => {\n const steps = get().stepSequence.map((step) =>\n step.id === stepId ? { ...step, ...updates } : step\n );\n set({ stepSequence: steps, isDirty: true });\n },\n\n // Validation actions\n setDirty: (dirty) => set({ isDirty: dirty }),\n\n validateTour: () => {\n const errors: ValidationError[] = [];\n const { selectedTour, stepSequence } = get();\n\n if (!selectedTour) {\n errors.push({ field: 'tour', message: 'validation.noTourSelected' });\n set({ validationErrors: errors });\n return errors;\n }\n\n if (!selectedTour.name || selectedTour.name.trim() === '') {\n errors.push({ field: 'name', message: 'validation.tourNameRequired' });\n }\n\n if (!selectedTour.productId) {\n errors.push({ field: 'productId', message: 'validation.productIdRequired' });\n }\n\n if (stepSequence.length === 0) {\n errors.push({ field: 'steps', message: 'validation.stepsRequired' });\n }\n\n stepSequence.forEach((step, index) => {\n const number = index + 1;\n if (!step.title || step.title.trim() === '') {\n errors.push({\n field: `step[${index}].title`,\n message: 'validation.stepTitleRequired',\n params: { number },\n });\n }\n if (!step.content || step.content.trim() === '') {\n errors.push({\n field: `step[${index}].content`,\n message: 'validation.stepContentRequired',\n params: { number },\n });\n }\n if (\n step.placement !== 'CENTER' &&\n (!step.targetSelector || step.targetSelector.trim() === '')\n ) {\n errors.push({\n field: `step[${index}].targetSelector`,\n message: 'validation.stepSelectorRequired',\n params: { number },\n });\n }\n });\n\n set({ validationErrors: errors });\n return errors;\n },\n\n clearValidationErrors: () => set({ validationErrors: [] }),\n\n // Media actions\n selectMedia: (media) => {\n const selected = [...get().selectedMedia];\n if (!selected.find((m) => m.id === media.id)) {\n selected.push(media);\n set({ selectedMedia: selected });\n }\n },\n\n unselectMedia: (mediaId) => {\n set({\n selectedMedia: get().selectedMedia.filter((m) => m.id !== mediaId),\n });\n },\n\n clearSelectedMedia: () => set({ selectedMedia: [] }),\n\n updateUploadProgress: (fileId, progress) => {\n set({\n uploadProgress: { ...get().uploadProgress, [fileId]: progress },\n });\n },\n\n removeUploadProgress: (fileId) => {\n const progress = { ...get().uploadProgress };\n delete progress[fileId];\n set({ uploadProgress: progress });\n },\n}));\n"],"mappings":"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"}
@@ -1,28 +1,41 @@
1
+ import { useToursI18n as e } from "../i18n.js";
2
+ import { useMemo as t } from "react";
1
3
  //#region src/tours/utils/index.ts
2
- function e(e) {
3
- if (e === 0) return "0 Bytes";
4
- let t = 1024, n = [
5
- "Bytes",
6
- "KB",
7
- "MB",
8
- "GB"
9
- ], r = Math.floor(Math.log(e) / Math.log(t));
10
- return Math.round(e / t ** +r * 100) / 100 + " " + n[r];
4
+ function n(e, t) {
5
+ let n = t ? t("common.fileSize.bytes") : "Bytes";
6
+ if (e === 0) return `0 ${n}`;
7
+ let r = 1024, i = [
8
+ t ? t("common.fileSize.bytes") : "Bytes",
9
+ t ? t("common.fileSize.kb") : "KB",
10
+ t ? t("common.fileSize.mb") : "MB",
11
+ t ? t("common.fileSize.gb") : "GB"
12
+ ], a = Math.floor(Math.log(e) / Math.log(r));
13
+ return Math.round(e / r ** +a * 100) / 100 + " " + i[a];
11
14
  }
12
- function t(e) {
13
- if (e < 60) return `${Math.round(e)}s`;
14
- let t = Math.floor(e / 60), n = Math.round(e % 60);
15
- if (t < 60) return n > 0 ? `${t}m ${n}s` : `${t}m`;
16
- let r = Math.floor(t / 60), i = t % 60;
17
- return i > 0 ? `${r}h ${i}m` : `${r}h`;
15
+ function r(e, t) {
16
+ let n = t ? t("common.duration.seconds") : "s", r = t ? t("common.duration.minutes") : "m", i = t ? t("common.duration.hours") : "h";
17
+ if (e < 60) return `${Math.round(e)}${n}`;
18
+ let a = Math.floor(e / 60), o = Math.round(e % 60);
19
+ if (a < 60) return o > 0 ? `${a}${r} ${o}${n}` : `${a}${r}`;
20
+ let s = Math.floor(a / 60), c = a % 60;
21
+ return c > 0 ? `${s}${i} ${c}${r}` : `${s}${i}`;
18
22
  }
19
- function n(e, t = 1) {
23
+ function i(e, t = 1) {
20
24
  return `${e.toFixed(t)}%`;
21
25
  }
22
- function r(e) {
23
- return e.toLocaleString("en-US");
26
+ function a(e) {
27
+ return e.toLocaleString();
28
+ }
29
+ function o() {
30
+ let { tr: o } = e();
31
+ return t(() => ({
32
+ formatFileSize: (e) => n(e, o),
33
+ formatDuration: (e) => r(e, o),
34
+ formatNumber: a,
35
+ formatPercentage: i
36
+ }), [o]);
24
37
  }
25
38
  //#endregion
26
- export { t as formatDuration, e as formatFileSize, r as formatNumber, n as formatPercentage };
39
+ export { o as useToursFormatters };
27
40
 
28
41
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/tours/utils/index.ts"],"sourcesContent":["/**\n * Format file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];\n}\n\n/**\n * Format duration (seconds to human readable)\n */\nexport function formatDuration(seconds: number): string {\n if (seconds < 60) {\n return `${Math.round(seconds)}s`;\n }\n\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = Math.round(seconds % 60);\n\n if (minutes < 60) {\n return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n\n return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;\n}\n\n/**\n * Format percentage\n */\nexport function formatPercentage(value: number, decimals: number = 1): string {\n return `${value.toFixed(decimals)}%`;\n}\n\n/**\n * Format number with commas\n */\nexport function formatNumber(num: number): string {\n return num.toLocaleString('en-US');\n}\n"],"mappings":";AAGA,SAAgB,EAAe,GAAuB;AACpD,KAAI,MAAU,EAAG,QAAO;CAExB,IAAM,IAAI,MACJ,IAAQ;EAAC;EAAS;EAAM;EAAM;EAAK,EACnC,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AAEnD,QAAO,KAAK,MAAO,IAAiB,MAAG,IAAM,IAAI,GAAG,MAAM,MAAM,EAAM;;AAMxE,SAAgB,EAAe,GAAyB;AACtD,KAAI,IAAU,GACZ,QAAO,GAAG,KAAK,MAAM,EAAQ,CAAC;CAGhC,IAAM,IAAU,KAAK,MAAM,IAAU,GAAG,EAClC,IAAmB,KAAK,MAAM,IAAU,GAAG;AAEjD,KAAI,IAAU,GACZ,QAAO,IAAmB,IAAI,GAAG,EAAQ,IAAI,EAAiB,KAAK,GAAG,EAAQ;CAGhF,IAAM,IAAQ,KAAK,MAAM,IAAU,GAAG,EAChC,IAAmB,IAAU;AAEnC,QAAO,IAAmB,IAAI,GAAG,EAAM,IAAI,EAAiB,KAAK,GAAG,EAAM;;AAM5E,SAAgB,EAAiB,GAAe,IAAmB,GAAW;AAC5E,QAAO,GAAG,EAAM,QAAQ,EAAS,CAAC;;AAMpC,SAAgB,EAAa,GAAqB;AAChD,QAAO,EAAI,eAAe,QAAQ"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/tours/utils/index.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport { useToursI18n, type ToursTranslationKey } from '../i18n';\n\ntype TranslateFn = (key: ToursTranslationKey) => string;\n\n/**\n * Format file size\n */\nexport function formatFileSize(bytes: number, t?: TranslateFn): string {\n const zeroLabel = t ? t('common.fileSize.bytes') : 'Bytes';\n if (bytes === 0) return `0 ${zeroLabel}`;\n\n const k = 1024;\n const sizes = [\n t ? t('common.fileSize.bytes') : 'Bytes',\n t ? t('common.fileSize.kb') : 'KB',\n t ? t('common.fileSize.mb') : 'MB',\n t ? t('common.fileSize.gb') : 'GB',\n ];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];\n}\n\n/**\n * Format duration (seconds to human readable)\n */\nexport function formatDuration(seconds: number, t?: TranslateFn): string {\n const secLabel = t ? t('common.duration.seconds') : 's';\n const minLabel = t ? t('common.duration.minutes') : 'm';\n const hrLabel = t ? t('common.duration.hours') : 'h';\n\n if (seconds < 60) {\n return `${Math.round(seconds)}${secLabel}`;\n }\n\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = Math.round(seconds % 60);\n\n if (minutes < 60) {\n return remainingSeconds > 0\n ? `${minutes}${minLabel} ${remainingSeconds}${secLabel}`\n : `${minutes}${minLabel}`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n\n return remainingMinutes > 0\n ? `${hours}${hrLabel} ${remainingMinutes}${minLabel}`\n : `${hours}${hrLabel}`;\n}\n\n/**\n * Format percentage\n */\nexport function formatPercentage(value: number, decimals: number = 1): string {\n return `${value.toFixed(decimals)}%`;\n}\n\n/**\n * Format number with locale-aware separators\n */\nexport function formatNumber(num: number): string {\n return num.toLocaleString();\n}\n\n/**\n * Hook that returns formatter helpers bound to the current translation context.\n */\nexport function useToursFormatters() {\n const { tr } = useToursI18n();\n\n return useMemo(\n () => ({\n formatFileSize: (bytes: number) => formatFileSize(bytes, tr),\n formatDuration: (seconds: number) => formatDuration(seconds, tr),\n formatNumber,\n formatPercentage,\n }),\n [tr]\n );\n}\n"],"mappings":";;;AAQA,SAAgB,EAAe,GAAe,GAAyB;CACrE,IAAM,IAAY,IAAI,EAAE,wBAAwB,GAAG;AACnD,KAAI,MAAU,EAAG,QAAO,KAAK;CAE7B,IAAM,IAAI,MACJ,IAAQ;EACZ,IAAI,EAAE,wBAAwB,GAAG;EACjC,IAAI,EAAE,qBAAqB,GAAG;EAC9B,IAAI,EAAE,qBAAqB,GAAG;EAC9B,IAAI,EAAE,qBAAqB,GAAG;EAC/B,EACK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AAEnD,QAAO,KAAK,MAAO,IAAiB,MAAG,IAAM,IAAI,GAAG,MAAM,MAAM,EAAM;;AAMxE,SAAgB,EAAe,GAAiB,GAAyB;CACvE,IAAM,IAAW,IAAI,EAAE,0BAA0B,GAAG,KAC9C,IAAW,IAAI,EAAE,0BAA0B,GAAG,KAC9C,IAAU,IAAI,EAAE,wBAAwB,GAAG;AAEjD,KAAI,IAAU,GACZ,QAAO,GAAG,KAAK,MAAM,EAAQ,GAAG;CAGlC,IAAM,IAAU,KAAK,MAAM,IAAU,GAAG,EAClC,IAAmB,KAAK,MAAM,IAAU,GAAG;AAEjD,KAAI,IAAU,GACZ,QAAO,IAAmB,IACtB,GAAG,IAAU,EAAS,GAAG,IAAmB,MAC5C,GAAG,IAAU;CAGnB,IAAM,IAAQ,KAAK,MAAM,IAAU,GAAG,EAChC,IAAmB,IAAU;AAEnC,QAAO,IAAmB,IACtB,GAAG,IAAQ,EAAQ,GAAG,IAAmB,MACzC,GAAG,IAAQ;;AAMjB,SAAgB,EAAiB,GAAe,IAAmB,GAAW;AAC5E,QAAO,GAAG,EAAM,QAAQ,EAAS,CAAC;;AAMpC,SAAgB,EAAa,GAAqB;AAChD,QAAO,EAAI,gBAAgB;;AAM7B,SAAgB,IAAqB;CACnC,IAAM,EAAE,UAAO,GAAc;AAE7B,QAAO,SACE;EACL,iBAAiB,MAAkB,EAAe,GAAO,EAAG;EAC5D,iBAAiB,MAAoB,EAAe,GAAS,EAAG;EAChE;EACA;EACD,GACD,CAAC,EAAG,CACL"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-tours",
3
- "version": "2026.830.1",
3
+ "version": "2026.918.1",
4
4
  "description": "Burdenoff Tours Microfrontend - Complete product tours and onboarding interface",
5
5
  "type": "module",
6
6
  "files": [