@burdenoff/microfe-tours 2026.626.2 → 2026.720.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.
@@ -0,0 +1,144 @@
1
+ import { useCallback as e, useRef as t, useState as n } from "react";
2
+ import { Markdown as r } from "@burdenoff/fe-libs/shared/components";
3
+ import { Bold as i, Code as a, Italic as o, Link as s, List as c } from "lucide-react";
4
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
5
+ //#region src/tours/components/MarkdownEditor.tsx
6
+ function d({ title: e, onClick: t, children: n }) {
7
+ return /* @__PURE__ */ l("button", {
8
+ type: "button",
9
+ onClick: t,
10
+ title: e,
11
+ "aria-label": e,
12
+ className: "p-2 min-h-[36px] min-w-[36px] rounded-md border border-border-subtle hover:bg-accent text-text-secondary",
13
+ children: n
14
+ });
15
+ }
16
+ function f(e, t, n = t) {
17
+ let r = e.selectionStart, i = e.selectionEnd, a = e.value, o = a.slice(r, i), s = `${t}${o}${n}`, c = a.slice(0, r) + s + a.slice(i);
18
+ return requestAnimationFrame(() => {
19
+ e.focus();
20
+ let n = r + t.length + o.length;
21
+ e.setSelectionRange(n, n);
22
+ }), c;
23
+ }
24
+ function p(e, t) {
25
+ let n = e.selectionStart, r = e.value, i = r.lastIndexOf("\n", n - 1) + 1, a = r.slice(0, i), o = r.slice(i);
26
+ return requestAnimationFrame(() => {
27
+ e.focus();
28
+ let n = i + t.length;
29
+ e.setSelectionRange(n, n);
30
+ }), `${a}${t}${o}`;
31
+ }
32
+ function m({ id: m, value: h, onChange: g, label: _, placeholder: v, rows: y = 6, required: b, disabled: x, "aria-label": S }) {
33
+ let C = t(null), [w, T] = n("write"), E = e(() => {
34
+ let e = C.current;
35
+ e && g(f(e, "**"));
36
+ }, [g]), D = e(() => {
37
+ let e = C.current;
38
+ e && g(f(e, "_"));
39
+ }, [g]), O = e(() => {
40
+ let e = C.current;
41
+ if (!e) return;
42
+ let t = e.selectionStart, n = e.selectionEnd, r = h.slice(t, n) || "link text", i = `[${r}](url)`, a = h.slice(0, t) + i + h.slice(n);
43
+ requestAnimationFrame(() => {
44
+ e.focus();
45
+ let n = t + r.length + 3, i = n + 3;
46
+ e.setSelectionRange(n, i);
47
+ }), g(a);
48
+ }, [g, h]), k = e(() => {
49
+ let e = C.current;
50
+ e && g(f(e, "`"));
51
+ }, [g]), A = e(() => {
52
+ let e = C.current;
53
+ e && g(p(e, "- "));
54
+ }, [g]);
55
+ return /* @__PURE__ */ u("div", {
56
+ className: "space-y-2",
57
+ children: [_ && /* @__PURE__ */ u("label", {
58
+ htmlFor: m,
59
+ className: "block text-sm font-medium",
60
+ children: [_, b && /* @__PURE__ */ l("span", {
61
+ className: "text-status-error-text ml-1",
62
+ children: "*"
63
+ })]
64
+ }), /* @__PURE__ */ u("div", {
65
+ className: "border border-border-subtle rounded-md bg-bg-surface overflow-hidden",
66
+ children: [/* @__PURE__ */ u("div", {
67
+ className: "flex items-center justify-between border-b border-border-subtle px-2 py-1",
68
+ children: [/* @__PURE__ */ u("div", {
69
+ className: "flex items-center gap-1",
70
+ children: [
71
+ /* @__PURE__ */ l(d, {
72
+ title: "Bold",
73
+ onClick: E,
74
+ children: /* @__PURE__ */ l(i, { size: 16 })
75
+ }),
76
+ /* @__PURE__ */ l(d, {
77
+ title: "Italic",
78
+ onClick: D,
79
+ children: /* @__PURE__ */ l(o, { size: 16 })
80
+ }),
81
+ /* @__PURE__ */ l(d, {
82
+ title: "Link",
83
+ onClick: O,
84
+ children: /* @__PURE__ */ l(s, { size: 16 })
85
+ }),
86
+ /* @__PURE__ */ l(d, {
87
+ title: "Inline code",
88
+ onClick: k,
89
+ children: /* @__PURE__ */ l(a, { size: 16 })
90
+ }),
91
+ /* @__PURE__ */ l(d, {
92
+ title: "Bullet list",
93
+ onClick: A,
94
+ children: /* @__PURE__ */ l(c, { size: 16 })
95
+ })
96
+ ]
97
+ }), /* @__PURE__ */ u("div", {
98
+ className: "flex items-center gap-1",
99
+ role: "tablist",
100
+ "aria-label": "Markdown editor tabs",
101
+ children: [/* @__PURE__ */ l("button", {
102
+ type: "button",
103
+ role: "tab",
104
+ "aria-selected": w === "write",
105
+ onClick: () => T("write"),
106
+ className: `px-3 py-1.5 text-sm rounded-md ${w === "write" ? "bg-action-primary-bg text-action-primary-text" : "hover:bg-accent text-text-secondary"}`,
107
+ children: "Write"
108
+ }), /* @__PURE__ */ l("button", {
109
+ type: "button",
110
+ role: "tab",
111
+ "aria-selected": w === "preview",
112
+ onClick: () => T("preview"),
113
+ className: `px-3 py-1.5 text-sm rounded-md ${w === "preview" ? "bg-action-primary-bg text-action-primary-text" : "hover:bg-accent text-text-secondary"}`,
114
+ children: "Preview"
115
+ })]
116
+ })]
117
+ }), /* @__PURE__ */ u("div", {
118
+ className: "relative",
119
+ children: [/* @__PURE__ */ l("textarea", {
120
+ ref: C,
121
+ id: m,
122
+ value: h,
123
+ onChange: (e) => g(e.target.value),
124
+ rows: y,
125
+ disabled: x,
126
+ required: b,
127
+ placeholder: v,
128
+ "aria-label": S ?? _,
129
+ className: `w-full px-3 py-2 bg-bg-surface text-text-primary text-sm sm:text-base resize-y min-h-[120px] focus:outline-none ${w === "write" ? "block" : "hidden"}`
130
+ }), w === "preview" && /* @__PURE__ */ l("div", {
131
+ className: "w-full px-3 py-2 min-h-[120px] max-h-[24rem] overflow-y-auto text-sm sm:text-base",
132
+ children: h ? /* @__PURE__ */ l(r, { children: h }) : /* @__PURE__ */ l("p", {
133
+ className: "text-text-secondary italic",
134
+ children: "Nothing to preview"
135
+ })
136
+ })]
137
+ })]
138
+ })]
139
+ });
140
+ }
141
+ //#endregion
142
+ export { m as MarkdownEditor };
143
+
144
+ //# sourceMappingURL=MarkdownEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../src/tours/components/MarkdownEditor.tsx"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { Bold, Italic, Link, Code, List } from 'lucide-react';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components';\n\nexport interface MarkdownEditorProps {\n id?: string;\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n rows?: number;\n required?: boolean;\n disabled?: boolean;\n 'aria-label'?: string;\n}\n\ninterface ToolbarButtonProps {\n title: string;\n onClick: () => void;\n children: React.ReactNode;\n}\n\nfunction ToolbarButton({ title, onClick, children }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n aria-label={title}\n className=\"p-2 min-h-[36px] min-w-[36px] rounded-md border border-border-subtle hover:bg-accent text-text-secondary\"\n >\n {children}\n </button>\n );\n}\n\nfunction wrapSelection(\n textarea: HTMLTextAreaElement,\n prefix: string,\n suffix: string = prefix\n): string {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const value = textarea.value;\n const selected = value.slice(start, end);\n const replacement = `${prefix}${selected}${suffix}`;\n const newValue = value.slice(0, start) + replacement + value.slice(end);\n\n // Defer cursor positioning so React state flush does not reset it.\n requestAnimationFrame(() => {\n textarea.focus();\n const newCursor = start + prefix.length + selected.length;\n textarea.setSelectionRange(newCursor, newCursor);\n });\n\n return newValue;\n}\n\nfunction insertAtLineStart(textarea: HTMLTextAreaElement, marker: string): string {\n const start = textarea.selectionStart;\n const value = textarea.value;\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1;\n const before = value.slice(0, lineStart);\n const after = value.slice(lineStart);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const newCursor = lineStart + marker.length;\n textarea.setSelectionRange(newCursor, newCursor);\n });\n\n return `${before}${marker}${after}`;\n}\n\nexport function MarkdownEditor({\n id,\n value,\n onChange,\n label,\n placeholder,\n rows = 6,\n required,\n disabled,\n 'aria-label': ariaLabel,\n}: MarkdownEditorProps) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [activeTab, setActiveTab] = useState<'write' | 'preview'>('write');\n\n const handleBold = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '**'));\n }, [onChange]);\n\n const handleItalic = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '_'));\n }, [onChange]);\n\n const handleLink = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const selected = value.slice(start, end) || 'link text';\n const replacement = `[${selected}](url)`;\n const newValue = value.slice(0, start) + replacement + value.slice(end);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const linkStart = start + selected.length + 3;\n const linkEnd = linkStart + 3;\n textarea.setSelectionRange(linkStart, linkEnd);\n });\n\n onChange(newValue);\n }, [onChange, value]);\n\n const handleCode = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '`'));\n }, [onChange]);\n\n const handleList = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(insertAtLineStart(textarea, '- '));\n }, [onChange]);\n\n return (\n <div className=\"space-y-2\">\n {label && (\n <label htmlFor={id} className=\"block text-sm font-medium\">\n {label}\n {required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n )}\n\n <div className=\"border border-border-subtle rounded-md bg-bg-surface overflow-hidden\">\n <div className=\"flex items-center justify-between border-b border-border-subtle px-2 py-1\">\n <div className=\"flex items-center gap-1\">\n <ToolbarButton title=\"Bold\" onClick={handleBold}>\n <Bold size={16} />\n </ToolbarButton>\n <ToolbarButton title=\"Italic\" onClick={handleItalic}>\n <Italic size={16} />\n </ToolbarButton>\n <ToolbarButton title=\"Link\" onClick={handleLink}>\n <Link size={16} />\n </ToolbarButton>\n <ToolbarButton title=\"Inline code\" onClick={handleCode}>\n <Code size={16} />\n </ToolbarButton>\n <ToolbarButton title=\"Bullet list\" onClick={handleList}>\n <List size={16} />\n </ToolbarButton>\n </div>\n\n <div className=\"flex items-center gap-1\" role=\"tablist\" aria-label=\"Markdown editor tabs\">\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'write'}\n onClick={() => setActiveTab('write')}\n className={`px-3 py-1.5 text-sm rounded-md ${\n activeTab === 'write'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'hover:bg-accent text-text-secondary'\n }`}\n >\n Write\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'preview'}\n onClick={() => setActiveTab('preview')}\n className={`px-3 py-1.5 text-sm rounded-md ${\n activeTab === 'preview'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'hover:bg-accent text-text-secondary'\n }`}\n >\n Preview\n </button>\n </div>\n </div>\n\n <div className=\"relative\">\n <textarea\n ref={textareaRef}\n id={id}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n rows={rows}\n disabled={disabled}\n required={required}\n placeholder={placeholder}\n aria-label={ariaLabel ?? label}\n className={`w-full px-3 py-2 bg-bg-surface text-text-primary text-sm sm:text-base resize-y min-h-[120px] focus:outline-none ${\n activeTab === 'write' ? 'block' : 'hidden'\n }`}\n />\n {activeTab === 'preview' && (\n <div className=\"w-full px-3 py-2 min-h-[120px] max-h-[24rem] overflow-y-auto text-sm sm:text-base\">\n {value ? (\n <Markdown>{value}</Markdown>\n ) : (\n <p className=\"text-text-secondary italic\">Nothing to preview</p>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -88,6 +88,14 @@ var n = {
88
88
  "tourBuilder.seedVibeControls.permissionHint": "Requires tour create and update permissions. The service also enforces RBAC when the seed mutation runs.",
89
89
  "tourBuilder.seedVibeControls.result": "Seeded {{totalCount}} tours for {{productId}} in workspace {{workspaceId}}: {{createdCount}} created, {{updatedCount}} updated.",
90
90
  "tourBuilder.seedVibeControls.title": "Seed VibeControls user tours",
91
+ "tourBuilder.seedBuildmyiq.button": "Seed Buildmyiq user tours",
92
+ "tourBuilder.seedBuildmyiq.confirm": "This will overwrite the built-in Buildmyiq dashboard user tour from the service seed file. Continue?",
93
+ "tourBuilder.seedBuildmyiq.description": "Overwrites the built-in Buildmyiq dashboard user tour from the service seed file.",
94
+ "tourBuilder.seedBuildmyiq.error": "Failed to seed Buildmyiq user tours.",
95
+ "tourBuilder.seedBuildmyiq.loading": "Seeding...",
96
+ "tourBuilder.seedBuildmyiq.permissionHint": "Requires tour create and update permissions. The service also enforces RBAC when the seed mutation runs.",
97
+ "tourBuilder.seedBuildmyiq.result": "Seeded {{totalCount}} tours for {{productId}} in workspace {{workspaceId}}: {{createdCount}} created, {{updatedCount}} updated.",
98
+ "tourBuilder.seedBuildmyiq.title": "Seed Buildmyiq user tours",
91
99
  "tourBuilder.subtitle": "Configure tour details and settings",
92
100
  "tourBuilder.titleCreate": "Create Tour",
93
101
  "tourBuilder.titleEdit": "Edit Tour",
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","names":[],"sources":["../../src/tours/i18n.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nconst toursTranslationFallbacks = {\n 'stepEditor.addFirstStep': 'Add First Step',\n 'stepEditor.addStep': 'Add Step',\n 'stepEditor.clickAddStep': 'Click \"Add Step\" to create one',\n 'stepEditor.closeSteps': 'Close steps',\n 'stepEditor.content': 'Content',\n 'stepEditor.contentPlaceholder': 'Enter step content or instructions',\n 'stepEditor.createFirstStep': 'Create your first step to get started',\n 'stepEditor.cssSelector': 'CSS Selector',\n 'stepEditor.cssSelectorPlaceholder': \"[data-tour='element-name'] or #id or .class\",\n 'stepEditor.dataTourHint': \"Use data-tour attributes like [data-tour='sidenav'] for best results\",\n 'stepEditor.deleteStep': 'Delete step',\n 'stepEditor.desktopSelector': 'Desktop Selector',\n 'stepEditor.deviceSelectorHint': 'Override the default selector for specific device types',\n 'stepEditor.deviceSpecificSelectors': 'Device-Specific Selectors (Advanced)',\n 'stepEditor.editStep': 'Edit Step',\n 'stepEditor.goBack': 'Go back',\n 'stepEditor.leaveEmptyDefault': 'Leave empty to use default',\n 'stepEditor.mobileSelector': 'Mobile Selector',\n 'stepEditor.noStepSelected': 'No step selected',\n 'stepEditor.noStepsYet': 'No steps yet',\n 'stepEditor.of': 'of',\n 'stepEditor.nextStep': 'Next step',\n 'stepEditor.placementAuto': 'Auto (recommended)',\n 'stepEditor.placementBottom': 'Bottom',\n 'stepEditor.placementCenter': 'Center (modal style)',\n 'stepEditor.placementLeft': 'Left',\n 'stepEditor.placementRight': 'Right',\n 'stepEditor.placementTop': 'Top',\n 'stepEditor.previousStep': 'Previous step',\n 'stepEditor.save': 'Save',\n 'stepEditor.saving': 'Saving...',\n 'stepEditor.selectStepToEdit': 'Select a step from the list to edit it',\n 'stepEditor.stepNumber': 'Step',\n 'stepEditor.steps': 'Steps',\n 'stepEditor.stepsCount': 'steps',\n 'stepEditor.stepsLabel': 'Steps',\n 'stepEditor.stepTitle': 'Step Title',\n 'stepEditor.stepTitlePlaceholder': 'Enter step title',\n 'stepEditor.tabletSelector': 'Tablet Selector',\n 'stepEditor.targetElement': 'target element',\n 'stepEditor.tooltipPlacement': 'Tooltip Placement',\n 'stepEditor.tourFallback': 'Tour',\n 'stepEditor.tourSteps': 'Tour Steps',\n 'stepEditor.untitled': 'Untitled',\n 'templateLibrary.adjustFilters': 'Try adjusting your filters or search query',\n 'templateLibrary.allCategories': 'All Categories',\n 'templateLibrary.categoryCustom': 'Custom',\n 'templateLibrary.categoryFeatureAnnouncement': 'Feature Announcement',\n 'templateLibrary.categoryHelp': 'Help',\n 'templateLibrary.categoryOnboarding': 'Onboarding',\n 'templateLibrary.deleteConfirm': 'Delete this template?',\n 'templateLibrary.deleteTemplate': 'Delete template',\n 'templateLibrary.loading': 'Loading templates...',\n 'templateLibrary.noTemplatesFound': 'No templates found',\n 'templateLibrary.publicOnly': 'Public only',\n 'templateLibrary.searchPlaceholder': 'Search templates...',\n 'templateLibrary.subtitle': 'Browse and use pre-built tour templates',\n 'templateLibrary.times': 'times',\n 'templateLibrary.title': 'Tour Templates',\n 'templateLibrary.useTemplate': 'Use Template',\n 'templateLibrary.usedTimes': 'Used',\n 'tourBuilder.accessDeniedTitle': 'Access Denied',\n 'tourBuilder.analytics': 'Analytics',\n 'tourBuilder.createAndAddSteps': 'Create & Add Steps',\n 'tourBuilder.createAccessDeniedMessage': 'You do not have permission to create tours.',\n 'tourBuilder.editSteps': 'Edit Steps',\n 'tourBuilder.errorTitle': 'Error',\n 'tourBuilder.goBack': 'Go back',\n 'tourBuilder.labelDescription': 'Description',\n 'tourBuilder.labelName': 'Tour Name *',\n 'tourBuilder.labelPriority': 'Priority (1-100)',\n 'tourBuilder.labelProductId': 'Product ID *',\n 'tourBuilder.labelTriggerType': 'Trigger Type',\n 'tourBuilder.loading': 'Loading...',\n 'tourBuilder.preview': 'Preview',\n 'tourBuilder.saving': 'Saving...',\n 'tourBuilder.sectionBasicInfo': 'Basic Information',\n 'tourBuilder.sectionTriggerSettings': 'Trigger Settings',\n 'tourBuilder.seedVibeControls.button': 'Seed VibeControls user tours',\n 'tourBuilder.seedVibeControls.confirm':\n 'This will overwrite the built-in Support, Notifications, and Conversations user tours from the service seed file. Continue?',\n 'tourBuilder.seedVibeControls.description':\n 'Overwrites the built-in Support, Notifications, and Conversations user tours from the service seed file.',\n 'tourBuilder.seedVibeControls.error': 'Failed to seed VibeControls user tours.',\n 'tourBuilder.seedVibeControls.loading': 'Seeding...',\n 'tourBuilder.seedVibeControls.permissionHint':\n 'Requires tour create and update permissions. The service also enforces RBAC when the seed mutation runs.',\n 'tourBuilder.seedVibeControls.result':\n 'Seeded {{totalCount}} tours for {{productId}} in workspace {{workspaceId}}: {{createdCount}} created, {{updatedCount}} updated.',\n 'tourBuilder.seedVibeControls.title': 'Seed VibeControls user tours',\n 'tourBuilder.subtitle': 'Configure tour details and settings',\n 'tourBuilder.titleCreate': 'Create Tour',\n 'tourBuilder.titleEdit': 'Edit Tour',\n 'tourBuilder.triggerAutoAlways': 'Auto (Always)',\n 'tourBuilder.triggerAutoFirstVisit': 'Auto (First Visit)',\n 'tourBuilder.triggerManual': 'Manual',\n 'tourBuilder.triggerOnEvent': 'On Event',\n 'tourBuilder.triggerScheduled': 'Scheduled',\n 'tourBuilder.updateTour': 'Update Tour',\n 'tourBuilder.updateAccessDeniedMessage': 'You do not have permission to update this tour.',\n 'tourBuilder.validationErrorsTitle': 'Validation Errors',\n 'tourPreview.accessDeniedMessage': 'You do not have permission to preview this tour.',\n 'tourPreview.accessDeniedTitle': 'Access Denied',\n 'tourPreview.deviceDesktop': 'Desktop preview',\n 'tourPreview.deviceMobile': 'Mobile preview',\n 'tourPreview.deviceTablet': 'Tablet preview',\n 'tourPreview.finish': 'Finish',\n 'tourPreview.loading': 'Loading tour...',\n 'tourPreview.next': 'Next',\n 'tourPreview.nextFooter': 'Next',\n 'tourPreview.previewSuffix': 'Preview',\n 'tourPreview.previous': 'Previous',\n 'tourPreview.readyHint': 'Click \"Start Preview\" to begin',\n 'tourPreview.readyTitle': 'Preview ready',\n 'tourPreview.startPreview': 'Start Preview',\n 'tourPreview.stepOf': 'Step',\n 'tourPreview.stepOfConnector': 'of',\n 'tourPreview.stopPreview': 'Stop Preview',\n 'tourPreview.validationErrorsTitle': 'Validation Errors',\n 'tourPreview.warningsTitle': 'Warnings',\n 'toursList.accessDeniedMessage': 'You do not have permission to view product tours.',\n 'toursList.accessDeniedTitle': 'Access Denied',\n 'toursList.actionArchive': 'Archive',\n 'toursList.actionDelete': 'Delete',\n 'toursList.actionDuplicate': 'Duplicate',\n 'toursList.actionPublish': 'Publish',\n 'toursList.actionUnpublish': 'Unpublish',\n 'toursList.confirmDelete': 'Are you sure you want to delete this tour?',\n 'toursList.createTour': 'Create Tour',\n 'toursList.editStepsTitle': 'Edit Steps',\n 'toursList.emptySubtitle': 'Create your first tour to get started',\n 'toursList.emptyTitle': 'No tours found',\n 'toursList.errorArchive': 'Failed to archive tour',\n 'toursList.errorDelete': 'Failed to delete tour',\n 'toursList.errorDuplicate': 'Failed to duplicate tour',\n 'toursList.errorPublish': 'Failed to publish tour',\n 'toursList.errorUnpublish': 'Failed to unpublish tour',\n 'toursList.gridView': 'Grid view',\n 'toursList.listView': 'List view',\n 'toursList.loading': 'Loading tours...',\n 'toursList.moreActionsTitle': 'More actions',\n 'toursList.previewButton': 'Preview',\n 'toursList.previewTitle': 'Preview Tour',\n 'toursList.searchPlaceholder': 'Search tours...',\n 'toursList.settingsTitle': 'Settings',\n 'toursList.statusAll': 'All Statuses',\n 'toursList.statusArchived': 'Archived',\n 'toursList.statusDraft': 'Draft',\n 'toursList.statusPublished': 'Published',\n 'toursList.statusScheduled': 'Scheduled',\n 'toursList.steps': 'steps',\n 'toursList.stepsButton': 'Steps',\n 'toursList.subtitle': 'Create and manage interactive product tours',\n 'toursList.title': 'Product Tours',\n 'toursList.views': 'views',\n\n // User-facing surface (BOFF-2391: My Tours + My Progress).\n 'myTours.title': 'My Tours',\n 'myTours.subtitle': 'Walkthroughs available in this product. Take a tour to learn a feature.',\n 'myTours.loading': 'Loading...',\n 'myTours.empty': 'No tours are available in this context yet.',\n 'myTours.status.completed': 'Completed',\n 'myTours.status.inProgress': 'In progress',\n 'myTours.status.skipped': 'Skipped',\n 'myTours.status.notStarted': 'Not started',\n 'myTours.notFoundTitle': 'Page not found',\n 'myTours.notFoundBody': \"That tour page isn't available — returning to My Tours.\",\n\n 'myToursProgress.title': 'My Progress',\n 'myToursProgress.subtitle': 'Tours you have started, completed, or skipped.',\n 'myToursProgress.loading': 'Loading...',\n 'myToursProgress.inProgress': 'In progress',\n 'myToursProgress.completed': 'Completed',\n 'myToursProgress.skipped': 'Skipped or dismissed',\n 'myToursProgress.emptyInProgress': 'No tours in progress.',\n 'myToursProgress.emptyCompleted': 'No completed tours yet.',\n 'myToursProgress.emptySkipped': 'No skipped tours.',\n 'myToursProgress.other': 'Other',\n 'myToursProgress.emptyOther': 'No tours in other states.',\n 'myToursProgress.empty':\n \"You haven't started any tours yet. Open My Tours and take a walkthrough — your progress will show up here.\",\n\n // Page-purpose (\"what is this page for\") + next-step guidance copy.\n 'toursList.purpose':\n 'Author and manage the interactive product tours users see in-app — onboarding flows, feature spotlights, and help walkthroughs. Create a tour, add steps that point at real UI elements, then publish it so it appears for the right product, module, and page.',\n 'myTours.purpose':\n 'Browse the guided walkthroughs available right here in this product. Take a tour to learn a feature step by step — your progress is saved so you can pick up where you left off.',\n 'myToursProgress.purpose':\n 'Track every tour you have interacted with, grouped by status. See what you have completed, what is still in progress, and what you skipped so you know which walkthroughs are left to finish.',\n 'templateLibrary.purpose':\n 'Start from a pre-built tour template instead of a blank canvas. Pick an onboarding, feature-announcement, or help template, then customise its steps — a fast way to ship a consistent walkthrough.',\n\n 'tourBuilder.purpose':\n 'Set up a tour: give it a name, choose where it appears (product, module, page) and how it triggers (manually, on first visit, or scheduled). Save it, then add the steps users will walk through.',\n 'tourBuilder.nextStepsTitle': 'Recommended next steps',\n 'tourBuilder.nextStepEditSteps': 'Add the steps',\n 'tourBuilder.nextStepEditStepsDesc': 'Define the tooltips and target elements users will see.',\n 'tourBuilder.nextStepPreview': 'Preview it',\n 'tourBuilder.nextStepPreviewDesc': 'Walk through the tour on mobile, tablet, and desktop.',\n 'tourBuilder.nextStepSchedule': 'Schedule activation',\n 'tourBuilder.nextStepScheduleDesc': 'Choose when this tour goes live and how often it recurs.',\n 'tourBuilder.nextStepAnalytics': 'Check analytics',\n 'tourBuilder.nextStepAnalyticsDesc': 'See views, completion, and drop-off once it is live.',\n\n 'stepEditor.purpose':\n 'Build the actual walkthrough. Each step is a tooltip anchored to a real UI element via a CSS or data-tour selector. Add steps, reorder them, attach media, and set placement so the tour guides users exactly where you intend.',\n 'stepEditor.emptyTitle': 'No step selected',\n\n 'tourPreview.purpose':\n 'Walk through this tour exactly as your users will, on simulated mobile, tablet, and desktop viewports. Validate that every step finds its target element before you publish.',\n} as const;\n\nexport type ToursTranslationKey = keyof typeof toursTranslationFallbacks;\n\ntype TranslationParams = Record<string, string | number>;\n\nexport function useToursI18n() {\n const i18n = useI18n();\n // The installed fe-libs typings still expose the older 2-argument shape, but the\n // runtime provider supports fallback strings. Narrow locally so this MFE can rely\n // on fallback-backed translations without changing the shared package here.\n const translateWithFallback = i18n.t as (key: string, fallback?: string) => string;\n\n const tr = useCallback(\n (key: ToursTranslationKey, params?: TranslationParams): string => {\n const resolved = translateWithFallback(key, toursTranslationFallbacks[key]);\n\n if (!params) {\n return resolved;\n }\n\n return Object.entries(params).reduce(\n (value, [paramKey, paramValue]) => value.replace(`{{${paramKey}}}`, String(paramValue)),\n resolved\n );\n },\n [translateWithFallback]\n );\n\n return {\n ...i18n,\n tr,\n };\n}\n\nexport function getToursTranslationFallback(key: ToursTranslationKey) {\n return toursTranslationFallbacks[key];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"i18n.js","names":[],"sources":["../../src/tours/i18n.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nconst toursTranslationFallbacks = {\n 'stepEditor.addFirstStep': 'Add First Step',\n 'stepEditor.addStep': 'Add Step',\n 'stepEditor.clickAddStep': 'Click \"Add Step\" to create one',\n 'stepEditor.closeSteps': 'Close steps',\n 'stepEditor.content': 'Content',\n 'stepEditor.contentPlaceholder': 'Enter step content or instructions',\n 'stepEditor.createFirstStep': 'Create your first step to get started',\n 'stepEditor.cssSelector': 'CSS Selector',\n 'stepEditor.cssSelectorPlaceholder': \"[data-tour='element-name'] or #id or .class\",\n 'stepEditor.dataTourHint': \"Use data-tour attributes like [data-tour='sidenav'] for best results\",\n 'stepEditor.deleteStep': 'Delete step',\n 'stepEditor.desktopSelector': 'Desktop Selector',\n 'stepEditor.deviceSelectorHint': 'Override the default selector for specific device types',\n 'stepEditor.deviceSpecificSelectors': 'Device-Specific Selectors (Advanced)',\n 'stepEditor.editStep': 'Edit Step',\n 'stepEditor.goBack': 'Go back',\n 'stepEditor.leaveEmptyDefault': 'Leave empty to use default',\n 'stepEditor.mobileSelector': 'Mobile Selector',\n 'stepEditor.noStepSelected': 'No step selected',\n 'stepEditor.noStepsYet': 'No steps yet',\n 'stepEditor.of': 'of',\n 'stepEditor.nextStep': 'Next step',\n 'stepEditor.placementAuto': 'Auto (recommended)',\n 'stepEditor.placementBottom': 'Bottom',\n 'stepEditor.placementCenter': 'Center (modal style)',\n 'stepEditor.placementLeft': 'Left',\n 'stepEditor.placementRight': 'Right',\n 'stepEditor.placementTop': 'Top',\n 'stepEditor.previousStep': 'Previous step',\n 'stepEditor.save': 'Save',\n 'stepEditor.saving': 'Saving...',\n 'stepEditor.selectStepToEdit': 'Select a step from the list to edit it',\n 'stepEditor.stepNumber': 'Step',\n 'stepEditor.steps': 'Steps',\n 'stepEditor.stepsCount': 'steps',\n 'stepEditor.stepsLabel': 'Steps',\n 'stepEditor.stepTitle': 'Step Title',\n 'stepEditor.stepTitlePlaceholder': 'Enter step title',\n 'stepEditor.tabletSelector': 'Tablet Selector',\n 'stepEditor.targetElement': 'target element',\n 'stepEditor.tooltipPlacement': 'Tooltip Placement',\n 'stepEditor.tourFallback': 'Tour',\n 'stepEditor.tourSteps': 'Tour Steps',\n 'stepEditor.untitled': 'Untitled',\n 'templateLibrary.adjustFilters': 'Try adjusting your filters or search query',\n 'templateLibrary.allCategories': 'All Categories',\n 'templateLibrary.categoryCustom': 'Custom',\n 'templateLibrary.categoryFeatureAnnouncement': 'Feature Announcement',\n 'templateLibrary.categoryHelp': 'Help',\n 'templateLibrary.categoryOnboarding': 'Onboarding',\n 'templateLibrary.deleteConfirm': 'Delete this template?',\n 'templateLibrary.deleteTemplate': 'Delete template',\n 'templateLibrary.loading': 'Loading templates...',\n 'templateLibrary.noTemplatesFound': 'No templates found',\n 'templateLibrary.publicOnly': 'Public only',\n 'templateLibrary.searchPlaceholder': 'Search templates...',\n 'templateLibrary.subtitle': 'Browse and use pre-built tour templates',\n 'templateLibrary.times': 'times',\n 'templateLibrary.title': 'Tour Templates',\n 'templateLibrary.useTemplate': 'Use Template',\n 'templateLibrary.usedTimes': 'Used',\n 'tourBuilder.accessDeniedTitle': 'Access Denied',\n 'tourBuilder.analytics': 'Analytics',\n 'tourBuilder.createAndAddSteps': 'Create & Add Steps',\n 'tourBuilder.createAccessDeniedMessage': 'You do not have permission to create tours.',\n 'tourBuilder.editSteps': 'Edit Steps',\n 'tourBuilder.errorTitle': 'Error',\n 'tourBuilder.goBack': 'Go back',\n 'tourBuilder.labelDescription': 'Description',\n 'tourBuilder.labelName': 'Tour Name *',\n 'tourBuilder.labelPriority': 'Priority (1-100)',\n 'tourBuilder.labelProductId': 'Product ID *',\n 'tourBuilder.labelTriggerType': 'Trigger Type',\n 'tourBuilder.loading': 'Loading...',\n 'tourBuilder.preview': 'Preview',\n 'tourBuilder.saving': 'Saving...',\n 'tourBuilder.sectionBasicInfo': 'Basic Information',\n 'tourBuilder.sectionTriggerSettings': 'Trigger Settings',\n 'tourBuilder.seedVibeControls.button': 'Seed VibeControls user tours',\n 'tourBuilder.seedVibeControls.confirm':\n 'This will overwrite the built-in Support, Notifications, and Conversations user tours from the service seed file. Continue?',\n 'tourBuilder.seedVibeControls.description':\n 'Overwrites the built-in Support, Notifications, and Conversations user tours from the service seed file.',\n 'tourBuilder.seedVibeControls.error': 'Failed to seed VibeControls user tours.',\n 'tourBuilder.seedVibeControls.loading': 'Seeding...',\n 'tourBuilder.seedVibeControls.permissionHint':\n 'Requires tour create and update permissions. The service also enforces RBAC when the seed mutation runs.',\n 'tourBuilder.seedVibeControls.result':\n 'Seeded {{totalCount}} tours for {{productId}} in workspace {{workspaceId}}: {{createdCount}} created, {{updatedCount}} updated.',\n 'tourBuilder.seedVibeControls.title': 'Seed VibeControls user tours',\n\n 'tourBuilder.seedBuildmyiq.button': 'Seed Buildmyiq user tours',\n 'tourBuilder.seedBuildmyiq.confirm':\n 'This will overwrite the built-in Buildmyiq dashboard user tour from the service seed file. Continue?',\n 'tourBuilder.seedBuildmyiq.description':\n 'Overwrites the built-in Buildmyiq dashboard user tour from the service seed file.',\n 'tourBuilder.seedBuildmyiq.error': 'Failed to seed Buildmyiq user tours.',\n 'tourBuilder.seedBuildmyiq.loading': 'Seeding...',\n 'tourBuilder.seedBuildmyiq.permissionHint':\n 'Requires tour create and update permissions. The service also enforces RBAC when the seed mutation runs.',\n 'tourBuilder.seedBuildmyiq.result':\n 'Seeded {{totalCount}} tours for {{productId}} in workspace {{workspaceId}}: {{createdCount}} created, {{updatedCount}} updated.',\n 'tourBuilder.seedBuildmyiq.title': 'Seed Buildmyiq user tours',\n 'tourBuilder.subtitle': 'Configure tour details and settings',\n 'tourBuilder.titleCreate': 'Create Tour',\n 'tourBuilder.titleEdit': 'Edit Tour',\n 'tourBuilder.triggerAutoAlways': 'Auto (Always)',\n 'tourBuilder.triggerAutoFirstVisit': 'Auto (First Visit)',\n 'tourBuilder.triggerManual': 'Manual',\n 'tourBuilder.triggerOnEvent': 'On Event',\n 'tourBuilder.triggerScheduled': 'Scheduled',\n 'tourBuilder.updateTour': 'Update Tour',\n 'tourBuilder.updateAccessDeniedMessage': 'You do not have permission to update this tour.',\n 'tourBuilder.validationErrorsTitle': 'Validation Errors',\n 'tourPreview.accessDeniedMessage': 'You do not have permission to preview this tour.',\n 'tourPreview.accessDeniedTitle': 'Access Denied',\n 'tourPreview.deviceDesktop': 'Desktop preview',\n 'tourPreview.deviceMobile': 'Mobile preview',\n 'tourPreview.deviceTablet': 'Tablet preview',\n 'tourPreview.finish': 'Finish',\n 'tourPreview.loading': 'Loading tour...',\n 'tourPreview.next': 'Next',\n 'tourPreview.nextFooter': 'Next',\n 'tourPreview.previewSuffix': 'Preview',\n 'tourPreview.previous': 'Previous',\n 'tourPreview.readyHint': 'Click \"Start Preview\" to begin',\n 'tourPreview.readyTitle': 'Preview ready',\n 'tourPreview.startPreview': 'Start Preview',\n 'tourPreview.stepOf': 'Step',\n 'tourPreview.stepOfConnector': 'of',\n 'tourPreview.stopPreview': 'Stop Preview',\n 'tourPreview.validationErrorsTitle': 'Validation Errors',\n 'tourPreview.warningsTitle': 'Warnings',\n 'toursList.accessDeniedMessage': 'You do not have permission to view product tours.',\n 'toursList.accessDeniedTitle': 'Access Denied',\n 'toursList.actionArchive': 'Archive',\n 'toursList.actionDelete': 'Delete',\n 'toursList.actionDuplicate': 'Duplicate',\n 'toursList.actionPublish': 'Publish',\n 'toursList.actionUnpublish': 'Unpublish',\n 'toursList.confirmDelete': 'Are you sure you want to delete this tour?',\n 'toursList.createTour': 'Create Tour',\n 'toursList.editStepsTitle': 'Edit Steps',\n 'toursList.emptySubtitle': 'Create your first tour to get started',\n 'toursList.emptyTitle': 'No tours found',\n 'toursList.errorArchive': 'Failed to archive tour',\n 'toursList.errorDelete': 'Failed to delete tour',\n 'toursList.errorDuplicate': 'Failed to duplicate tour',\n 'toursList.errorPublish': 'Failed to publish tour',\n 'toursList.errorUnpublish': 'Failed to unpublish tour',\n 'toursList.gridView': 'Grid view',\n 'toursList.listView': 'List view',\n 'toursList.loading': 'Loading tours...',\n 'toursList.moreActionsTitle': 'More actions',\n 'toursList.previewButton': 'Preview',\n 'toursList.previewTitle': 'Preview Tour',\n 'toursList.searchPlaceholder': 'Search tours...',\n 'toursList.settingsTitle': 'Settings',\n 'toursList.statusAll': 'All Statuses',\n 'toursList.statusArchived': 'Archived',\n 'toursList.statusDraft': 'Draft',\n 'toursList.statusPublished': 'Published',\n 'toursList.statusScheduled': 'Scheduled',\n 'toursList.steps': 'steps',\n 'toursList.stepsButton': 'Steps',\n 'toursList.subtitle': 'Create and manage interactive product tours',\n 'toursList.title': 'Product Tours',\n 'toursList.views': 'views',\n\n // User-facing surface (BOFF-2391: My Tours + My Progress).\n 'myTours.title': 'My Tours',\n 'myTours.subtitle': 'Walkthroughs available in this product. Take a tour to learn a feature.',\n 'myTours.loading': 'Loading...',\n 'myTours.empty': 'No tours are available in this context yet.',\n 'myTours.status.completed': 'Completed',\n 'myTours.status.inProgress': 'In progress',\n 'myTours.status.skipped': 'Skipped',\n 'myTours.status.notStarted': 'Not started',\n 'myTours.notFoundTitle': 'Page not found',\n 'myTours.notFoundBody': \"That tour page isn't available — returning to My Tours.\",\n\n 'myToursProgress.title': 'My Progress',\n 'myToursProgress.subtitle': 'Tours you have started, completed, or skipped.',\n 'myToursProgress.loading': 'Loading...',\n 'myToursProgress.inProgress': 'In progress',\n 'myToursProgress.completed': 'Completed',\n 'myToursProgress.skipped': 'Skipped or dismissed',\n 'myToursProgress.emptyInProgress': 'No tours in progress.',\n 'myToursProgress.emptyCompleted': 'No completed tours yet.',\n 'myToursProgress.emptySkipped': 'No skipped tours.',\n 'myToursProgress.other': 'Other',\n 'myToursProgress.emptyOther': 'No tours in other states.',\n 'myToursProgress.empty':\n \"You haven't started any tours yet. Open My Tours and take a walkthrough — your progress will show up here.\",\n\n // Page-purpose (\"what is this page for\") + next-step guidance copy.\n 'toursList.purpose':\n 'Author and manage the interactive product tours users see in-app — onboarding flows, feature spotlights, and help walkthroughs. Create a tour, add steps that point at real UI elements, then publish it so it appears for the right product, module, and page.',\n 'myTours.purpose':\n 'Browse the guided walkthroughs available right here in this product. Take a tour to learn a feature step by step — your progress is saved so you can pick up where you left off.',\n 'myToursProgress.purpose':\n 'Track every tour you have interacted with, grouped by status. See what you have completed, what is still in progress, and what you skipped so you know which walkthroughs are left to finish.',\n 'templateLibrary.purpose':\n 'Start from a pre-built tour template instead of a blank canvas. Pick an onboarding, feature-announcement, or help template, then customise its steps — a fast way to ship a consistent walkthrough.',\n\n 'tourBuilder.purpose':\n 'Set up a tour: give it a name, choose where it appears (product, module, page) and how it triggers (manually, on first visit, or scheduled). Save it, then add the steps users will walk through.',\n 'tourBuilder.nextStepsTitle': 'Recommended next steps',\n 'tourBuilder.nextStepEditSteps': 'Add the steps',\n 'tourBuilder.nextStepEditStepsDesc': 'Define the tooltips and target elements users will see.',\n 'tourBuilder.nextStepPreview': 'Preview it',\n 'tourBuilder.nextStepPreviewDesc': 'Walk through the tour on mobile, tablet, and desktop.',\n 'tourBuilder.nextStepSchedule': 'Schedule activation',\n 'tourBuilder.nextStepScheduleDesc': 'Choose when this tour goes live and how often it recurs.',\n 'tourBuilder.nextStepAnalytics': 'Check analytics',\n 'tourBuilder.nextStepAnalyticsDesc': 'See views, completion, and drop-off once it is live.',\n\n 'stepEditor.purpose':\n 'Build the actual walkthrough. Each step is a tooltip anchored to a real UI element via a CSS or data-tour selector. Add steps, reorder them, attach media, and set placement so the tour guides users exactly where you intend.',\n 'stepEditor.emptyTitle': 'No step selected',\n\n 'tourPreview.purpose':\n 'Walk through this tour exactly as your users will, on simulated mobile, tablet, and desktop viewports. Validate that every step finds its target element before you publish.',\n} as const;\n\nexport type ToursTranslationKey = keyof typeof toursTranslationFallbacks;\n\ntype TranslationParams = Record<string, string | number>;\n\nexport function useToursI18n() {\n const i18n = useI18n();\n // The installed fe-libs typings still expose the older 2-argument shape, but the\n // runtime provider supports fallback strings. Narrow locally so this MFE can rely\n // on fallback-backed translations without changing the shared package here.\n const translateWithFallback = i18n.t as (key: string, fallback?: string) => string;\n\n const tr = useCallback(\n (key: ToursTranslationKey, params?: TranslationParams): string => {\n const resolved = translateWithFallback(key, toursTranslationFallbacks[key]);\n\n if (!params) {\n return resolved;\n }\n\n return Object.entries(params).reduce(\n (value, [paramKey, paramValue]) => value.replace(`{{${paramKey}}}`, String(paramValue)),\n resolved\n );\n },\n [translateWithFallback]\n );\n\n return {\n ...i18n,\n tr,\n };\n}\n\nexport function getToursTranslationFallback(key: ToursTranslationKey) {\n return toursTranslationFallbacks[key];\n}\n"],"mappings":"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"}
@@ -2,94 +2,97 @@ import { useToursContext as e } from "../providers/ToursProvider.js";
2
2
  import { useGetToursForContextQuery as t } from "../../generated/global-operations.js";
3
3
  import { useToursI18n as n } from "../i18n.js";
4
4
  import { useEffect as r, useRef as i } from "react";
5
- import { CheckCircle2 as a, Circle as o, MinusCircle as s, Play as c } from "lucide-react";
6
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
7
- import { useSearchParams as d } from "react-router-dom";
8
- import { EmphasisPanel as f, GlassCard as p, GradientText as m, IllustratedEmptyState as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
9
- import { useEventBus as _ } from "@burdenoff/fe-libs/shared/events";
5
+ import { Markdown as a } from "@burdenoff/fe-libs/shared/components";
6
+ import { CheckCircle2 as o, Circle as s, MinusCircle as c, Play as l } from "lucide-react";
7
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
8
+ import { useSearchParams as f } from "react-router-dom";
9
+ import { EmphasisPanel as p, GlassCard as m, GradientText as h, IllustratedEmptyState as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
10
+ import { useEventBus as v } from "@burdenoff/fe-libs/shared/events";
10
11
  //#region src/tours/pages/MyToursPage.tsx
11
- var v = /^[a-zA-Z0-9_-]+$/, y = /^[a-zA-Z0-9_\-/]+$/;
12
- function b(e, t) {
13
- return e && v.test(e) ? e : t;
14
- }
12
+ var y = /^[a-zA-Z0-9_-]+$/, b = /^[a-zA-Z0-9_\-/]+$/;
15
13
  function x(e, t) {
16
14
  return e && y.test(e) ? e : t;
17
15
  }
18
- function S() {
19
- let { tr: a } = n(), { productId: o, moduleId: s, pageId: c, workspaceId: v } = e(), [y] = d(), S = _(), w = i(!1);
16
+ function S(e, t) {
17
+ return e && b.test(e) ? e : t;
18
+ }
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
21
  r(() => {
21
- w.current || (w.current = !0, S.emit("tours.page.viewed", {
22
+ T.current || (T.current = !0, C.emit("tours.page.viewed", {
22
23
  page: "/tours",
23
- productId: o,
24
- moduleId: s,
25
- pageId: c,
26
- workspaceId: v
24
+ productId: s,
25
+ moduleId: c,
26
+ pageId: l,
27
+ workspaceId: y
27
28
  }));
28
29
  }, [
29
- S,
30
- o,
30
+ C,
31
31
  s,
32
32
  c,
33
- v
33
+ l,
34
+ y
34
35
  ]);
35
- let T = b(o, void 0) ?? b(y.get("productId"), void 0), E = b(s, void 0) ?? b(y.get("moduleId"), void 0), D = x(c, void 0) ?? x(y.get("pageId"), void 0), { data: O, loading: k } = t({
36
- variables: T ? { context: {
37
- productId: T,
38
- moduleId: E,
39
- pageId: D
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({
37
+ variables: E ? { context: {
38
+ productId: E,
39
+ moduleId: D,
40
+ pageId: O
40
41
  } } : void 0,
41
- skip: !T,
42
+ skip: !E,
42
43
  fetchPolicy: "cache-and-network"
43
- }), A = O?.toursForContext ?? [], j = k, M = A.find((e) => e.myProgress?.status === "IN_PROGRESS") ?? A.find((e) => !e.myProgress?.status || e.myProgress.status !== "COMPLETED" && e.myProgress.status !== "SKIPPED" && e.myProgress.status !== "DISMISSED"), N = A.filter((e) => e.id !== M?.id);
44
- return /* @__PURE__ */ u("div", {
44
+ }), j = k?.toursForContext ?? [], M = A, N = j.find((e) => e.myProgress?.status === "IN_PROGRESS") ?? j.find((e) => !e.myProgress?.status || e.myProgress.status !== "COMPLETED" && e.myProgress.status !== "SKIPPED" && e.myProgress.status !== "DISMISSED"), P = j.filter((e) => e.id !== N?.id);
45
+ return /* @__PURE__ */ d("div", {
45
46
  className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
46
47
  children: [
47
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
48
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
48
49
  className: "text-xl sm:text-2xl font-bold text-text-primary",
49
- children: /* @__PURE__ */ l(m, { children: a("myTours.title") })
50
- }), /* @__PURE__ */ l("p", {
50
+ children: /* @__PURE__ */ u(h, { children: o("myTours.title") })
51
+ }), /* @__PURE__ */ u("p", {
51
52
  className: "text-sm sm:text-base text-text-secondary",
52
- children: a("myTours.subtitle")
53
+ children: o("myTours.subtitle")
53
54
  })] }),
54
- /* @__PURE__ */ l(g, { children: a("myTours.purpose") }),
55
- j && A.length === 0 ? /* @__PURE__ */ l("output", {
55
+ /* @__PURE__ */ u(_, { children: o("myTours.purpose") }),
56
+ M && j.length === 0 ? /* @__PURE__ */ u("output", {
56
57
  className: "text-sm text-text-secondary",
57
- children: a("myTours.loading")
58
- }) : A.length === 0 ? /* @__PURE__ */ l(h, {
58
+ children: o("myTours.loading")
59
+ }) : j.length === 0 ? /* @__PURE__ */ u(g, {
59
60
  illustration: "empty-generic",
60
- title: a("myTours.empty")
61
- }) : /* @__PURE__ */ u("div", {
61
+ title: o("myTours.empty")
62
+ }) : /* @__PURE__ */ d("div", {
62
63
  className: "space-y-4 sm:space-y-6",
63
- children: [M ? /* @__PURE__ */ u(f, {
64
+ children: [N ? /* @__PURE__ */ d(p, {
64
65
  className: "p-4 sm:p-5",
65
- children: [/* @__PURE__ */ u("div", {
66
+ children: [/* @__PURE__ */ d("div", {
66
67
  className: "flex items-start justify-between gap-3",
67
- children: [/* @__PURE__ */ l("h2", {
68
+ children: [/* @__PURE__ */ u("h2", {
68
69
  className: "text-lg font-semibold text-text-primary line-clamp-2",
69
- children: M.name
70
- }), /* @__PURE__ */ l(C, {
71
- status: M.myProgress?.status,
72
- tr: a
70
+ children: N.name
71
+ }), /* @__PURE__ */ u(w, {
72
+ status: N.myProgress?.status,
73
+ tr: o
73
74
  })]
74
- }), M.description ? /* @__PURE__ */ l("p", {
75
+ }), N.description ? /* @__PURE__ */ u(a, {
76
+ inline: !0,
75
77
  className: "mt-2 text-sm text-text-secondary line-clamp-3",
76
- children: M.description
78
+ children: N.description
77
79
  }) : null]
78
- }) : null, N.length > 0 ? /* @__PURE__ */ l("ul", {
80
+ }) : null, P.length > 0 ? /* @__PURE__ */ u("ul", {
79
81
  className: "grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
80
- children: N.map((e) => /* @__PURE__ */ l("li", { children: /* @__PURE__ */ u(p, {
82
+ children: P.map((e) => /* @__PURE__ */ u("li", { children: /* @__PURE__ */ d(m, {
81
83
  treatment: "plain",
82
84
  className: "h-full p-4 flex flex-col gap-2 border-border-seam shadow-[var(--shadow-pop)]",
83
- children: [/* @__PURE__ */ u("div", {
85
+ children: [/* @__PURE__ */ d("div", {
84
86
  className: "flex items-start justify-between gap-2",
85
- children: [/* @__PURE__ */ l("h2", {
87
+ children: [/* @__PURE__ */ u("h2", {
86
88
  className: "font-semibold text-text-primary line-clamp-2",
87
89
  children: e.name
88
- }), /* @__PURE__ */ l(C, {
90
+ }), /* @__PURE__ */ u(w, {
89
91
  status: e.myProgress?.status,
90
- tr: a
92
+ tr: o
91
93
  })]
92
- }), e.description ? /* @__PURE__ */ l("p", {
94
+ }), e.description ? /* @__PURE__ */ u(a, {
95
+ inline: !0,
93
96
  className: "text-sm text-text-secondary line-clamp-3",
94
97
  children: e.description
95
98
  }) : null]
@@ -99,28 +102,28 @@ function S() {
99
102
  ]
100
103
  });
101
104
  }
102
- function C({ status: e, tr: t }) {
105
+ function w({ status: e, tr: t }) {
103
106
  switch (e) {
104
- case "COMPLETED": return /* @__PURE__ */ u("span", {
107
+ case "COMPLETED": return /* @__PURE__ */ d("span", {
105
108
  className: "inline-flex items-center gap-1 text-xs text-success",
106
- children: [/* @__PURE__ */ l(a, { size: 14 }), t("myTours.status.completed")]
109
+ children: [/* @__PURE__ */ u(o, { size: 14 }), t("myTours.status.completed")]
107
110
  });
108
- case "IN_PROGRESS": return /* @__PURE__ */ u("span", {
111
+ case "IN_PROGRESS": return /* @__PURE__ */ d("span", {
109
112
  className: "inline-flex items-center gap-1 text-xs text-warning motion-safe:animate-vc-glow-pulse",
110
- children: [/* @__PURE__ */ l(c, { size: 14 }), t("myTours.status.inProgress")]
113
+ children: [/* @__PURE__ */ u(l, { size: 14 }), t("myTours.status.inProgress")]
111
114
  });
112
115
  case "SKIPPED":
113
- case "DISMISSED": return /* @__PURE__ */ u("span", {
116
+ case "DISMISSED": return /* @__PURE__ */ d("span", {
114
117
  className: "inline-flex items-center gap-1 text-xs text-text-secondary",
115
- children: [/* @__PURE__ */ l(s, { size: 14 }), t("myTours.status.skipped")]
118
+ children: [/* @__PURE__ */ u(c, { size: 14 }), t("myTours.status.skipped")]
116
119
  });
117
- default: return /* @__PURE__ */ u("span", {
120
+ default: return /* @__PURE__ */ d("span", {
118
121
  className: "inline-flex items-center gap-1 text-xs text-text-secondary",
119
- children: [/* @__PURE__ */ l(o, { size: 14 }), t("myTours.status.notStarted")]
122
+ children: [/* @__PURE__ */ u(s, { size: 14 }), t("myTours.status.notStarted")]
120
123
  });
121
124
  }
122
125
  }
123
126
  //#endregion
124
- export { S as MyToursPage };
127
+ export { C as MyToursPage };
125
128
 
126
129
  //# sourceMappingURL=MyToursPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MyToursPage.js","names":[],"sources":["../../../src/tours/pages/MyToursPage.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { CheckCircle2, Circle, MinusCircle, Play } from 'lucide-react';\nimport {\n useGetToursForContextQuery,\n type GetToursForContextQuery,\n} from '../../generated/global-operations';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursI18n, type ToursTranslationKey } from '../i18n';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\ntype TourSummary = GetToursForContextQuery['toursForContext'][number];\n\n// Plain slug validator for productId and moduleId.\nconst SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;\n\n// pageId is documented with path-style values like \"settings/team\" per\n// ToursRootProps — tighten to URL-safe characters only, but permit `/`.\nconst PAGE_PATH_PATTERN = /^[a-zA-Z0-9_\\-/]+$/;\n\nfunction safeSlug<T extends string | undefined>(\n raw: string | null | undefined,\n fallback: T\n): string | T {\n if (raw && SLUG_PATTERN.test(raw)) return raw;\n return fallback;\n}\n\nfunction safePageId<T extends string | undefined>(\n raw: string | null | undefined,\n fallback: T\n): string | T {\n if (raw && PAGE_PATH_PATTERN.test(raw)) return raw;\n return fallback;\n}\n\n/**\n * MyToursPage — user-facing browse for available tours.\n *\n * Lists tours returned by `toursForContext` (filtered to the current\n * product/module/page surface) and decorates each with the requesting\n * user's `TourUserProgress` status (Completed / In progress / Skipped /\n * Not started). Read-only — tours are actually played via the SDK overlay,\n * not via mutations on this page.\n */\nexport function MyToursPage() {\n const { tr } = useToursI18n();\n const {\n productId: shellProductId,\n moduleId: shellModuleId,\n pageId: shellPageId,\n workspaceId,\n } = useToursContext();\n const [searchParams] = useSearchParams();\n const eventBus = useEventBus();\n\n // Emit page view event exactly once per mount. A ref guard keeps the effect\n // semantically mount-only while satisfying exhaustive-deps with the real\n // dependency list, so we don't have to disable the lint rule.\n const pageViewedRef = useRef(false);\n useEffect(() => {\n if (pageViewedRef.current) return;\n pageViewedRef.current = true;\n eventBus.emit('tours.page.viewed', {\n page: '/tours',\n productId: shellProductId,\n moduleId: shellModuleId,\n pageId: shellPageId,\n workspaceId,\n });\n }, [eventBus, shellProductId, shellModuleId, shellPageId, workspaceId]);\n\n // Preferred source is the shell's productId prop (the host app knows\n // which product it is — \"workspaces\", \"vibecontrols\", \"bigconsole\", etc.).\n // Fall back to a query-param override for deep-link scenarios, but only\n // after passing a plain-slug validator so we never forward arbitrary\n // strings into the GraphQL variables. No hardcoded product default —\n // if neither source provides one, skip the query entirely so non-\n // workspaces integrations don't get workspaces tours.\n const productId =\n safeSlug(shellProductId, undefined) ?? safeSlug(searchParams.get('productId'), undefined);\n const moduleId =\n safeSlug(shellModuleId, undefined) ?? safeSlug(searchParams.get('moduleId'), undefined);\n const pageId =\n safePageId(shellPageId, undefined) ?? safePageId(searchParams.get('pageId'), undefined);\n\n const { data: toursData, loading: toursLoading } = useGetToursForContextQuery({\n variables: productId ? { context: { productId, moduleId, pageId } } : undefined,\n skip: !productId,\n fetchPolicy: 'cache-and-network',\n });\n\n const tours = toursData?.toursForContext ?? [];\n const isLoading = toursLoading;\n\n // Surface a single, clearly-emphasised \"pick up where you left off / start\n // here\" tour: prefer one already in progress, then the first not-yet-started\n // tour, so a new user always has one obvious next walkthrough to take.\n const featuredTour =\n tours.find((tour: TourSummary) => tour.myProgress?.status === 'IN_PROGRESS') ??\n tours.find(\n (tour: TourSummary) =>\n !tour.myProgress?.status ||\n (tour.myProgress.status !== 'COMPLETED' &&\n tour.myProgress.status !== 'SKIPPED' &&\n tour.myProgress.status !== 'DISMISSED')\n );\n const restTours = tours.filter((tour: TourSummary) => tour.id !== featuredTour?.id);\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n <GradientText>{tr('myTours.title')}</GradientText>\n </h1>\n <p className=\"text-sm sm:text-base text-text-secondary\">{tr('myTours.subtitle')}</p>\n </div>\n\n <PagePurpose>{tr('myTours.purpose')}</PagePurpose>\n\n {isLoading && tours.length === 0 ? (\n <output className=\"text-sm text-text-secondary\">{tr('myTours.loading')}</output>\n ) : tours.length === 0 ? (\n <IllustratedEmptyState illustration=\"empty-generic\" title={tr('myTours.empty')} />\n ) : (\n <div className=\"space-y-4 sm:space-y-6\">\n {/* Featured \"start here\" tour — the single emphasis zone on this page. */}\n {featuredTour ? (\n <EmphasisPanel className=\"p-4 sm:p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary line-clamp-2\">\n {featuredTour.name}\n </h2>\n <ProgressBadge status={featuredTour.myProgress?.status} tr={tr} />\n </div>\n {featuredTour.description ? (\n <p className=\"mt-2 text-sm text-text-secondary line-clamp-3\">\n {featuredTour.description}\n </p>\n ) : null}\n </EmphasisPanel>\n ) : null}\n\n {restTours.length > 0 ? (\n <ul className=\"grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {restTours.map((tour: TourSummary) => (\n <li key={tour.id}>\n <GlassCard\n treatment=\"plain\"\n className=\"h-full p-4 flex flex-col gap-2 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <h2 className=\"font-semibold text-text-primary line-clamp-2\">{tour.name}</h2>\n <ProgressBadge status={tour.myProgress?.status} tr={tr} />\n </div>\n {tour.description ? (\n <p className=\"text-sm text-text-secondary line-clamp-3\">{tour.description}</p>\n ) : null}\n </GlassCard>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nfunction ProgressBadge({\n status,\n tr,\n}: {\n status: string | undefined;\n tr: (key: ToursTranslationKey) => string;\n}) {\n switch (status) {\n case 'COMPLETED':\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-success\">\n <CheckCircle2 size={14} />\n {tr('myTours.status.completed')}\n </span>\n );\n case 'IN_PROGRESS':\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-warning motion-safe:animate-vc-glow-pulse\">\n <Play size={14} />\n {tr('myTours.status.inProgress')}\n </span>\n );\n case 'SKIPPED':\n case 'DISMISSED':\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <MinusCircle size={14} />\n {tr('myTours.status.skipped')}\n </span>\n );\n default:\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Circle size={14} />\n {tr('myTours.status.notStarted')}\n </span>\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MyToursPage.js","names":[],"sources":["../../../src/tours/pages/MyToursPage.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { CheckCircle2, Circle, MinusCircle, Play } from 'lucide-react';\nimport {\n useGetToursForContextQuery,\n type GetToursForContextQuery,\n} from '../../generated/global-operations';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursI18n, type ToursTranslationKey } from '../i18n';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\ntype TourSummary = GetToursForContextQuery['toursForContext'][number];\n\n// Plain slug validator for productId and moduleId.\nconst SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;\n\n// pageId is documented with path-style values like \"settings/team\" per\n// ToursRootProps — tighten to URL-safe characters only, but permit `/`.\nconst PAGE_PATH_PATTERN = /^[a-zA-Z0-9_\\-/]+$/;\n\nfunction safeSlug<T extends string | undefined>(\n raw: string | null | undefined,\n fallback: T\n): string | T {\n if (raw && SLUG_PATTERN.test(raw)) return raw;\n return fallback;\n}\n\nfunction safePageId<T extends string | undefined>(\n raw: string | null | undefined,\n fallback: T\n): string | T {\n if (raw && PAGE_PATH_PATTERN.test(raw)) return raw;\n return fallback;\n}\n\n/**\n * MyToursPage — user-facing browse for available tours.\n *\n * Lists tours returned by `toursForContext` (filtered to the current\n * product/module/page surface) and decorates each with the requesting\n * user's `TourUserProgress` status (Completed / In progress / Skipped /\n * Not started). Read-only — tours are actually played via the SDK overlay,\n * not via mutations on this page.\n */\nexport function MyToursPage() {\n const { tr } = useToursI18n();\n const {\n productId: shellProductId,\n moduleId: shellModuleId,\n pageId: shellPageId,\n workspaceId,\n } = useToursContext();\n const [searchParams] = useSearchParams();\n const eventBus = useEventBus();\n\n // Emit page view event exactly once per mount. A ref guard keeps the effect\n // semantically mount-only while satisfying exhaustive-deps with the real\n // dependency list, so we don't have to disable the lint rule.\n const pageViewedRef = useRef(false);\n useEffect(() => {\n if (pageViewedRef.current) return;\n pageViewedRef.current = true;\n eventBus.emit('tours.page.viewed', {\n page: '/tours',\n productId: shellProductId,\n moduleId: shellModuleId,\n pageId: shellPageId,\n workspaceId,\n });\n }, [eventBus, shellProductId, shellModuleId, shellPageId, workspaceId]);\n\n // Preferred source is the shell's productId prop (the host app knows\n // which product it is — \"workspaces\", \"vibecontrols\", \"bigconsole\", etc.).\n // Fall back to a query-param override for deep-link scenarios, but only\n // after passing a plain-slug validator so we never forward arbitrary\n // strings into the GraphQL variables. No hardcoded product default —\n // if neither source provides one, skip the query entirely so non-\n // workspaces integrations don't get workspaces tours.\n const productId =\n safeSlug(shellProductId, undefined) ?? safeSlug(searchParams.get('productId'), undefined);\n const moduleId =\n safeSlug(shellModuleId, undefined) ?? safeSlug(searchParams.get('moduleId'), undefined);\n const pageId =\n safePageId(shellPageId, undefined) ?? safePageId(searchParams.get('pageId'), undefined);\n\n const { data: toursData, loading: toursLoading } = useGetToursForContextQuery({\n variables: productId ? { context: { productId, moduleId, pageId } } : undefined,\n skip: !productId,\n fetchPolicy: 'cache-and-network',\n });\n\n const tours = toursData?.toursForContext ?? [];\n const isLoading = toursLoading;\n\n // Surface a single, clearly-emphasised \"pick up where you left off / start\n // here\" tour: prefer one already in progress, then the first not-yet-started\n // tour, so a new user always has one obvious next walkthrough to take.\n const featuredTour =\n tours.find((tour: TourSummary) => tour.myProgress?.status === 'IN_PROGRESS') ??\n tours.find(\n (tour: TourSummary) =>\n !tour.myProgress?.status ||\n (tour.myProgress.status !== 'COMPLETED' &&\n tour.myProgress.status !== 'SKIPPED' &&\n tour.myProgress.status !== 'DISMISSED')\n );\n const restTours = tours.filter((tour: TourSummary) => tour.id !== featuredTour?.id);\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n <GradientText>{tr('myTours.title')}</GradientText>\n </h1>\n <p className=\"text-sm sm:text-base text-text-secondary\">{tr('myTours.subtitle')}</p>\n </div>\n\n <PagePurpose>{tr('myTours.purpose')}</PagePurpose>\n\n {isLoading && tours.length === 0 ? (\n <output className=\"text-sm text-text-secondary\">{tr('myTours.loading')}</output>\n ) : tours.length === 0 ? (\n <IllustratedEmptyState illustration=\"empty-generic\" title={tr('myTours.empty')} />\n ) : (\n <div className=\"space-y-4 sm:space-y-6\">\n {/* Featured \"start here\" tour — the single emphasis zone on this page. */}\n {featuredTour ? (\n <EmphasisPanel className=\"p-4 sm:p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary line-clamp-2\">\n {featuredTour.name}\n </h2>\n <ProgressBadge status={featuredTour.myProgress?.status} tr={tr} />\n </div>\n {featuredTour.description ? (\n <Markdown inline className=\"mt-2 text-sm text-text-secondary line-clamp-3\">\n {featuredTour.description}\n </Markdown>\n ) : null}\n </EmphasisPanel>\n ) : null}\n\n {restTours.length > 0 ? (\n <ul className=\"grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {restTours.map((tour: TourSummary) => (\n <li key={tour.id}>\n <GlassCard\n treatment=\"plain\"\n className=\"h-full p-4 flex flex-col gap-2 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <h2 className=\"font-semibold text-text-primary line-clamp-2\">{tour.name}</h2>\n <ProgressBadge status={tour.myProgress?.status} tr={tr} />\n </div>\n {tour.description ? (\n <Markdown inline className=\"text-sm text-text-secondary line-clamp-3\">\n {tour.description}\n </Markdown>\n ) : null}\n </GlassCard>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nfunction ProgressBadge({\n status,\n tr,\n}: {\n status: string | undefined;\n tr: (key: ToursTranslationKey) => string;\n}) {\n switch (status) {\n case 'COMPLETED':\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-success\">\n <CheckCircle2 size={14} />\n {tr('myTours.status.completed')}\n </span>\n );\n case 'IN_PROGRESS':\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-warning motion-safe:animate-vc-glow-pulse\">\n <Play size={14} />\n {tr('myTours.status.inProgress')}\n </span>\n );\n case 'SKIPPED':\n case 'DISMISSED':\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <MinusCircle size={14} />\n {tr('myTours.status.skipped')}\n </span>\n );\n default:\n return (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Circle size={14} />\n {tr('myTours.status.notStarted')}\n </span>\n );\n }\n}\n"],"mappings":"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"}