@burdenoff/microfe-tours 2026.1014.1 → 2026.1109.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/featureFlags.d.ts +24 -0
- package/dist/constants/featureFlags.js +1 -2
- package/dist/constants/featureFlags.js.map +1 -1
- package/dist/constants/permissions.d.ts +48 -0
- package/dist/constants/permissions.js +1 -2
- package/dist/constants/permissions.js.map +1 -1
- package/dist/dev/main.d.ts +0 -0
- package/dist/generated/global-operations.d.ts +1318 -0
- package/dist/generated/global-operations.js +2 -3
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.d.ts +26469 -0
- package/dist/generated/wspace-operations.d.ts +1 -0
- package/dist/generated/wspace-types.d.ts +108647 -0
- package/dist/index.d.ts +5 -0
- package/dist/test/happydom.d.ts +1 -0
- package/dist/test/testing-library.d.ts +1 -0
- package/dist/tours/ToursAdminRoot.d.ts +14 -0
- package/dist/tours/ToursAdminRoot.js +14 -13
- package/dist/tours/ToursAdminRoot.js.map +1 -1
- package/dist/tours/ToursAdminRoutes.d.ts +11 -0
- package/dist/tours/ToursAdminRoutes.js +12 -13
- package/dist/tours/ToursAdminRoutes.js.map +1 -1
- package/dist/tours/ToursRoot.d.ts +9 -0
- package/dist/tours/ToursRoot.js +1 -1
- package/dist/tours/ToursUserRoot.d.ts +13 -0
- package/dist/tours/ToursUserRoot.js +14 -13
- package/dist/tours/ToursUserRoot.js.map +1 -1
- package/dist/tours/ToursUserRoutes.d.ts +14 -0
- package/dist/tours/ToursUserRoutes.js +13 -13
- package/dist/tours/ToursUserRoutes.js.map +1 -1
- package/dist/tours/components/MarkdownEditor.d.ts +12 -0
- package/dist/tours/components/MarkdownEditor.js +1 -2
- package/dist/tours/components/MarkdownEditor.js.map +1 -1
- package/dist/tours/components/RouteHelpers.d.ts +31 -0
- package/dist/tours/components/RouteHelpers.js +1 -2
- package/dist/tours/components/RouteHelpers.js.map +1 -1
- package/dist/tours/components/ToursAdminNavRegistrar.d.ts +23 -0
- package/dist/tours/components/ToursAdminNavRegistrar.js +4 -3
- package/dist/tours/components/ToursAdminNavRegistrar.js.map +1 -1
- package/dist/tours/hooks/useMediaLibrary.d.ts +20 -0
- package/dist/tours/hooks/useMediaLibrary.js +1 -2
- package/dist/tours/hooks/useMediaLibrary.js.map +1 -1
- package/dist/tours/hooks/useStepSequencer.d.ts +18 -0
- package/dist/tours/hooks/useStepSequencer.js +16 -13
- package/dist/tours/hooks/useStepSequencer.js.map +1 -1
- package/dist/tours/hooks/useTourAnalytics.d.ts +30 -0
- package/dist/tours/hooks/useTourAnalytics.js +1 -2
- package/dist/tours/hooks/useTourAnalytics.js.map +1 -1
- package/dist/tours/hooks/useTourBuilder.d.ts +59 -0
- package/dist/tours/hooks/useTourBuilder.js +10 -6
- package/dist/tours/hooks/useTourBuilder.js.map +1 -1
- package/dist/tours/hooks/useTourLifecycle.d.ts +21 -0
- package/dist/tours/hooks/useTourLifecycle.js +4 -5
- package/dist/tours/hooks/useTourLifecycle.js.map +1 -1
- package/dist/tours/hooks/useTourPreview.d.ts +31 -0
- package/dist/tours/hooks/useTourPreview.js +5 -6
- package/dist/tours/hooks/useTourPreview.js.map +1 -1
- package/dist/tours/hooks/useToursList.d.ts +15 -0
- package/dist/tours/hooks/useToursList.js +1 -2
- package/dist/tours/hooks/useToursList.js.map +1 -1
- package/dist/tours/hooks/useToursPermissionAccess.d.ts +15 -0
- package/dist/tours/hooks/useToursPermissionAccess.js +1 -2
- package/dist/tours/hooks/useToursPermissionAccess.js.map +1 -1
- package/dist/tours/i18n.d.ts +413 -0
- package/dist/tours/i18n.js +1 -2
- package/dist/tours/i18n.js.map +1 -1
- package/dist/tours/index.d.ts +21 -0
- package/dist/tours/index.js +6 -5
- package/dist/tours/pages/AnalyticsDashboardPage.d.ts +5 -0
- package/dist/tours/pages/AnalyticsDashboardPage.js +6 -7
- package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/tours/pages/MediaLibraryPage.d.ts +5 -0
- package/dist/tours/pages/MediaLibraryPage.js +10 -11
- package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
- package/dist/tours/pages/MyToursPage.d.ts +10 -0
- package/dist/tours/pages/MyToursPage.js +5 -6
- package/dist/tours/pages/MyToursPage.js.map +1 -1
- package/dist/tours/pages/MyToursProgressPage.d.ts +5 -0
- package/dist/tours/pages/MyToursProgressPage.js +5 -6
- package/dist/tours/pages/MyToursProgressPage.js.map +1 -1
- package/dist/tours/pages/SchedulerPage.d.ts +5 -0
- package/dist/tours/pages/SchedulerPage.js +10 -11
- package/dist/tours/pages/SchedulerPage.js.map +1 -1
- package/dist/tours/pages/StepEditorPage.d.ts +6 -0
- package/dist/tours/pages/StepEditorPage.js +302 -309
- package/dist/tours/pages/StepEditorPage.js.map +1 -1
- package/dist/tours/pages/SystemAnalyticsPage.d.ts +5 -0
- package/dist/tours/pages/SystemAnalyticsPage.js +1 -2
- package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
- package/dist/tours/pages/TemplateLibraryPage.d.ts +5 -0
- package/dist/tours/pages/TemplateLibraryPage.js +15 -16
- package/dist/tours/pages/TemplateLibraryPage.js.map +1 -1
- package/dist/tours/pages/TourBuilderPage.d.ts +1 -0
- package/dist/tours/pages/TourBuilderPage.js +273 -300
- package/dist/tours/pages/TourBuilderPage.js.map +1 -1
- package/dist/tours/pages/TourPreviewPage.d.ts +5 -0
- package/dist/tours/pages/TourPreviewPage.js +9 -10
- package/dist/tours/pages/TourPreviewPage.js.map +1 -1
- package/dist/tours/pages/ToursListPage.d.ts +5 -0
- package/dist/tours/pages/ToursListPage.js +157 -157
- package/dist/tours/pages/ToursListPage.js.map +1 -1
- package/dist/tours/providers/ToursProvider.d.ts +25 -0
- package/dist/tours/providers/ToursProvider.js +1 -2
- package/dist/tours/providers/ToursProvider.js.map +1 -1
- package/dist/tours/store/toursStore.d.ts +40 -0
- package/dist/tours/store/toursStore.js +5 -6
- package/dist/tours/store/toursStore.js.map +1 -1
- package/dist/tours/telemetry.d.ts +7 -0
- package/dist/tours/telemetry.js +1 -2
- package/dist/tours/telemetry.js.map +1 -1
- package/dist/tours/types/index.d.ts +348 -0
- package/dist/tours/utils/index.d.ts +28 -0
- package/dist/tours/utils/index.js +3 -4
- package/dist/tours/utils/index.js.map +1 -1
- package/dist/tours/utils/joinPath.d.ts +8 -0
- package/dist/tours/utils/joinPath.js +1 -2
- package/dist/tours/utils/joinPath.js.map +1 -1
- package/dist/utils/nativeBridge.d.ts +2 -0
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +62 -51
|
@@ -2,7 +2,7 @@ import { createContext as e, use as t } from "react";
|
|
|
2
2
|
import { jsx as n } from "react/jsx-runtime";
|
|
3
3
|
import { MemoryRouter as r } from "react-router";
|
|
4
4
|
import { PermissionProvider as i } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
5
|
-
|
|
5
|
+
// src/tours/providers/ToursProvider.tsx
|
|
6
6
|
var a = e(null), o = () => {
|
|
7
7
|
let e = t(a);
|
|
8
8
|
if (!e) throw Error("useToursContext must be used within ToursProvider");
|
|
@@ -22,7 +22,6 @@ function s({ children: e, ...t }) {
|
|
|
22
22
|
});
|
|
23
23
|
return t.basePath ? c : /* @__PURE__ */ n(r, { children: c });
|
|
24
24
|
}
|
|
25
|
-
//#endregion
|
|
26
25
|
export { s as ToursProvider, o as useToursContext };
|
|
27
26
|
|
|
28
27
|
//# sourceMappingURL=ToursProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToursProvider.js","names":[],"sources":["../../../src/tours/providers/ToursProvider.tsx"],"sourcesContent":["import { createContext, use, type ReactNode } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { ToursRootProps, ToursVariant } from '../types';\n\n/**\n * Context value for Tours microfrontend.\n *\n * Carries the ToursRoot props plus a resolved `variant` (always set; defaults\n * to \"admin\" when the caller did not specify one). Pages can read `variant`\n * to branch UI affordances (e.g., the user surface should never attempt\n * admin-only queries).\n */\ntype ToursContextValue = ToursRootProps & { variant: ToursVariant };\n\nconst ToursContext = createContext<ToursContextValue | null>(null);\n\n/**\n * Hook to access Tours context.\n */\nexport const useToursContext = () => {\n const context = use(ToursContext);\n if (!context) {\n throw new Error('useToursContext must be used within ToursProvider');\n }\n return context;\n};\n\n/**\n * Provider component for Tours microfrontend.\n * Sets up routing, context, and global-scoped permission checking.\n */\nexport function ToursProvider({ children, ...props }: ToursRootProps & { children: ReactNode }) {\n // No useMemo here: `props` is a fresh object on every parent render (spread\n // from the shell's JSX), so any memo keyed on `props` would re-create the\n // value every render and defeat itself. Consumer re-rendering is governed\n // by the shell anyway; keeping this lean avoids a false sense of stability.\n const contextValue: ToursContextValue = { ...props, variant: props.variant ?? 'admin' };\n const permissionScopeId = props.tenantId ?? props.organizationId ?? props.workspaceId ?? null;\n\n const coreProviders = (\n <ToursContext.Provider value={contextValue}>\n <PermissionProvider scopeId={permissionScopeId} gateway=\"global\">\n {children}\n </PermissionProvider>\n </ToursContext.Provider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router\n // so we don't create another one. When standalone (no basePath), use MemoryRouter.\n if (props.basePath) {\n // Integrated mode: shell already has BrowserRouter, don't create nested router\n return coreProviders;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n return <MemoryRouter>{coreProviders}</MemoryRouter>;\n}\n"],"mappings":";;;;;AAeA,
|
|
1
|
+
{"version":3,"file":"ToursProvider.js","names":["ToursContext","createContext","useToursContext","context","use","ToursProvider","props","contextValue","permissionScopeId","coreProviders","PermissionProvider","MemoryRouter"],"sources":["../../../src/tours/providers/ToursProvider.tsx"],"sourcesContent":["import { createContext, use, type ReactNode } from 'react';\nimport { MemoryRouter } from 'react-router';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { ToursRootProps, ToursVariant } from '../types';\n\n/**\n * Context value for Tours microfrontend.\n *\n * Carries the ToursRoot props plus a resolved `variant` (always set; defaults\n * to \"admin\" when the caller did not specify one). Pages can read `variant`\n * to branch UI affordances (e.g., the user surface should never attempt\n * admin-only queries).\n */\ntype ToursContextValue = ToursRootProps & { variant: ToursVariant };\n\nconst ToursContext = createContext<ToursContextValue | null>(null);\n\n/**\n * Hook to access Tours context.\n */\nexport const useToursContext = () => {\n const context = use(ToursContext);\n if (!context) {\n throw new Error('useToursContext must be used within ToursProvider');\n }\n return context;\n};\n\n/**\n * Provider component for Tours microfrontend.\n * Sets up routing, context, and global-scoped permission checking.\n */\nexport function ToursProvider({ children, ...props }: ToursRootProps & { children: ReactNode }) {\n // No useMemo here: `props` is a fresh object on every parent render (spread\n // from the shell's JSX), so any memo keyed on `props` would re-create the\n // value every render and defeat itself. Consumer re-rendering is governed\n // by the shell anyway; keeping this lean avoids a false sense of stability.\n const contextValue: ToursContextValue = { ...props, variant: props.variant ?? 'admin' };\n const permissionScopeId = props.tenantId ?? props.organizationId ?? props.workspaceId ?? null;\n\n const coreProviders = (\n <ToursContext.Provider value={contextValue}>\n <PermissionProvider scopeId={permissionScopeId} gateway=\"global\">\n {children}\n </PermissionProvider>\n </ToursContext.Provider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router\n // so we don't create another one. When standalone (no basePath), use MemoryRouter.\n if (props.basePath) {\n // Integrated mode: shell already has BrowserRouter, don't create nested router\n return coreProviders;\n }\n\n // Standalone mode: create MemoryRouter for independent operation\n return <MemoryRouter>{coreProviders}</MemoryRouter>;\n}\n"],"mappings":";;;;;AAeA,IAAMA,IAAeC,EAAwC,IAAI,GAKpDC,UAAwB;CACnC,IAAMC,IAAUC,EAAIJ,CAAY;CAChC,IAAI,CAACG,GACH,MAAU,MAAM,mDAAmD;CAErE,OAAOA;AACT;AAMA,SAAgBE,EAAc,EAAE,aAAU,GAAGC,KAAmD;CAK9F,IAAMC,IAAkC;EAAE,GAAGD;EAAO,SAASA,EAAM,WAAW;CAAQ,GAChFE,IAAoBF,EAAM,YAAYA,EAAM,kBAAkBA,EAAM,eAAe,MAEnFG,IACJ,kBAACT,EAAa,UAAd;EAAuB,OAAOO;EAC5B,UAAA,kBAACG,GAAD;GAAoB,SAASF;GAAmB,SAAQ;GACrD;EACiB,CAAA;CACC,CAAA;CAWzB,OANIF,EAAM,WAEDG,IAIF,kBAACE,GAAD,EAAA,UAAeF,EAA4B,CAAA;AACpD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Tour, TourStep, TourMedia, TourFilters, ValidationError, ToursViewMode, DeviceType } from '../types';
|
|
2
|
+
interface ToursState {
|
|
3
|
+
selectedTour: Tour | null;
|
|
4
|
+
selectedStep: TourStep | null;
|
|
5
|
+
viewMode: ToursViewMode;
|
|
6
|
+
devicePreview: DeviceType;
|
|
7
|
+
tourFilter: TourFilters;
|
|
8
|
+
toursListRevision: number;
|
|
9
|
+
stepSequence: TourStep[];
|
|
10
|
+
isDirty: boolean;
|
|
11
|
+
validationErrors: ValidationError[];
|
|
12
|
+
selectedMedia: TourMedia[];
|
|
13
|
+
uploadProgress: Record<string, number>;
|
|
14
|
+
sidebarOpen: boolean;
|
|
15
|
+
resetTransientState: () => void;
|
|
16
|
+
setSelectedTour: (tour: Tour | null) => void;
|
|
17
|
+
setSelectedStep: (step: TourStep | null) => void;
|
|
18
|
+
setViewMode: (mode: ToursViewMode) => void;
|
|
19
|
+
setDevicePreview: (device: DeviceType) => void;
|
|
20
|
+
toggleSidebar: () => void;
|
|
21
|
+
setSidebarOpen: (open: boolean) => void;
|
|
22
|
+
updateTourFilter: (filter: Partial<TourFilters>) => void;
|
|
23
|
+
resetTourFilter: () => void;
|
|
24
|
+
notifyToursListChanged: () => void;
|
|
25
|
+
updateStepSequence: (steps: TourStep[]) => void;
|
|
26
|
+
reorderSteps: (startIndex: number, endIndex: number) => void;
|
|
27
|
+
addStep: (step: Partial<TourStep>) => void;
|
|
28
|
+
removeStep: (stepId: string) => void;
|
|
29
|
+
updateStep: (stepId: string, updates: Partial<TourStep>) => void;
|
|
30
|
+
setDirty: (dirty: boolean) => void;
|
|
31
|
+
validateTour: () => ValidationError[];
|
|
32
|
+
clearValidationErrors: () => void;
|
|
33
|
+
selectMedia: (media: TourMedia) => void;
|
|
34
|
+
unselectMedia: (mediaId: string) => void;
|
|
35
|
+
clearSelectedMedia: () => void;
|
|
36
|
+
updateUploadProgress: (fileId: string, progress: number) => void;
|
|
37
|
+
removeUploadProgress: (fileId: string) => void;
|
|
38
|
+
}
|
|
39
|
+
export declare const useToursStore: import('zustand').UseBoundStore<import('zustand').StoreApi<ToursState>>;
|
|
40
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { create as e } from "zustand";
|
|
2
|
-
|
|
2
|
+
// src/tours/store/toursStore.ts
|
|
3
3
|
var t = {
|
|
4
4
|
search: "",
|
|
5
5
|
status: "all",
|
|
@@ -108,7 +108,7 @@ var t = {
|
|
|
108
108
|
setDirty: (t) => e({ isDirty: t }),
|
|
109
109
|
validateTour: () => {
|
|
110
110
|
let t = [], { selectedTour: r, stepSequence: i } = n();
|
|
111
|
-
return r ? (
|
|
111
|
+
return r ? (r.name && r.name.trim() !== "" || t.push({
|
|
112
112
|
field: "name",
|
|
113
113
|
message: "validation.tourNameRequired"
|
|
114
114
|
}), r.productId || t.push({
|
|
@@ -119,15 +119,15 @@ var t = {
|
|
|
119
119
|
message: "validation.stepsRequired"
|
|
120
120
|
}), i.forEach((e, n) => {
|
|
121
121
|
let r = n + 1;
|
|
122
|
-
|
|
122
|
+
e.title && e.title.trim() !== "" || t.push({
|
|
123
123
|
field: `step[${n}].title`,
|
|
124
124
|
message: "validation.stepTitleRequired",
|
|
125
125
|
params: { number: r }
|
|
126
|
-
}),
|
|
126
|
+
}), e.content && e.content.trim() !== "" || t.push({
|
|
127
127
|
field: `step[${n}].content`,
|
|
128
128
|
message: "validation.stepContentRequired",
|
|
129
129
|
params: { number: r }
|
|
130
|
-
}), e.placement
|
|
130
|
+
}), e.placement === "CENTER" || e.targetSelector && e.targetSelector.trim() !== "" || t.push({
|
|
131
131
|
field: `step[${n}].targetSelector`,
|
|
132
132
|
message: "validation.stepSelectorRequired",
|
|
133
133
|
params: { number: r }
|
|
@@ -157,7 +157,6 @@ var t = {
|
|
|
157
157
|
delete r[t], e({ uploadProgress: r });
|
|
158
158
|
}
|
|
159
159
|
}));
|
|
160
|
-
//#endregion
|
|
161
160
|
export { n as useToursStore };
|
|
162
161
|
|
|
163
162
|
//# sourceMappingURL=toursStore.js.map
|
|
@@ -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: '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":";;AAsEA,IAAM,IAA6B;CACjC,QAAQ;CACR,QAAQ;CACR,WAAW,KAAA;CACX,UAAU,KAAA;CACV,QAAQ,KAAA;CACR,WAAW;EAAE,OAAO;EAAM,KAAK;EAAM;CACrC,WAAW,KAAA;CACZ,EAEY,IAAgB,GAAoB,GAAK,OAAS;CAE7D,cAAc;CACd,cAAc;CACd,UAAU;CACV,eAAe;CACf,YAAY;CACZ,mBAAmB;CACnB,cAAc,EAAE;CAChB,SAAS;CACT,kBAAkB,EAAE;CACpB,eAAe,EAAE;CACjB,gBAAgB,EAAE;CAClB,aAAa;CAEb,2BACE,EAAI;EACF,cAAc;EACd,cAAc;EACd,UAAU;EACV,eAAe;EACf,YAAY;EACZ,mBAAmB;EACnB,cAAc,EAAE;EAChB,SAAS;EACT,kBAAkB,EAAE;EACpB,eAAe,EAAE;EACjB,gBAAgB,EAAE;EAClB,aAAa;EACd,CAAC;CAGJ,kBAAkB,MAChB,EAAI;EACF,cAAc;EACd,cAAc,GAAM,SAAS,EAAE;EAC/B,SAAS;EACT,kBAAkB,EAAE;EACrB,CAAC;CAEJ,kBAAkB,MAAS,EAAI,EAAE,cAAc,GAAM,CAAC;CAEtD,cAAc,MAAS,EAAI,EAAE,UAAU,GAAM,CAAC;CAE9C,mBAAmB,MAAW,EAAI,EAAE,eAAe,GAAQ,CAAC;CAE5D,qBAAqB,GAAK,OAAW,EAAE,aAAa,CAAC,EAAM,aAAa,EAAE;CAE1E,iBAAiB,MAAS,EAAI,EAAE,aAAa,GAAM,CAAC;CAGpD,mBAAmB,MACjB,GAAK,OAAW,EACd,YAAY;EAAE,GAAG,EAAM;EAAY,GAAG;EAAQ,EAC/C,EAAE;CAEL,uBAAuB,EAAI,EAAE,YAAY,GAAe,CAAC;CAEzD,8BACE,GAAK,OAAW,EAAE,mBAAmB,EAAM,oBAAoB,GAAG,EAAE;CAGtE,qBAAqB,MAAU,EAAI;EAAE,cAAc;EAAO,SAAS;EAAM,CAAC;CAE1E,eAAe,GAAY,MAAa;EACtC,IAAM,IAAQ,CAAC,GAAG,GAAK,CAAC,aAAa,EAC/B,CAAC,KAAW,EAAM,OAAO,GAAY,EAAE;AAM7C,EALA,EAAM,OAAO,GAAU,GAAG,EAAQ,EAKlC,EAAI;GAAE,cAJiB,EAAM,KAAK,GAAM,OAAW;IACjD,GAAG;IACH,WAAW,IAAQ;IACpB,EAAE;GACiC,SAAS;GAAM,CAAC;;CAGtD,UAAU,MAAS;EACjB,IAAM,IAAQ,CAAC,GAAG,GAAK,CAAC,aAAa,EAC/B,IAAoB;GACxB,IAAI,QAAQ,KAAK,KAAK;GACtB,QAAQ,GAAK,CAAC,cAAc,MAAM;GAClC,OAAO,EAAK,SAAS;GACrB,SAAS,EAAK,WAAW;GACzB,WAAW,EAAM,SAAS;GAC1B,gBAAgB,EAAK,kBAAkB;GACvC,WAAW,EAAK,aAAa;GAC7B,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,GAAG;GACJ;AAED,EADA,EAAM,KAAK,EAAQ,EACnB,EAAI;GAAE,cAAc;GAAO,SAAS;GAAM,CAAC;;CAG7C,aAAa,MAAW;AAMtB,IAAI;GAAE,cALQ,GAAK,CAAC,aAAa,QAAQ,MAAM,EAAE,OAAO,EAAO,CAClC,KAAK,GAAM,OAAW;IACjD,GAAG;IACH,WAAW,IAAQ;IACpB,EAAE;GACiC,SAAS;GAAM,CAAC;;CAGtD,aAAa,GAAQ,MAAY;AAI/B,IAAI;GAAE,cAHQ,GAAK,CAAC,aAAa,KAAK,MACpC,EAAK,OAAO,IAAS;IAAE,GAAG;IAAM,GAAG;IAAS,GAAG,EAChD;GAC0B,SAAS;GAAM,CAAC;;CAI7C,WAAW,MAAU,EAAI,EAAE,SAAS,GAAO,CAAC;CAE5C,oBAAoB;EAClB,IAAM,IAA4B,EAAE,EAC9B,EAAE,iBAAc,oBAAiB,GAAK;AAiD5C,SA/CK,MAMD,CAAC,EAAa,QAAQ,EAAa,KAAK,MAAM,KAAK,OACrD,EAAO,KAAK;GAAE,OAAO;GAAQ,SAAS;GAA+B,CAAC,EAGnE,EAAa,aAChB,EAAO,KAAK;GAAE,OAAO;GAAa,SAAS;GAAgC,CAAC,EAG1E,EAAa,WAAW,KAC1B,EAAO,KAAK;GAAE,OAAO;GAAS,SAAS;GAA4B,CAAC,EAGtE,EAAa,SAAS,GAAM,MAAU;GACpC,IAAM,IAAS,IAAQ;AAevB,IAdI,CAAC,EAAK,SAAS,EAAK,MAAM,MAAM,KAAK,OACvC,EAAO,KAAK;IACV,OAAO,QAAQ,EAAM;IACrB,SAAS;IACT,QAAQ,EAAE,WAAQ;IACnB,CAAC,GAEA,CAAC,EAAK,WAAW,EAAK,QAAQ,MAAM,KAAK,OAC3C,EAAO,KAAK;IACV,OAAO,QAAQ,EAAM;IACrB,SAAS;IACT,QAAQ,EAAE,WAAQ;IACnB,CAAC,EAGF,EAAK,cAAc,aAClB,CAAC,EAAK,kBAAkB,EAAK,eAAe,MAAM,KAAK,OAExD,EAAO,KAAK;IACV,OAAO,QAAQ,EAAM;IACrB,SAAS;IACT,QAAQ,EAAE,WAAQ;IACnB,CAAC;IAEJ,EAEF,EAAI,EAAE,kBAAkB,GAAQ,CAAC,EAC1B,MA9CL,EAAO,KAAK;GAAE,OAAO;GAAQ,SAAS;GAA6B,CAAC,EACpE,EAAI,EAAE,kBAAkB,GAAQ,CAAC,EAC1B;;CA+CX,6BAA6B,EAAI,EAAE,kBAAkB,EAAE,EAAE,CAAC;CAG1D,cAAc,MAAU;EACtB,IAAM,IAAW,CAAC,GAAG,GAAK,CAAC,cAAc;AACzC,EAAK,EAAS,MAAM,MAAM,EAAE,OAAO,EAAM,GAAG,KAC1C,EAAS,KAAK,EAAM,EACpB,EAAI,EAAE,eAAe,GAAU,CAAC;;CAIpC,gBAAgB,MAAY;AAC1B,IAAI,EACF,eAAe,GAAK,CAAC,cAAc,QAAQ,MAAM,EAAE,OAAO,EAAQ,EACnE,CAAC;;CAGJ,0BAA0B,EAAI,EAAE,eAAe,EAAE,EAAE,CAAC;CAEpD,uBAAuB,GAAQ,MAAa;AAC1C,IAAI,EACF,gBAAgB;GAAE,GAAG,GAAK,CAAC;IAAiB,IAAS;GAAU,EAChE,CAAC;;CAGJ,uBAAuB,MAAW;EAChC,IAAM,IAAW,EAAE,GAAG,GAAK,CAAC,gBAAgB;AAE5C,EADA,OAAO,EAAS,IAChB,EAAI,EAAE,gBAAgB,GAAU,CAAC;;CAEpC,EAAE"}
|
|
1
|
+
{"version":3,"file":"toursStore.js","names":["defaultFilter","useToursStore","create","set","tour","step","mode","device","state","open","filter","steps","get","removed","startIndex","endIndex","index","newStep","s","stepId","updates","dirty","errors","selectedTour","stepSequence","number","selected","m","media","mediaId","fileId","progress"],"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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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BurdenoffEventName, EventBus } from '@burdenoff/fe-libs/shared/events';
|
|
2
|
+
import { ToursRootProps } from './types';
|
|
3
|
+
export declare const TOURS_RYBBIT_ALLOWLIST: BurdenoffEventName[];
|
|
4
|
+
export type ToursApiOperation = 'tour.save' | 'step.save' | 'media.attach' | 'media.upload' | 'schedule.create' | 'template.create_from_template' | 'template.delete' | 'template.save';
|
|
5
|
+
type ToursTelemetryContext = Pick<ToursRootProps, 'productId' | 'workspaceId'>;
|
|
6
|
+
export declare function emitToursApiFailed(eventBus: EventBus, operation: ToursApiOperation, context: ToursTelemetryContext): void;
|
|
7
|
+
export {};
|
package/dist/tours/telemetry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/tours/telemetry.ts
|
|
2
2
|
var e = [
|
|
3
3
|
"tours.page.viewed",
|
|
4
4
|
"tours.tour.started",
|
|
@@ -13,7 +13,6 @@ function t(e, t, n) {
|
|
|
13
13
|
workspaceId: n.workspaceId
|
|
14
14
|
}, { component: "microfe-tours" });
|
|
15
15
|
}
|
|
16
|
-
//#endregion
|
|
17
16
|
export { e as TOURS_RYBBIT_ALLOWLIST, t as emitToursApiFailed };
|
|
18
17
|
|
|
19
18
|
//# sourceMappingURL=telemetry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.js","names":[],"sources":["../../src/tours/telemetry.ts"],"sourcesContent":["import type { BurdenoffEventName, EventBus } from '@burdenoff/fe-libs/shared/events';\nimport type { ToursRootProps } from './types';\n\nexport const TOURS_RYBBIT_ALLOWLIST: BurdenoffEventName[] = [\n 'tours.page.viewed',\n 'tours.tour.started',\n 'tours.tour.completed',\n 'tours.tour.skipped',\n];\n\nexport type ToursApiOperation =\n | 'tour.save'\n | 'step.save'\n | 'media.attach'\n | 'media.upload'\n | 'schedule.create'\n | 'template.create_from_template'\n | 'template.delete'\n | 'template.save';\n\ntype ToursTelemetryContext = Pick<ToursRootProps, 'productId' | 'workspaceId'>;\n\nexport function emitToursApiFailed(\n eventBus: EventBus,\n operation: ToursApiOperation,\n context: ToursTelemetryContext\n) {\n eventBus.emit(\n 'tours.api.failed',\n {\n // The event registry currently allows only user-facing Tours routes.\n // App-shell event metadata still carries the exact browser pathname.\n route: '/tours',\n operation,\n productId: context.productId,\n workspaceId: context.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n}\n"],"mappings":";AAGA,
|
|
1
|
+
{"version":3,"file":"telemetry.js","names":["TOURS_RYBBIT_ALLOWLIST","emitToursApiFailed","context"],"sources":["../../src/tours/telemetry.ts"],"sourcesContent":["import type { BurdenoffEventName, EventBus } from '@burdenoff/fe-libs/shared/events';\nimport type { ToursRootProps } from './types';\n\nexport const TOURS_RYBBIT_ALLOWLIST: BurdenoffEventName[] = [\n 'tours.page.viewed',\n 'tours.tour.started',\n 'tours.tour.completed',\n 'tours.tour.skipped',\n];\n\nexport type ToursApiOperation =\n | 'tour.save'\n | 'step.save'\n | 'media.attach'\n | 'media.upload'\n | 'schedule.create'\n | 'template.create_from_template'\n | 'template.delete'\n | 'template.save';\n\ntype ToursTelemetryContext = Pick<ToursRootProps, 'productId' | 'workspaceId'>;\n\nexport function emitToursApiFailed(\n eventBus: EventBus,\n operation: ToursApiOperation,\n context: ToursTelemetryContext\n) {\n eventBus.emit(\n 'tours.api.failed',\n {\n // The event registry currently allows only user-facing Tours routes.\n // App-shell event metadata still carries the exact browser pathname.\n route: '/tours',\n operation,\n productId: context.productId,\n workspaceId: context.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n}\n"],"mappings":";AAGA,IAAaA,IAA+C;CAC1D;CACA;CACA;CACA;AACF;AAcA,SAAgBC,EACd,GACA,GACA,GACA;CACA,EAAS,KACP,oBACA;EAGE,OAAO;EACP;EACA,WAAWC,EAAQ;EACnB,aAAaA,EAAQ;CACvB,GACA,EAAE,WAAW,gBAAgB,CAC/B;AACF"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { ToursTranslationKey } from '../i18n';
|
|
2
|
+
export interface NavItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
path: string;
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
order?: number;
|
|
8
|
+
group?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Footer item for shell integration
|
|
12
|
+
*/
|
|
13
|
+
export interface FooterItem {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
type: 'status' | 'action';
|
|
17
|
+
order?: number;
|
|
18
|
+
section: 'left' | 'center' | 'right';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Base microfrontend root props
|
|
22
|
+
*/
|
|
23
|
+
export interface MicrofrontendRootProps {
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Tours UI variant — selects which surface area is rendered.
|
|
27
|
+
*
|
|
28
|
+
* - "admin" — full authoring suite (CRUD, media library, scheduler, analytics).
|
|
29
|
+
* Consumed by `ToursAdminRoot` from `admin-app`.
|
|
30
|
+
* - "user" — read-only "My Tours" + "My Progress" surface. Consumed by
|
|
31
|
+
* `ToursUserRoot` from product user shells (e.g., vibecontrols-app).
|
|
32
|
+
*
|
|
33
|
+
* Defaults to "admin" for back-compatibility with the original `ToursRoot`.
|
|
34
|
+
*/
|
|
35
|
+
export type ToursVariant = 'admin' | 'user';
|
|
36
|
+
/**
|
|
37
|
+
* Props for the ToursRoot component
|
|
38
|
+
*/
|
|
39
|
+
export interface ToursRootProps extends MicrofrontendRootProps {
|
|
40
|
+
/**
|
|
41
|
+
* Base path for the tours microfrontend
|
|
42
|
+
*/
|
|
43
|
+
basePath?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Which Tours surface to render. Defaults to "admin".
|
|
46
|
+
* @see ToursVariant
|
|
47
|
+
*/
|
|
48
|
+
variant?: ToursVariant;
|
|
49
|
+
/**
|
|
50
|
+
* Product identifier the shell is mounting this MFE under (e.g., "workspaces",
|
|
51
|
+
* "vibecontrols", "bigconsole"). Used by `MyToursPage.toursForContext` to
|
|
52
|
+
* scope the listed tours to the right product — NOT a user-controlled query
|
|
53
|
+
* param. Shells should pass the product slug they are themselves known as.
|
|
54
|
+
*/
|
|
55
|
+
productId?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Optional module identifier within the product (e.g., "calendar"). Narrows
|
|
58
|
+
* the tour list further when the shell can supply it.
|
|
59
|
+
*/
|
|
60
|
+
moduleId?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Optional page identifier within the module (e.g., "settings/team"). Narrows
|
|
63
|
+
* the tour list to tours scoped at the current page.
|
|
64
|
+
*/
|
|
65
|
+
pageId?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Current workspace ID (fallback scope for embedded contexts)
|
|
68
|
+
*/
|
|
69
|
+
workspaceId?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Current tenant ID (preferred scope for global permission checks)
|
|
72
|
+
*/
|
|
73
|
+
tenantId?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Current organization ID (fallback scope for global permission checks)
|
|
76
|
+
*/
|
|
77
|
+
organizationId?: string;
|
|
78
|
+
/**
|
|
79
|
+
* API Gateway URL for GraphQL requests
|
|
80
|
+
*/
|
|
81
|
+
apiGatewayUrl?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Authentication token for API requests
|
|
84
|
+
*/
|
|
85
|
+
authToken?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Navigation function provided by the shell
|
|
88
|
+
*/
|
|
89
|
+
navigate?: (path: string) => void;
|
|
90
|
+
/**
|
|
91
|
+
* Current user information
|
|
92
|
+
*/
|
|
93
|
+
currentUser?: {
|
|
94
|
+
id: string;
|
|
95
|
+
email: string;
|
|
96
|
+
firstName?: string;
|
|
97
|
+
lastName?: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Optional function to register navigation items with the shell
|
|
101
|
+
* Injected by the shell app
|
|
102
|
+
*/
|
|
103
|
+
registerNavItems?: (items: NavItem | NavItem[]) => () => void;
|
|
104
|
+
/**
|
|
105
|
+
* Optional function to register footer items with the shell
|
|
106
|
+
* Injected by the shell app
|
|
107
|
+
*/
|
|
108
|
+
registerFooterItems?: (items: FooterItem | FooterItem[]) => () => void;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Tour status enumeration
|
|
112
|
+
*/
|
|
113
|
+
export type TourStatus = 'DRAFT' | 'PUBLISHED' | 'ARCHIVED' | 'SCHEDULED';
|
|
114
|
+
/**
|
|
115
|
+
* Tour trigger type enumeration
|
|
116
|
+
*/
|
|
117
|
+
export type TourTriggerType = 'MANUAL' | 'AUTO_FIRST_VISIT' | 'AUTO_ALWAYS' | 'ON_EVENT' | 'SCHEDULED';
|
|
118
|
+
/**
|
|
119
|
+
* Step placement enumeration
|
|
120
|
+
*/
|
|
121
|
+
export type StepPlacement = 'AUTO' | 'TOP' | 'BOTTOM' | 'LEFT' | 'RIGHT' | 'CENTER';
|
|
122
|
+
/**
|
|
123
|
+
* Media type enumeration
|
|
124
|
+
*/
|
|
125
|
+
export type TourMediaType = 'IMAGE' | 'VIDEO' | 'AUDIO' | 'DOCUMENT';
|
|
126
|
+
/**
|
|
127
|
+
* Device type enumeration
|
|
128
|
+
*/
|
|
129
|
+
export type DeviceType = 'mobile' | 'tablet' | 'desktop';
|
|
130
|
+
/**
|
|
131
|
+
* Media position within step
|
|
132
|
+
*/
|
|
133
|
+
export type MediaPosition = 'header' | 'body' | 'inline' | 'footer';
|
|
134
|
+
/**
|
|
135
|
+
* Tour template category
|
|
136
|
+
*/
|
|
137
|
+
export type TemplateCategory = 'ONBOARDING' | 'FEATURE_ANNOUNCEMENT' | 'HELP' | 'CUSTOM';
|
|
138
|
+
/**
|
|
139
|
+
* Recurrence pattern for scheduled tours
|
|
140
|
+
*/
|
|
141
|
+
export type RecurrencePattern = 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'CUSTOM';
|
|
142
|
+
/**
|
|
143
|
+
* Tour entity
|
|
144
|
+
*/
|
|
145
|
+
export interface Tour {
|
|
146
|
+
id: string;
|
|
147
|
+
name: string;
|
|
148
|
+
slug: string;
|
|
149
|
+
description?: string;
|
|
150
|
+
productId: string;
|
|
151
|
+
moduleId?: string;
|
|
152
|
+
pageId?: string;
|
|
153
|
+
status: TourStatus;
|
|
154
|
+
isActive: boolean;
|
|
155
|
+
triggerType: TourTriggerType;
|
|
156
|
+
triggerConfig?: Record<string, unknown>;
|
|
157
|
+
conditions?: Record<string, unknown>;
|
|
158
|
+
config?: Record<string, unknown>;
|
|
159
|
+
mobileConfig?: Record<string, unknown>;
|
|
160
|
+
tabletConfig?: Record<string, unknown>;
|
|
161
|
+
desktopConfig?: Record<string, unknown>;
|
|
162
|
+
priority: number;
|
|
163
|
+
displayOrder: number;
|
|
164
|
+
startDate?: string;
|
|
165
|
+
endDate?: string;
|
|
166
|
+
viewCount: number;
|
|
167
|
+
completionCount: number;
|
|
168
|
+
skipCount: number;
|
|
169
|
+
steps?: TourStep[];
|
|
170
|
+
schedules?: TourSchedule[];
|
|
171
|
+
metadata?: Record<string, unknown>;
|
|
172
|
+
version: number;
|
|
173
|
+
createdBy: string;
|
|
174
|
+
updatedBy?: string;
|
|
175
|
+
createdAt: string;
|
|
176
|
+
updatedAt: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Tour step entity
|
|
180
|
+
*/
|
|
181
|
+
export interface TourStep {
|
|
182
|
+
id: string;
|
|
183
|
+
tourId: string;
|
|
184
|
+
title: string;
|
|
185
|
+
content: string;
|
|
186
|
+
stepOrder: number;
|
|
187
|
+
targetSelector?: string;
|
|
188
|
+
placement: StepPlacement;
|
|
189
|
+
targetSelectorMobile?: string;
|
|
190
|
+
targetSelectorTablet?: string;
|
|
191
|
+
targetSelectorDesktop?: string;
|
|
192
|
+
customData?: Record<string, unknown>;
|
|
193
|
+
actions?: Record<string, unknown>;
|
|
194
|
+
requireInteraction?: boolean;
|
|
195
|
+
waitForSelector?: string;
|
|
196
|
+
waitForEvent?: string;
|
|
197
|
+
waitForTimeout?: number;
|
|
198
|
+
skipConditions?: Record<string, unknown>;
|
|
199
|
+
media?: TourStepMedia[];
|
|
200
|
+
metadata?: Record<string, unknown>;
|
|
201
|
+
createdAt: string;
|
|
202
|
+
updatedAt: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Tour media entity
|
|
206
|
+
*/
|
|
207
|
+
export interface TourMedia {
|
|
208
|
+
id: string;
|
|
209
|
+
name: string;
|
|
210
|
+
type: TourMediaType;
|
|
211
|
+
cdnUrl: string;
|
|
212
|
+
mimeType: string;
|
|
213
|
+
size: number;
|
|
214
|
+
duration?: number;
|
|
215
|
+
width?: number;
|
|
216
|
+
height?: number;
|
|
217
|
+
altText?: string;
|
|
218
|
+
caption?: string;
|
|
219
|
+
transcript?: string;
|
|
220
|
+
metadata?: Record<string, unknown>;
|
|
221
|
+
createdBy: string;
|
|
222
|
+
createdAt: string;
|
|
223
|
+
updatedAt: string;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Tour step media (junction entity)
|
|
227
|
+
*/
|
|
228
|
+
export interface TourStepMedia {
|
|
229
|
+
id: string;
|
|
230
|
+
stepId: string;
|
|
231
|
+
mediaId: string;
|
|
232
|
+
order?: number;
|
|
233
|
+
position: MediaPosition;
|
|
234
|
+
media: TourMedia;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Tour schedule entity
|
|
238
|
+
*/
|
|
239
|
+
export interface TourSchedule {
|
|
240
|
+
id: string;
|
|
241
|
+
tourId: string;
|
|
242
|
+
startDate: string;
|
|
243
|
+
endDate?: string;
|
|
244
|
+
recurrenceRule?: string;
|
|
245
|
+
timezone: string;
|
|
246
|
+
isActive: boolean;
|
|
247
|
+
nextExecutionAt?: string;
|
|
248
|
+
executionCount?: number;
|
|
249
|
+
createdAt: string;
|
|
250
|
+
updatedAt: string;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Tour template entity
|
|
254
|
+
*/
|
|
255
|
+
export interface TourTemplate {
|
|
256
|
+
id: string;
|
|
257
|
+
name: string;
|
|
258
|
+
slug: string;
|
|
259
|
+
description?: string;
|
|
260
|
+
category: TemplateCategory;
|
|
261
|
+
tags: string[];
|
|
262
|
+
thumbnail?: string;
|
|
263
|
+
config: Record<string, unknown>;
|
|
264
|
+
isPublic: boolean;
|
|
265
|
+
isPremium: boolean;
|
|
266
|
+
useCount: number;
|
|
267
|
+
createdBy: string;
|
|
268
|
+
createdAt: string;
|
|
269
|
+
updatedAt: string;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Tour analytics data
|
|
273
|
+
*/
|
|
274
|
+
export interface TourAnalytics {
|
|
275
|
+
tourId: string;
|
|
276
|
+
totalViews: number;
|
|
277
|
+
totalCompletions: number;
|
|
278
|
+
totalSkips: number;
|
|
279
|
+
averageCompletionRate: number;
|
|
280
|
+
averageDuration: number;
|
|
281
|
+
byDeviceType: Array<{
|
|
282
|
+
deviceType: DeviceType;
|
|
283
|
+
count: number;
|
|
284
|
+
completionRate: number;
|
|
285
|
+
}>;
|
|
286
|
+
byEventType: Array<{
|
|
287
|
+
eventType: string;
|
|
288
|
+
count: number;
|
|
289
|
+
}>;
|
|
290
|
+
stepAnalytics?: Array<{
|
|
291
|
+
stepId: string;
|
|
292
|
+
stepOrder: number;
|
|
293
|
+
views: number;
|
|
294
|
+
completions: number;
|
|
295
|
+
dropOffRate: number;
|
|
296
|
+
averageDuration: number;
|
|
297
|
+
}>;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Tour filters for queries
|
|
301
|
+
*/
|
|
302
|
+
export interface TourFilters {
|
|
303
|
+
search?: string;
|
|
304
|
+
status?: TourStatus | 'all';
|
|
305
|
+
productId?: string;
|
|
306
|
+
moduleId?: string;
|
|
307
|
+
pageId?: string;
|
|
308
|
+
dateRange?: {
|
|
309
|
+
start: string | null;
|
|
310
|
+
end: string | null;
|
|
311
|
+
};
|
|
312
|
+
createdBy?: string;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Pagination for tour queries
|
|
316
|
+
*/
|
|
317
|
+
export interface TourPagination {
|
|
318
|
+
page: number;
|
|
319
|
+
limit: number;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Tours query result
|
|
323
|
+
*/
|
|
324
|
+
export interface ToursQueryResult {
|
|
325
|
+
tours: Tour[];
|
|
326
|
+
total: number;
|
|
327
|
+
hasMore: boolean;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Validation error
|
|
331
|
+
*/
|
|
332
|
+
export interface ValidationError {
|
|
333
|
+
field: string;
|
|
334
|
+
message: ToursTranslationKey;
|
|
335
|
+
params?: Record<string, string | number>;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* View mode for tours list
|
|
339
|
+
*/
|
|
340
|
+
export type ToursViewMode = 'grid' | 'list';
|
|
341
|
+
/**
|
|
342
|
+
* Tab in step editor
|
|
343
|
+
*/
|
|
344
|
+
export type StepEditorTab = 'content' | 'targeting' | 'actions' | 'advanced';
|
|
345
|
+
/**
|
|
346
|
+
* Tour builder step
|
|
347
|
+
*/
|
|
348
|
+
export type TourBuilderStep = 'info' | 'steps' | 'targeting' | 'schedule' | 'preview';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ToursTranslationKey } from '../i18n';
|
|
2
|
+
type TranslateFn = (key: ToursTranslationKey) => string;
|
|
3
|
+
/**
|
|
4
|
+
* Format file size
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatFileSize(bytes: number, t?: TranslateFn): string;
|
|
7
|
+
/**
|
|
8
|
+
* Format duration (seconds to human readable)
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatDuration(seconds: number, t?: TranslateFn): string;
|
|
11
|
+
/**
|
|
12
|
+
* Format percentage
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatPercentage(value: number, decimals?: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* Format number with locale-aware separators
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatNumber(num: number): string;
|
|
19
|
+
/**
|
|
20
|
+
* Hook that returns formatter helpers bound to the current translation context.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useToursFormatters(): {
|
|
23
|
+
formatFileSize: (bytes: number) => string;
|
|
24
|
+
formatDuration: (seconds: number) => string;
|
|
25
|
+
formatNumber: typeof formatNumber;
|
|
26
|
+
formatPercentage: typeof formatPercentage;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useToursI18n as e } from "../i18n.js";
|
|
2
2
|
import { useMemo as t } from "react";
|
|
3
|
-
|
|
3
|
+
// src/tours/utils/index.ts
|
|
4
4
|
function n(e, t) {
|
|
5
5
|
let n = t ? t("common.fileSize.bytes") : "Bytes";
|
|
6
6
|
if (e === 0) return `0 ${n}`;
|
|
@@ -10,7 +10,7 @@ function n(e, t) {
|
|
|
10
10
|
t ? t("common.fileSize.mb") : "MB",
|
|
11
11
|
t ? t("common.fileSize.gb") : "GB"
|
|
12
12
|
], a = Math.floor(Math.log(e) / Math.log(r));
|
|
13
|
-
return Math.round(e / r **
|
|
13
|
+
return Math.round(e / r ** a * 100) / 100 + " " + i[a];
|
|
14
14
|
}
|
|
15
15
|
function r(e, t) {
|
|
16
16
|
let n = t ? t("common.duration.seconds") : "s", r = t ? t("common.duration.minutes") : "m", i = t ? t("common.duration.hours") : "h";
|
|
@@ -35,7 +35,6 @@ function o() {
|
|
|
35
35
|
formatPercentage: i
|
|
36
36
|
}), [o]);
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
export { o as useToursFormatters };
|
|
38
|
+
export { r as formatDuration, n as formatFileSize, a as formatNumber, i as formatPercentage, o as useToursFormatters };
|
|
40
39
|
|
|
41
40
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"index.js","names":["formatFileSize","zeroLabel","bytes","k","sizes","i","formatDuration","secLabel","minLabel","hrLabel","seconds","minutes","remainingSeconds","hours","remainingMinutes","formatPercentage","value","decimals","formatNumber","num","useToursFormatters","useToursI18n","useMemo","tr"],"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,SAAgBA,EAAe,GAAe,GAAyB;CACrE,IAAMC,IAAY,IAAI,EAAE,uBAAuB,IAAI;CACnD,IAAIC,MAAU,GAAG,OAAO,KAAKD;CAE7B,IAAME,IAAI,MACJC,IAAQ;EACZ,IAAI,EAAE,uBAAuB,IAAI;EACjC,IAAI,EAAE,oBAAoB,IAAI;EAC9B,IAAI,EAAE,oBAAoB,IAAI;EAC9B,IAAI,EAAE,oBAAoB,IAAI;CAChC,GACMC,IAAI,KAAK,MAAM,KAAK,IAAIH,CAAK,IAAI,KAAK,IAAIC,CAAC,CAAC;CAElD,OAAO,KAAK,MAAOD,IAAiBC,KAAGE,IAAM,GAAG,IAAI,MAAM,MAAMD,EAAMC;AACxE;AAKA,SAAgBC,EAAe,GAAiB,GAAyB;CACvE,IAAMC,IAAW,IAAI,EAAE,yBAAyB,IAAI,KAC9CC,IAAW,IAAI,EAAE,yBAAyB,IAAI,KAC9CC,IAAU,IAAI,EAAE,uBAAuB,IAAI;CAEjD,IAAIC,IAAU,IACZ,OAAO,GAAG,KAAK,MAAMA,CAAO,IAAIH;CAGlC,IAAMI,IAAU,KAAK,MAAMD,IAAU,EAAE,GACjCE,IAAmB,KAAK,MAAMF,IAAU,EAAE;CAEhD,IAAIC,IAAU,IACZ,OAAOC,IAAmB,IACtB,GAAGD,IAAUH,EAAS,GAAGI,IAAmBL,MAC5C,GAAGI,IAAUH;CAGnB,IAAMK,IAAQ,KAAK,MAAMF,IAAU,EAAE,GAC/BG,IAAmBH,IAAU;CAEnC,OAAOG,IAAmB,IACtB,GAAGD,IAAQJ,EAAQ,GAAGK,IAAmBN,MACzC,GAAGK,IAAQJ;AACjB;AAKA,SAAgBM,EAAiB,GAAe,IAAmB,GAAW;CAC5E,OAAO,GAAGC,EAAM,QAAQC,CAAQ,EAAE;AACpC;AAKA,SAAgBC,EAAa,GAAqB;CAChD,OAAOC,EAAI,eAAe;AAC5B;AAKA,SAAgBC,IAAqB;CACnC,IAAM,EAAE,UAAOC,EAAa;CAE5B,OAAOC,SACE;EACL,iBAAiB,MAAkBtB,EAAeE,GAAOqB,CAAE;EAC3D,iBAAiB,MAAoBjB,EAAeI,GAASa,CAAE;EAC/D;EACA;CACF,IACA,CAACA,CAAE,CACL;AACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Join a shell `basePath` (e.g. `/tours`) with a route fragment, collapsing
|
|
3
|
+
* duplicate slashes and handling leading/trailing-slash variants safely.
|
|
4
|
+
*
|
|
5
|
+
* Extracted so ToursAdminRoot and ToursUserRoot share one implementation
|
|
6
|
+
* instead of maintaining copy-pasted helpers.
|
|
7
|
+
*/
|
|
8
|
+
export declare function joinPath(base: string, path: string): string;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
// src/tours/utils/joinPath.ts
|
|
2
2
|
function e(e, t) {
|
|
3
3
|
return (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
4
4
|
}
|
|
5
|
-
//#endregion
|
|
6
5
|
export { e as joinPath };
|
|
7
6
|
|
|
8
7
|
//# sourceMappingURL=joinPath.js.map
|