@burdenoff/microfe-tours 2026.1004.2 → 2026.1013.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.
- package/dist/tours/pages/MediaLibraryPage.js +1 -1
- package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
- package/dist/tours/pages/SchedulerPage.js +1 -1
- package/dist/tours/pages/SchedulerPage.js.map +1 -1
- package/dist/tours/pages/StepEditorPage.js +3 -3
- package/dist/tours/pages/StepEditorPage.js.map +1 -1
- package/dist/tours/pages/TemplateLibraryPage.js +1 -1
- package/dist/tours/pages/TemplateLibraryPage.js.map +1 -1
- package/dist/tours/pages/TourBuilderPage.js +4 -4
- package/dist/tours/pages/TourBuilderPage.js.map +1 -1
- package/dist/tours/pages/ToursListPage.js +1 -1
- package/dist/tours/pages/ToursListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -88,7 +88,7 @@ function C() {
|
|
|
88
88
|
}), /* @__PURE__ */ v("select", {
|
|
89
89
|
value: R,
|
|
90
90
|
onChange: (e) => z(e.target.value),
|
|
91
|
-
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
91
|
+
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
92
92
|
"aria-label": C("mediaLibrary.filterByType", "Filter by media type"),
|
|
93
93
|
children: [
|
|
94
94
|
/* @__PURE__ */ _("option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaLibraryPage.js","names":[],"sources":["../../../src/tours/pages/MediaLibraryPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Upload, Search, Image as ImageIcon, Video, Music, FileText, Trash2 } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursI18n } from '../i18n';\nimport { useMediaLibrary } from '../hooks/useMediaLibrary';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursFormatters } from '../utils';\nimport type { TourMediaType } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\n\n/**\n * Media library page\n * Browse, upload, and manage tour media\n */\nexport function MediaLibraryPage() {\n const { tr } = useToursI18n();\n\n const toursContext = useToursContext();\n const eventBus = useEventBus();\n\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canListMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_LIST);\n const canDeleteMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_DELETE);\n\n const { media, fetchMedia, uploadMedia, deleteMedia, uploadProgress } = useMediaLibrary();\n const { formatFileSize } = useToursFormatters();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState<TourMediaType | 'all'>('all');\n\n useEffect(() => {\n if (permissionsLoading || !canListMedia) return;\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n }, [fetchMedia, typeFilter, searchQuery, permissionsLoading, canListMedia]);\n\n const handleFileUpload = async (files: FileList | null) => {\n if (!files) return;\n\n await Promise.all(\n Array.from(files).map(async (file) => {\n try {\n await uploadMedia(file);\n } catch {\n emitToursApiFailed(eventBus, 'media.upload', toursContext);\n }\n })\n );\n\n // Refresh media list\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n };\n\n const getMediaIcon = (type: TourMediaType) => {\n switch (type) {\n case 'IMAGE':\n return <ImageIcon size={20} />;\n case 'VIDEO':\n return <Video size={20} />;\n case 'AUDIO':\n return <Music size={20} />;\n case 'DOCUMENT':\n return <FileText size={20} />;\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('mediaLibrary.title', 'Media Library')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('mediaLibrary.subtitle', 'Upload and manage media for tour steps')}\n </p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"w-full sm:w-auto px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n </div>\n\n <PagePurpose>\n {tr(\n 'mediaLibrary.purpose',\n 'Store and organise the images, videos, audio, and documents you attach to tour steps. Upload an asset here once, then reference it from any step — handy for product screenshots, short demo clips, or callout graphics that make a walkthrough clearer.'\n )}\n </PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n />\n </div>\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value as TourMediaType | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.filterByType', 'Filter by media type')}\n >\n <option value=\"all\">{tr('mediaLibrary.allTypes', 'All Types')}</option>\n <option value=\"IMAGE\">{tr('mediaLibrary.typeImages', 'Images')}</option>\n <option value=\"VIDEO\">{tr('mediaLibrary.typeVideos', 'Videos')}</option>\n <option value=\"AUDIO\">{tr('mediaLibrary.typeAudio', 'Audio')}</option>\n <option value=\"DOCUMENT\">{tr('mediaLibrary.typeDocuments', 'Documents')}</option>\n </select>\n </div>\n\n {/* Upload Progress */}\n {Object.keys(uploadProgress).length > 0 && (\n <div className=\"space-y-2\">\n {Object.entries(uploadProgress).map(([fileId, progress]) => (\n <div key={fileId} className=\"border border-border-subtle rounded-md p-3\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm\">{tr('mediaLibrary.uploading', 'Uploading...')}</span>\n <span className=\"text-sm text-text-secondary\">{progress}%</span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Media Grid */}\n {media.length > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">\n {media.map((item) => (\n <GlassCard\n key={item.id}\n treatment=\"plain\"\n className=\"overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n {/* Thumbnail */}\n <div className=\"aspect-video bg-bg-sunken flex items-center justify-center\">\n {item.type === 'IMAGE' ? (\n <img\n src={item.cdnUrl}\n alt={item.altText || item.name}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"text-text-secondary\">{getMediaIcon(item.type)}</div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"p-3\">\n <h3 className=\"font-medium text-sm truncate\">{item.name}</h3>\n <div className=\"flex items-center justify-between mt-2 text-xs text-text-secondary\">\n <span>{item.type}</span>\n <span>{formatFileSize(item.size)}</span>\n </div>\n {canDeleteMedia && (\n <button\n onClick={() => {\n if (window.confirm(tr('mediaLibrary.deleteConfirm', 'Delete this media?')))\n deleteMedia(item.id);\n }}\n type=\"button\"\n className=\"mt-2 w-full p-2 min-h-[44px] text-xs text-status-error-text border border-status-error-border/30 rounded hover:bg-status-error-bg/10 flex items-center justify-center gap-1\"\n >\n <Trash2 size={12} />\n {tr('mediaLibrary.delete', 'Delete')}\n </button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title={tr('mediaLibrary.noMedia', 'No media files')}\n description={tr(\n 'mediaLibrary.uploadFirstMedia',\n 'Upload your first media file to get started'\n )}\n action={\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"px-4 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer inline-flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n }\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MediaLibraryPage.js","names":[],"sources":["../../../src/tours/pages/MediaLibraryPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Upload, Search, Image as ImageIcon, Video, Music, FileText, Trash2 } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursI18n } from '../i18n';\nimport { useMediaLibrary } from '../hooks/useMediaLibrary';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursFormatters } from '../utils';\nimport type { TourMediaType } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\n\n/**\n * Media library page\n * Browse, upload, and manage tour media\n */\nexport function MediaLibraryPage() {\n const { tr } = useToursI18n();\n\n const toursContext = useToursContext();\n const eventBus = useEventBus();\n\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canListMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_LIST);\n const canDeleteMedia = hasPermission(TOUR_PERMISSIONS.TOUR_MEDIA_DELETE);\n\n const { media, fetchMedia, uploadMedia, deleteMedia, uploadProgress } = useMediaLibrary();\n const { formatFileSize } = useToursFormatters();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState<TourMediaType | 'all'>('all');\n\n useEffect(() => {\n if (permissionsLoading || !canListMedia) return;\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n }, [fetchMedia, typeFilter, searchQuery, permissionsLoading, canListMedia]);\n\n const handleFileUpload = async (files: FileList | null) => {\n if (!files) return;\n\n await Promise.all(\n Array.from(files).map(async (file) => {\n try {\n await uploadMedia(file);\n } catch {\n emitToursApiFailed(eventBus, 'media.upload', toursContext);\n }\n })\n );\n\n // Refresh media list\n fetchMedia(typeFilter === 'all' ? undefined : typeFilter, searchQuery);\n };\n\n const getMediaIcon = (type: TourMediaType) => {\n switch (type) {\n case 'IMAGE':\n return <ImageIcon size={20} />;\n case 'VIDEO':\n return <Video size={20} />;\n case 'AUDIO':\n return <Music size={20} />;\n case 'DOCUMENT':\n return <FileText size={20} />;\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('mediaLibrary.title', 'Media Library')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('mediaLibrary.subtitle', 'Upload and manage media for tour steps')}\n </p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"w-full sm:w-auto px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n </div>\n\n <PagePurpose>\n {tr(\n 'mediaLibrary.purpose',\n 'Store and organise the images, videos, audio, and documents you attach to tour steps. Upload an asset here once, then reference it from any step — handy for product screenshots, short demo clips, or callout graphics that make a walkthrough clearer.'\n )}\n </PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('mediaLibrary.searchPlaceholder', 'Search media...')}\n />\n </div>\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value as TourMediaType | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('mediaLibrary.filterByType', 'Filter by media type')}\n >\n <option value=\"all\">{tr('mediaLibrary.allTypes', 'All Types')}</option>\n <option value=\"IMAGE\">{tr('mediaLibrary.typeImages', 'Images')}</option>\n <option value=\"VIDEO\">{tr('mediaLibrary.typeVideos', 'Videos')}</option>\n <option value=\"AUDIO\">{tr('mediaLibrary.typeAudio', 'Audio')}</option>\n <option value=\"DOCUMENT\">{tr('mediaLibrary.typeDocuments', 'Documents')}</option>\n </select>\n </div>\n\n {/* Upload Progress */}\n {Object.keys(uploadProgress).length > 0 && (\n <div className=\"space-y-2\">\n {Object.entries(uploadProgress).map(([fileId, progress]) => (\n <div key={fileId} className=\"border border-border-subtle rounded-md p-3\">\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm\">{tr('mediaLibrary.uploading', 'Uploading...')}</span>\n <span className=\"text-sm text-text-secondary\">{progress}%</span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n\n {/* Media Grid */}\n {media.length > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4\">\n {media.map((item) => (\n <GlassCard\n key={item.id}\n treatment=\"plain\"\n className=\"overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n {/* Thumbnail */}\n <div className=\"aspect-video bg-bg-sunken flex items-center justify-center\">\n {item.type === 'IMAGE' ? (\n <img\n src={item.cdnUrl}\n alt={item.altText || item.name}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"text-text-secondary\">{getMediaIcon(item.type)}</div>\n )}\n </div>\n\n {/* Info */}\n <div className=\"p-3\">\n <h3 className=\"font-medium text-sm truncate\">{item.name}</h3>\n <div className=\"flex items-center justify-between mt-2 text-xs text-text-secondary\">\n <span>{item.type}</span>\n <span>{formatFileSize(item.size)}</span>\n </div>\n {canDeleteMedia && (\n <button\n onClick={() => {\n if (window.confirm(tr('mediaLibrary.deleteConfirm', 'Delete this media?')))\n deleteMedia(item.id);\n }}\n type=\"button\"\n className=\"mt-2 w-full p-2 min-h-[44px] text-xs text-status-error-text border border-status-error-border/30 rounded hover:bg-status-error-bg/10 flex items-center justify-center gap-1\"\n >\n <Trash2 size={12} />\n {tr('mediaLibrary.delete', 'Delete')}\n </button>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title={tr('mediaLibrary.noMedia', 'No media files')}\n description={tr(\n 'mediaLibrary.uploadFirstMedia',\n 'Upload your first media file to get started'\n )}\n action={\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_MEDIA_CREATE}>\n <label className=\"px-4 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 cursor-pointer inline-flex items-center justify-center gap-2\">\n <Upload size={20} />\n {tr('mediaLibrary.uploadMedia', 'Upload Media')}\n <input\n type=\"file\"\n multiple\n accept=\"image/*,video/*,audio/*,.pdf,.doc,.docx\"\n onChange={(e) => handleFileUpload(e.target.files)}\n className=\"hidden\"\n aria-label={tr('mediaLibrary.uploadMedia', 'Upload Media')}\n />\n </label>\n </PermissionGate>\n }\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -161,7 +161,7 @@ function S() {
|
|
|
161
161
|
...j,
|
|
162
162
|
recurrencePattern: e.target.value
|
|
163
163
|
}),
|
|
164
|
-
className: "w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
164
|
+
className: "w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
165
165
|
"aria-label": S("tourScheduler.labelRecurrencePattern", "Recurrence Pattern"),
|
|
166
166
|
children: [
|
|
167
167
|
/* @__PURE__ */ p("option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SchedulerPage.js","names":[],"sources":["../../../src/tours/pages/SchedulerPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport { Calendar, Clock, ArrowLeft, Save } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { Button, EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursI18n } from '../i18n';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { RecurrencePattern } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\nimport { useCreateTourScheduleMutation } from '../../generated/global-operations';\nimport { useGetTourSchedulesQuery } from '../../generated/global-operations';\n\n/**\n * Tour scheduler page\n * Configure tour scheduling and recurrence\n */\nexport function SchedulerPage() {\n const { tr } = useToursI18n();\n const { tourId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canCreateSchedule = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE);\n const canListSchedules = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_LIST);\n\n const [scheduleData, setScheduleData] = useState({\n startDate: '',\n endDate: '',\n recurrencePattern: 'DAILY' as RecurrencePattern,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n isActive: true,\n });\n\n const [saving, setSaving] = useState(false);\n\n const [createSchedule] = useCreateTourScheduleMutation();\n const { data: schedulesData } = useGetTourSchedulesQuery({\n variables: { tourId: tourId! },\n skip: !tourId || !canListSchedules,\n });\n const existingSchedules = schedulesData?.tourSchedules ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!tourId) return;\n setSaving(true);\n try {\n // Convert recurrence pattern to RFC 5545 recurrence rule\n const recurrenceRule =\n scheduleData.recurrencePattern === 'DAILY'\n ? 'FREQ=DAILY'\n : scheduleData.recurrencePattern === 'WEEKLY'\n ? 'FREQ=WEEKLY'\n : scheduleData.recurrencePattern === 'MONTHLY'\n ? 'FREQ=MONTHLY'\n : undefined;\n\n await createSchedule({\n variables: {\n input: {\n tourId,\n startDate: new Date(scheduleData.startDate).toISOString(),\n endDate: scheduleData.endDate\n ? new Date(scheduleData.endDate).toISOString()\n : undefined,\n recurrenceRule,\n timezone: scheduleData.timezone,\n },\n },\n refetchQueries: ['GetTourSchedules'],\n });\n navigate(`${basePath}/${tourId}/edit`);\n } catch {\n emitToursApiFailed(eventBus, 'schedule.create', toursContext);\n } finally {\n setSaving(false);\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/${tourId}/edit`);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-3xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 sm:gap-4\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md\"\n aria-label={tr('tourScheduler.goBack', 'Go back')}\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('tourScheduler.title', 'Schedule Tour')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourScheduler.subtitle', 'Configure when this tour should be activated')}\n </p>\n </div>\n </div>\n\n <PagePurpose>\n {tr(\n 'tourScheduler.purpose',\n 'Decide exactly when this tour goes live and how often it repeats — for example, run an onboarding tour every Monday, or activate a feature-announcement tour for a single launch window. Set a start (and optional end) date plus a recurrence pattern, and the tour will surface to users automatically on schedule.'\n )}\n </PagePurpose>\n\n {/* Existing Schedules — emphasised so an editor sees current activation at a glance. */}\n {existingSchedules.length > 0 && (\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('tourScheduler.existingSchedules', 'Existing Schedules')}\n </h2>\n <div className=\"space-y-2\">\n {existingSchedules.map((schedule: (typeof existingSchedules)[number]) => (\n <div\n key={schedule.id}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 border border-border-seam rounded-md\"\n >\n <div>\n <div className=\"text-sm font-medium\">\n {new Date(schedule.startDate).toLocaleDateString()}\n {schedule.endDate\n ? ` — ${new Date(schedule.endDate).toLocaleDateString()}`\n : ` — ${tr('tourScheduler.noEndDate', 'No end date')}`}\n </div>\n <div className=\"text-xs text-text-secondary\">\n {schedule.recurrenceRule || tr('tourScheduler.oneTime', 'One-time')} ·{' '}\n {schedule.timezone}\n {schedule.isActive\n ? ` · ${tr('tourScheduler.active', 'Active')}`\n : ` · ${tr('tourScheduler.inactive', 'Inactive')}`}\n </div>\n </div>\n <div className=\"text-xs text-text-secondary tabular-nums\">\n {tr('tourScheduler.runs', 'Runs')}: {schedule.executionCount}\n </div>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n )}\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Date Range */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Calendar size={20} />\n {tr('tourScheduler.sectionDateRange', 'Date Range')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-start-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelStartDate', 'Start Date *')}\n </label>\n <input\n id=\"scheduler-start-date\"\n type=\"datetime-local\"\n value={scheduleData.startDate}\n onChange={(e) => setScheduleData({ ...scheduleData, startDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n required\n aria-label={tr('tourScheduler.labelStartDate', 'Start Date *')}\n />\n </div>\n\n <div>\n <label htmlFor=\"scheduler-end-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n </label>\n <input\n id=\"scheduler-end-date\"\n type=\"datetime-local\"\n value={scheduleData.endDate}\n onChange={(e) => setScheduleData({ ...scheduleData, endDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Recurrence */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Clock size={20} />\n {tr('tourScheduler.sectionRecurrence', 'Recurrence')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-recurrence\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n </label>\n <select\n id=\"scheduler-recurrence\"\n value={scheduleData.recurrencePattern}\n onChange={(e) =>\n setScheduleData({\n ...scheduleData,\n recurrencePattern: e.target.value as RecurrencePattern,\n })\n }\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n >\n <option value=\"DAILY\">{tr('tourScheduler.recurrenceDaily', 'Daily')}</option>\n <option value=\"WEEKLY\">{tr('tourScheduler.recurrenceWeekly', 'Weekly')}</option>\n <option value=\"MONTHLY\">{tr('tourScheduler.recurrenceMonthly', 'Monthly')}</option>\n <option value=\"CUSTOM\">{tr('tourScheduler.recurrenceCustom', 'Custom')}</option>\n </select>\n </div>\n\n <div>\n <label htmlFor=\"scheduler-timezone\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelTimezone', 'Timezone')}\n </label>\n <input\n id=\"scheduler-timezone\"\n type=\"text\"\n value={scheduleData.timezone}\n onChange={(e) => setScheduleData({ ...scheduleData, timezone: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n readOnly\n aria-label={tr('tourScheduler.labelTimezone', 'Timezone')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Status */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={scheduleData.isActive}\n onChange={(e) => setScheduleData({ ...scheduleData, isActive: e.target.checked })}\n className=\"size-5 rounded\"\n aria-label={tr('tourScheduler.statusActiveLabel', 'Active')}\n />\n <div>\n <div className=\"font-medium\">{tr('tourScheduler.statusActiveLabel', 'Active')}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'tourScheduler.statusActiveDescription',\n 'Enable this schedule immediately after creation'\n )}\n </div>\n </div>\n </label>\n </GlassCard>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleBack}\n className=\"flex-1 min-h-[44px]\"\n >\n {tr('tourScheduler.cancel', 'Cancel')}\n </Button>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE}>\n <Button\n type=\"submit\"\n disabled={saving || !canCreateSchedule}\n className=\"flex-1 min-h-[44px]\"\n >\n <Save size={20} />\n {saving\n ? tr('tourScheduler.creating', 'Creating...')\n : tr('tourScheduler.createSchedule', 'Create Schedule')}\n </Button>\n </PermissionGate>\n </div>\n </form>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SchedulerPage.js","names":[],"sources":["../../../src/tours/pages/SchedulerPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport { Calendar, Clock, ArrowLeft, Save } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { Button, EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursI18n } from '../i18n';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { RecurrencePattern } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\nimport { useCreateTourScheduleMutation } from '../../generated/global-operations';\nimport { useGetTourSchedulesQuery } from '../../generated/global-operations';\n\n/**\n * Tour scheduler page\n * Configure tour scheduling and recurrence\n */\nexport function SchedulerPage() {\n const { tr } = useToursI18n();\n const { tourId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canCreateSchedule = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE);\n const canListSchedules = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_LIST);\n\n const [scheduleData, setScheduleData] = useState({\n startDate: '',\n endDate: '',\n recurrencePattern: 'DAILY' as RecurrencePattern,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n isActive: true,\n });\n\n const [saving, setSaving] = useState(false);\n\n const [createSchedule] = useCreateTourScheduleMutation();\n const { data: schedulesData } = useGetTourSchedulesQuery({\n variables: { tourId: tourId! },\n skip: !tourId || !canListSchedules,\n });\n const existingSchedules = schedulesData?.tourSchedules ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!tourId) return;\n setSaving(true);\n try {\n // Convert recurrence pattern to RFC 5545 recurrence rule\n const recurrenceRule =\n scheduleData.recurrencePattern === 'DAILY'\n ? 'FREQ=DAILY'\n : scheduleData.recurrencePattern === 'WEEKLY'\n ? 'FREQ=WEEKLY'\n : scheduleData.recurrencePattern === 'MONTHLY'\n ? 'FREQ=MONTHLY'\n : undefined;\n\n await createSchedule({\n variables: {\n input: {\n tourId,\n startDate: new Date(scheduleData.startDate).toISOString(),\n endDate: scheduleData.endDate\n ? new Date(scheduleData.endDate).toISOString()\n : undefined,\n recurrenceRule,\n timezone: scheduleData.timezone,\n },\n },\n refetchQueries: ['GetTourSchedules'],\n });\n navigate(`${basePath}/${tourId}/edit`);\n } catch {\n emitToursApiFailed(eventBus, 'schedule.create', toursContext);\n } finally {\n setSaving(false);\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/${tourId}/edit`);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-3xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 sm:gap-4\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md\"\n aria-label={tr('tourScheduler.goBack', 'Go back')}\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('tourScheduler.title', 'Schedule Tour')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourScheduler.subtitle', 'Configure when this tour should be activated')}\n </p>\n </div>\n </div>\n\n <PagePurpose>\n {tr(\n 'tourScheduler.purpose',\n 'Decide exactly when this tour goes live and how often it repeats — for example, run an onboarding tour every Monday, or activate a feature-announcement tour for a single launch window. Set a start (and optional end) date plus a recurrence pattern, and the tour will surface to users automatically on schedule.'\n )}\n </PagePurpose>\n\n {/* Existing Schedules — emphasised so an editor sees current activation at a glance. */}\n {existingSchedules.length > 0 && (\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('tourScheduler.existingSchedules', 'Existing Schedules')}\n </h2>\n <div className=\"space-y-2\">\n {existingSchedules.map((schedule: (typeof existingSchedules)[number]) => (\n <div\n key={schedule.id}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 border border-border-seam rounded-md\"\n >\n <div>\n <div className=\"text-sm font-medium\">\n {new Date(schedule.startDate).toLocaleDateString()}\n {schedule.endDate\n ? ` — ${new Date(schedule.endDate).toLocaleDateString()}`\n : ` — ${tr('tourScheduler.noEndDate', 'No end date')}`}\n </div>\n <div className=\"text-xs text-text-secondary\">\n {schedule.recurrenceRule || tr('tourScheduler.oneTime', 'One-time')} ·{' '}\n {schedule.timezone}\n {schedule.isActive\n ? ` · ${tr('tourScheduler.active', 'Active')}`\n : ` · ${tr('tourScheduler.inactive', 'Inactive')}`}\n </div>\n </div>\n <div className=\"text-xs text-text-secondary tabular-nums\">\n {tr('tourScheduler.runs', 'Runs')}: {schedule.executionCount}\n </div>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n )}\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Date Range */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Calendar size={20} />\n {tr('tourScheduler.sectionDateRange', 'Date Range')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-start-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelStartDate', 'Start Date *')}\n </label>\n <input\n id=\"scheduler-start-date\"\n type=\"datetime-local\"\n value={scheduleData.startDate}\n onChange={(e) => setScheduleData({ ...scheduleData, startDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n required\n aria-label={tr('tourScheduler.labelStartDate', 'Start Date *')}\n />\n </div>\n\n <div>\n <label htmlFor=\"scheduler-end-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n </label>\n <input\n id=\"scheduler-end-date\"\n type=\"datetime-local\"\n value={scheduleData.endDate}\n onChange={(e) => setScheduleData({ ...scheduleData, endDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Recurrence */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Clock size={20} />\n {tr('tourScheduler.sectionRecurrence', 'Recurrence')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-recurrence\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n </label>\n <select\n id=\"scheduler-recurrence\"\n value={scheduleData.recurrencePattern}\n onChange={(e) =>\n setScheduleData({\n ...scheduleData,\n recurrencePattern: e.target.value as RecurrencePattern,\n })\n }\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n >\n <option value=\"DAILY\">{tr('tourScheduler.recurrenceDaily', 'Daily')}</option>\n <option value=\"WEEKLY\">{tr('tourScheduler.recurrenceWeekly', 'Weekly')}</option>\n <option value=\"MONTHLY\">{tr('tourScheduler.recurrenceMonthly', 'Monthly')}</option>\n <option value=\"CUSTOM\">{tr('tourScheduler.recurrenceCustom', 'Custom')}</option>\n </select>\n </div>\n\n <div>\n <label htmlFor=\"scheduler-timezone\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelTimezone', 'Timezone')}\n </label>\n <input\n id=\"scheduler-timezone\"\n type=\"text\"\n value={scheduleData.timezone}\n onChange={(e) => setScheduleData({ ...scheduleData, timezone: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n readOnly\n aria-label={tr('tourScheduler.labelTimezone', 'Timezone')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Status */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={scheduleData.isActive}\n onChange={(e) => setScheduleData({ ...scheduleData, isActive: e.target.checked })}\n className=\"size-5 rounded\"\n aria-label={tr('tourScheduler.statusActiveLabel', 'Active')}\n />\n <div>\n <div className=\"font-medium\">{tr('tourScheduler.statusActiveLabel', 'Active')}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'tourScheduler.statusActiveDescription',\n 'Enable this schedule immediately after creation'\n )}\n </div>\n </div>\n </label>\n </GlassCard>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleBack}\n className=\"flex-1 min-h-[44px]\"\n >\n {tr('tourScheduler.cancel', 'Cancel')}\n </Button>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE}>\n <Button\n type=\"submit\"\n disabled={saving || !canCreateSchedule}\n className=\"flex-1 min-h-[44px]\"\n >\n <Save size={20} />\n {saving\n ? tr('tourScheduler.creating', 'Creating...')\n : tr('tourScheduler.createSchedule', 'Create Schedule')}\n </Button>\n </PermissionGate>\n </div>\n </form>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -363,7 +363,7 @@ function S() {
|
|
|
363
363
|
id: "step-editor-placement",
|
|
364
364
|
value: R.placement,
|
|
365
365
|
onChange: (e) => V(R.id, "placement", e.target.value),
|
|
366
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm sm:text-base",
|
|
366
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm sm:text-base scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
367
367
|
"aria-label": S("stepEditor.tooltipPlacement"),
|
|
368
368
|
children: [
|
|
369
369
|
/* @__PURE__ */ y("option", {
|
|
@@ -572,7 +572,7 @@ function S() {
|
|
|
572
572
|
/* @__PURE__ */ b("select", {
|
|
573
573
|
value: q,
|
|
574
574
|
onChange: (e) => J(e.target.value),
|
|
575
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm",
|
|
575
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
576
576
|
"aria-label": S("stepEditor.selectUploadedMedia", "Select uploaded media"),
|
|
577
577
|
children: [/* @__PURE__ */ y("option", {
|
|
578
578
|
value: "",
|
|
@@ -590,7 +590,7 @@ function S() {
|
|
|
590
590
|
/* @__PURE__ */ b("select", {
|
|
591
591
|
value: Y,
|
|
592
592
|
onChange: (e) => ge(e.target.value),
|
|
593
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm",
|
|
593
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
594
594
|
"aria-label": S("stepEditor.positionHeader", "Header"),
|
|
595
595
|
children: [
|
|
596
596
|
/* @__PURE__ */ y("option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StepEditorPage.js","names":[],"sources":["../../../src/tours/pages/StepEditorPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport {\n ArrowLeft,\n Plus,\n Save,\n Trash2,\n ChevronLeft,\n ChevronRight,\n ChevronUp,\n ChevronDown,\n Menu,\n Paperclip,\n X,\n} from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n OptionalFields,\n Button,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { MarkdownEditor } from '../components/MarkdownEditor';\nimport { useStepSequencer } from '../hooks/useStepSequencer';\nimport { useTourBuilder } from '../hooks/useTourBuilder';\nimport { useMediaLibrary } from '../hooks/useMediaLibrary';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursI18n } from '../i18n';\nimport { emitToursApiFailed } from '../telemetry';\n\n/**\n * Step editor page\n * Manage tour steps with sequencer and editor\n * Responsive: sidebar drawer on mobile, side panel on desktop\n */\nexport function StepEditorPage() {\n const { tr } = useToursI18n();\n\n const { tourId, stepId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canCreateStep = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_CREATE);\n const canUpdateStep = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_UPDATE);\n const canDeleteStep = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_DELETE);\n\n const { tour, loadTour, syncSteps, saving } = useTourBuilder(tourId);\n const { steps, selectedStep, selectStep, createStep, deleteStep, updateStepField, moveStep } =\n useStepSequencer();\n const { media, fetchMedia, attachToStep, loading: mediaLoading } = useMediaLibrary();\n\n // Mobile sidebar state\n const [isSidebarOpen, setIsSidebarOpen] = useState(false);\n const [isMobile, setIsMobile] = useState(\n () => typeof window !== 'undefined' && window.innerWidth < 768\n );\n const [attachmentMediaId, setAttachmentMediaId] = useState('');\n const [attachmentPosition, setAttachmentPosition] = useState<'header' | 'body' | 'footer'>(\n 'body'\n );\n const [attachingMedia, setAttachingMedia] = useState(false);\n\n // Detect mobile viewport\n useEffect(() => {\n const checkMobile = () => {\n const mobile = window.innerWidth < 768;\n setIsMobile(mobile);\n // Auto-close sidebar on desktop\n if (!mobile) {\n setIsSidebarOpen(false);\n }\n };\n window.addEventListener('resize', checkMobile);\n return () => window.removeEventListener('resize', checkMobile);\n }, []);\n\n useEffect(() => {\n if (tourId) {\n loadTour(tourId);\n }\n }, [tourId, loadTour]);\n\n useEffect(() => {\n void fetchMedia();\n }, [fetchMedia]);\n\n useEffect(() => {\n const selectedStepId = stepId || selectedStep?.id;\n if (selectedStepId && steps.length > 0) {\n const step = steps.find((s) => s.id === selectedStepId);\n if (step) {\n selectStep(step);\n }\n }\n }, [stepId, steps, selectStep, selectedStep?.id]);\n\n const handleAddStep = () => {\n createStep();\n };\n\n const handleSaveSteps = async () => {\n if (tourId) {\n try {\n await syncSteps(tourId, steps);\n } catch {\n emitToursApiFailed(eventBus, 'step.save', toursContext);\n }\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/${tourId}/edit`);\n };\n\n const handleAttachMedia = async () => {\n if (!selectedStep || !tourId || !attachmentMediaId) return;\n if (selectedStep.id.startsWith('temp_')) {\n window.alert(\n tr('stepEditor.saveBeforeAttach', 'Save your steps before attaching media to them.')\n );\n return;\n }\n\n try {\n setAttachingMedia(true);\n await attachToStep(\n selectedStep.id,\n attachmentMediaId,\n attachmentPosition,\n (selectedStep.media?.length ?? 0) + 1\n );\n await loadTour(tourId);\n setAttachmentMediaId('');\n } catch {\n emitToursApiFailed(eventBus, 'media.attach', toursContext);\n } finally {\n setAttachingMedia(false);\n }\n };\n\n const handleSelectStep = (step: (typeof steps)[0]) => {\n selectStep(step);\n // Close sidebar on mobile after selecting\n if (isMobile) {\n setIsSidebarOpen(false);\n }\n };\n\n return (\n <div className=\"flex flex-col h-full bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b border-border-subtle p-3 sm:p-4 flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4 bg-bg-surface\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 hover:bg-accent rounded-md shrink-0\"\n aria-label={tr('stepEditor.goBack')}\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-base sm:text-lg font-semibold truncate\">\n {tour?.name || tr('stepEditor.tourFallback')} - {tr('stepEditor.stepsLabel')}\n </h1>\n <p className=\"text-xs sm:text-sm text-text-secondary\">\n {steps.length} {tr('stepEditor.stepsCount')}\n </p>\n </div>\n </div>\n\n {/* Action buttons */}\n <div className=\"flex items-center gap-2 sm:gap-3 ml-auto\">\n {/* Mobile: Toggle sidebar button */}\n {isMobile && (\n <button\n type=\"button\"\n onClick={() => setIsSidebarOpen(!isSidebarOpen)}\n className=\"px-3 py-2 border border-border-subtle rounded-md hover:bg-accent flex items-center gap-2 sm:hidden\"\n >\n <Menu size={18} />\n <span className=\"text-sm\">{tr('stepEditor.steps')}</span>\n {steps.length > 0 && (\n <span className=\"px-1.5 py-0.5 text-xs bg-action-primary-bg/20 rounded-full\">\n {steps.length}\n </span>\n )}\n </button>\n )}\n\n {canCreateStep && (\n <button\n type=\"button\"\n onClick={handleAddStep}\n className=\"px-3 sm:px-4 py-2 border border-border-subtle rounded-md hover:bg-accent flex items-center gap-2 text-sm sm:text-base\"\n >\n <Plus size={18} />\n <span className=\"hidden xs:inline\">{tr('stepEditor.addStep')}</span>\n </button>\n )}\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_STEP_UPDATE}>\n <Button\n type=\"button\"\n onClick={handleSaveSteps}\n disabled={saving || !canUpdateStep}\n className=\"min-h-[44px] text-sm sm:text-base\"\n >\n <Save size={18} />\n <span className=\"hidden xs:inline\">\n {saving ? tr('stepEditor.saving') : tr('stepEditor.save')}\n </span>\n </Button>\n </PermissionGate>\n </div>\n </div>\n\n <div className=\"px-3 sm:px-4 pt-3 sm:pt-4\">\n <PagePurpose>{tr('stepEditor.purpose')}</PagePurpose>\n </div>\n\n <div className=\"flex-1 flex overflow-hidden relative\">\n {/* Mobile Sidebar Overlay */}\n {isMobile && isSidebarOpen && (\n <button\n type=\"button\"\n aria-label={tr('stepEditor.closeSteps')}\n className=\"fixed inset-0 bg-foreground/40 z-40 md:hidden\"\n onClick={() => setIsSidebarOpen(false)}\n />\n )}\n\n {/* Step List Sidebar */}\n <div\n className={`\n ${isMobile ? 'fixed inset-y-0 left-0 z-50 w-[min(18rem,calc(100vw-2rem))] transform transition-transform duration-300' : 'w-64 lg:w-80 shrink-0'}\n ${isMobile && !isSidebarOpen ? '-translate-x-full' : 'translate-x-0'}\n border-r border-border-subtle overflow-y-auto bg-bg-surface\n `}\n >\n {/* Mobile sidebar header */}\n {isMobile && (\n <div className=\"flex items-center justify-between p-4 border-b border-border-subtle sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"font-semibold\">{tr('stepEditor.tourSteps')}</h2>\n <button\n type=\"button\"\n onClick={() => setIsSidebarOpen(false)}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md\"\n aria-label={tr('stepEditor.closeSteps')}\n >\n <X size={20} />\n </button>\n </div>\n )}\n\n <div className=\"p-3 sm:p-4 space-y-2\">\n {steps.length === 0 ? (\n <div className=\"text-center py-8 text-text-secondary\">\n <p className=\"text-sm\">{tr('stepEditor.noStepsYet')}</p>\n <p className=\"text-xs mt-1\">{tr('stepEditor.clickAddStep')}</p>\n </div>\n ) : (\n steps.map((step, index) => (\n <div\n key={step.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => handleSelectStep(step)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleSelectStep(step);\n }\n }}\n className={`p-3 border rounded-md cursor-pointer transition-colors ${\n selectedStep?.id === step.id\n ? 'border-primary bg-action-primary-bg/10 shadow-[var(--shadow-pop)]'\n : 'border-border-seam hover:bg-accent'\n }`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-xs text-text-secondary mb-1\">\n {tr('stepEditor.stepNumber')} {index + 1}\n </div>\n <div className=\"font-medium text-sm truncate\">\n {step.title || tr('stepEditor.untitled')}\n </div>\n {step.targetSelector && (\n <div className=\"text-xs text-text-secondary mt-1 truncate font-mono\">\n {step.targetSelector}\n </div>\n )}\n </div>\n {canDeleteStep && (\n <div className=\"flex items-center gap-1 shrink-0\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n if (index > 0) {\n moveStep(index, index - 1);\n }\n }}\n className=\"p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-accent rounded\"\n title={tr('stepEditor.moveStepUp', 'Move step up')}\n aria-label={tr('stepEditor.moveStepUp', 'Move step up')}\n disabled={index === 0}\n >\n <ChevronUp size={14} />\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n if (index < steps.length - 1) {\n moveStep(index, index + 1);\n }\n }}\n className=\"p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-accent rounded\"\n title={tr('stepEditor.moveStepDown', 'Move step down')}\n aria-label={tr('stepEditor.moveStepDown', 'Move step down')}\n disabled={index === steps.length - 1}\n >\n <ChevronDown size={14} />\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n deleteStep(step.id);\n }}\n className=\"p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-status-error-bg/10 rounded text-status-error-text\"\n title={tr('stepEditor.deleteStep')}\n aria-label={tr('stepEditor.deleteStep')}\n >\n <Trash2 size={14} />\n </button>\n </div>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n </div>\n\n {/* Step Editor */}\n <div className=\"flex-1 overflow-y-auto p-4 sm:p-6\">\n {selectedStep ? (\n <div className=\"max-w-2xl mx-auto space-y-4 sm:space-y-6\">\n {/* Mobile: Step navigation */}\n {isMobile && steps.length > 1 && (\n <div className=\"flex items-center justify-between bg-bg-sunken/50 rounded-lg p-2 mb-4\">\n <button\n type=\"button\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === selectedStep.id);\n if (currentIndex > 0) {\n selectStep(steps[currentIndex - 1]);\n }\n }}\n disabled={steps.findIndex((s) => s.id === selectedStep.id) === 0}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md disabled:opacity-30\"\n aria-label={tr('stepEditor.previousStep')}\n >\n <ChevronLeft size={20} />\n </button>\n <span className=\"text-sm text-text-secondary\">\n {tr('stepEditor.stepNumber')}{' '}\n {steps.findIndex((s) => s.id === selectedStep.id) + 1} {tr('stepEditor.of')}{' '}\n {steps.length}\n </span>\n <button\n type=\"button\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === selectedStep.id);\n if (currentIndex < steps.length - 1) {\n selectStep(steps[currentIndex + 1]);\n }\n }}\n disabled={steps.findIndex((s) => s.id === selectedStep.id) === steps.length - 1}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md disabled:opacity-30\"\n aria-label={tr('stepEditor.nextStep')}\n >\n <ChevronRight size={20} />\n </button>\n </div>\n )}\n\n <h2 className=\"text-lg sm:text-xl font-semibold\">{tr('stepEditor.editStep')}</h2>\n\n {/* Title */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"step-editor-title\">\n {tr('stepEditor.stepTitle')} *\n </label>\n <input\n id=\"step-editor-title\"\n type=\"text\"\n value={selectedStep.title}\n onChange={(e) => updateStepField(selectedStep.id, 'title', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm sm:text-base\"\n placeholder={tr('stepEditor.stepTitlePlaceholder')}\n aria-label={tr('stepEditor.stepTitle')}\n />\n </div>\n\n {/* Content */}\n <MarkdownEditor\n id=\"step-editor-content\"\n label={`${tr('stepEditor.content')} *`}\n value={selectedStep.content}\n onChange={(content) => updateStepField(selectedStep.id, 'content', content)}\n rows={4}\n placeholder={tr('stepEditor.contentPlaceholder')}\n aria-label={tr('stepEditor.content')}\n />\n\n {/* Selector */}\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-css-selector\"\n >\n {tr('stepEditor.cssSelector')} *\n <span className=\"text-text-secondary font-normal ml-2\">\n ({tr('stepEditor.targetElement')})\n </span>\n </label>\n <input\n id=\"step-editor-css-selector\"\n type=\"text\"\n value={selectedStep.targetSelector || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelector', e.target.value)\n }\n placeholder={tr('stepEditor.cssSelectorPlaceholder')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface font-mono text-sm\"\n aria-label={`${tr('stepEditor.cssSelector')} * (${tr('stepEditor.targetElement')})`}\n />\n <p className=\"text-xs text-text-secondary mt-1\">{tr('stepEditor.dataTourHint')}</p>\n </div>\n\n {/* Placement */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"step-editor-placement\">\n {tr('stepEditor.tooltipPlacement')}\n </label>\n <select\n id=\"step-editor-placement\"\n value={selectedStep.placement}\n onChange={(e) => updateStepField(selectedStep.id, 'placement', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm sm:text-base\"\n aria-label={tr('stepEditor.tooltipPlacement')}\n >\n <option value=\"AUTO\">{tr('stepEditor.placementAuto')}</option>\n <option value=\"TOP\">{tr('stepEditor.placementTop')}</option>\n <option value=\"BOTTOM\">{tr('stepEditor.placementBottom')}</option>\n <option value=\"LEFT\">{tr('stepEditor.placementLeft')}</option>\n <option value=\"RIGHT\">{tr('stepEditor.placementRight')}</option>\n <option value=\"CENTER\">{tr('stepEditor.placementCenter')}</option>\n </select>\n </div>\n\n {/* Device-specific selectors */}\n <details className=\"border border-border-subtle rounded-md\">\n <summary className=\"px-4 py-3 cursor-pointer font-medium text-sm hover:bg-accent/50 rounded-t-md\">\n {tr('stepEditor.deviceSpecificSelectors')}\n </summary>\n <div className=\"p-4 pt-0 space-y-3 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary mb-3\">\n {tr('stepEditor.deviceSelectorHint')}\n </p>\n <div>\n <label\n className=\"block text-xs font-medium mb-1\"\n htmlFor=\"step-editor-mobile-selector\"\n >\n {tr('stepEditor.mobileSelector')}\n </label>\n <input\n id=\"step-editor-mobile-selector\"\n type=\"text\"\n value={selectedStep.targetSelectorMobile || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelectorMobile', e.target.value)\n }\n placeholder={tr('stepEditor.leaveEmptyDefault')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n aria-label={tr('stepEditor.mobileSelector')}\n />\n </div>\n <div>\n <label\n className=\"block text-xs font-medium mb-1\"\n htmlFor=\"step-editor-tablet-selector\"\n >\n {tr('stepEditor.tabletSelector')}\n </label>\n <input\n id=\"step-editor-tablet-selector\"\n type=\"text\"\n value={selectedStep.targetSelectorTablet || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelectorTablet', e.target.value)\n }\n placeholder={tr('stepEditor.leaveEmptyDefault')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n aria-label={tr('stepEditor.tabletSelector')}\n />\n </div>\n <div>\n <label\n className=\"block text-xs font-medium mb-1\"\n htmlFor=\"step-editor-desktop-selector\"\n >\n {tr('stepEditor.desktopSelector')}\n </label>\n <input\n id=\"step-editor-desktop-selector\"\n type=\"text\"\n value={selectedStep.targetSelectorDesktop || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelectorDesktop', e.target.value)\n }\n placeholder={tr('stepEditor.leaveEmptyDefault')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n aria-label={tr('stepEditor.desktopSelector')}\n />\n </div>\n </div>\n </details>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label className=\"flex items-center gap-3 cursor-pointer rounded-md border border-border-subtle p-3\">\n <input\n type=\"checkbox\"\n checked={selectedStep.requireInteraction ?? false}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'requireInteraction', e.target.checked)\n }\n className=\"size-4 rounded\"\n aria-label={tr('stepEditor.requireInteraction', 'Require interaction')}\n />\n <div>\n <div className=\"text-sm font-medium\">\n {tr('stepEditor.requireInteraction', 'Require interaction')}\n </div>\n <div className=\"text-xs text-text-secondary\">\n {tr(\n 'stepEditor.requireInteractionDescription',\n 'User must interact with the highlighted element before advancing.'\n )}\n </div>\n </div>\n </label>\n\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-wait-timeout\"\n >\n {tr('stepEditor.waitForTimeout', 'Wait for timeout (ms)')}\n </label>\n <input\n id=\"step-editor-wait-timeout\"\n type=\"number\"\n min=\"0\"\n value={selectedStep.waitForTimeout ?? ''}\n onChange={(e) =>\n updateStepField(\n selectedStep.id,\n 'waitForTimeout',\n e.target.value ? Number(e.target.value) : undefined\n )\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm\"\n placeholder=\"1500\"\n aria-label={tr('stepEditor.waitForTimeout', 'Wait for timeout (ms)')}\n />\n </div>\n </div>\n\n {/* Advanced: step timing / skip conditions / analytics tags — collapse the long-tail knobs */}\n <OptionalFields\n title={tr('stepEditor.advancedSettings', 'Advanced settings')}\n description={tr(\n 'stepEditor.advancedSettingsDescription',\n 'Step-timing waits, skip conditions, and analytics tags.'\n )}\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-wait-selector\"\n >\n {tr('stepEditor.waitForSelector', 'Wait for selector')}\n </label>\n <input\n id=\"step-editor-wait-selector\"\n type=\"text\"\n value={selectedStep.waitForSelector || ''}\n onChange={(e) =>\n updateStepField(\n selectedStep.id,\n 'waitForSelector',\n e.target.value || undefined\n )\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n placeholder=\"[data-tour='agents-tabs']\"\n aria-label={tr('stepEditor.waitForSelector', 'Wait for selector')}\n />\n </div>\n\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-wait-event\"\n >\n {tr('stepEditor.waitForEvent', 'Wait for event')}\n </label>\n <input\n id=\"step-editor-wait-event\"\n type=\"text\"\n value={selectedStep.waitForEvent || ''}\n onChange={(e) =>\n updateStepField(\n selectedStep.id,\n 'waitForEvent',\n e.target.value || undefined\n )\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm\"\n placeholder=\"tour:ready\"\n aria-label={tr('stepEditor.waitForEvent', 'Wait for event')}\n />\n </div>\n </div>\n\n <div className=\"mt-4\">\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-skip-conditions\"\n >\n {tr('stepEditor.skipConditions', 'Skip conditions (JSON)')}\n </label>\n <textarea\n id=\"step-editor-skip-conditions\"\n value={\n selectedStep.skipConditions\n ? JSON.stringify(selectedStep.skipConditions, null, 2)\n : ''\n }\n onChange={(e) => {\n const rawValue = e.target.value.trim();\n if (!rawValue) {\n updateStepField(selectedStep.id, 'skipConditions', undefined);\n return;\n }\n\n try {\n updateStepField(selectedStep.id, 'skipConditions', JSON.parse(rawValue));\n } catch {\n updateStepField(selectedStep.id, 'skipConditions', rawValue);\n }\n }}\n rows={4}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n placeholder='{\"selectorMissing\":\"[data-tour=\\\"missing\\\"]\"}'\n aria-label={tr('stepEditor.skipConditions', 'Skip conditions (JSON)')}\n />\n </div>\n </OptionalFields>\n\n <GlassCard treatment=\"plain\" className=\"p-4 space-y-4 border-border-seam\">\n <div className=\"flex items-center gap-2\">\n <Paperclip size={18} />\n <h3 className=\"text-sm font-semibold\">\n {tr('stepEditor.stepMedia', 'Step media')}\n </h3>\n </div>\n\n {selectedStep.media && selectedStep.media.length > 0 ? (\n <div className=\"space-y-2\">\n {selectedStep.media.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border border-border-subtle p-2 text-sm\"\n >\n <div>\n <div className=\"font-medium\">{item.media.name}</div>\n <div className=\"text-xs text-text-secondary\">{item.position}</div>\n </div>\n <span className=\"text-xs text-text-secondary\">{item.media.type}</span>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr('stepEditor.noMediaAttached', 'No media attached to this step yet.')}\n </p>\n )}\n\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-[minmax(0,1fr)_160px_auto]\">\n <select\n value={attachmentMediaId}\n onChange={(e) => setAttachmentMediaId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm\"\n aria-label={tr('stepEditor.selectUploadedMedia', 'Select uploaded media')}\n >\n <option value=\"\">\n {mediaLoading\n ? tr('stepEditor.loadingMedia', 'Loading media...')\n : tr('stepEditor.selectUploadedMedia', 'Select uploaded media')}\n </option>\n {media.map((item) => (\n <option key={item.id} value={item.id}>\n {item.name} ({item.type})\n </option>\n ))}\n </select>\n\n <select\n value={attachmentPosition}\n onChange={(e) =>\n setAttachmentPosition(e.target.value as 'header' | 'body' | 'footer')\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm\"\n aria-label={tr('stepEditor.positionHeader', 'Header')}\n >\n <option value=\"header\">{tr('stepEditor.positionHeader', 'Header')}</option>\n <option value=\"body\">{tr('stepEditor.positionBody', 'Body')}</option>\n <option value=\"footer\">{tr('stepEditor.positionFooter', 'Footer')}</option>\n </select>\n\n <button\n type=\"button\"\n onClick={handleAttachMedia}\n disabled={attachingMedia || !attachmentMediaId}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md hover:bg-accent disabled:opacity-50\"\n >\n {tr('stepEditor.attach', 'Attach')}\n </button>\n </div>\n </GlassCard>\n </div>\n ) : (\n <div className=\"flex items-center justify-center h-full\">\n <IllustratedEmptyState\n illustration={steps.length > 0 ? 'empty-generic' : 'onboarding'}\n title={tr('stepEditor.emptyTitle')}\n description={\n steps.length > 0\n ? tr('stepEditor.selectStepToEdit')\n : tr('stepEditor.createFirstStep')\n }\n action={\n steps.length === 0 && canCreateStep ? (\n <Button type=\"button\" onClick={handleAddStep} className=\"min-h-[44px]\">\n <Plus size={18} />\n {tr('stepEditor.addFirstStep')}\n </Button>\n ) : undefined\n }\n />\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StepEditorPage.js","names":[],"sources":["../../../src/tours/pages/StepEditorPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport {\n ArrowLeft,\n Plus,\n Save,\n Trash2,\n ChevronLeft,\n ChevronRight,\n ChevronUp,\n ChevronDown,\n Menu,\n Paperclip,\n X,\n} from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n OptionalFields,\n Button,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { MarkdownEditor } from '../components/MarkdownEditor';\nimport { useStepSequencer } from '../hooks/useStepSequencer';\nimport { useTourBuilder } from '../hooks/useTourBuilder';\nimport { useMediaLibrary } from '../hooks/useMediaLibrary';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursI18n } from '../i18n';\nimport { emitToursApiFailed } from '../telemetry';\n\n/**\n * Step editor page\n * Manage tour steps with sequencer and editor\n * Responsive: sidebar drawer on mobile, side panel on desktop\n */\nexport function StepEditorPage() {\n const { tr } = useToursI18n();\n\n const { tourId, stepId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canCreateStep = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_CREATE);\n const canUpdateStep = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_UPDATE);\n const canDeleteStep = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_DELETE);\n\n const { tour, loadTour, syncSteps, saving } = useTourBuilder(tourId);\n const { steps, selectedStep, selectStep, createStep, deleteStep, updateStepField, moveStep } =\n useStepSequencer();\n const { media, fetchMedia, attachToStep, loading: mediaLoading } = useMediaLibrary();\n\n // Mobile sidebar state\n const [isSidebarOpen, setIsSidebarOpen] = useState(false);\n const [isMobile, setIsMobile] = useState(\n () => typeof window !== 'undefined' && window.innerWidth < 768\n );\n const [attachmentMediaId, setAttachmentMediaId] = useState('');\n const [attachmentPosition, setAttachmentPosition] = useState<'header' | 'body' | 'footer'>(\n 'body'\n );\n const [attachingMedia, setAttachingMedia] = useState(false);\n\n // Detect mobile viewport\n useEffect(() => {\n const checkMobile = () => {\n const mobile = window.innerWidth < 768;\n setIsMobile(mobile);\n // Auto-close sidebar on desktop\n if (!mobile) {\n setIsSidebarOpen(false);\n }\n };\n window.addEventListener('resize', checkMobile);\n return () => window.removeEventListener('resize', checkMobile);\n }, []);\n\n useEffect(() => {\n if (tourId) {\n loadTour(tourId);\n }\n }, [tourId, loadTour]);\n\n useEffect(() => {\n void fetchMedia();\n }, [fetchMedia]);\n\n useEffect(() => {\n const selectedStepId = stepId || selectedStep?.id;\n if (selectedStepId && steps.length > 0) {\n const step = steps.find((s) => s.id === selectedStepId);\n if (step) {\n selectStep(step);\n }\n }\n }, [stepId, steps, selectStep, selectedStep?.id]);\n\n const handleAddStep = () => {\n createStep();\n };\n\n const handleSaveSteps = async () => {\n if (tourId) {\n try {\n await syncSteps(tourId, steps);\n } catch {\n emitToursApiFailed(eventBus, 'step.save', toursContext);\n }\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/${tourId}/edit`);\n };\n\n const handleAttachMedia = async () => {\n if (!selectedStep || !tourId || !attachmentMediaId) return;\n if (selectedStep.id.startsWith('temp_')) {\n window.alert(\n tr('stepEditor.saveBeforeAttach', 'Save your steps before attaching media to them.')\n );\n return;\n }\n\n try {\n setAttachingMedia(true);\n await attachToStep(\n selectedStep.id,\n attachmentMediaId,\n attachmentPosition,\n (selectedStep.media?.length ?? 0) + 1\n );\n await loadTour(tourId);\n setAttachmentMediaId('');\n } catch {\n emitToursApiFailed(eventBus, 'media.attach', toursContext);\n } finally {\n setAttachingMedia(false);\n }\n };\n\n const handleSelectStep = (step: (typeof steps)[0]) => {\n selectStep(step);\n // Close sidebar on mobile after selecting\n if (isMobile) {\n setIsSidebarOpen(false);\n }\n };\n\n return (\n <div className=\"flex flex-col h-full bg-bg-surface\">\n {/* Header */}\n <div className=\"border-b border-border-subtle p-3 sm:p-4 flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4 bg-bg-surface\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 hover:bg-accent rounded-md shrink-0\"\n aria-label={tr('stepEditor.goBack')}\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-base sm:text-lg font-semibold truncate\">\n {tour?.name || tr('stepEditor.tourFallback')} - {tr('stepEditor.stepsLabel')}\n </h1>\n <p className=\"text-xs sm:text-sm text-text-secondary\">\n {steps.length} {tr('stepEditor.stepsCount')}\n </p>\n </div>\n </div>\n\n {/* Action buttons */}\n <div className=\"flex items-center gap-2 sm:gap-3 ml-auto\">\n {/* Mobile: Toggle sidebar button */}\n {isMobile && (\n <button\n type=\"button\"\n onClick={() => setIsSidebarOpen(!isSidebarOpen)}\n className=\"px-3 py-2 border border-border-subtle rounded-md hover:bg-accent flex items-center gap-2 sm:hidden\"\n >\n <Menu size={18} />\n <span className=\"text-sm\">{tr('stepEditor.steps')}</span>\n {steps.length > 0 && (\n <span className=\"px-1.5 py-0.5 text-xs bg-action-primary-bg/20 rounded-full\">\n {steps.length}\n </span>\n )}\n </button>\n )}\n\n {canCreateStep && (\n <button\n type=\"button\"\n onClick={handleAddStep}\n className=\"px-3 sm:px-4 py-2 border border-border-subtle rounded-md hover:bg-accent flex items-center gap-2 text-sm sm:text-base\"\n >\n <Plus size={18} />\n <span className=\"hidden xs:inline\">{tr('stepEditor.addStep')}</span>\n </button>\n )}\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_STEP_UPDATE}>\n <Button\n type=\"button\"\n onClick={handleSaveSteps}\n disabled={saving || !canUpdateStep}\n className=\"min-h-[44px] text-sm sm:text-base\"\n >\n <Save size={18} />\n <span className=\"hidden xs:inline\">\n {saving ? tr('stepEditor.saving') : tr('stepEditor.save')}\n </span>\n </Button>\n </PermissionGate>\n </div>\n </div>\n\n <div className=\"px-3 sm:px-4 pt-3 sm:pt-4\">\n <PagePurpose>{tr('stepEditor.purpose')}</PagePurpose>\n </div>\n\n <div className=\"flex-1 flex overflow-hidden relative\">\n {/* Mobile Sidebar Overlay */}\n {isMobile && isSidebarOpen && (\n <button\n type=\"button\"\n aria-label={tr('stepEditor.closeSteps')}\n className=\"fixed inset-0 bg-foreground/40 z-40 md:hidden\"\n onClick={() => setIsSidebarOpen(false)}\n />\n )}\n\n {/* Step List Sidebar */}\n <div\n className={`\n ${isMobile ? 'fixed inset-y-0 left-0 z-50 w-[min(18rem,calc(100vw-2rem))] transform transition-transform duration-300' : 'w-64 lg:w-80 shrink-0'}\n ${isMobile && !isSidebarOpen ? '-translate-x-full' : 'translate-x-0'}\n border-r border-border-subtle overflow-y-auto bg-bg-surface\n `}\n >\n {/* Mobile sidebar header */}\n {isMobile && (\n <div className=\"flex items-center justify-between p-4 border-b border-border-subtle sticky top-0 bg-bg-surface z-10\">\n <h2 className=\"font-semibold\">{tr('stepEditor.tourSteps')}</h2>\n <button\n type=\"button\"\n onClick={() => setIsSidebarOpen(false)}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md\"\n aria-label={tr('stepEditor.closeSteps')}\n >\n <X size={20} />\n </button>\n </div>\n )}\n\n <div className=\"p-3 sm:p-4 space-y-2\">\n {steps.length === 0 ? (\n <div className=\"text-center py-8 text-text-secondary\">\n <p className=\"text-sm\">{tr('stepEditor.noStepsYet')}</p>\n <p className=\"text-xs mt-1\">{tr('stepEditor.clickAddStep')}</p>\n </div>\n ) : (\n steps.map((step, index) => (\n <div\n key={step.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => handleSelectStep(step)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleSelectStep(step);\n }\n }}\n className={`p-3 border rounded-md cursor-pointer transition-colors ${\n selectedStep?.id === step.id\n ? 'border-primary bg-action-primary-bg/10 shadow-[var(--shadow-pop)]'\n : 'border-border-seam hover:bg-accent'\n }`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-xs text-text-secondary mb-1\">\n {tr('stepEditor.stepNumber')} {index + 1}\n </div>\n <div className=\"font-medium text-sm truncate\">\n {step.title || tr('stepEditor.untitled')}\n </div>\n {step.targetSelector && (\n <div className=\"text-xs text-text-secondary mt-1 truncate font-mono\">\n {step.targetSelector}\n </div>\n )}\n </div>\n {canDeleteStep && (\n <div className=\"flex items-center gap-1 shrink-0\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n if (index > 0) {\n moveStep(index, index - 1);\n }\n }}\n className=\"p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-accent rounded\"\n title={tr('stepEditor.moveStepUp', 'Move step up')}\n aria-label={tr('stepEditor.moveStepUp', 'Move step up')}\n disabled={index === 0}\n >\n <ChevronUp size={14} />\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n if (index < steps.length - 1) {\n moveStep(index, index + 1);\n }\n }}\n className=\"p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-accent rounded\"\n title={tr('stepEditor.moveStepDown', 'Move step down')}\n aria-label={tr('stepEditor.moveStepDown', 'Move step down')}\n disabled={index === steps.length - 1}\n >\n <ChevronDown size={14} />\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n deleteStep(step.id);\n }}\n className=\"p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-status-error-bg/10 rounded text-status-error-text\"\n title={tr('stepEditor.deleteStep')}\n aria-label={tr('stepEditor.deleteStep')}\n >\n <Trash2 size={14} />\n </button>\n </div>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n </div>\n\n {/* Step Editor */}\n <div className=\"flex-1 overflow-y-auto p-4 sm:p-6\">\n {selectedStep ? (\n <div className=\"max-w-2xl mx-auto space-y-4 sm:space-y-6\">\n {/* Mobile: Step navigation */}\n {isMobile && steps.length > 1 && (\n <div className=\"flex items-center justify-between bg-bg-sunken/50 rounded-lg p-2 mb-4\">\n <button\n type=\"button\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === selectedStep.id);\n if (currentIndex > 0) {\n selectStep(steps[currentIndex - 1]);\n }\n }}\n disabled={steps.findIndex((s) => s.id === selectedStep.id) === 0}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md disabled:opacity-30\"\n aria-label={tr('stepEditor.previousStep')}\n >\n <ChevronLeft size={20} />\n </button>\n <span className=\"text-sm text-text-secondary\">\n {tr('stepEditor.stepNumber')}{' '}\n {steps.findIndex((s) => s.id === selectedStep.id) + 1} {tr('stepEditor.of')}{' '}\n {steps.length}\n </span>\n <button\n type=\"button\"\n onClick={() => {\n const currentIndex = steps.findIndex((s) => s.id === selectedStep.id);\n if (currentIndex < steps.length - 1) {\n selectStep(steps[currentIndex + 1]);\n }\n }}\n disabled={steps.findIndex((s) => s.id === selectedStep.id) === steps.length - 1}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md disabled:opacity-30\"\n aria-label={tr('stepEditor.nextStep')}\n >\n <ChevronRight size={20} />\n </button>\n </div>\n )}\n\n <h2 className=\"text-lg sm:text-xl font-semibold\">{tr('stepEditor.editStep')}</h2>\n\n {/* Title */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"step-editor-title\">\n {tr('stepEditor.stepTitle')} *\n </label>\n <input\n id=\"step-editor-title\"\n type=\"text\"\n value={selectedStep.title}\n onChange={(e) => updateStepField(selectedStep.id, 'title', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm sm:text-base\"\n placeholder={tr('stepEditor.stepTitlePlaceholder')}\n aria-label={tr('stepEditor.stepTitle')}\n />\n </div>\n\n {/* Content */}\n <MarkdownEditor\n id=\"step-editor-content\"\n label={`${tr('stepEditor.content')} *`}\n value={selectedStep.content}\n onChange={(content) => updateStepField(selectedStep.id, 'content', content)}\n rows={4}\n placeholder={tr('stepEditor.contentPlaceholder')}\n aria-label={tr('stepEditor.content')}\n />\n\n {/* Selector */}\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-css-selector\"\n >\n {tr('stepEditor.cssSelector')} *\n <span className=\"text-text-secondary font-normal ml-2\">\n ({tr('stepEditor.targetElement')})\n </span>\n </label>\n <input\n id=\"step-editor-css-selector\"\n type=\"text\"\n value={selectedStep.targetSelector || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelector', e.target.value)\n }\n placeholder={tr('stepEditor.cssSelectorPlaceholder')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface font-mono text-sm\"\n aria-label={`${tr('stepEditor.cssSelector')} * (${tr('stepEditor.targetElement')})`}\n />\n <p className=\"text-xs text-text-secondary mt-1\">{tr('stepEditor.dataTourHint')}</p>\n </div>\n\n {/* Placement */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"step-editor-placement\">\n {tr('stepEditor.tooltipPlacement')}\n </label>\n <select\n id=\"step-editor-placement\"\n value={selectedStep.placement}\n onChange={(e) => updateStepField(selectedStep.id, 'placement', e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm sm:text-base scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('stepEditor.tooltipPlacement')}\n >\n <option value=\"AUTO\">{tr('stepEditor.placementAuto')}</option>\n <option value=\"TOP\">{tr('stepEditor.placementTop')}</option>\n <option value=\"BOTTOM\">{tr('stepEditor.placementBottom')}</option>\n <option value=\"LEFT\">{tr('stepEditor.placementLeft')}</option>\n <option value=\"RIGHT\">{tr('stepEditor.placementRight')}</option>\n <option value=\"CENTER\">{tr('stepEditor.placementCenter')}</option>\n </select>\n </div>\n\n {/* Device-specific selectors */}\n <details className=\"border border-border-subtle rounded-md\">\n <summary className=\"px-4 py-3 cursor-pointer font-medium text-sm hover:bg-accent/50 rounded-t-md\">\n {tr('stepEditor.deviceSpecificSelectors')}\n </summary>\n <div className=\"p-4 pt-0 space-y-3 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary mb-3\">\n {tr('stepEditor.deviceSelectorHint')}\n </p>\n <div>\n <label\n className=\"block text-xs font-medium mb-1\"\n htmlFor=\"step-editor-mobile-selector\"\n >\n {tr('stepEditor.mobileSelector')}\n </label>\n <input\n id=\"step-editor-mobile-selector\"\n type=\"text\"\n value={selectedStep.targetSelectorMobile || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelectorMobile', e.target.value)\n }\n placeholder={tr('stepEditor.leaveEmptyDefault')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n aria-label={tr('stepEditor.mobileSelector')}\n />\n </div>\n <div>\n <label\n className=\"block text-xs font-medium mb-1\"\n htmlFor=\"step-editor-tablet-selector\"\n >\n {tr('stepEditor.tabletSelector')}\n </label>\n <input\n id=\"step-editor-tablet-selector\"\n type=\"text\"\n value={selectedStep.targetSelectorTablet || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelectorTablet', e.target.value)\n }\n placeholder={tr('stepEditor.leaveEmptyDefault')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n aria-label={tr('stepEditor.tabletSelector')}\n />\n </div>\n <div>\n <label\n className=\"block text-xs font-medium mb-1\"\n htmlFor=\"step-editor-desktop-selector\"\n >\n {tr('stepEditor.desktopSelector')}\n </label>\n <input\n id=\"step-editor-desktop-selector\"\n type=\"text\"\n value={selectedStep.targetSelectorDesktop || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'targetSelectorDesktop', e.target.value)\n }\n placeholder={tr('stepEditor.leaveEmptyDefault')}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n aria-label={tr('stepEditor.desktopSelector')}\n />\n </div>\n </div>\n </details>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label className=\"flex items-center gap-3 cursor-pointer rounded-md border border-border-subtle p-3\">\n <input\n type=\"checkbox\"\n checked={selectedStep.requireInteraction ?? false}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'requireInteraction', e.target.checked)\n }\n className=\"size-4 rounded\"\n aria-label={tr('stepEditor.requireInteraction', 'Require interaction')}\n />\n <div>\n <div className=\"text-sm font-medium\">\n {tr('stepEditor.requireInteraction', 'Require interaction')}\n </div>\n <div className=\"text-xs text-text-secondary\">\n {tr(\n 'stepEditor.requireInteractionDescription',\n 'User must interact with the highlighted element before advancing.'\n )}\n </div>\n </div>\n </label>\n\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-wait-timeout\"\n >\n {tr('stepEditor.waitForTimeout', 'Wait for timeout (ms)')}\n </label>\n <input\n id=\"step-editor-wait-timeout\"\n type=\"number\"\n min=\"0\"\n value={selectedStep.waitForTimeout ?? ''}\n onChange={(e) =>\n updateStepField(\n selectedStep.id,\n 'waitForTimeout',\n e.target.value ? Number(e.target.value) : undefined\n )\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm\"\n placeholder=\"1500\"\n aria-label={tr('stepEditor.waitForTimeout', 'Wait for timeout (ms)')}\n />\n </div>\n </div>\n\n {/* Advanced: step timing / skip conditions / analytics tags — collapse the long-tail knobs */}\n <OptionalFields\n title={tr('stepEditor.advancedSettings', 'Advanced settings')}\n description={tr(\n 'stepEditor.advancedSettingsDescription',\n 'Step-timing waits, skip conditions, and analytics tags.'\n )}\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-wait-selector\"\n >\n {tr('stepEditor.waitForSelector', 'Wait for selector')}\n </label>\n <input\n id=\"step-editor-wait-selector\"\n type=\"text\"\n value={selectedStep.waitForSelector || ''}\n onChange={(e) =>\n updateStepField(\n selectedStep.id,\n 'waitForSelector',\n e.target.value || undefined\n )\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n placeholder=\"[data-tour='agents-tabs']\"\n aria-label={tr('stepEditor.waitForSelector', 'Wait for selector')}\n />\n </div>\n\n <div>\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-wait-event\"\n >\n {tr('stepEditor.waitForEvent', 'Wait for event')}\n </label>\n <input\n id=\"step-editor-wait-event\"\n type=\"text\"\n value={selectedStep.waitForEvent || ''}\n onChange={(e) =>\n updateStepField(\n selectedStep.id,\n 'waitForEvent',\n e.target.value || undefined\n )\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm\"\n placeholder=\"tour:ready\"\n aria-label={tr('stepEditor.waitForEvent', 'Wait for event')}\n />\n </div>\n </div>\n\n <div className=\"mt-4\">\n <label\n className=\"block text-sm font-medium mb-2\"\n htmlFor=\"step-editor-skip-conditions\"\n >\n {tr('stepEditor.skipConditions', 'Skip conditions (JSON)')}\n </label>\n <textarea\n id=\"step-editor-skip-conditions\"\n value={\n selectedStep.skipConditions\n ? JSON.stringify(selectedStep.skipConditions, null, 2)\n : ''\n }\n onChange={(e) => {\n const rawValue = e.target.value.trim();\n if (!rawValue) {\n updateStepField(selectedStep.id, 'skipConditions', undefined);\n return;\n }\n\n try {\n updateStepField(selectedStep.id, 'skipConditions', JSON.parse(rawValue));\n } catch {\n updateStepField(selectedStep.id, 'skipConditions', rawValue);\n }\n }}\n rows={4}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono\"\n placeholder='{\"selectorMissing\":\"[data-tour=\\\"missing\\\"]\"}'\n aria-label={tr('stepEditor.skipConditions', 'Skip conditions (JSON)')}\n />\n </div>\n </OptionalFields>\n\n <GlassCard treatment=\"plain\" className=\"p-4 space-y-4 border-border-seam\">\n <div className=\"flex items-center gap-2\">\n <Paperclip size={18} />\n <h3 className=\"text-sm font-semibold\">\n {tr('stepEditor.stepMedia', 'Step media')}\n </h3>\n </div>\n\n {selectedStep.media && selectedStep.media.length > 0 ? (\n <div className=\"space-y-2\">\n {selectedStep.media.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border border-border-subtle p-2 text-sm\"\n >\n <div>\n <div className=\"font-medium\">{item.media.name}</div>\n <div className=\"text-xs text-text-secondary\">{item.position}</div>\n </div>\n <span className=\"text-xs text-text-secondary\">{item.media.type}</span>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr('stepEditor.noMediaAttached', 'No media attached to this step yet.')}\n </p>\n )}\n\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-[minmax(0,1fr)_160px_auto]\">\n <select\n value={attachmentMediaId}\n onChange={(e) => setAttachmentMediaId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('stepEditor.selectUploadedMedia', 'Select uploaded media')}\n >\n <option value=\"\">\n {mediaLoading\n ? tr('stepEditor.loadingMedia', 'Loading media...')\n : tr('stepEditor.selectUploadedMedia', 'Select uploaded media')}\n </option>\n {media.map((item) => (\n <option key={item.id} value={item.id}>\n {item.name} ({item.type})\n </option>\n ))}\n </select>\n\n <select\n value={attachmentPosition}\n onChange={(e) =>\n setAttachmentPosition(e.target.value as 'header' | 'body' | 'footer')\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('stepEditor.positionHeader', 'Header')}\n >\n <option value=\"header\">{tr('stepEditor.positionHeader', 'Header')}</option>\n <option value=\"body\">{tr('stepEditor.positionBody', 'Body')}</option>\n <option value=\"footer\">{tr('stepEditor.positionFooter', 'Footer')}</option>\n </select>\n\n <button\n type=\"button\"\n onClick={handleAttachMedia}\n disabled={attachingMedia || !attachmentMediaId}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md hover:bg-accent disabled:opacity-50\"\n >\n {tr('stepEditor.attach', 'Attach')}\n </button>\n </div>\n </GlassCard>\n </div>\n ) : (\n <div className=\"flex items-center justify-center h-full\">\n <IllustratedEmptyState\n illustration={steps.length > 0 ? 'empty-generic' : 'onboarding'}\n title={tr('stepEditor.emptyTitle')}\n description={\n steps.length > 0\n ? tr('stepEditor.selectStepToEdit')\n : tr('stepEditor.createFirstStep')\n }\n action={\n steps.length === 0 && canCreateStep ? (\n <Button type=\"button\" onClick={handleAddStep} className=\"min-h-[44px]\">\n <Plus size={18} />\n {tr('stepEditor.addFirstStep')}\n </Button>\n ) : undefined\n }\n />\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -75,7 +75,7 @@ function T() {
|
|
|
75
75
|
/* @__PURE__ */ y("select", {
|
|
76
76
|
value: I,
|
|
77
77
|
onChange: (e) => L(e.target.value),
|
|
78
|
-
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
78
|
+
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
79
79
|
"aria-label": T("templateLibrary.allCategories"),
|
|
80
80
|
children: [
|
|
81
81
|
/* @__PURE__ */ v("option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TemplateLibraryPage.js","names":[],"sources":["../../../src/tours/pages/TemplateLibraryPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Search, LayoutTemplate, Star, Globe, Lock, Trash2 } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport {\n useGetTourTemplatesQuery,\n useCreateTourFromTemplateMutation,\n useDeleteTourTemplateMutation,\n} from '../../generated/global-operations';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { TemplateCategory } from '../types';\nimport { useToursI18n } from '../i18n';\nimport { emitToursApiFailed } from '../telemetry';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\n\n/**\n * Template library page\n * Browse and use tour templates\n */\nexport function TemplateLibraryPage() {\n const { tr } = useToursI18n();\n\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canListTemplates = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_LIST);\n const canUseTemplate = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_USE);\n const canDeleteTemplate = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_DELETE);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [categoryFilter, setCategoryFilter] = useState<TemplateCategory | 'all'>('all');\n const [publicOnly, setPublicOnly] = useState(false);\n\n const { data, loading } = useGetTourTemplatesQuery({\n skip: !canListTemplates,\n variables: {\n isPublic: publicOnly || undefined,\n category: categoryFilter === 'all' ? undefined : categoryFilter,\n },\n });\n const templates = data?.tourTemplates ?? [];\n\n const filteredTemplates = templates.filter(\n (t: (typeof templates)[number]) =>\n !searchQuery || t.name.toLowerCase().includes(searchQuery.toLowerCase())\n );\n\n const [createFromTemplate] = useCreateTourFromTemplateMutation();\n\n const handleUseTemplate = async (templateId: string) => {\n nativeImpact('light');\n try {\n const { data } = await createFromTemplate({\n variables: { templateId },\n });\n if (data?.createTourFromTemplate) {\n void nativeNotify('success');\n navigate(`${basePath}/${data.createTourFromTemplate.id}/edit`);\n }\n } catch {\n void nativeNotify('error');\n emitToursApiFailed(eventBus, 'template.create_from_template', toursContext);\n }\n };\n\n const [deleteTemplate] = useDeleteTourTemplateMutation();\n\n const handleDeleteTemplate = async (templateId: string) => {\n try {\n await deleteTemplate({\n variables: { id: templateId },\n refetchQueries: ['GetTourTemplates'],\n });\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n emitToursApiFailed(eventBus, 'template.delete', toursContext);\n }\n };\n\n const handleDeleteTemplateClick = async (templateId: string) => {\n nativeImpact('medium');\n const message = tr('templateLibrary.deleteConfirm');\n const nativeResult = await nativeConfirm({\n title: tr('templateLibrary.deleteConfirm'),\n message,\n });\n const confirmed = nativeResult === null ? window.confirm(message) : nativeResult;\n if (confirmed) await handleDeleteTemplate(templateId);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">{tr('templateLibrary.title')}</h1>\n <p className=\"text-sm text-text-secondary\">{tr('templateLibrary.subtitle')}</p>\n </div>\n\n <PagePurpose>{tr('templateLibrary.purpose')}</PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('templateLibrary.searchPlaceholder')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('templateLibrary.searchPlaceholder')}\n />\n </div>\n <select\n value={categoryFilter}\n onChange={(e) => setCategoryFilter(e.target.value as TemplateCategory | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('templateLibrary.allCategories')}\n >\n <option value=\"all\">{tr('templateLibrary.allCategories')}</option>\n <option value=\"ONBOARDING\">{tr('templateLibrary.categoryOnboarding')}</option>\n <option value=\"FEATURE_ANNOUNCEMENT\">\n {tr('templateLibrary.categoryFeatureAnnouncement')}\n </option>\n <option value=\"HELP\">{tr('templateLibrary.categoryHelp')}</option>\n <option value=\"CUSTOM\">{tr('templateLibrary.categoryCustom')}</option>\n </select>\n <label className=\"flex items-center gap-2 px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={publicOnly}\n onChange={(e) => setPublicOnly(e.target.checked)}\n className=\"rounded\"\n aria-label={tr('templateLibrary.publicOnly')}\n />\n <span className=\"text-sm\">{tr('templateLibrary.publicOnly')}</span>\n </label>\n </div>\n\n {/* Templates Grid */}\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-text-secondary\">{tr('templateLibrary.loading')}</p>\n </div>\n ) : filteredTemplates.length > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredTemplates.map((template: (typeof filteredTemplates)[number]) => (\n <GlassCard\n key={template.id}\n treatment=\"plain\"\n className=\"overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n {/* Thumbnail */}\n <div className=\"aspect-video bg-bg-sunken flex items-center justify-center\">\n {template.thumbnail ? (\n <img\n src={template.thumbnail}\n alt={template.name}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <LayoutTemplate className=\"text-text-secondary\" size={48} />\n )}\n </div>\n\n {/* Content */}\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between mb-2\">\n <h3 className=\"font-semibold\">{template.name}</h3>\n <div className=\"flex gap-1\">\n {template.isPremium && <Star className=\"text-warning\" size={16} />}\n {template.isPublic ? (\n <Globe className=\"text-text-secondary\" size={16} />\n ) : (\n <Lock className=\"text-text-secondary\" size={16} />\n )}\n </div>\n </div>\n\n {template.description && (\n <p className=\"text-sm text-text-secondary mb-3 line-clamp-2\">\n {template.description}\n </p>\n )}\n\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">\n {tr('templateLibrary.usedTimes')} {template.useCount}{' '}\n {tr('templateLibrary.times')}\n </span>\n <div className=\"flex items-center gap-2\">\n {canDeleteTemplate && (\n <button\n type=\"button\"\n onClick={() => void handleDeleteTemplateClick(template.id)}\n className=\"p-2 min-h-[44px] min-w-[44px] text-text-secondary hover:text-status-error-text rounded-md\"\n aria-label={tr('templateLibrary.deleteTemplate')}\n >\n <Trash2 size={16} />\n </button>\n )}\n {canUseTemplate && (\n <button\n type=\"button\"\n onClick={() => handleUseTemplate(template.id)}\n className=\"px-3 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 text-sm\"\n >\n {tr('templateLibrary.useTemplate')}\n </button>\n )}\n </div>\n </div>\n </div>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('templateLibrary.noTemplatesFound')}\n description={tr('templateLibrary.adjustFilters')}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TemplateLibraryPage.js","names":[],"sources":["../../../src/tours/pages/TemplateLibraryPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Search, LayoutTemplate, Star, Globe, Lock, Trash2 } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport {\n useGetTourTemplatesQuery,\n useCreateTourFromTemplateMutation,\n useDeleteTourTemplateMutation,\n} from '../../generated/global-operations';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { TemplateCategory } from '../types';\nimport { useToursI18n } from '../i18n';\nimport { emitToursApiFailed } from '../telemetry';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\n\n/**\n * Template library page\n * Browse and use tour templates\n */\nexport function TemplateLibraryPage() {\n const { tr } = useToursI18n();\n\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canListTemplates = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_LIST);\n const canUseTemplate = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_USE);\n const canDeleteTemplate = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_DELETE);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [categoryFilter, setCategoryFilter] = useState<TemplateCategory | 'all'>('all');\n const [publicOnly, setPublicOnly] = useState(false);\n\n const { data, loading } = useGetTourTemplatesQuery({\n skip: !canListTemplates,\n variables: {\n isPublic: publicOnly || undefined,\n category: categoryFilter === 'all' ? undefined : categoryFilter,\n },\n });\n const templates = data?.tourTemplates ?? [];\n\n const filteredTemplates = templates.filter(\n (t: (typeof templates)[number]) =>\n !searchQuery || t.name.toLowerCase().includes(searchQuery.toLowerCase())\n );\n\n const [createFromTemplate] = useCreateTourFromTemplateMutation();\n\n const handleUseTemplate = async (templateId: string) => {\n nativeImpact('light');\n try {\n const { data } = await createFromTemplate({\n variables: { templateId },\n });\n if (data?.createTourFromTemplate) {\n void nativeNotify('success');\n navigate(`${basePath}/${data.createTourFromTemplate.id}/edit`);\n }\n } catch {\n void nativeNotify('error');\n emitToursApiFailed(eventBus, 'template.create_from_template', toursContext);\n }\n };\n\n const [deleteTemplate] = useDeleteTourTemplateMutation();\n\n const handleDeleteTemplate = async (templateId: string) => {\n try {\n await deleteTemplate({\n variables: { id: templateId },\n refetchQueries: ['GetTourTemplates'],\n });\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n emitToursApiFailed(eventBus, 'template.delete', toursContext);\n }\n };\n\n const handleDeleteTemplateClick = async (templateId: string) => {\n nativeImpact('medium');\n const message = tr('templateLibrary.deleteConfirm');\n const nativeResult = await nativeConfirm({\n title: tr('templateLibrary.deleteConfirm'),\n message,\n });\n const confirmed = nativeResult === null ? window.confirm(message) : nativeResult;\n if (confirmed) await handleDeleteTemplate(templateId);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">{tr('templateLibrary.title')}</h1>\n <p className=\"text-sm text-text-secondary\">{tr('templateLibrary.subtitle')}</p>\n </div>\n\n <PagePurpose>{tr('templateLibrary.purpose')}</PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('templateLibrary.searchPlaceholder')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('templateLibrary.searchPlaceholder')}\n />\n </div>\n <select\n value={categoryFilter}\n onChange={(e) => setCategoryFilter(e.target.value as TemplateCategory | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('templateLibrary.allCategories')}\n >\n <option value=\"all\">{tr('templateLibrary.allCategories')}</option>\n <option value=\"ONBOARDING\">{tr('templateLibrary.categoryOnboarding')}</option>\n <option value=\"FEATURE_ANNOUNCEMENT\">\n {tr('templateLibrary.categoryFeatureAnnouncement')}\n </option>\n <option value=\"HELP\">{tr('templateLibrary.categoryHelp')}</option>\n <option value=\"CUSTOM\">{tr('templateLibrary.categoryCustom')}</option>\n </select>\n <label className=\"flex items-center gap-2 px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={publicOnly}\n onChange={(e) => setPublicOnly(e.target.checked)}\n className=\"rounded\"\n aria-label={tr('templateLibrary.publicOnly')}\n />\n <span className=\"text-sm\">{tr('templateLibrary.publicOnly')}</span>\n </label>\n </div>\n\n {/* Templates Grid */}\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-text-secondary\">{tr('templateLibrary.loading')}</p>\n </div>\n ) : filteredTemplates.length > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredTemplates.map((template: (typeof filteredTemplates)[number]) => (\n <GlassCard\n key={template.id}\n treatment=\"plain\"\n className=\"overflow-hidden p-0 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n {/* Thumbnail */}\n <div className=\"aspect-video bg-bg-sunken flex items-center justify-center\">\n {template.thumbnail ? (\n <img\n src={template.thumbnail}\n alt={template.name}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <LayoutTemplate className=\"text-text-secondary\" size={48} />\n )}\n </div>\n\n {/* Content */}\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between mb-2\">\n <h3 className=\"font-semibold\">{template.name}</h3>\n <div className=\"flex gap-1\">\n {template.isPremium && <Star className=\"text-warning\" size={16} />}\n {template.isPublic ? (\n <Globe className=\"text-text-secondary\" size={16} />\n ) : (\n <Lock className=\"text-text-secondary\" size={16} />\n )}\n </div>\n </div>\n\n {template.description && (\n <p className=\"text-sm text-text-secondary mb-3 line-clamp-2\">\n {template.description}\n </p>\n )}\n\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">\n {tr('templateLibrary.usedTimes')} {template.useCount}{' '}\n {tr('templateLibrary.times')}\n </span>\n <div className=\"flex items-center gap-2\">\n {canDeleteTemplate && (\n <button\n type=\"button\"\n onClick={() => void handleDeleteTemplateClick(template.id)}\n className=\"p-2 min-h-[44px] min-w-[44px] text-text-secondary hover:text-status-error-text rounded-md\"\n aria-label={tr('templateLibrary.deleteTemplate')}\n >\n <Trash2 size={16} />\n </button>\n )}\n {canUseTemplate && (\n <button\n type=\"button\"\n onClick={() => handleUseTemplate(template.id)}\n className=\"px-3 py-2 min-h-[44px] bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg/90 text-sm\"\n >\n {tr('templateLibrary.useTemplate')}\n </button>\n )}\n </div>\n </div>\n </div>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('templateLibrary.noTemplatesFound')}\n description={tr('templateLibrary.adjustFilters')}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1196,7 +1196,7 @@ function y() {
|
|
|
1196
1196
|
pageId: void 0
|
|
1197
1197
|
});
|
|
1198
1198
|
},
|
|
1199
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary",
|
|
1199
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
1200
1200
|
required: !0,
|
|
1201
1201
|
"aria-label": m("tourBuilder.labelProductId"),
|
|
1202
1202
|
children: [
|
|
@@ -1269,7 +1269,7 @@ function y() {
|
|
|
1269
1269
|
pageId: void 0
|
|
1270
1270
|
});
|
|
1271
1271
|
},
|
|
1272
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary",
|
|
1272
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
1273
1273
|
disabled: !I.productId,
|
|
1274
1274
|
"aria-label": m("tourBuilder.labelModuleId", "Module ID"),
|
|
1275
1275
|
children: [
|
|
@@ -1331,7 +1331,7 @@ function y() {
|
|
|
1331
1331
|
pageId: e.target.value || void 0
|
|
1332
1332
|
});
|
|
1333
1333
|
},
|
|
1334
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary",
|
|
1334
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
1335
1335
|
disabled: !I.productId,
|
|
1336
1336
|
"aria-label": m("tourBuilder.labelPageId", "Page ID"),
|
|
1337
1337
|
children: [
|
|
@@ -1411,7 +1411,7 @@ function y() {
|
|
|
1411
1411
|
...I,
|
|
1412
1412
|
triggerType: e.target.value
|
|
1413
1413
|
}),
|
|
1414
|
-
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary",
|
|
1414
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
1415
1415
|
"aria-label": m("tourBuilder.labelTriggerType"),
|
|
1416
1416
|
children: [
|
|
1417
1417
|
/* @__PURE__ */ d("option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TourBuilderPage.js","names":[],"sources":["../../../src/tours/pages/TourBuilderPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport {\n Save,\n ArrowLeft,\n Eye,\n ListOrdered,\n BarChart3,\n Calendar,\n LayoutTemplate,\n Sparkles,\n} from 'lucide-react';\nimport { gql } from '@apollo/client/core';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport {\n Button,\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n type NextStep,\n CTAOverflowMenu,\n type CTAAction,\n} from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { MarkdownEditor } from '../components/MarkdownEditor';\nimport { useTourBuilder } from '../hooks/useTourBuilder';\nimport { useTourLifecycle } from '../hooks/useTourLifecycle';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { Tour, TourTriggerType } from '../types';\nimport { useToursI18n } from '../i18n';\nimport { emitToursApiFailed } from '../telemetry';\nimport { useGetPlatformProductsForToursQuery } from '../../generated/global-operations';\n\n// Covers the current platform catalog with room for planned products; custom slug entry remains the fallback.\nconst PLATFORM_PRODUCTS_QUERY_LIMIT = 250;\n\nconst SEED_VIBECONTROLS_USER_TOURS = gql`\n mutation SeedVibeControlsUserTours {\n seedVibeControlsUserTours {\n productId\n workspaceId\n createdCount\n updatedCount\n totalCount\n tours {\n tourId\n slug\n name\n action\n stepCount\n }\n }\n }\n`;\n\nconst SEED_BUILDMYIQ_USER_TOURS = gql`\n mutation SeedBuildmyiqUserTours {\n seedBuildmyiqUserTours {\n productId\n workspaceId\n createdCount\n updatedCount\n totalCount\n tours {\n tourId\n slug\n name\n action\n stepCount\n }\n }\n }\n`;\n\nconst SEED_BIGCONSOLE_USER_TOURS = gql`\n mutation SeedBigconsoleUserTours {\n seedBigconsoleUserTours {\n productId\n workspaceId\n createdCount\n updatedCount\n totalCount\n tours {\n tourId\n slug\n name\n action\n stepCount\n }\n }\n }\n`;\n\ninterface SeededUserTourResult {\n tourId: string;\n slug: string;\n name: string;\n action: string;\n stepCount: number;\n}\n\ninterface SeedVibeControlsUserToursPayload {\n productId: string;\n workspaceId: string;\n createdCount: number;\n updatedCount: number;\n totalCount: number;\n tours: SeededUserTourResult[];\n}\n\ninterface SeedVibeControlsUserToursMutation {\n seedVibeControlsUserTours: SeedVibeControlsUserToursPayload;\n}\n\ninterface SeedBuildmyiqUserToursMutation {\n seedBuildmyiqUserTours: SeedVibeControlsUserToursPayload;\n}\n\ninterface SeedBigconsoleUserToursMutation {\n seedBigconsoleUserTours: SeedVibeControlsUserToursPayload;\n}\n\nfunction getMutationErrorMessage(result: unknown, fallbackMessage: string): string | null {\n if (!result || typeof result !== 'object') return null;\n\n const record = result as Record<string, unknown>;\n const singleError = record.error;\n if (singleError instanceof Error) return singleError.message;\n\n const errors = record.errors;\n if (!Array.isArray(errors) || errors.length === 0) return null;\n\n const messages = errors\n .map((error) => {\n if (error instanceof Error) return error.message;\n if (!error || typeof error !== 'object') return null;\n\n const message = (error as { message?: unknown }).message;\n return typeof message === 'string' ? message : null;\n })\n .filter((message): message is string => Boolean(message));\n\n return messages.length > 0 ? messages.join('; ') : fallbackMessage;\n}\n\ninterface TourRouteModuleOption {\n moduleId: string;\n label: string;\n path: string;\n}\n\ninterface TourRoutePageOption {\n pageId: string;\n label: string;\n path: string;\n}\n\ninterface TourRouteCatalogEntry {\n productId: string;\n moduleId: string;\n moduleLabel: string;\n pageId: string;\n pageLabel: string;\n path: string;\n isModuleHome?: boolean;\n}\n\nconst TOUR_ROUTE_CATALOG: readonly TourRouteCatalogEntry[] = [\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'home',\n pageLabel: 'Overview',\n path: '/bigconsole',\n isModuleHome: true,\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'dashboards',\n pageLabel: 'Dashboards',\n path: '/bigconsole/dashboards',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'datasink-builder',\n pageLabel: 'Create data sink',\n path: '/bigconsole/datasinks/new',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'parser-builder',\n pageLabel: 'Create parser',\n path: '/bigconsole/parsers/new',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'workflows',\n pageLabel: 'Workflows',\n path: '/bigconsole/workflows/list',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'insights',\n pageLabel: 'AI Insights',\n path: '/bigconsole/insights',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'dashboard',\n moduleLabel: 'Dashboard',\n pageId: 'dashboard',\n pageLabel: 'Dashboard',\n path: '/dashboard',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'home',\n pageLabel: 'Overview',\n path: '/vibecontrols',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'agents',\n pageLabel: 'Targets & Agents',\n path: '/vibecontrols/agents',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'vibes',\n pageLabel: 'Vibes',\n path: '/vibecontrols/vibes',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'sessions',\n pageLabel: 'Sessions',\n path: '/vibecontrols/sessions',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'ai',\n pageLabel: 'AI',\n path: '/vibecontrols/ai',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'catalogs',\n pageLabel: 'Catalogs',\n path: '/vibecontrols/catalogs',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'components',\n pageLabel: 'Components',\n path: '/vibecontrols/components',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'templates',\n pageLabel: 'Templates',\n path: '/vibecontrols/templates',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'actions',\n pageLabel: 'Actions',\n path: '/vibecontrols/actions',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'vibedeck',\n pageLabel: 'Decks',\n path: '/vibecontrols/vibedeck',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'calendar',\n pageLabel: 'Calendar',\n path: '/vibecontrols/calendar',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'tunnels',\n pageLabel: 'Tunnels',\n path: '/vibecontrols/tunnels',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'analytics',\n pageLabel: 'Analytics',\n path: '/vibecontrols/analytics',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'audit',\n pageLabel: 'Audit Log',\n path: '/vibecontrols/audit',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'health',\n pageLabel: 'Health',\n path: '/vibecontrols/health',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'settings',\n pageLabel: 'Settings',\n path: '/vibecontrols/settings',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'scheduler',\n moduleLabel: 'Scheduler',\n pageId: 'scheduler',\n pageLabel: 'Scheduler',\n path: '/scheduler',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'sandbox',\n moduleLabel: 'Sandbox',\n pageId: 'sandbox',\n pageLabel: 'Sandbox',\n path: '/sandbox',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'support',\n moduleLabel: 'Support',\n pageId: 'tickets',\n pageLabel: 'Tickets',\n path: '/support/tickets',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'support',\n moduleLabel: 'Support',\n pageId: 'knowledge-base',\n pageLabel: 'Knowledge Base',\n path: '/support/kb',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'notifications',\n moduleLabel: 'Notifications',\n pageId: 'center',\n pageLabel: 'Notification Center',\n path: '/notifications/center',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'notifications',\n moduleLabel: 'Notifications',\n pageId: 'history',\n pageLabel: 'History',\n path: '/notifications/history',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'notifications',\n moduleLabel: 'Notifications',\n pageId: 'preferences',\n pageLabel: 'Preferences',\n path: '/notifications/preferences',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'home',\n pageLabel: 'All Conversations',\n path: '/conversations',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'list',\n pageLabel: 'Conversation List',\n path: '/conversations/list',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'new',\n pageLabel: 'New Conversation',\n path: '/conversations/new',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'settings',\n pageLabel: 'Settings',\n path: '/conversations/settings',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'auth',\n moduleLabel: 'Auth',\n pageId: 'home',\n pageLabel: 'Auth',\n path: '/auth/security',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'groups',\n moduleLabel: 'Groups and Teams',\n pageId: 'home',\n pageLabel: 'Groups and Teams',\n path: '/groups',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'tags',\n moduleLabel: 'Tags',\n pageId: 'home',\n pageLabel: 'Tags',\n path: '/tags',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'files',\n moduleLabel: 'Files',\n pageId: 'home',\n pageLabel: 'Files',\n path: '/files/browse',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'security',\n moduleLabel: 'Vault',\n pageId: 'home',\n pageLabel: 'Vault',\n path: '/security',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'integrations',\n moduleLabel: 'Integrations',\n pageId: 'home',\n pageLabel: 'Integrations',\n path: '/integrations',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'export',\n moduleLabel: 'Exports',\n pageId: 'home',\n pageLabel: 'Exports',\n path: '/export',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'organizations',\n moduleLabel: 'Organizations',\n pageId: 'home',\n pageLabel: 'Organizations',\n path: '/organizations',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'workspaces',\n moduleLabel: 'Workspaces',\n pageId: 'home',\n pageLabel: 'Workspaces',\n path: '/workspaces',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'billing',\n moduleLabel: 'Billing',\n pageId: 'home',\n pageLabel: 'Billing',\n path: '/billing/overview',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'store',\n moduleLabel: 'Marketplace',\n pageId: 'home',\n pageLabel: 'Marketplace',\n path: '/store/marketplace',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'channels',\n moduleLabel: 'Channels',\n pageId: 'home',\n pageLabel: 'Channels',\n path: '/channels',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'devportal',\n moduleLabel: 'Developer Portal',\n pageId: 'home',\n pageLabel: 'Developer Portal',\n path: '/devportal',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'feature-previews',\n moduleLabel: 'Feature Previews',\n pageId: 'home',\n pageLabel: 'Feature Previews',\n path: '/feature-previews',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'search',\n moduleLabel: 'Search',\n pageId: 'home',\n pageLabel: 'Search',\n path: '/search',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'dashboard',\n moduleLabel: 'Dashboard',\n pageId: 'dashboard',\n pageLabel: 'Dashboard',\n path: '/dashboard',\n isModuleHome: true,\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'list',\n pageLabel: 'My Workflows',\n path: '/workflows/list',\n isModuleHome: true,\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'builder',\n pageLabel: 'Workflow Builder',\n path: '/workflows/workflow',\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'runs',\n pageLabel: 'Runs',\n path: '/workflows/runs',\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'history',\n pageLabel: 'History',\n path: '/workflows/history',\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'templates',\n pageLabel: 'Templates',\n path: '/workflows/templates',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'catalog',\n moduleLabel: 'Course Catalog',\n pageId: 'home',\n pageLabel: 'Catalog',\n path: '/catalog',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'catalog',\n moduleLabel: 'Course Catalog',\n pageId: 'detail',\n pageLabel: 'Course Detail',\n path: '/catalog/:courseId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'learning',\n moduleLabel: 'My Learning',\n pageId: 'home',\n pageLabel: 'My Learning',\n path: '/learning',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'learning',\n moduleLabel: 'My Learning',\n pageId: 'player',\n pageLabel: 'Lesson Player',\n path: '/learning/:enrollmentId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'paths',\n moduleLabel: 'Skill Journeys',\n pageId: 'home',\n pageLabel: 'Skill Journeys',\n path: '/paths',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'authoring',\n moduleLabel: 'Authoring',\n pageId: 'home',\n pageLabel: 'Authoring',\n path: '/authoring',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'authoring',\n moduleLabel: 'Authoring',\n pageId: 'builder',\n pageLabel: 'Course Builder',\n path: '/authoring/:courseId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'assessments',\n moduleLabel: 'Assessments',\n pageId: 'home',\n pageLabel: 'Assessments',\n path: '/assessments',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'assessments',\n moduleLabel: 'Assessments',\n pageId: 'detail',\n pageLabel: 'Assessment Detail',\n path: '/assessments/:assessmentId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'assessments',\n moduleLabel: 'Assessments',\n pageId: 'take',\n pageLabel: 'Take Assessment',\n path: '/assessments/:assessmentId/take',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'insights',\n moduleLabel: 'Insights',\n pageId: 'home',\n pageLabel: 'Insights',\n path: '/insights',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'submissions',\n moduleLabel: 'Submissions',\n pageId: 'home',\n pageLabel: 'Submissions',\n path: '/submissions',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'cohorts',\n moduleLabel: 'Cohorts',\n pageId: 'home',\n pageLabel: 'Cohorts',\n path: '/cohorts',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'skills',\n moduleLabel: 'Skills',\n pageId: 'home',\n pageLabel: 'Skills',\n path: '/skills',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'certificates',\n moduleLabel: 'Certificates',\n pageId: 'home',\n pageLabel: 'Certificates',\n path: '/certificates',\n isModuleHome: true,\n },\n // moduleId/pageId values match the shell's runtime route derivation\n // (deriveRouteContext): moduleId is the product slug, pageId the second\n // path segment ('home' at the root) — same convention as vibecontrols.\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'home',\n pageLabel: 'Dashboard',\n path: '/collabkin',\n isModuleHome: true,\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'trees',\n pageLabel: 'Family Trees',\n path: '/collabkin/trees',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'people',\n pageLabel: 'People',\n path: '/collabkin/people',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'memories',\n pageLabel: 'Memories',\n path: '/collabkin/memories',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'stories',\n pageLabel: 'Stories',\n path: '/collabkin/stories',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'timeline',\n pageLabel: 'Timeline',\n path: '/collabkin/timeline',\n },\n] as const;\n\nfunction normalizeCatalogValue(value: string | null | undefined): string | undefined {\n const normalized = value?.trim();\n return normalized ? normalized : undefined;\n}\n\nfunction getRouteCatalog(productId: string | null | undefined): TourRouteCatalogEntry[] {\n const productKey = normalizeCatalogValue(productId);\n if (!productKey) return [];\n return TOUR_ROUTE_CATALOG.filter((entry) => entry.productId === productKey);\n}\n\nfunction getTourRouteModules(productId: string | null | undefined): TourRouteModuleOption[] {\n const modules = new Map<string, TourRouteModuleOption>();\n\n for (const entry of getRouteCatalog(productId)) {\n const existing = modules.get(entry.moduleId);\n if (!existing || entry.isModuleHome) {\n modules.set(entry.moduleId, {\n moduleId: entry.moduleId,\n label: entry.moduleLabel,\n path: entry.path,\n });\n }\n }\n\n return [...modules.values()];\n}\n\nfunction getTourRoutePages(\n productId: string | null | undefined,\n moduleId: string | null | undefined\n): TourRoutePageOption[] {\n const moduleKey = normalizeCatalogValue(moduleId);\n if (!moduleKey) return [];\n\n return getRouteCatalog(productId).flatMap((entry) =>\n entry.moduleId === moduleKey\n ? [{ pageId: entry.pageId, label: entry.pageLabel, path: entry.path }]\n : []\n );\n}\n\n/**\n * Tour builder page\n * Create or edit tour metadata\n */\nexport function TourBuilderPage() {\n const { tr } = useToursI18n();\n const { tourId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const globalClient = useGatewayClient('global');\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const isNew = !tourId || tourId === 'new';\n const canCreate = hasPermission(TOUR_PERMISSIONS.TOUR_CREATE);\n const canUpdate = hasPermission(TOUR_PERMISSIONS.TOUR_UPDATE);\n const canCreateTemplate = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_CREATE);\n const canSave = isNew ? canCreate : canUpdate;\n const canSeedVibeControlsUserTours = canCreate && canUpdate;\n const canSeedBuildmyiqUserTours = canCreate && canUpdate;\n const canSeedBigconsoleUserTours = canCreate && canUpdate;\n\n const { tour, loadTour, createTour, updateTour, saving, validationErrors, error } =\n useTourBuilder(tourId);\n const { saveAsTemplate } = useTourLifecycle();\n const { data: productsData, loading: productsLoading } = useGetPlatformProductsForToursQuery({\n variables: { limit: PLATFORM_PRODUCTS_QUERY_LIMIT, offset: 0 },\n fetchPolicy: 'cache-and-network',\n skip: permissionsLoading || !canSave,\n });\n\n const defaultFormData = {\n name: '',\n description: '',\n productId: '',\n moduleId: '',\n pageId: '',\n isActive: true,\n status: 'DRAFT' as const,\n triggerType: 'MANUAL' as const,\n priority: 50,\n displayOrder: 1,\n startDate: '',\n endDate: '',\n };\n\n const [formData, setFormData] = useState<Partial<Tour>>(defaultFormData);\n const [customProductEntry, setCustomProductEntry] = useState(false);\n const [customModuleEntry, setCustomModuleEntry] = useState(false);\n const [customPageEntry, setCustomPageEntry] = useState(false);\n const [seedLoading, setSeedLoading] = useState(false);\n const [seedResult, setSeedResult] = useState<SeedVibeControlsUserToursPayload | null>(null);\n const [seedError, setSeedError] = useState<string | null>(null);\n const [seedBuildmyiqLoading, setSeedBuildmyiqLoading] = useState(false);\n const [seedBuildmyiqResult, setSeedBuildmyiqResult] =\n useState<SeedVibeControlsUserToursPayload | null>(null);\n const [seedBuildmyiqError, setSeedBuildmyiqError] = useState<string | null>(null);\n const [seedBigconsoleLoading, setSeedBigconsoleLoading] = useState(false);\n const [seedBigconsoleResult, setSeedBigconsoleResult] =\n useState<SeedVibeControlsUserToursPayload | null>(null);\n const [seedBigconsoleError, setSeedBigconsoleError] = useState<string | null>(null);\n\n const formInitializedRef = useRef(false);\n\n const productOptions = useMemo(() => {\n return [...(productsData?.platformProducts.items ?? [])].sort((a, b) =>\n a.name.localeCompare(b.name)\n );\n }, [productsData]);\n\n const productOptionSlugs = useMemo(\n () => new Set(productOptions.map((product) => product.slug)),\n [productOptions]\n );\n\n const moduleOptions = useMemo(\n () => getTourRouteModules(formData.productId),\n [formData.productId]\n );\n\n const moduleOptionIds = useMemo(\n () => new Set(moduleOptions.map((moduleOption) => moduleOption.moduleId)),\n [moduleOptions]\n );\n\n const pageOptions = useMemo(\n () => getTourRoutePages(formData.productId, formData.moduleId),\n [formData.moduleId, formData.productId]\n );\n\n const pageOptionIds = useMemo(\n () => new Set(pageOptions.map((pageOption) => pageOption.pageId)),\n [pageOptions]\n );\n\n useEffect(() => {\n if (formInitializedRef.current) return;\n if (!permissionsLoading && canSave && tourId && tourId !== 'new') {\n loadTour(tourId);\n }\n }, [canSave, loadTour, permissionsLoading, tourId]);\n\n useEffect(() => {\n if (tour && !formInitializedRef.current) {\n setFormData(tour);\n formInitializedRef.current = true;\n }\n }, [tour]);\n\n useEffect(() => {\n if (!formData.productId || productsLoading) {\n setCustomProductEntry(false);\n return;\n }\n setCustomProductEntry(productOptions.length > 0 && !productOptionSlugs.has(formData.productId));\n }, [formData.productId, productOptionSlugs, productOptions.length, productsLoading]);\n\n useEffect(() => {\n if (!formData.productId || !formData.moduleId) {\n setCustomModuleEntry(false);\n return;\n }\n setCustomModuleEntry(moduleOptions.length === 0 || !moduleOptionIds.has(formData.moduleId));\n }, [formData.moduleId, formData.productId, moduleOptionIds, moduleOptions.length]);\n\n useEffect(() => {\n if (!formData.productId || !formData.pageId) {\n setCustomPageEntry(false);\n return;\n }\n setCustomPageEntry(pageOptions.length === 0 || !pageOptionIds.has(formData.pageId));\n }, [formData.pageId, formData.productId, pageOptionIds, pageOptions.length]);\n\n const showCurrentProductOption = Boolean(\n formData.productId && !customProductEntry && !productOptionSlugs.has(formData.productId)\n );\n const showCurrentModuleOption = Boolean(\n formData.moduleId && !customModuleEntry && !moduleOptionIds.has(formData.moduleId)\n );\n const showCurrentPageOption = Boolean(\n formData.pageId && !customPageEntry && !pageOptionIds.has(formData.pageId)\n );\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n if (tourId && tourId !== 'new') {\n await updateTour(tourId, formData);\n } else {\n await createTour(formData);\n }\n } catch {\n emitToursApiFailed(eventBus, 'tour.save', toursContext);\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/list`);\n };\n\n const formatDateTimeLocal = (value?: string) => {\n if (!value) return '';\n\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n\n return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().slice(0, 16);\n };\n\n const handleSaveTemplate = async () => {\n if (!tourId || tourId === 'new') return;\n\n const defaultName = tr('tourBuilder.templateDefaultName', '{{name}} Template', {\n name: formData.name || tour?.name || 'Tour',\n });\n const name = window.prompt(tr('tourBuilder.templateNamePrompt', 'Template name'), defaultName);\n if (!name) return;\n\n const category =\n window.prompt(\n tr(\n 'tourBuilder.templateCategoryPrompt',\n 'Template category (ONBOARDING, FEATURE_ANNOUNCEMENT, HELP, CUSTOM)'\n ),\n 'ONBOARDING'\n ) || 'ONBOARDING';\n\n try {\n await saveAsTemplate(tourId, {\n name,\n description: formData.description,\n category,\n isPublic: false,\n });\n navigate(`${basePath}/templates`);\n } catch {\n emitToursApiFailed(eventBus, 'template.save', toursContext);\n }\n };\n\n const handleSeedVibeControlsUserTours = async () => {\n const confirmed = window.confirm(tr('tourBuilder.seedVibeControls.confirm'));\n if (!confirmed) return;\n\n setSeedLoading(true);\n setSeedResult(null);\n setSeedError(null);\n\n try {\n const result = await globalClient.mutate<SeedVibeControlsUserToursMutation>({\n mutation: SEED_VIBECONTROLS_USER_TOURS,\n });\n\n const mutationErrorMessage = getMutationErrorMessage(\n result,\n tr('tourBuilder.seedMutationErrors', 'Seed mutation returned errors.')\n );\n if (mutationErrorMessage) {\n throw new Error(mutationErrorMessage);\n }\n\n const payload = result.data?.seedVibeControlsUserTours;\n if (!payload) {\n throw new Error(\n tr('tourBuilder.seedMutationNoResult', 'Seed mutation returned no result.')\n );\n }\n\n setSeedResult(payload);\n } catch (seedMutationError) {\n const errorMessage =\n seedMutationError instanceof Error\n ? seedMutationError.message\n : tr('tourBuilder.seedVibeControls.error');\n setSeedError(errorMessage);\n eventBus.emit(\n 'tours.api.failed',\n {\n route: '/tours',\n operation: 'tour.seed_vibecontrols_user_tours',\n errorMessage,\n productId: toursContext.productId,\n workspaceId: toursContext.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n } finally {\n setSeedLoading(false);\n }\n };\n\n const handleSeedBuildmyiqUserTours = async () => {\n const confirmed = window.confirm(tr('tourBuilder.seedBuildmyiq.confirm'));\n if (!confirmed) return;\n\n setSeedBuildmyiqLoading(true);\n setSeedBuildmyiqResult(null);\n setSeedBuildmyiqError(null);\n\n try {\n const result = await globalClient.mutate<SeedBuildmyiqUserToursMutation>({\n mutation: SEED_BUILDMYIQ_USER_TOURS,\n });\n\n const mutationErrorMessage = getMutationErrorMessage(\n result,\n tr('tourBuilder.seedMutationErrors', 'Seed mutation returned errors.')\n );\n if (mutationErrorMessage) {\n throw new Error(mutationErrorMessage);\n }\n\n const payload = result.data?.seedBuildmyiqUserTours;\n if (!payload) {\n throw new Error(\n tr('tourBuilder.seedMutationNoResult', 'Seed mutation returned no result.')\n );\n }\n\n setSeedBuildmyiqResult(payload);\n } catch (seedMutationError) {\n const errorMessage =\n seedMutationError instanceof Error\n ? seedMutationError.message\n : tr('tourBuilder.seedBuildmyiq.error');\n setSeedBuildmyiqError(errorMessage);\n eventBus.emit(\n 'tours.api.failed',\n {\n route: '/tours',\n operation: 'tour.seed_buildmyiq_user_tours',\n errorMessage,\n productId: toursContext.productId,\n workspaceId: toursContext.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n } finally {\n setSeedBuildmyiqLoading(false);\n }\n };\n\n const handleSeedBigconsoleUserTours = async () => {\n const confirmed = window.confirm(tr('tourBuilder.seedBigconsole.confirm'));\n if (!confirmed) return;\n\n setSeedBigconsoleLoading(true);\n setSeedBigconsoleResult(null);\n setSeedBigconsoleError(null);\n\n try {\n const result = await globalClient.mutate<SeedBigconsoleUserToursMutation>({\n mutation: SEED_BIGCONSOLE_USER_TOURS,\n });\n\n const mutationErrorMessage = getMutationErrorMessage(\n result,\n tr('tourBuilder.seedMutationErrors', 'Seed mutation returned errors.')\n );\n if (mutationErrorMessage) {\n throw new Error(mutationErrorMessage);\n }\n\n const payload = result.data?.seedBigconsoleUserTours;\n if (!payload) {\n throw new Error(\n tr('tourBuilder.seedMutationNoResult', 'Seed mutation returned no result.')\n );\n }\n\n setSeedBigconsoleResult(payload);\n } catch (seedMutationError) {\n const errorMessage =\n seedMutationError instanceof Error\n ? seedMutationError.message\n : tr('tourBuilder.seedBigconsole.error');\n setSeedBigconsoleError(errorMessage);\n eventBus.emit(\n 'tours.api.failed',\n {\n route: '/tours',\n operation: 'tour.seed_bigconsole_user_tours',\n errorMessage,\n productId: toursContext.productId,\n workspaceId: toursContext.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n } finally {\n setSeedBigconsoleLoading(false);\n }\n };\n\n const secondaryActions: CTAAction[] =\n tourId && tourId !== 'new'\n ? [\n {\n label:\n tour?.steps && tour.steps.length > 0\n ? `${tr('tourBuilder.editSteps')} (${tour.steps.length})`\n : tr('tourBuilder.editSteps'),\n onSelect: () => navigate(`${basePath}/${tourId}/steps`),\n icon: <ListOrdered size={16} />,\n },\n {\n label: tr('tourBuilder.preview'),\n onSelect: () => navigate(`${basePath}/${tourId}/preview`),\n icon: <Eye size={16} />,\n },\n {\n label: tr('tourBuilder.analytics'),\n onSelect: () => navigate(`${basePath}/${tourId}/analytics`),\n icon: <BarChart3 size={16} />,\n },\n {\n label: tr('tourBuilder.schedule', 'Schedule'),\n onSelect: () => navigate(`${basePath}/${tourId}/schedule`),\n icon: <Calendar size={16} />,\n },\n ...(canCreateTemplate\n ? [\n {\n label: tr('tourBuilder.saveAsTemplate', 'Save as Template'),\n onSelect: () => void handleSaveTemplate(),\n icon: <LayoutTemplate size={16} />,\n } satisfies CTAAction,\n ]\n : []),\n ]\n : [];\n\n // Concrete, real-handler next steps for the (complex) edit view so a new\n // author isn't left guessing what to do after saving the tour's metadata.\n const isExistingTour = Boolean(tourId && tourId !== 'new');\n const nextSteps: NextStep[] =\n isExistingTour && canUpdate\n ? [\n {\n id: 'edit-steps',\n label: tr('tourBuilder.nextStepEditSteps'),\n description: tr('tourBuilder.nextStepEditStepsDesc'),\n icon: <ListOrdered size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/steps`),\n },\n {\n id: 'preview',\n label: tr('tourBuilder.nextStepPreview'),\n description: tr('tourBuilder.nextStepPreviewDesc'),\n icon: <Eye size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/preview`),\n },\n {\n id: 'schedule',\n label: tr('tourBuilder.nextStepSchedule'),\n description: tr('tourBuilder.nextStepScheduleDesc'),\n icon: <Calendar size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/schedule`),\n },\n {\n id: 'analytics',\n label: tr('tourBuilder.nextStepAnalytics'),\n description: tr('tourBuilder.nextStepAnalyticsDesc'),\n icon: <BarChart3 size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/analytics`),\n },\n ]\n : [];\n\n if (permissionsLoading) {\n return (\n <div className=\"flex h-full items-center justify-center p-3 sm:p-6\">\n <p className=\"text-sm text-text-secondary\">{tr('tourBuilder.loading')}</p>\n </div>\n );\n }\n\n if (!canSave) {\n return (\n <div className=\"flex h-full items-center justify-center p-3 sm:p-6\">\n <div className=\"text-center space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('tourBuilder.accessDeniedTitle')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {isNew\n ? tr('tourBuilder.createAccessDeniedMessage')\n : tr('tourBuilder.updateAccessDeniedMessage')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center justify-center rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent\"\n >\n {tr('tourBuilder.goBack')}\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-4xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 hover:bg-accent rounded-md\"\n aria-label={tr('tourBuilder.goBack')}\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex-1\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n {tourId && tourId !== 'new'\n ? tr('tourBuilder.titleEdit')\n : tr('tourBuilder.titleCreate')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{tr('tourBuilder.subtitle')}</p>\n </div>\n </div>\n\n <PagePurpose>{tr('tourBuilder.purpose')}</PagePurpose>\n\n {nextSteps.length > 0 ? (\n <NextSteps\n storageKey=\"tours-builder\"\n title={tr('tourBuilder.nextStepsTitle')}\n steps={nextSteps}\n />\n ) : null}\n\n {isNew ? (\n <>\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('tourBuilder.seedVibeControls.title')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourBuilder.seedVibeControls.description')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void handleSeedVibeControlsUserTours()}\n disabled={seedLoading || !canSeedVibeControlsUserTours}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-border-subtle bg-secondary px-4 py-2 text-sm font-medium text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Sparkles size={18} />\n {seedLoading\n ? tr('tourBuilder.seedVibeControls.loading')\n : tr('tourBuilder.seedVibeControls.button')}\n </button>\n </div>\n {!canSeedVibeControlsUserTours ? (\n <p className=\"mt-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedVibeControls.permissionHint')}\n </p>\n ) : null}\n {seedError ? <p className=\"mt-3 text-sm text-status-error-text\">{seedError}</p> : null}\n {seedResult ? (\n <div className=\"mt-3 rounded-md border border-border-seam bg-bg-surface p-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedVibeControls.result', {\n totalCount: seedResult.totalCount,\n productId: seedResult.productId,\n workspaceId: seedResult.workspaceId,\n createdCount: seedResult.createdCount,\n updatedCount: seedResult.updatedCount,\n })}\n </div>\n ) : null}\n </EmphasisPanel>\n\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('tourBuilder.seedBuildmyiq.title')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourBuilder.seedBuildmyiq.description')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void handleSeedBuildmyiqUserTours()}\n disabled={seedBuildmyiqLoading || !canSeedBuildmyiqUserTours}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-border-subtle bg-secondary px-4 py-2 text-sm font-medium text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Sparkles size={18} />\n {seedBuildmyiqLoading\n ? tr('tourBuilder.seedBuildmyiq.loading')\n : tr('tourBuilder.seedBuildmyiq.button')}\n </button>\n </div>\n {!canSeedBuildmyiqUserTours ? (\n <p className=\"mt-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBuildmyiq.permissionHint')}\n </p>\n ) : null}\n {seedBuildmyiqError ? (\n <p className=\"mt-3 text-sm text-status-error-text\">{seedBuildmyiqError}</p>\n ) : null}\n {seedBuildmyiqResult ? (\n <div className=\"mt-3 rounded-md border border-border-seam bg-bg-surface p-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBuildmyiq.result', {\n totalCount: seedBuildmyiqResult.totalCount,\n productId: seedBuildmyiqResult.productId,\n workspaceId: seedBuildmyiqResult.workspaceId,\n createdCount: seedBuildmyiqResult.createdCount,\n updatedCount: seedBuildmyiqResult.updatedCount,\n })}\n </div>\n ) : null}\n </EmphasisPanel>\n\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('tourBuilder.seedBigconsole.title')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourBuilder.seedBigconsole.description')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void handleSeedBigconsoleUserTours()}\n disabled={seedBigconsoleLoading || !canSeedBigconsoleUserTours}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-border-subtle bg-secondary px-4 py-2 text-sm font-medium text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Sparkles size={18} />\n {seedBigconsoleLoading\n ? tr('tourBuilder.seedBigconsole.loading')\n : tr('tourBuilder.seedBigconsole.button')}\n </button>\n </div>\n {!canSeedBigconsoleUserTours ? (\n <p className=\"mt-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBigconsole.permissionHint')}\n </p>\n ) : null}\n {seedBigconsoleError ? (\n <p className=\"mt-3 text-sm text-status-error-text\">{seedBigconsoleError}</p>\n ) : null}\n {seedBigconsoleResult ? (\n <div className=\"mt-3 rounded-md border border-border-seam bg-bg-surface p-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBigconsole.result', {\n totalCount: seedBigconsoleResult.totalCount,\n productId: seedBigconsoleResult.productId,\n workspaceId: seedBigconsoleResult.workspaceId,\n createdCount: seedBigconsoleResult.createdCount,\n updatedCount: seedBigconsoleResult.updatedCount,\n })}\n </div>\n ) : null}\n </EmphasisPanel>\n </>\n ) : null}\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Basic Info */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4\">{tr('tourBuilder.sectionBasicInfo')}</h2>\n\n <div className=\"space-y-4\">\n {/* Name */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"tour-name\">\n {tr('tourBuilder.labelName')}\n </label>\n <input\n id=\"tour-name\"\n type=\"text\"\n value={formData.name || ''}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n required\n aria-label={tr('tourBuilder.labelName')}\n />\n </div>\n\n {/* Description */}\n <MarkdownEditor\n id=\"tour-description\"\n label={tr('tourBuilder.labelDescription')}\n value={formData.description || ''}\n onChange={(description) => setFormData({ ...formData, description })}\n rows={4}\n aria-label={tr('tourBuilder.labelDescription')}\n />\n\n {/* Product ID */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"product-id\">\n {tr('tourBuilder.labelProductId')}\n </label>\n <select\n id=\"product-id\"\n value={customProductEntry ? '__custom__' : formData.productId || ''}\n onChange={(e) => {\n if (e.target.value === '__custom__') {\n setCustomProductEntry(true);\n setFormData({\n ...formData,\n productId: '',\n moduleId: undefined,\n pageId: undefined,\n });\n return;\n }\n\n setCustomProductEntry(false);\n setCustomModuleEntry(false);\n setCustomPageEntry(false);\n setFormData({\n ...formData,\n productId: e.target.value,\n moduleId: undefined,\n pageId: undefined,\n });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n required\n aria-label={tr('tourBuilder.labelProductId')}\n >\n <option value=\"\">{tr('tourBuilder.selectProduct', 'Select product')}</option>\n {productsLoading ? (\n <option disabled>{tr('tourBuilder.loadingProducts', 'Loading products…')}</option>\n ) : null}\n {productOptions.map((product) => (\n <option key={product.id} value={product.slug}>\n {product.name} ({product.slug})\n </option>\n ))}\n {showCurrentProductOption ? (\n <option value={formData.productId}>\n {tr('tourBuilder.currentValue', 'Current: {{value}}', {\n value: formData.productId ?? '',\n })}\n </option>\n ) : null}\n <option value=\"__custom__\">\n {tr('tourBuilder.customProductSlug', 'Custom product slug')}\n </option>\n </select>\n {customProductEntry ? (\n <input\n id=\"product-id-custom\"\n aria-label={tr('tourBuilder.customProductSlug', 'Custom product slug')}\n type=\"text\"\n value={formData.productId || ''}\n onChange={(e) =>\n setFormData({\n ...formData,\n productId: e.target.value,\n moduleId: undefined,\n pageId: undefined,\n })\n }\n className=\"mt-2 w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n placeholder={tr('tourBuilder.productSlugPlaceholder', 'vibecontrols')}\n required\n />\n ) : null}\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"module-id\">\n {tr('tourBuilder.labelModuleId', 'Module ID')}\n </label>\n <select\n id=\"module-id\"\n value={customModuleEntry ? '__custom__' : formData.moduleId || ''}\n onChange={(e) => {\n if (e.target.value === '__custom__') {\n setCustomModuleEntry(true);\n setFormData({ ...formData, moduleId: '', pageId: undefined });\n return;\n }\n\n setCustomModuleEntry(false);\n setCustomPageEntry(false);\n setFormData({\n ...formData,\n moduleId: e.target.value || undefined,\n pageId: undefined,\n });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n disabled={!formData.productId}\n aria-label={tr('tourBuilder.labelModuleId', 'Module ID')}\n >\n <option value=\"\">{tr('tourBuilder.allModules', 'All modules')}</option>\n {moduleOptions.map((moduleOption) => (\n <option key={moduleOption.moduleId} value={moduleOption.moduleId}>\n {moduleOption.label} ({moduleOption.moduleId})\n </option>\n ))}\n {showCurrentModuleOption ? (\n <option value={formData.moduleId}>\n {tr('tourBuilder.currentValue', 'Current: {{value}}', {\n value: formData.moduleId ?? '',\n })}\n </option>\n ) : null}\n <option value=\"__custom__\">\n {tr('tourBuilder.customModuleId', 'Custom module ID')}\n </option>\n </select>\n {customModuleEntry ? (\n <input\n id=\"module-id-custom\"\n aria-label={tr('tourBuilder.customModuleId', 'Custom module ID')}\n type=\"text\"\n value={formData.moduleId || ''}\n onChange={(e) =>\n setFormData({\n ...formData,\n moduleId: e.target.value || undefined,\n pageId: undefined,\n })\n }\n className=\"mt-2 w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n placeholder={tr('tourBuilder.moduleSlugPlaceholder', 'vibecontrols')}\n />\n ) : null}\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"page-id\">\n {tr('tourBuilder.labelPageId', 'Page ID')}\n </label>\n <select\n id=\"page-id\"\n value={customPageEntry ? '__custom__' : formData.pageId || ''}\n onChange={(e) => {\n if (e.target.value === '__custom__') {\n setCustomPageEntry(true);\n setFormData({ ...formData, pageId: '' });\n return;\n }\n\n setCustomPageEntry(false);\n setFormData({ ...formData, pageId: e.target.value || undefined });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n disabled={!formData.productId}\n aria-label={tr('tourBuilder.labelPageId', 'Page ID')}\n >\n <option value=\"\">{tr('tourBuilder.allPages', 'All pages')}</option>\n {pageOptions.map((pageOption) => (\n <option key={pageOption.pageId} value={pageOption.pageId}>\n {pageOption.label} ({pageOption.pageId})\n </option>\n ))}\n {showCurrentPageOption ? (\n <option value={formData.pageId}>\n {tr('tourBuilder.currentValue', 'Current: {{value}}', {\n value: formData.pageId ?? '',\n })}\n </option>\n ) : null}\n <option value=\"__custom__\">\n {tr('tourBuilder.customPageId', 'Custom page ID')}\n </option>\n </select>\n {customPageEntry ? (\n <input\n id=\"page-id-custom\"\n aria-label={tr('tourBuilder.customPageId', 'Custom page ID')}\n type=\"text\"\n value={formData.pageId || ''}\n onChange={(e) =>\n setFormData({ ...formData, pageId: e.target.value || undefined })\n }\n className=\"mt-2 w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n placeholder={tr('tourBuilder.pageSlugPlaceholder', 'agents')}\n />\n ) : null}\n </div>\n </div>\n\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={formData.isActive ?? true}\n onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}\n className=\"size-4 rounded\"\n aria-label={tr('tourBuilder.labelActive', 'Active')}\n />\n <span className=\"text-sm font-medium\">{tr('tourBuilder.labelActive', 'Active')}</span>\n </label>\n </div>\n </GlassCard>\n\n {/* Trigger Settings */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4\">{tr('tourBuilder.sectionTriggerSettings')}</h2>\n\n <div className=\"space-y-4\">\n {/* Trigger Type */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"trigger-type\">\n {tr('tourBuilder.labelTriggerType')}\n </label>\n <select\n id=\"trigger-type\"\n value={formData.triggerType || 'MANUAL'}\n onChange={(e) =>\n setFormData({ ...formData, triggerType: e.target.value as TourTriggerType })\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelTriggerType')}\n >\n <option value=\"MANUAL\">{tr('tourBuilder.triggerManual')}</option>\n <option value=\"AUTO_FIRST_VISIT\">{tr('tourBuilder.triggerAutoFirstVisit')}</option>\n <option value=\"AUTO_ALWAYS\">{tr('tourBuilder.triggerAutoAlways')}</option>\n <option value=\"ON_EVENT\">{tr('tourBuilder.triggerOnEvent')}</option>\n <option value=\"SCHEDULED\">{tr('tourBuilder.triggerScheduled')}</option>\n </select>\n </div>\n\n {/* Priority */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"priority\">\n {tr('tourBuilder.labelPriority')}\n </label>\n <input\n id=\"priority\"\n type=\"number\"\n min=\"1\"\n max=\"100\"\n value={formData.priority || 50}\n onChange={(e) => {\n const priority = Number.parseInt(e.target.value, 10) || 50;\n setFormData({ ...formData, priority });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelPriority')}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"tour-start-date\">\n {tr('tourBuilder.labelStartDate', 'Start Date')}\n </label>\n <input\n id=\"tour-start-date\"\n type=\"datetime-local\"\n value={formatDateTimeLocal(formData.startDate)}\n onChange={(e) =>\n setFormData({\n ...formData,\n startDate: e.target.value\n ? new Date(e.target.value).toISOString()\n : undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelStartDate', 'Start Date')}\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"tour-end-date\">\n {tr('tourBuilder.labelEndDate', 'End Date')}\n </label>\n <input\n id=\"tour-end-date\"\n type=\"datetime-local\"\n value={formatDateTimeLocal(formData.endDate)}\n onChange={(e) =>\n setFormData({\n ...formData,\n endDate: e.target.value ? new Date(e.target.value).toISOString() : undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelEndDate', 'End Date')}\n />\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* API Error */}\n {error && (\n <div className=\"border border-status-error-border rounded-md p-4 bg-status-error-bg/10\">\n <h3 className=\"font-semibold text-status-error-text mb-2\">\n {tr('tourBuilder.errorTitle')}\n </h3>\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n </div>\n )}\n\n {/* Validation Errors */}\n {validationErrors.length > 0 && (\n <div className=\"border border-status-error-border rounded-md p-4 bg-status-error-bg/10\">\n <h3 className=\"font-semibold text-status-error-text mb-2\">\n {tr('tourBuilder.validationErrorsTitle')}\n </h3>\n <ul className=\"list-disc list-inside space-y-1\">\n {validationErrors.map((err) => (\n <li key={err.field} className=\"text-sm text-status-error-text\">\n {tr(err.message, err.params)}\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {/* Actions — one primary submit CTA, related navigation collapsed into the overflow menu */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <PermissionGate\n permission={isNew ? TOUR_PERMISSIONS.TOUR_CREATE : TOUR_PERMISSIONS.TOUR_UPDATE}\n >\n <Button\n type=\"submit\"\n disabled={saving || !canSave}\n className=\"flex-1 min-w-[200px] min-h-[44px]\"\n >\n <Save size={20} />\n {saving\n ? tr('tourBuilder.saving')\n : tourId && tourId !== 'new'\n ? tr('tourBuilder.updateTour')\n : tr('tourBuilder.createAndAddSteps')}\n </Button>\n </PermissionGate>\n {tourId && tourId !== 'new' && secondaryActions.length > 0 && (\n <CTAOverflowMenu actions={secondaryActions} />\n )}\n </div>\n </form>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAsCA,IAAM,KAAgC,KAEhC,KAA+B,CAAG;;;;;;;;;;;;;;;;;GAmBlC,KAA4B,CAAG;;;;;;;;;;;;;;;;;GAmB/B,KAA6B,CAAG;;;;;;;;;;;;;;;;;;AAgDtC,SAAS,EAAwB,GAAiB,GAAwC;AACxF,KAAI,CAAC,KAAU,OAAO,KAAW,SAAU,QAAO;CAElD,IAAM,IAAS,GACT,IAAc,EAAO;AAC3B,KAAI,aAAuB,MAAO,QAAO,EAAY;CAErD,IAAM,IAAS,EAAO;AACtB,KAAI,CAAC,MAAM,QAAQ,EAAO,IAAI,EAAO,WAAW,EAAG,QAAO;CAE1D,IAAM,IAAW,EACd,KAAK,MAAU;AACd,MAAI,aAAiB,MAAO,QAAO,EAAM;AACzC,MAAI,CAAC,KAAS,OAAO,KAAU,SAAU,QAAO;EAEhD,IAAM,IAAW,EAAgC;AACjD,SAAO,OAAO,KAAY,WAAW,IAAU;GAC/C,CACD,QAAQ,MAA+B,EAAQ,EAAS;AAE3D,QAAO,EAAS,SAAS,IAAI,EAAS,KAAK,KAAK,GAAG;;AAyBrD,IAAM,IAAuD;CAC3D;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CAID;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACF;AAED,SAAS,EAAsB,GAAsD;AAEnF,QADmB,GAAO,MAAM,IACC,KAAA;;AAGnC,SAAS,EAAgB,GAA+D;CACtF,IAAM,IAAa,EAAsB,EAAU;AAEnD,QADK,IACE,EAAmB,QAAQ,MAAU,EAAM,cAAc,EAAW,GADnD,EAAE;;AAI5B,SAAS,GAAoB,GAA+D;CAC1F,IAAM,oBAAU,IAAI,KAAoC;AAExD,MAAK,IAAM,KAAS,EAAgB,EAAU,CAE5C,EAAI,CADa,EAAQ,IAAI,EAAM,SAAS,IAC3B,EAAM,iBACrB,EAAQ,IAAI,EAAM,UAAU;EAC1B,UAAU,EAAM;EAChB,OAAO,EAAM;EACb,MAAM,EAAM;EACb,CAAC;AAIN,QAAO,CAAC,GAAG,EAAQ,QAAQ,CAAC;;AAG9B,SAAS,GACP,GACA,GACuB;CACvB,IAAM,IAAY,EAAsB,EAAS;AAGjD,QAFK,IAEE,EAAgB,EAAU,CAAC,SAAS,MACzC,EAAM,aAAa,IACf,CAAC;EAAE,QAAQ,EAAM;EAAQ,OAAO,EAAM;EAAW,MAAM,EAAM;EAAM,CAAC,GACpE,EAAE,CACP,GANsB,EAAE;;AAa3B,SAAgB,IAAkB;CAChC,IAAM,EAAE,UAAO,GAAc,EACvB,EAAE,cAAW,IAAW,EACxB,IAAW,IAAa,EACxB,IAAe,GAAiB,EAChC,EAAE,cAAW,QAAQ,GACrB,IAAW,IAAa,EACxB,IAAe,GAAiB,SAAS,EACzC,EAAE,kBAAe,WAAW,MAAuB,GAA0B,EAC7E,IAAQ,CAAC,KAAU,MAAW,OAC9B,IAAY,EAAc,EAAiB,YAAY,EACvD,IAAY,EAAc,EAAiB,YAAY,EACvD,KAAoB,EAAc,EAAiB,qBAAqB,EACxE,IAAU,IAAQ,IAAY,GAC9B,IAA+B,KAAa,GAC5C,IAA4B,KAAa,GACzC,IAA6B,KAAa,GAE1C,EAAE,SAAM,aAAU,gBAAY,gBAAY,WAAQ,qBAAkB,cACxE,GAAe,EAAO,EAClB,EAAE,uBAAmB,GAAkB,EACvC,EAAE,MAAM,IAAc,SAAS,MAAoB,EAAoC;EAC3F,WAAW;GAAE,OAAO;GAA+B,QAAQ;GAAG;EAC9D,aAAa;EACb,MAAM,KAAsB,CAAC;EAC9B,CAAC,EAiBI,CAAC,GAAU,KAAe,EAfR;EACtB,MAAM;EACN,aAAa;EACb,WAAW;EACX,UAAU;EACV,QAAQ;EACR,UAAU;EACV,QAAQ;EACR,aAAa;EACb,UAAU;EACV,cAAc;EACd,WAAW;EACX,SAAS;EACV,CAEuE,EAClE,CAAC,GAAoB,KAAyB,EAAS,GAAM,EAC7D,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,IAAa,MAAkB,EAAS,GAAM,EAC/C,CAAC,GAAY,MAAiB,EAAkD,KAAK,EACrF,CAAC,IAAW,MAAgB,EAAwB,KAAK,EACzD,CAAC,IAAsB,MAA2B,EAAS,GAAM,EACjE,CAAC,GAAqB,MAC1B,EAAkD,KAAK,EACnD,CAAC,IAAoB,MAAyB,EAAwB,KAAK,EAC3E,CAAC,IAAuB,MAA4B,EAAS,GAAM,EACnE,CAAC,GAAsB,MAC3B,EAAkD,KAAK,EACnD,CAAC,IAAqB,MAA0B,EAAwB,KAAK,EAE7E,IAAqB,GAAO,GAAM,EAElC,IAAiB,QACd,CAAC,GAAI,IAAc,iBAAiB,SAAS,EAAE,CAAE,CAAC,MAAM,GAAG,MAChE,EAAE,KAAK,cAAc,EAAE,KAAK,CAC7B,EACA,CAAC,GAAa,CAAC,EAEZ,IAAqB,QACnB,IAAI,IAAI,EAAe,KAAK,MAAY,EAAQ,KAAK,CAAC,EAC5D,CAAC,EAAe,CACjB,EAEK,IAAgB,QACd,GAAoB,EAAS,UAAU,EAC7C,CAAC,EAAS,UAAU,CACrB,EAEK,IAAkB,QAChB,IAAI,IAAI,EAAc,KAAK,MAAiB,EAAa,SAAS,CAAC,EACzE,CAAC,EAAc,CAChB,EAEK,IAAc,QACZ,GAAkB,EAAS,WAAW,EAAS,SAAS,EAC9D,CAAC,EAAS,UAAU,EAAS,UAAU,CACxC,EAEK,IAAgB,QACd,IAAI,IAAI,EAAY,KAAK,MAAe,EAAW,OAAO,CAAC,EACjE,CAAC,EAAY,CACd;AAgCD,CA9BA,QAAgB;AACV,IAAmB,WACnB,CAAC,KAAsB,KAAW,KAAU,MAAW,SACzD,EAAS,EAAO;IAEjB;EAAC;EAAS;EAAU;EAAoB;EAAO,CAAC,EAEnD,QAAgB;AACd,EAAI,KAAQ,CAAC,EAAmB,YAC9B,EAAY,EAAK,EACjB,EAAmB,UAAU;IAE9B,CAAC,EAAK,CAAC,EAEV,QAAgB;AACd,MAAI,CAAC,EAAS,aAAa,GAAiB;AAC1C,KAAsB,GAAM;AAC5B;;AAEF,IAAsB,EAAe,SAAS,KAAK,CAAC,EAAmB,IAAI,EAAS,UAAU,CAAC;IAC9F;EAAC,EAAS;EAAW;EAAoB,EAAe;EAAQ;EAAgB,CAAC,EAEpF,QAAgB;AACd,MAAI,CAAC,EAAS,aAAa,CAAC,EAAS,UAAU;AAC7C,KAAqB,GAAM;AAC3B;;AAEF,IAAqB,EAAc,WAAW,KAAK,CAAC,EAAgB,IAAI,EAAS,SAAS,CAAC;IAC1F;EAAC,EAAS;EAAU,EAAS;EAAW;EAAiB,EAAc;EAAO,CAAC,EAElF,QAAgB;AACd,MAAI,CAAC,EAAS,aAAa,CAAC,EAAS,QAAQ;AAC3C,KAAmB,GAAM;AACzB;;AAEF,IAAmB,EAAY,WAAW,KAAK,CAAC,EAAc,IAAI,EAAS,OAAO,CAAC;IAClF;EAAC,EAAS;EAAQ,EAAS;EAAW;EAAe,EAAY;EAAO,CAAC;CAE5E,IAAM,KAA2B,GAC/B,EAAS,aAAa,CAAC,KAAsB,CAAC,EAAmB,IAAI,EAAS,UAAU,GAEpF,KAA0B,GAC9B,EAAS,YAAY,CAAC,KAAqB,CAAC,EAAgB,IAAI,EAAS,SAAS,GAE9E,KAAwB,GAC5B,EAAS,UAAU,CAAC,KAAmB,CAAC,EAAc,IAAI,EAAS,OAAO,GAGtE,KAAe,OAAO,MAAuB;AACjD,IAAE,gBAAgB;AAClB,MAAI;AACF,GAAI,KAAU,MAAW,QACvB,MAAM,GAAW,GAAQ,EAAS,GAElC,MAAM,GAAW,EAAS;UAEtB;AACN,KAAmB,GAAU,aAAa,EAAa;;IAIrD,WAAmB;AACvB,IAAS,GAAG,EAAS,OAAO;IAGxB,MAAuB,MAAmB;AAC9C,MAAI,CAAC,EAAO,QAAO;EAEnB,IAAM,IAAO,IAAI,KAAK,EAAM;AAK5B,SAJI,OAAO,MAAM,EAAK,SAAS,CAAC,GACvB,sBAGF,IAAI,KAAK,EAAK,SAAS,GAAG,EAAK,mBAAmB,GAAG,IAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;IAGzF,KAAqB,YAAY;AACrC,MAAI,CAAC,KAAU,MAAW,MAAO;EAEjC,IAAM,IAAc,EAAG,mCAAmC,qBAAqB,EAC7E,MAAM,EAAS,QAAQ,GAAM,QAAQ,QACtC,CAAC,EACI,IAAO,OAAO,OAAO,EAAG,kCAAkC,gBAAgB,EAAE,EAAY;AAC9F,MAAI,CAAC,EAAM;EAEX,IAAM,IACJ,OAAO,OACL,EACE,sCACA,qEACD,EACD,aACD,IAAI;AAEP,MAAI;AAOF,GANA,MAAM,GAAe,GAAQ;IAC3B;IACA,aAAa,EAAS;IACtB;IACA,UAAU;IACX,CAAC,EACF,EAAS,GAAG,EAAS,YAAY;UAC3B;AACN,KAAmB,GAAU,iBAAiB,EAAa;;IAIzD,KAAkC,YAAY;AAChC,aAAO,QAAQ,EAAG,uCAAuC,CAAC,EAK5E;GAFA,GAAe,GAAK,EACpB,GAAc,KAAK,EACnB,GAAa,KAAK;AAElB,OAAI;IACF,IAAM,IAAS,MAAM,EAAa,OAA0C,EAC1E,UAAU,IACX,CAAC,EAEI,IAAuB,EAC3B,GACA,EAAG,kCAAkC,iCAAiC,CACvE;AACD,QAAI,EACF,OAAU,MAAM,EAAqB;IAGvC,IAAM,IAAU,EAAO,MAAM;AAC7B,QAAI,CAAC,EACH,OAAU,MACR,EAAG,oCAAoC,oCAAoC,CAC5E;AAGH,OAAc,EAAQ;YACf,GAAmB;IAC1B,IAAM,IACJ,aAA6B,QACzB,EAAkB,UAClB,EAAG,qCAAqC;AAE9C,IADA,GAAa,EAAa,EAC1B,EAAS,KACP,oBACA;KACE,OAAO;KACP,WAAW;KACX;KACA,WAAW,EAAa;KACxB,aAAa,EAAa;KAC3B,EACD,EAAE,WAAW,iBAAiB,CAC/B;aACO;AACR,OAAe,GAAM;;;IAInB,KAA+B,YAAY;AAC7B,aAAO,QAAQ,EAAG,oCAAoC,CAAC,EAKzE;GAFA,GAAwB,GAAK,EAC7B,GAAuB,KAAK,EAC5B,GAAsB,KAAK;AAE3B,OAAI;IACF,IAAM,IAAS,MAAM,EAAa,OAAuC,EACvE,UAAU,IACX,CAAC,EAEI,IAAuB,EAC3B,GACA,EAAG,kCAAkC,iCAAiC,CACvE;AACD,QAAI,EACF,OAAU,MAAM,EAAqB;IAGvC,IAAM,IAAU,EAAO,MAAM;AAC7B,QAAI,CAAC,EACH,OAAU,MACR,EAAG,oCAAoC,oCAAoC,CAC5E;AAGH,OAAuB,EAAQ;YACxB,GAAmB;IAC1B,IAAM,IACJ,aAA6B,QACzB,EAAkB,UAClB,EAAG,kCAAkC;AAE3C,IADA,GAAsB,EAAa,EACnC,EAAS,KACP,oBACA;KACE,OAAO;KACP,WAAW;KACX;KACA,WAAW,EAAa;KACxB,aAAa,EAAa;KAC3B,EACD,EAAE,WAAW,iBAAiB,CAC/B;aACO;AACR,OAAwB,GAAM;;;IAI5B,KAAgC,YAAY;AAC9B,aAAO,QAAQ,EAAG,qCAAqC,CAAC,EAK1E;GAFA,GAAyB,GAAK,EAC9B,GAAwB,KAAK,EAC7B,GAAuB,KAAK;AAE5B,OAAI;IACF,IAAM,IAAS,MAAM,EAAa,OAAwC,EACxE,UAAU,IACX,CAAC,EAEI,IAAuB,EAC3B,GACA,EAAG,kCAAkC,iCAAiC,CACvE;AACD,QAAI,EACF,OAAU,MAAM,EAAqB;IAGvC,IAAM,IAAU,EAAO,MAAM;AAC7B,QAAI,CAAC,EACH,OAAU,MACR,EAAG,oCAAoC,oCAAoC,CAC5E;AAGH,OAAwB,EAAQ;YACzB,GAAmB;IAC1B,IAAM,IACJ,aAA6B,QACzB,EAAkB,UAClB,EAAG,mCAAmC;AAE5C,IADA,GAAuB,EAAa,EACpC,EAAS,KACP,oBACA;KACE,OAAO;KACP,WAAW;KACX;KACA,WAAW,EAAa;KACxB,aAAa,EAAa;KAC3B,EACD,EAAE,WAAW,iBAAiB,CAC/B;aACO;AACR,OAAyB,GAAM;;;IAI7B,KACJ,KAAU,MAAW,QACjB;EACE;GACE,OACE,GAAM,SAAS,EAAK,MAAM,SAAS,IAC/B,GAAG,EAAG,wBAAwB,CAAC,IAAI,EAAK,MAAM,OAAO,KACrD,EAAG,wBAAwB;GACjC,gBAAgB,EAAS,GAAG,EAAS,GAAG,EAAO,QAAQ;GACvD,MAAM,kBAAC,IAAD,EAAa,MAAM,IAAM,CAAA;GAChC;EACD;GACE,OAAO,EAAG,sBAAsB;GAChC,gBAAgB,EAAS,GAAG,EAAS,GAAG,EAAO,UAAU;GACzD,MAAM,kBAAC,IAAD,EAAK,MAAM,IAAM,CAAA;GACxB;EACD;GACE,OAAO,EAAG,wBAAwB;GAClC,gBAAgB,EAAS,GAAG,EAAS,GAAG,EAAO,YAAY;GAC3D,MAAM,kBAAC,IAAD,EAAW,MAAM,IAAM,CAAA;GAC9B;EACD;GACE,OAAO,EAAG,wBAAwB,WAAW;GAC7C,gBAAgB,EAAS,GAAG,EAAS,GAAG,EAAO,WAAW;GAC1D,MAAM,kBAAC,IAAD,EAAU,MAAM,IAAM,CAAA;GAC7B;EACD,GAAI,KACA,CACE;GACE,OAAO,EAAG,8BAA8B,mBAAmB;GAC3D,gBAAgB,KAAK,IAAoB;GACzC,MAAM,kBAAC,IAAD,EAAgB,MAAM,IAAM,CAAA;GACnC,CACF,GACD,EAAE;EACP,GACD,EAAE,EAKF,KADyB,KAAU,MAAW,SAEhC,IACd;EACE;GACE,IAAI;GACJ,OAAO,EAAG,gCAAgC;GAC1C,aAAa,EAAG,oCAAoC;GACpD,MAAM,kBAAC,IAAD,EAAa,MAAM,IAAM,CAAA;GAC/B,eAAe,EAAS,GAAG,EAAS,GAAG,EAAO,QAAQ;GACvD;EACD;GACE,IAAI;GACJ,OAAO,EAAG,8BAA8B;GACxC,aAAa,EAAG,kCAAkC;GAClD,MAAM,kBAAC,IAAD,EAAK,MAAM,IAAM,CAAA;GACvB,eAAe,EAAS,GAAG,EAAS,GAAG,EAAO,UAAU;GACzD;EACD;GACE,IAAI;GACJ,OAAO,EAAG,+BAA+B;GACzC,aAAa,EAAG,mCAAmC;GACnD,MAAM,kBAAC,IAAD,EAAU,MAAM,IAAM,CAAA;GAC5B,eAAe,EAAS,GAAG,EAAS,GAAG,EAAO,WAAW;GAC1D;EACD;GACE,IAAI;GACJ,OAAO,EAAG,gCAAgC;GAC1C,aAAa,EAAG,oCAAoC;GACpD,MAAM,kBAAC,IAAD,EAAW,MAAM,IAAM,CAAA;GAC7B,eAAe,EAAS,GAAG,EAAS,GAAG,EAAO,YAAY;GAC3D;EACF,GACD,EAAE;AAoCR,QAlCI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAA+B,EAAG,sBAAsB;GAAK,CAAA;EACtE,CAAA,GAIL,IA2BH,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;KACV,cAAY,EAAG,qBAAqB;eAEpC,kBAAC,IAAD,EAAW,MAAM,IAAM,CAAA;KAChB,CAAA,EACT,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAER,EADH,KAAU,MAAW,QACf,0BACA,0BAA0B;MAC9B,CAAA,EACL,kBAAC,KAAD;MAAG,WAAU;gBAA+B,EAAG,uBAAuB;MAAK,CAAA,CACvE;OACF;;GAEN,kBAAC,IAAD,EAAA,UAAc,EAAG,sBAAsB,EAAe,CAAA;GAErD,GAAU,SAAS,IAClB,kBAAC,IAAD;IACE,YAAW;IACX,OAAO,EAAG,6BAA6B;IACvC,OAAO;IACP,CAAA,GACA;GAEH,IACC,kBAAA,IAAA,EAAA,UAAA;IACE,kBAAC,GAAD;KAAe,WAAU;eAAzB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,qCAAqC;SACtC,CAAA,EACL,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,2CAA2C;SAC7C,CAAA,CACA;WACN,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,KAAK,IAAiC;QACrD,UAAU,MAAe,CAAC;QAC1B,WAAU;kBAJZ,CAME,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EAElB,EADH,KACM,yCACA,sCAAsC,CACtC;UACL;;MACJ,IAIE,OAHF,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,8CAA8C;OAChD,CAAA;MAEL,KAAY,kBAAC,KAAD;OAAG,WAAU;iBAAuC;OAAc,CAAA,GAAG;MACjF,IACC,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAG,uCAAuC;QACzC,YAAY,EAAW;QACvB,WAAW,EAAW;QACtB,aAAa,EAAW;QACxB,cAAc,EAAW;QACzB,cAAc,EAAW;QAC1B,CAAC;OACE,CAAA,GACJ;MACU;;IAEhB,kBAAC,GAAD;KAAe,WAAU;eAAzB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,kCAAkC;SACnC,CAAA,EACL,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,wCAAwC;SAC1C,CAAA,CACA;WACN,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,KAAK,IAA8B;QAClD,UAAU,MAAwB,CAAC;QACnC,WAAU;kBAJZ,CAME,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EAElB,EADH,KACM,sCACA,mCAAmC,CACnC;UACL;;MACJ,IAIE,OAHF,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,2CAA2C;OAC7C,CAAA;MAEL,KACC,kBAAC,KAAD;OAAG,WAAU;iBAAuC;OAAuB,CAAA,GACzE;MACH,IACC,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAG,oCAAoC;QACtC,YAAY,EAAoB;QAChC,WAAW,EAAoB;QAC/B,aAAa,EAAoB;QACjC,cAAc,EAAoB;QAClC,cAAc,EAAoB;QACnC,CAAC;OACE,CAAA,GACJ;MACU;;IAEhB,kBAAC,GAAD;KAAe,WAAU;eAAzB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,mCAAmC;SACpC,CAAA,EACL,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,yCAAyC;SAC3C,CAAA,CACA;WACN,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,KAAK,IAA+B;QACnD,UAAU,MAAyB,CAAC;QACpC,WAAU;kBAJZ,CAME,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EAElB,EADH,KACM,uCACA,oCAAoC,CACpC;UACL;;MACJ,IAIE,OAHF,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,4CAA4C;OAC9C,CAAA;MAEL,KACC,kBAAC,KAAD;OAAG,WAAU;iBAAuC;OAAwB,CAAA,GAC1E;MACH,IACC,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAG,qCAAqC;QACvC,YAAY,EAAqB;QACjC,WAAW,EAAqB;QAChC,aAAa,EAAqB;QAClC,cAAc,EAAqB;QACnC,cAAc,EAAqB;QACpC,CAAC;OACE,CAAA,GACJ;MACU;;IACf,EAAA,CAAA,GACD;GAGJ,kBAAC,QAAD;IAAM,UAAU;IAAc,WAAU;cAAxC;KAEE,kBAAC,IAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA8B,EAAG,+BAA+B;OAAM,CAAA,EAEpF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SAAiC,SAAQ;mBACvD,EAAG,wBAAwB;SACtB,CAAA,EACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO,EAAS,QAAQ;SACxB,WAAW,MAAM,EAAY;UAAE,GAAG;UAAU,MAAM,EAAE,OAAO;UAAO,CAAC;SACnE,WAAU;SACV,UAAA;SACA,cAAY,EAAG,wBAAwB;SACvC,CAAA,CACE,EAAA,CAAA;QAGN,kBAAC,IAAD;SACE,IAAG;SACH,OAAO,EAAG,+BAA+B;SACzC,OAAO,EAAS,eAAe;SAC/B,WAAW,MAAgB,EAAY;UAAE,GAAG;UAAU;UAAa,CAAC;SACpE,MAAM;SACN,cAAY,EAAG,+BAA+B;SAC9C,CAAA;QAGF,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UAAO,WAAU;UAAiC,SAAQ;oBACvD,EAAG,6BAA6B;UAC3B,CAAA;SACR,kBAAC,UAAD;UACE,IAAG;UACH,OAAO,IAAqB,eAAe,EAAS,aAAa;UACjE,WAAW,MAAM;AACf,eAAI,EAAE,OAAO,UAAU,cAAc;AAEnC,YADA,EAAsB,GAAK,EAC3B,EAAY;aACV,GAAG;aACH,WAAW;aACX,UAAU,KAAA;aACV,QAAQ,KAAA;aACT,CAAC;AACF;;AAMF,WAHA,EAAsB,GAAM,EAC5B,EAAqB,GAAM,EAC3B,EAAmB,GAAM,EACzB,EAAY;YACV,GAAG;YACH,WAAW,EAAE,OAAO;YACpB,UAAU,KAAA;YACV,QAAQ,KAAA;YACT,CAAC;;UAEJ,WAAU;UACV,UAAA;UACA,cAAY,EAAG,6BAA6B;oBA3B9C;WA6BE,kBAAC,UAAD;YAAQ,OAAM;sBAAI,EAAG,6BAA6B,iBAAiB;YAAU,CAAA;WAC5E,IACC,kBAAC,UAAD;YAAQ,UAAA;sBAAU,EAAG,+BAA+B,oBAAoB;YAAU,CAAA,GAChF;WACH,EAAe,KAAK,MACnB,kBAAC,UAAD;YAAyB,OAAO,EAAQ;sBAAxC;aACG,EAAQ;aAAK;aAAG,EAAQ;aAAK;aACvB;cAFI,EAAQ,GAEZ,CACT;WACD,KACC,kBAAC,UAAD;YAAQ,OAAO,EAAS;sBACrB,EAAG,4BAA4B,sBAAsB,EACpD,OAAO,EAAS,aAAa,IAC9B,CAAC;YACK,CAAA,GACP;WACJ,kBAAC,UAAD;YAAQ,OAAM;sBACX,EAAG,iCAAiC,sBAAsB;YACpD,CAAA;WACF;;SACR,IACC,kBAAC,SAAD;UACE,IAAG;UACH,cAAY,EAAG,iCAAiC,sBAAsB;UACtE,MAAK;UACL,OAAO,EAAS,aAAa;UAC7B,WAAW,MACT,EAAY;WACV,GAAG;WACH,WAAW,EAAE,OAAO;WACpB,UAAU,KAAA;WACV,QAAQ,KAAA;WACT,CAAC;UAEJ,WAAU;UACV,aAAa,EAAG,sCAAsC,eAAe;UACrE,UAAA;UACA,CAAA,GACA;SACA,EAAA,CAAA;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WAAO,WAAU;WAAiC,SAAQ;qBACvD,EAAG,6BAA6B,YAAY;WACvC,CAAA;UACR,kBAAC,UAAD;WACE,IAAG;WACH,OAAO,IAAoB,eAAe,EAAS,YAAY;WAC/D,WAAW,MAAM;AACf,gBAAI,EAAE,OAAO,UAAU,cAAc;AAEnC,aADA,EAAqB,GAAK,EAC1B,EAAY;cAAE,GAAG;cAAU,UAAU;cAAI,QAAQ,KAAA;cAAW,CAAC;AAC7D;;AAKF,YAFA,EAAqB,GAAM,EAC3B,EAAmB,GAAM,EACzB,EAAY;aACV,GAAG;aACH,UAAU,EAAE,OAAO,SAAS,KAAA;aAC5B,QAAQ,KAAA;aACT,CAAC;;WAEJ,WAAU;WACV,UAAU,CAAC,EAAS;WACpB,cAAY,EAAG,6BAA6B,YAAY;qBApB1D;YAsBE,kBAAC,UAAD;aAAQ,OAAM;uBAAI,EAAG,0BAA0B,cAAc;aAAU,CAAA;YACtE,EAAc,KAAK,MAClB,kBAAC,UAAD;aAAoC,OAAO,EAAa;uBAAxD;cACG,EAAa;cAAM;cAAG,EAAa;cAAS;cACtC;eAFI,EAAa,SAEjB,CACT;YACD,KACC,kBAAC,UAAD;aAAQ,OAAO,EAAS;uBACrB,EAAG,4BAA4B,sBAAsB,EACpD,OAAO,EAAS,YAAY,IAC7B,CAAC;aACK,CAAA,GACP;YACJ,kBAAC,UAAD;aAAQ,OAAM;uBACX,EAAG,8BAA8B,mBAAmB;aAC9C,CAAA;YACF;;UACR,IACC,kBAAC,SAAD;WACE,IAAG;WACH,cAAY,EAAG,8BAA8B,mBAAmB;WAChE,MAAK;WACL,OAAO,EAAS,YAAY;WAC5B,WAAW,MACT,EAAY;YACV,GAAG;YACH,UAAU,EAAE,OAAO,SAAS,KAAA;YAC5B,QAAQ,KAAA;YACT,CAAC;WAEJ,WAAU;WACV,aAAa,EAAG,qCAAqC,eAAe;WACpE,CAAA,GACA;UACA,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WAAO,WAAU;WAAiC,SAAQ;qBACvD,EAAG,2BAA2B,UAAU;WACnC,CAAA;UACR,kBAAC,UAAD;WACE,IAAG;WACH,OAAO,IAAkB,eAAe,EAAS,UAAU;WAC3D,WAAW,MAAM;AACf,gBAAI,EAAE,OAAO,UAAU,cAAc;AAEnC,aADA,EAAmB,GAAK,EACxB,EAAY;cAAE,GAAG;cAAU,QAAQ;cAAI,CAAC;AACxC;;AAIF,YADA,EAAmB,GAAM,EACzB,EAAY;aAAE,GAAG;aAAU,QAAQ,EAAE,OAAO,SAAS,KAAA;aAAW,CAAC;;WAEnE,WAAU;WACV,UAAU,CAAC,EAAS;WACpB,cAAY,EAAG,2BAA2B,UAAU;qBAftD;YAiBE,kBAAC,UAAD;aAAQ,OAAM;uBAAI,EAAG,wBAAwB,YAAY;aAAU,CAAA;YAClE,EAAY,KAAK,MAChB,kBAAC,UAAD;aAAgC,OAAO,EAAW;uBAAlD;cACG,EAAW;cAAM;cAAG,EAAW;cAAO;cAChC;eAFI,EAAW,OAEf,CACT;YACD,KACC,kBAAC,UAAD;aAAQ,OAAO,EAAS;uBACrB,EAAG,4BAA4B,sBAAsB,EACpD,OAAO,EAAS,UAAU,IAC3B,CAAC;aACK,CAAA,GACP;YACJ,kBAAC,UAAD;aAAQ,OAAM;uBACX,EAAG,4BAA4B,iBAAiB;aAC1C,CAAA;YACF;;UACR,IACC,kBAAC,SAAD;WACE,IAAG;WACH,cAAY,EAAG,4BAA4B,iBAAiB;WAC5D,MAAK;WACL,OAAO,EAAS,UAAU;WAC1B,WAAW,MACT,EAAY;YAAE,GAAG;YAAU,QAAQ,EAAE,OAAO,SAAS,KAAA;YAAW,CAAC;WAEnE,WAAU;WACV,aAAa,EAAG,mCAAmC,SAAS;WAC5D,CAAA,GACA;UACA,EAAA,CAAA,CACF;;QAEN,kBAAC,SAAD;SAAO,WAAU;mBAAjB,CACE,kBAAC,SAAD;UACE,MAAK;UACL,SAAS,EAAS,YAAY;UAC9B,WAAW,MAAM,EAAY;WAAE,GAAG;WAAU,UAAU,EAAE,OAAO;WAAS,CAAC;UACzE,WAAU;UACV,cAAY,EAAG,2BAA2B,SAAS;UACnD,CAAA,EACF,kBAAC,QAAD;UAAM,WAAU;oBAAuB,EAAG,2BAA2B,SAAS;UAAQ,CAAA,CAChF;;QACJ;SACI;;KAGZ,kBAAC,IAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA8B,EAAG,qCAAqC;OAAM,CAAA,EAE1F,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SAAiC,SAAQ;mBACvD,EAAG,+BAA+B;SAC7B,CAAA,EACR,kBAAC,UAAD;SACE,IAAG;SACH,OAAO,EAAS,eAAe;SAC/B,WAAW,MACT,EAAY;UAAE,GAAG;UAAU,aAAa,EAAE,OAAO;UAA0B,CAAC;SAE9E,WAAU;SACV,cAAY,EAAG,+BAA+B;mBAPhD;UASE,kBAAC,UAAD;WAAQ,OAAM;qBAAU,EAAG,4BAA4B;WAAU,CAAA;UACjE,kBAAC,UAAD;WAAQ,OAAM;qBAAoB,EAAG,oCAAoC;WAAU,CAAA;UACnF,kBAAC,UAAD;WAAQ,OAAM;qBAAe,EAAG,gCAAgC;WAAU,CAAA;UAC1E,kBAAC,UAAD;WAAQ,OAAM;qBAAY,EAAG,6BAA6B;WAAU,CAAA;UACpE,kBAAC,UAAD;WAAQ,OAAM;qBAAa,EAAG,+BAA+B;WAAU,CAAA;UAChE;WACL,EAAA,CAAA;QAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SAAiC,SAAQ;mBACvD,EAAG,4BAA4B;SAC1B,CAAA,EACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,KAAI;SACJ,KAAI;SACJ,OAAO,EAAS,YAAY;SAC5B,WAAW,MAAM;UACf,IAAM,IAAW,OAAO,SAAS,EAAE,OAAO,OAAO,GAAG,IAAI;AACxD,YAAY;WAAE,GAAG;WAAU;WAAU,CAAC;;SAExC,WAAU;SACV,cAAY,EAAG,4BAA4B;SAC3C,CAAA,CACE,EAAA,CAAA;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UAAO,WAAU;UAAiC,SAAQ;oBACvD,EAAG,8BAA8B,aAAa;UACzC,CAAA,EACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,GAAoB,EAAS,UAAU;UAC9C,WAAW,MACT,EAAY;WACV,GAAG;WACH,WAAW,EAAE,OAAO,QAChB,IAAI,KAAK,EAAE,OAAO,MAAM,CAAC,aAAa,GACtC,KAAA;WACL,CAAC;UAEJ,WAAU;UACV,cAAY,EAAG,8BAA8B,aAAa;UAC1D,CAAA,CACE,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UAAO,WAAU;UAAiC,SAAQ;oBACvD,EAAG,4BAA4B,WAAW;UACrC,CAAA,EACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,GAAoB,EAAS,QAAQ;UAC5C,WAAW,MACT,EAAY;WACV,GAAG;WACH,SAAS,EAAE,OAAO,QAAQ,IAAI,KAAK,EAAE,OAAO,MAAM,CAAC,aAAa,GAAG,KAAA;WACpE,CAAC;UAEJ,WAAU;UACV,cAAY,EAAG,4BAA4B,WAAW;UACtD,CAAA,CACE,EAAA,CAAA,CACF;;QACF;SACI;;KAGX,MACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,yBAAyB;OAC1B,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBAAkC,GAAM;OAAY,CAAA,CAC7D;;KAIP,EAAiB,SAAS,KACzB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,oCAAoC;OACrC,CAAA,EACL,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAiB,KAAK,MACrB,kBAAC,MAAD;QAAoB,WAAU;kBAC3B,EAAG,EAAI,SAAS,EAAI,OAAO;QACzB,EAFI,EAAI,MAER,CACL;OACC,CAAA,CACD;;KAIR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD;OACE,YAAY,IAAQ,EAAiB,cAAc,EAAiB;iBAEpE,kBAAC,IAAD;QACE,MAAK;QACL,UAAU,KAAU,CAAC;QACrB,WAAU;kBAHZ,CAKE,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,EAEd,EADH,IACM,uBACH,KAAU,MAAW,QAChB,2BACA,gCAAgC,CAClC;;OACM,CAAA,EAChB,KAAU,MAAW,SAAS,GAAiB,SAAS,KACvD,kBAAC,IAAD,EAAiB,SAAS,IAAoB,CAAA,CAE5C;;KACD;;GACH;MArjBJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,gCAAgC;IACjC,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cAEP,EADH,IACM,0CACA,wCAAwC;IAC7C,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAET,EAAG,qBAAqB;IAClB,CAAA,CACL;;EACF,CAAA"}
|
|
1
|
+
{"version":3,"file":"TourBuilderPage.js","names":[],"sources":["../../../src/tours/pages/TourBuilderPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport {\n Save,\n ArrowLeft,\n Eye,\n ListOrdered,\n BarChart3,\n Calendar,\n LayoutTemplate,\n Sparkles,\n} from 'lucide-react';\nimport { gql } from '@apollo/client/core';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport {\n Button,\n GlassCard,\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n type NextStep,\n CTAOverflowMenu,\n type CTAAction,\n} from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { MarkdownEditor } from '../components/MarkdownEditor';\nimport { useTourBuilder } from '../hooks/useTourBuilder';\nimport { useTourLifecycle } from '../hooks/useTourLifecycle';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { Tour, TourTriggerType } from '../types';\nimport { useToursI18n } from '../i18n';\nimport { emitToursApiFailed } from '../telemetry';\nimport { useGetPlatformProductsForToursQuery } from '../../generated/global-operations';\n\n// Covers the current platform catalog with room for planned products; custom slug entry remains the fallback.\nconst PLATFORM_PRODUCTS_QUERY_LIMIT = 250;\n\nconst SEED_VIBECONTROLS_USER_TOURS = gql`\n mutation SeedVibeControlsUserTours {\n seedVibeControlsUserTours {\n productId\n workspaceId\n createdCount\n updatedCount\n totalCount\n tours {\n tourId\n slug\n name\n action\n stepCount\n }\n }\n }\n`;\n\nconst SEED_BUILDMYIQ_USER_TOURS = gql`\n mutation SeedBuildmyiqUserTours {\n seedBuildmyiqUserTours {\n productId\n workspaceId\n createdCount\n updatedCount\n totalCount\n tours {\n tourId\n slug\n name\n action\n stepCount\n }\n }\n }\n`;\n\nconst SEED_BIGCONSOLE_USER_TOURS = gql`\n mutation SeedBigconsoleUserTours {\n seedBigconsoleUserTours {\n productId\n workspaceId\n createdCount\n updatedCount\n totalCount\n tours {\n tourId\n slug\n name\n action\n stepCount\n }\n }\n }\n`;\n\ninterface SeededUserTourResult {\n tourId: string;\n slug: string;\n name: string;\n action: string;\n stepCount: number;\n}\n\ninterface SeedVibeControlsUserToursPayload {\n productId: string;\n workspaceId: string;\n createdCount: number;\n updatedCount: number;\n totalCount: number;\n tours: SeededUserTourResult[];\n}\n\ninterface SeedVibeControlsUserToursMutation {\n seedVibeControlsUserTours: SeedVibeControlsUserToursPayload;\n}\n\ninterface SeedBuildmyiqUserToursMutation {\n seedBuildmyiqUserTours: SeedVibeControlsUserToursPayload;\n}\n\ninterface SeedBigconsoleUserToursMutation {\n seedBigconsoleUserTours: SeedVibeControlsUserToursPayload;\n}\n\nfunction getMutationErrorMessage(result: unknown, fallbackMessage: string): string | null {\n if (!result || typeof result !== 'object') return null;\n\n const record = result as Record<string, unknown>;\n const singleError = record.error;\n if (singleError instanceof Error) return singleError.message;\n\n const errors = record.errors;\n if (!Array.isArray(errors) || errors.length === 0) return null;\n\n const messages = errors\n .map((error) => {\n if (error instanceof Error) return error.message;\n if (!error || typeof error !== 'object') return null;\n\n const message = (error as { message?: unknown }).message;\n return typeof message === 'string' ? message : null;\n })\n .filter((message): message is string => Boolean(message));\n\n return messages.length > 0 ? messages.join('; ') : fallbackMessage;\n}\n\ninterface TourRouteModuleOption {\n moduleId: string;\n label: string;\n path: string;\n}\n\ninterface TourRoutePageOption {\n pageId: string;\n label: string;\n path: string;\n}\n\ninterface TourRouteCatalogEntry {\n productId: string;\n moduleId: string;\n moduleLabel: string;\n pageId: string;\n pageLabel: string;\n path: string;\n isModuleHome?: boolean;\n}\n\nconst TOUR_ROUTE_CATALOG: readonly TourRouteCatalogEntry[] = [\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'home',\n pageLabel: 'Overview',\n path: '/bigconsole',\n isModuleHome: true,\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'dashboards',\n pageLabel: 'Dashboards',\n path: '/bigconsole/dashboards',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'datasink-builder',\n pageLabel: 'Create data sink',\n path: '/bigconsole/datasinks/new',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'parser-builder',\n pageLabel: 'Create parser',\n path: '/bigconsole/parsers/new',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'workflows',\n pageLabel: 'Workflows',\n path: '/bigconsole/workflows/list',\n },\n {\n productId: 'bigconsole',\n moduleId: 'bigconsole',\n moduleLabel: 'BigConsole',\n pageId: 'insights',\n pageLabel: 'AI Insights',\n path: '/bigconsole/insights',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'dashboard',\n moduleLabel: 'Dashboard',\n pageId: 'dashboard',\n pageLabel: 'Dashboard',\n path: '/dashboard',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'home',\n pageLabel: 'Overview',\n path: '/vibecontrols',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'agents',\n pageLabel: 'Targets & Agents',\n path: '/vibecontrols/agents',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'vibes',\n pageLabel: 'Vibes',\n path: '/vibecontrols/vibes',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'sessions',\n pageLabel: 'Sessions',\n path: '/vibecontrols/sessions',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'ai',\n pageLabel: 'AI',\n path: '/vibecontrols/ai',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'catalogs',\n pageLabel: 'Catalogs',\n path: '/vibecontrols/catalogs',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'components',\n pageLabel: 'Components',\n path: '/vibecontrols/components',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'templates',\n pageLabel: 'Templates',\n path: '/vibecontrols/templates',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'actions',\n pageLabel: 'Actions',\n path: '/vibecontrols/actions',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'vibedeck',\n pageLabel: 'Decks',\n path: '/vibecontrols/vibedeck',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'calendar',\n pageLabel: 'Calendar',\n path: '/vibecontrols/calendar',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'tunnels',\n pageLabel: 'Tunnels',\n path: '/vibecontrols/tunnels',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'analytics',\n pageLabel: 'Analytics',\n path: '/vibecontrols/analytics',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'audit',\n pageLabel: 'Audit Log',\n path: '/vibecontrols/audit',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'health',\n pageLabel: 'Health',\n path: '/vibecontrols/health',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'vibecontrols',\n moduleLabel: 'VibeControls',\n pageId: 'settings',\n pageLabel: 'Settings',\n path: '/vibecontrols/settings',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'scheduler',\n moduleLabel: 'Scheduler',\n pageId: 'scheduler',\n pageLabel: 'Scheduler',\n path: '/scheduler',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'sandbox',\n moduleLabel: 'Sandbox',\n pageId: 'sandbox',\n pageLabel: 'Sandbox',\n path: '/sandbox',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'support',\n moduleLabel: 'Support',\n pageId: 'tickets',\n pageLabel: 'Tickets',\n path: '/support/tickets',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'support',\n moduleLabel: 'Support',\n pageId: 'knowledge-base',\n pageLabel: 'Knowledge Base',\n path: '/support/kb',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'notifications',\n moduleLabel: 'Notifications',\n pageId: 'center',\n pageLabel: 'Notification Center',\n path: '/notifications/center',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'notifications',\n moduleLabel: 'Notifications',\n pageId: 'history',\n pageLabel: 'History',\n path: '/notifications/history',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'notifications',\n moduleLabel: 'Notifications',\n pageId: 'preferences',\n pageLabel: 'Preferences',\n path: '/notifications/preferences',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'home',\n pageLabel: 'All Conversations',\n path: '/conversations',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'list',\n pageLabel: 'Conversation List',\n path: '/conversations/list',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'new',\n pageLabel: 'New Conversation',\n path: '/conversations/new',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'conversations',\n moduleLabel: 'Conversations',\n pageId: 'settings',\n pageLabel: 'Settings',\n path: '/conversations/settings',\n },\n {\n productId: 'vibecontrols',\n moduleId: 'auth',\n moduleLabel: 'Auth',\n pageId: 'home',\n pageLabel: 'Auth',\n path: '/auth/security',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'groups',\n moduleLabel: 'Groups and Teams',\n pageId: 'home',\n pageLabel: 'Groups and Teams',\n path: '/groups',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'tags',\n moduleLabel: 'Tags',\n pageId: 'home',\n pageLabel: 'Tags',\n path: '/tags',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'files',\n moduleLabel: 'Files',\n pageId: 'home',\n pageLabel: 'Files',\n path: '/files/browse',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'security',\n moduleLabel: 'Vault',\n pageId: 'home',\n pageLabel: 'Vault',\n path: '/security',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'integrations',\n moduleLabel: 'Integrations',\n pageId: 'home',\n pageLabel: 'Integrations',\n path: '/integrations',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'export',\n moduleLabel: 'Exports',\n pageId: 'home',\n pageLabel: 'Exports',\n path: '/export',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'organizations',\n moduleLabel: 'Organizations',\n pageId: 'home',\n pageLabel: 'Organizations',\n path: '/organizations',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'workspaces',\n moduleLabel: 'Workspaces',\n pageId: 'home',\n pageLabel: 'Workspaces',\n path: '/workspaces',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'billing',\n moduleLabel: 'Billing',\n pageId: 'home',\n pageLabel: 'Billing',\n path: '/billing/overview',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'store',\n moduleLabel: 'Marketplace',\n pageId: 'home',\n pageLabel: 'Marketplace',\n path: '/store/marketplace',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'channels',\n moduleLabel: 'Channels',\n pageId: 'home',\n pageLabel: 'Channels',\n path: '/channels',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'devportal',\n moduleLabel: 'Developer Portal',\n pageId: 'home',\n pageLabel: 'Developer Portal',\n path: '/devportal',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'feature-previews',\n moduleLabel: 'Feature Previews',\n pageId: 'home',\n pageLabel: 'Feature Previews',\n path: '/feature-previews',\n isModuleHome: true,\n },\n {\n productId: 'vibecontrols',\n moduleId: 'search',\n moduleLabel: 'Search',\n pageId: 'home',\n pageLabel: 'Search',\n path: '/search',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'dashboard',\n moduleLabel: 'Dashboard',\n pageId: 'dashboard',\n pageLabel: 'Dashboard',\n path: '/dashboard',\n isModuleHome: true,\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'list',\n pageLabel: 'My Workflows',\n path: '/workflows/list',\n isModuleHome: true,\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'builder',\n pageLabel: 'Workflow Builder',\n path: '/workflows/workflow',\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'runs',\n pageLabel: 'Runs',\n path: '/workflows/runs',\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'history',\n pageLabel: 'History',\n path: '/workflows/history',\n },\n {\n productId: 'fluidgrids',\n moduleId: 'workflows',\n moduleLabel: 'Workflows',\n pageId: 'templates',\n pageLabel: 'Templates',\n path: '/workflows/templates',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'catalog',\n moduleLabel: 'Course Catalog',\n pageId: 'home',\n pageLabel: 'Catalog',\n path: '/catalog',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'catalog',\n moduleLabel: 'Course Catalog',\n pageId: 'detail',\n pageLabel: 'Course Detail',\n path: '/catalog/:courseId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'learning',\n moduleLabel: 'My Learning',\n pageId: 'home',\n pageLabel: 'My Learning',\n path: '/learning',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'learning',\n moduleLabel: 'My Learning',\n pageId: 'player',\n pageLabel: 'Lesson Player',\n path: '/learning/:enrollmentId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'paths',\n moduleLabel: 'Skill Journeys',\n pageId: 'home',\n pageLabel: 'Skill Journeys',\n path: '/paths',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'authoring',\n moduleLabel: 'Authoring',\n pageId: 'home',\n pageLabel: 'Authoring',\n path: '/authoring',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'authoring',\n moduleLabel: 'Authoring',\n pageId: 'builder',\n pageLabel: 'Course Builder',\n path: '/authoring/:courseId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'assessments',\n moduleLabel: 'Assessments',\n pageId: 'home',\n pageLabel: 'Assessments',\n path: '/assessments',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'assessments',\n moduleLabel: 'Assessments',\n pageId: 'detail',\n pageLabel: 'Assessment Detail',\n path: '/assessments/:assessmentId',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'assessments',\n moduleLabel: 'Assessments',\n pageId: 'take',\n pageLabel: 'Take Assessment',\n path: '/assessments/:assessmentId/take',\n },\n {\n productId: 'buildmyiq',\n moduleId: 'insights',\n moduleLabel: 'Insights',\n pageId: 'home',\n pageLabel: 'Insights',\n path: '/insights',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'submissions',\n moduleLabel: 'Submissions',\n pageId: 'home',\n pageLabel: 'Submissions',\n path: '/submissions',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'cohorts',\n moduleLabel: 'Cohorts',\n pageId: 'home',\n pageLabel: 'Cohorts',\n path: '/cohorts',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'skills',\n moduleLabel: 'Skills',\n pageId: 'home',\n pageLabel: 'Skills',\n path: '/skills',\n isModuleHome: true,\n },\n {\n productId: 'buildmyiq',\n moduleId: 'certificates',\n moduleLabel: 'Certificates',\n pageId: 'home',\n pageLabel: 'Certificates',\n path: '/certificates',\n isModuleHome: true,\n },\n // moduleId/pageId values match the shell's runtime route derivation\n // (deriveRouteContext): moduleId is the product slug, pageId the second\n // path segment ('home' at the root) — same convention as vibecontrols.\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'home',\n pageLabel: 'Dashboard',\n path: '/collabkin',\n isModuleHome: true,\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'trees',\n pageLabel: 'Family Trees',\n path: '/collabkin/trees',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'people',\n pageLabel: 'People',\n path: '/collabkin/people',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'memories',\n pageLabel: 'Memories',\n path: '/collabkin/memories',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'stories',\n pageLabel: 'Stories',\n path: '/collabkin/stories',\n },\n {\n productId: 'collabkin',\n moduleId: 'collabkin',\n moduleLabel: 'CollabKin',\n pageId: 'timeline',\n pageLabel: 'Timeline',\n path: '/collabkin/timeline',\n },\n] as const;\n\nfunction normalizeCatalogValue(value: string | null | undefined): string | undefined {\n const normalized = value?.trim();\n return normalized ? normalized : undefined;\n}\n\nfunction getRouteCatalog(productId: string | null | undefined): TourRouteCatalogEntry[] {\n const productKey = normalizeCatalogValue(productId);\n if (!productKey) return [];\n return TOUR_ROUTE_CATALOG.filter((entry) => entry.productId === productKey);\n}\n\nfunction getTourRouteModules(productId: string | null | undefined): TourRouteModuleOption[] {\n const modules = new Map<string, TourRouteModuleOption>();\n\n for (const entry of getRouteCatalog(productId)) {\n const existing = modules.get(entry.moduleId);\n if (!existing || entry.isModuleHome) {\n modules.set(entry.moduleId, {\n moduleId: entry.moduleId,\n label: entry.moduleLabel,\n path: entry.path,\n });\n }\n }\n\n return [...modules.values()];\n}\n\nfunction getTourRoutePages(\n productId: string | null | undefined,\n moduleId: string | null | undefined\n): TourRoutePageOption[] {\n const moduleKey = normalizeCatalogValue(moduleId);\n if (!moduleKey) return [];\n\n return getRouteCatalog(productId).flatMap((entry) =>\n entry.moduleId === moduleKey\n ? [{ pageId: entry.pageId, label: entry.pageLabel, path: entry.path }]\n : []\n );\n}\n\n/**\n * Tour builder page\n * Create or edit tour metadata\n */\nexport function TourBuilderPage() {\n const { tr } = useToursI18n();\n const { tourId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const globalClient = useGatewayClient('global');\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const isNew = !tourId || tourId === 'new';\n const canCreate = hasPermission(TOUR_PERMISSIONS.TOUR_CREATE);\n const canUpdate = hasPermission(TOUR_PERMISSIONS.TOUR_UPDATE);\n const canCreateTemplate = hasPermission(TOUR_PERMISSIONS.TOUR_TEMPLATE_CREATE);\n const canSave = isNew ? canCreate : canUpdate;\n const canSeedVibeControlsUserTours = canCreate && canUpdate;\n const canSeedBuildmyiqUserTours = canCreate && canUpdate;\n const canSeedBigconsoleUserTours = canCreate && canUpdate;\n\n const { tour, loadTour, createTour, updateTour, saving, validationErrors, error } =\n useTourBuilder(tourId);\n const { saveAsTemplate } = useTourLifecycle();\n const { data: productsData, loading: productsLoading } = useGetPlatformProductsForToursQuery({\n variables: { limit: PLATFORM_PRODUCTS_QUERY_LIMIT, offset: 0 },\n fetchPolicy: 'cache-and-network',\n skip: permissionsLoading || !canSave,\n });\n\n const defaultFormData = {\n name: '',\n description: '',\n productId: '',\n moduleId: '',\n pageId: '',\n isActive: true,\n status: 'DRAFT' as const,\n triggerType: 'MANUAL' as const,\n priority: 50,\n displayOrder: 1,\n startDate: '',\n endDate: '',\n };\n\n const [formData, setFormData] = useState<Partial<Tour>>(defaultFormData);\n const [customProductEntry, setCustomProductEntry] = useState(false);\n const [customModuleEntry, setCustomModuleEntry] = useState(false);\n const [customPageEntry, setCustomPageEntry] = useState(false);\n const [seedLoading, setSeedLoading] = useState(false);\n const [seedResult, setSeedResult] = useState<SeedVibeControlsUserToursPayload | null>(null);\n const [seedError, setSeedError] = useState<string | null>(null);\n const [seedBuildmyiqLoading, setSeedBuildmyiqLoading] = useState(false);\n const [seedBuildmyiqResult, setSeedBuildmyiqResult] =\n useState<SeedVibeControlsUserToursPayload | null>(null);\n const [seedBuildmyiqError, setSeedBuildmyiqError] = useState<string | null>(null);\n const [seedBigconsoleLoading, setSeedBigconsoleLoading] = useState(false);\n const [seedBigconsoleResult, setSeedBigconsoleResult] =\n useState<SeedVibeControlsUserToursPayload | null>(null);\n const [seedBigconsoleError, setSeedBigconsoleError] = useState<string | null>(null);\n\n const formInitializedRef = useRef(false);\n\n const productOptions = useMemo(() => {\n return [...(productsData?.platformProducts.items ?? [])].sort((a, b) =>\n a.name.localeCompare(b.name)\n );\n }, [productsData]);\n\n const productOptionSlugs = useMemo(\n () => new Set(productOptions.map((product) => product.slug)),\n [productOptions]\n );\n\n const moduleOptions = useMemo(\n () => getTourRouteModules(formData.productId),\n [formData.productId]\n );\n\n const moduleOptionIds = useMemo(\n () => new Set(moduleOptions.map((moduleOption) => moduleOption.moduleId)),\n [moduleOptions]\n );\n\n const pageOptions = useMemo(\n () => getTourRoutePages(formData.productId, formData.moduleId),\n [formData.moduleId, formData.productId]\n );\n\n const pageOptionIds = useMemo(\n () => new Set(pageOptions.map((pageOption) => pageOption.pageId)),\n [pageOptions]\n );\n\n useEffect(() => {\n if (formInitializedRef.current) return;\n if (!permissionsLoading && canSave && tourId && tourId !== 'new') {\n loadTour(tourId);\n }\n }, [canSave, loadTour, permissionsLoading, tourId]);\n\n useEffect(() => {\n if (tour && !formInitializedRef.current) {\n setFormData(tour);\n formInitializedRef.current = true;\n }\n }, [tour]);\n\n useEffect(() => {\n if (!formData.productId || productsLoading) {\n setCustomProductEntry(false);\n return;\n }\n setCustomProductEntry(productOptions.length > 0 && !productOptionSlugs.has(formData.productId));\n }, [formData.productId, productOptionSlugs, productOptions.length, productsLoading]);\n\n useEffect(() => {\n if (!formData.productId || !formData.moduleId) {\n setCustomModuleEntry(false);\n return;\n }\n setCustomModuleEntry(moduleOptions.length === 0 || !moduleOptionIds.has(formData.moduleId));\n }, [formData.moduleId, formData.productId, moduleOptionIds, moduleOptions.length]);\n\n useEffect(() => {\n if (!formData.productId || !formData.pageId) {\n setCustomPageEntry(false);\n return;\n }\n setCustomPageEntry(pageOptions.length === 0 || !pageOptionIds.has(formData.pageId));\n }, [formData.pageId, formData.productId, pageOptionIds, pageOptions.length]);\n\n const showCurrentProductOption = Boolean(\n formData.productId && !customProductEntry && !productOptionSlugs.has(formData.productId)\n );\n const showCurrentModuleOption = Boolean(\n formData.moduleId && !customModuleEntry && !moduleOptionIds.has(formData.moduleId)\n );\n const showCurrentPageOption = Boolean(\n formData.pageId && !customPageEntry && !pageOptionIds.has(formData.pageId)\n );\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n try {\n if (tourId && tourId !== 'new') {\n await updateTour(tourId, formData);\n } else {\n await createTour(formData);\n }\n } catch {\n emitToursApiFailed(eventBus, 'tour.save', toursContext);\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/list`);\n };\n\n const formatDateTimeLocal = (value?: string) => {\n if (!value) return '';\n\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n\n return new Date(date.getTime() - date.getTimezoneOffset() * 60000).toISOString().slice(0, 16);\n };\n\n const handleSaveTemplate = async () => {\n if (!tourId || tourId === 'new') return;\n\n const defaultName = tr('tourBuilder.templateDefaultName', '{{name}} Template', {\n name: formData.name || tour?.name || 'Tour',\n });\n const name = window.prompt(tr('tourBuilder.templateNamePrompt', 'Template name'), defaultName);\n if (!name) return;\n\n const category =\n window.prompt(\n tr(\n 'tourBuilder.templateCategoryPrompt',\n 'Template category (ONBOARDING, FEATURE_ANNOUNCEMENT, HELP, CUSTOM)'\n ),\n 'ONBOARDING'\n ) || 'ONBOARDING';\n\n try {\n await saveAsTemplate(tourId, {\n name,\n description: formData.description,\n category,\n isPublic: false,\n });\n navigate(`${basePath}/templates`);\n } catch {\n emitToursApiFailed(eventBus, 'template.save', toursContext);\n }\n };\n\n const handleSeedVibeControlsUserTours = async () => {\n const confirmed = window.confirm(tr('tourBuilder.seedVibeControls.confirm'));\n if (!confirmed) return;\n\n setSeedLoading(true);\n setSeedResult(null);\n setSeedError(null);\n\n try {\n const result = await globalClient.mutate<SeedVibeControlsUserToursMutation>({\n mutation: SEED_VIBECONTROLS_USER_TOURS,\n });\n\n const mutationErrorMessage = getMutationErrorMessage(\n result,\n tr('tourBuilder.seedMutationErrors', 'Seed mutation returned errors.')\n );\n if (mutationErrorMessage) {\n throw new Error(mutationErrorMessage);\n }\n\n const payload = result.data?.seedVibeControlsUserTours;\n if (!payload) {\n throw new Error(\n tr('tourBuilder.seedMutationNoResult', 'Seed mutation returned no result.')\n );\n }\n\n setSeedResult(payload);\n } catch (seedMutationError) {\n const errorMessage =\n seedMutationError instanceof Error\n ? seedMutationError.message\n : tr('tourBuilder.seedVibeControls.error');\n setSeedError(errorMessage);\n eventBus.emit(\n 'tours.api.failed',\n {\n route: '/tours',\n operation: 'tour.seed_vibecontrols_user_tours',\n errorMessage,\n productId: toursContext.productId,\n workspaceId: toursContext.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n } finally {\n setSeedLoading(false);\n }\n };\n\n const handleSeedBuildmyiqUserTours = async () => {\n const confirmed = window.confirm(tr('tourBuilder.seedBuildmyiq.confirm'));\n if (!confirmed) return;\n\n setSeedBuildmyiqLoading(true);\n setSeedBuildmyiqResult(null);\n setSeedBuildmyiqError(null);\n\n try {\n const result = await globalClient.mutate<SeedBuildmyiqUserToursMutation>({\n mutation: SEED_BUILDMYIQ_USER_TOURS,\n });\n\n const mutationErrorMessage = getMutationErrorMessage(\n result,\n tr('tourBuilder.seedMutationErrors', 'Seed mutation returned errors.')\n );\n if (mutationErrorMessage) {\n throw new Error(mutationErrorMessage);\n }\n\n const payload = result.data?.seedBuildmyiqUserTours;\n if (!payload) {\n throw new Error(\n tr('tourBuilder.seedMutationNoResult', 'Seed mutation returned no result.')\n );\n }\n\n setSeedBuildmyiqResult(payload);\n } catch (seedMutationError) {\n const errorMessage =\n seedMutationError instanceof Error\n ? seedMutationError.message\n : tr('tourBuilder.seedBuildmyiq.error');\n setSeedBuildmyiqError(errorMessage);\n eventBus.emit(\n 'tours.api.failed',\n {\n route: '/tours',\n operation: 'tour.seed_buildmyiq_user_tours',\n errorMessage,\n productId: toursContext.productId,\n workspaceId: toursContext.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n } finally {\n setSeedBuildmyiqLoading(false);\n }\n };\n\n const handleSeedBigconsoleUserTours = async () => {\n const confirmed = window.confirm(tr('tourBuilder.seedBigconsole.confirm'));\n if (!confirmed) return;\n\n setSeedBigconsoleLoading(true);\n setSeedBigconsoleResult(null);\n setSeedBigconsoleError(null);\n\n try {\n const result = await globalClient.mutate<SeedBigconsoleUserToursMutation>({\n mutation: SEED_BIGCONSOLE_USER_TOURS,\n });\n\n const mutationErrorMessage = getMutationErrorMessage(\n result,\n tr('tourBuilder.seedMutationErrors', 'Seed mutation returned errors.')\n );\n if (mutationErrorMessage) {\n throw new Error(mutationErrorMessage);\n }\n\n const payload = result.data?.seedBigconsoleUserTours;\n if (!payload) {\n throw new Error(\n tr('tourBuilder.seedMutationNoResult', 'Seed mutation returned no result.')\n );\n }\n\n setSeedBigconsoleResult(payload);\n } catch (seedMutationError) {\n const errorMessage =\n seedMutationError instanceof Error\n ? seedMutationError.message\n : tr('tourBuilder.seedBigconsole.error');\n setSeedBigconsoleError(errorMessage);\n eventBus.emit(\n 'tours.api.failed',\n {\n route: '/tours',\n operation: 'tour.seed_bigconsole_user_tours',\n errorMessage,\n productId: toursContext.productId,\n workspaceId: toursContext.workspaceId,\n },\n { component: 'microfe-tours' }\n );\n } finally {\n setSeedBigconsoleLoading(false);\n }\n };\n\n const secondaryActions: CTAAction[] =\n tourId && tourId !== 'new'\n ? [\n {\n label:\n tour?.steps && tour.steps.length > 0\n ? `${tr('tourBuilder.editSteps')} (${tour.steps.length})`\n : tr('tourBuilder.editSteps'),\n onSelect: () => navigate(`${basePath}/${tourId}/steps`),\n icon: <ListOrdered size={16} />,\n },\n {\n label: tr('tourBuilder.preview'),\n onSelect: () => navigate(`${basePath}/${tourId}/preview`),\n icon: <Eye size={16} />,\n },\n {\n label: tr('tourBuilder.analytics'),\n onSelect: () => navigate(`${basePath}/${tourId}/analytics`),\n icon: <BarChart3 size={16} />,\n },\n {\n label: tr('tourBuilder.schedule', 'Schedule'),\n onSelect: () => navigate(`${basePath}/${tourId}/schedule`),\n icon: <Calendar size={16} />,\n },\n ...(canCreateTemplate\n ? [\n {\n label: tr('tourBuilder.saveAsTemplate', 'Save as Template'),\n onSelect: () => void handleSaveTemplate(),\n icon: <LayoutTemplate size={16} />,\n } satisfies CTAAction,\n ]\n : []),\n ]\n : [];\n\n // Concrete, real-handler next steps for the (complex) edit view so a new\n // author isn't left guessing what to do after saving the tour's metadata.\n const isExistingTour = Boolean(tourId && tourId !== 'new');\n const nextSteps: NextStep[] =\n isExistingTour && canUpdate\n ? [\n {\n id: 'edit-steps',\n label: tr('tourBuilder.nextStepEditSteps'),\n description: tr('tourBuilder.nextStepEditStepsDesc'),\n icon: <ListOrdered size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/steps`),\n },\n {\n id: 'preview',\n label: tr('tourBuilder.nextStepPreview'),\n description: tr('tourBuilder.nextStepPreviewDesc'),\n icon: <Eye size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/preview`),\n },\n {\n id: 'schedule',\n label: tr('tourBuilder.nextStepSchedule'),\n description: tr('tourBuilder.nextStepScheduleDesc'),\n icon: <Calendar size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/schedule`),\n },\n {\n id: 'analytics',\n label: tr('tourBuilder.nextStepAnalytics'),\n description: tr('tourBuilder.nextStepAnalyticsDesc'),\n icon: <BarChart3 size={16} />,\n onClick: () => navigate(`${basePath}/${tourId}/analytics`),\n },\n ]\n : [];\n\n if (permissionsLoading) {\n return (\n <div className=\"flex h-full items-center justify-center p-3 sm:p-6\">\n <p className=\"text-sm text-text-secondary\">{tr('tourBuilder.loading')}</p>\n </div>\n );\n }\n\n if (!canSave) {\n return (\n <div className=\"flex h-full items-center justify-center p-3 sm:p-6\">\n <div className=\"text-center space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('tourBuilder.accessDeniedTitle')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {isNew\n ? tr('tourBuilder.createAccessDeniedMessage')\n : tr('tourBuilder.updateAccessDeniedMessage')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center justify-center rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent\"\n >\n {tr('tourBuilder.goBack')}\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-4xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 hover:bg-accent rounded-md\"\n aria-label={tr('tourBuilder.goBack')}\n >\n <ArrowLeft size={20} />\n </button>\n <div className=\"flex-1\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n {tourId && tourId !== 'new'\n ? tr('tourBuilder.titleEdit')\n : tr('tourBuilder.titleCreate')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{tr('tourBuilder.subtitle')}</p>\n </div>\n </div>\n\n <PagePurpose>{tr('tourBuilder.purpose')}</PagePurpose>\n\n {nextSteps.length > 0 ? (\n <NextSteps\n storageKey=\"tours-builder\"\n title={tr('tourBuilder.nextStepsTitle')}\n steps={nextSteps}\n />\n ) : null}\n\n {isNew ? (\n <>\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('tourBuilder.seedVibeControls.title')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourBuilder.seedVibeControls.description')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void handleSeedVibeControlsUserTours()}\n disabled={seedLoading || !canSeedVibeControlsUserTours}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-border-subtle bg-secondary px-4 py-2 text-sm font-medium text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Sparkles size={18} />\n {seedLoading\n ? tr('tourBuilder.seedVibeControls.loading')\n : tr('tourBuilder.seedVibeControls.button')}\n </button>\n </div>\n {!canSeedVibeControlsUserTours ? (\n <p className=\"mt-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedVibeControls.permissionHint')}\n </p>\n ) : null}\n {seedError ? <p className=\"mt-3 text-sm text-status-error-text\">{seedError}</p> : null}\n {seedResult ? (\n <div className=\"mt-3 rounded-md border border-border-seam bg-bg-surface p-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedVibeControls.result', {\n totalCount: seedResult.totalCount,\n productId: seedResult.productId,\n workspaceId: seedResult.workspaceId,\n createdCount: seedResult.createdCount,\n updatedCount: seedResult.updatedCount,\n })}\n </div>\n ) : null}\n </EmphasisPanel>\n\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('tourBuilder.seedBuildmyiq.title')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourBuilder.seedBuildmyiq.description')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void handleSeedBuildmyiqUserTours()}\n disabled={seedBuildmyiqLoading || !canSeedBuildmyiqUserTours}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-border-subtle bg-secondary px-4 py-2 text-sm font-medium text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Sparkles size={18} />\n {seedBuildmyiqLoading\n ? tr('tourBuilder.seedBuildmyiq.loading')\n : tr('tourBuilder.seedBuildmyiq.button')}\n </button>\n </div>\n {!canSeedBuildmyiqUserTours ? (\n <p className=\"mt-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBuildmyiq.permissionHint')}\n </p>\n ) : null}\n {seedBuildmyiqError ? (\n <p className=\"mt-3 text-sm text-status-error-text\">{seedBuildmyiqError}</p>\n ) : null}\n {seedBuildmyiqResult ? (\n <div className=\"mt-3 rounded-md border border-border-seam bg-bg-surface p-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBuildmyiq.result', {\n totalCount: seedBuildmyiqResult.totalCount,\n productId: seedBuildmyiqResult.productId,\n workspaceId: seedBuildmyiqResult.workspaceId,\n createdCount: seedBuildmyiqResult.createdCount,\n updatedCount: seedBuildmyiqResult.updatedCount,\n })}\n </div>\n ) : null}\n </EmphasisPanel>\n\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('tourBuilder.seedBigconsole.title')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourBuilder.seedBigconsole.description')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => void handleSeedBigconsoleUserTours()}\n disabled={seedBigconsoleLoading || !canSeedBigconsoleUserTours}\n className=\"inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-border-subtle bg-secondary px-4 py-2 text-sm font-medium text-secondary-foreground hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <Sparkles size={18} />\n {seedBigconsoleLoading\n ? tr('tourBuilder.seedBigconsole.loading')\n : tr('tourBuilder.seedBigconsole.button')}\n </button>\n </div>\n {!canSeedBigconsoleUserTours ? (\n <p className=\"mt-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBigconsole.permissionHint')}\n </p>\n ) : null}\n {seedBigconsoleError ? (\n <p className=\"mt-3 text-sm text-status-error-text\">{seedBigconsoleError}</p>\n ) : null}\n {seedBigconsoleResult ? (\n <div className=\"mt-3 rounded-md border border-border-seam bg-bg-surface p-3 text-sm text-text-secondary\">\n {tr('tourBuilder.seedBigconsole.result', {\n totalCount: seedBigconsoleResult.totalCount,\n productId: seedBigconsoleResult.productId,\n workspaceId: seedBigconsoleResult.workspaceId,\n createdCount: seedBigconsoleResult.createdCount,\n updatedCount: seedBigconsoleResult.updatedCount,\n })}\n </div>\n ) : null}\n </EmphasisPanel>\n </>\n ) : null}\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Basic Info */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4\">{tr('tourBuilder.sectionBasicInfo')}</h2>\n\n <div className=\"space-y-4\">\n {/* Name */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"tour-name\">\n {tr('tourBuilder.labelName')}\n </label>\n <input\n id=\"tour-name\"\n type=\"text\"\n value={formData.name || ''}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n required\n aria-label={tr('tourBuilder.labelName')}\n />\n </div>\n\n {/* Description */}\n <MarkdownEditor\n id=\"tour-description\"\n label={tr('tourBuilder.labelDescription')}\n value={formData.description || ''}\n onChange={(description) => setFormData({ ...formData, description })}\n rows={4}\n aria-label={tr('tourBuilder.labelDescription')}\n />\n\n {/* Product ID */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"product-id\">\n {tr('tourBuilder.labelProductId')}\n </label>\n <select\n id=\"product-id\"\n value={customProductEntry ? '__custom__' : formData.productId || ''}\n onChange={(e) => {\n if (e.target.value === '__custom__') {\n setCustomProductEntry(true);\n setFormData({\n ...formData,\n productId: '',\n moduleId: undefined,\n pageId: undefined,\n });\n return;\n }\n\n setCustomProductEntry(false);\n setCustomModuleEntry(false);\n setCustomPageEntry(false);\n setFormData({\n ...formData,\n productId: e.target.value,\n moduleId: undefined,\n pageId: undefined,\n });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n required\n aria-label={tr('tourBuilder.labelProductId')}\n >\n <option value=\"\">{tr('tourBuilder.selectProduct', 'Select product')}</option>\n {productsLoading ? (\n <option disabled>{tr('tourBuilder.loadingProducts', 'Loading products…')}</option>\n ) : null}\n {productOptions.map((product) => (\n <option key={product.id} value={product.slug}>\n {product.name} ({product.slug})\n </option>\n ))}\n {showCurrentProductOption ? (\n <option value={formData.productId}>\n {tr('tourBuilder.currentValue', 'Current: {{value}}', {\n value: formData.productId ?? '',\n })}\n </option>\n ) : null}\n <option value=\"__custom__\">\n {tr('tourBuilder.customProductSlug', 'Custom product slug')}\n </option>\n </select>\n {customProductEntry ? (\n <input\n id=\"product-id-custom\"\n aria-label={tr('tourBuilder.customProductSlug', 'Custom product slug')}\n type=\"text\"\n value={formData.productId || ''}\n onChange={(e) =>\n setFormData({\n ...formData,\n productId: e.target.value,\n moduleId: undefined,\n pageId: undefined,\n })\n }\n className=\"mt-2 w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n placeholder={tr('tourBuilder.productSlugPlaceholder', 'vibecontrols')}\n required\n />\n ) : null}\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"module-id\">\n {tr('tourBuilder.labelModuleId', 'Module ID')}\n </label>\n <select\n id=\"module-id\"\n value={customModuleEntry ? '__custom__' : formData.moduleId || ''}\n onChange={(e) => {\n if (e.target.value === '__custom__') {\n setCustomModuleEntry(true);\n setFormData({ ...formData, moduleId: '', pageId: undefined });\n return;\n }\n\n setCustomModuleEntry(false);\n setCustomPageEntry(false);\n setFormData({\n ...formData,\n moduleId: e.target.value || undefined,\n pageId: undefined,\n });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n disabled={!formData.productId}\n aria-label={tr('tourBuilder.labelModuleId', 'Module ID')}\n >\n <option value=\"\">{tr('tourBuilder.allModules', 'All modules')}</option>\n {moduleOptions.map((moduleOption) => (\n <option key={moduleOption.moduleId} value={moduleOption.moduleId}>\n {moduleOption.label} ({moduleOption.moduleId})\n </option>\n ))}\n {showCurrentModuleOption ? (\n <option value={formData.moduleId}>\n {tr('tourBuilder.currentValue', 'Current: {{value}}', {\n value: formData.moduleId ?? '',\n })}\n </option>\n ) : null}\n <option value=\"__custom__\">\n {tr('tourBuilder.customModuleId', 'Custom module ID')}\n </option>\n </select>\n {customModuleEntry ? (\n <input\n id=\"module-id-custom\"\n aria-label={tr('tourBuilder.customModuleId', 'Custom module ID')}\n type=\"text\"\n value={formData.moduleId || ''}\n onChange={(e) =>\n setFormData({\n ...formData,\n moduleId: e.target.value || undefined,\n pageId: undefined,\n })\n }\n className=\"mt-2 w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n placeholder={tr('tourBuilder.moduleSlugPlaceholder', 'vibecontrols')}\n />\n ) : null}\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"page-id\">\n {tr('tourBuilder.labelPageId', 'Page ID')}\n </label>\n <select\n id=\"page-id\"\n value={customPageEntry ? '__custom__' : formData.pageId || ''}\n onChange={(e) => {\n if (e.target.value === '__custom__') {\n setCustomPageEntry(true);\n setFormData({ ...formData, pageId: '' });\n return;\n }\n\n setCustomPageEntry(false);\n setFormData({ ...formData, pageId: e.target.value || undefined });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n disabled={!formData.productId}\n aria-label={tr('tourBuilder.labelPageId', 'Page ID')}\n >\n <option value=\"\">{tr('tourBuilder.allPages', 'All pages')}</option>\n {pageOptions.map((pageOption) => (\n <option key={pageOption.pageId} value={pageOption.pageId}>\n {pageOption.label} ({pageOption.pageId})\n </option>\n ))}\n {showCurrentPageOption ? (\n <option value={formData.pageId}>\n {tr('tourBuilder.currentValue', 'Current: {{value}}', {\n value: formData.pageId ?? '',\n })}\n </option>\n ) : null}\n <option value=\"__custom__\">\n {tr('tourBuilder.customPageId', 'Custom page ID')}\n </option>\n </select>\n {customPageEntry ? (\n <input\n id=\"page-id-custom\"\n aria-label={tr('tourBuilder.customPageId', 'Custom page ID')}\n type=\"text\"\n value={formData.pageId || ''}\n onChange={(e) =>\n setFormData({ ...formData, pageId: e.target.value || undefined })\n }\n className=\"mt-2 w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n placeholder={tr('tourBuilder.pageSlugPlaceholder', 'agents')}\n />\n ) : null}\n </div>\n </div>\n\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={formData.isActive ?? true}\n onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}\n className=\"size-4 rounded\"\n aria-label={tr('tourBuilder.labelActive', 'Active')}\n />\n <span className=\"text-sm font-medium\">{tr('tourBuilder.labelActive', 'Active')}</span>\n </label>\n </div>\n </GlassCard>\n\n {/* Trigger Settings */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4\">{tr('tourBuilder.sectionTriggerSettings')}</h2>\n\n <div className=\"space-y-4\">\n {/* Trigger Type */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"trigger-type\">\n {tr('tourBuilder.labelTriggerType')}\n </label>\n <select\n id=\"trigger-type\"\n value={formData.triggerType || 'MANUAL'}\n onChange={(e) =>\n setFormData({ ...formData, triggerType: e.target.value as TourTriggerType })\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('tourBuilder.labelTriggerType')}\n >\n <option value=\"MANUAL\">{tr('tourBuilder.triggerManual')}</option>\n <option value=\"AUTO_FIRST_VISIT\">{tr('tourBuilder.triggerAutoFirstVisit')}</option>\n <option value=\"AUTO_ALWAYS\">{tr('tourBuilder.triggerAutoAlways')}</option>\n <option value=\"ON_EVENT\">{tr('tourBuilder.triggerOnEvent')}</option>\n <option value=\"SCHEDULED\">{tr('tourBuilder.triggerScheduled')}</option>\n </select>\n </div>\n\n {/* Priority */}\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"priority\">\n {tr('tourBuilder.labelPriority')}\n </label>\n <input\n id=\"priority\"\n type=\"number\"\n min=\"1\"\n max=\"100\"\n value={formData.priority || 50}\n onChange={(e) => {\n const priority = Number.parseInt(e.target.value, 10) || 50;\n setFormData({ ...formData, priority });\n }}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelPriority')}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"tour-start-date\">\n {tr('tourBuilder.labelStartDate', 'Start Date')}\n </label>\n <input\n id=\"tour-start-date\"\n type=\"datetime-local\"\n value={formatDateTimeLocal(formData.startDate)}\n onChange={(e) =>\n setFormData({\n ...formData,\n startDate: e.target.value\n ? new Date(e.target.value).toISOString()\n : undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelStartDate', 'Start Date')}\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\" htmlFor=\"tour-end-date\">\n {tr('tourBuilder.labelEndDate', 'End Date')}\n </label>\n <input\n id=\"tour-end-date\"\n type=\"datetime-local\"\n value={formatDateTimeLocal(formData.endDate)}\n onChange={(e) =>\n setFormData({\n ...formData,\n endDate: e.target.value ? new Date(e.target.value).toISOString() : undefined,\n })\n }\n className=\"w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('tourBuilder.labelEndDate', 'End Date')}\n />\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* API Error */}\n {error && (\n <div className=\"border border-status-error-border rounded-md p-4 bg-status-error-bg/10\">\n <h3 className=\"font-semibold text-status-error-text mb-2\">\n {tr('tourBuilder.errorTitle')}\n </h3>\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n </div>\n )}\n\n {/* Validation Errors */}\n {validationErrors.length > 0 && (\n <div className=\"border border-status-error-border rounded-md p-4 bg-status-error-bg/10\">\n <h3 className=\"font-semibold text-status-error-text mb-2\">\n {tr('tourBuilder.validationErrorsTitle')}\n </h3>\n <ul className=\"list-disc list-inside space-y-1\">\n {validationErrors.map((err) => (\n <li key={err.field} className=\"text-sm text-status-error-text\">\n {tr(err.message, err.params)}\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {/* Actions — one primary submit CTA, related navigation collapsed into the overflow menu */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <PermissionGate\n permission={isNew ? TOUR_PERMISSIONS.TOUR_CREATE : TOUR_PERMISSIONS.TOUR_UPDATE}\n >\n <Button\n type=\"submit\"\n disabled={saving || !canSave}\n className=\"flex-1 min-w-[200px] min-h-[44px]\"\n >\n <Save size={20} />\n {saving\n ? tr('tourBuilder.saving')\n : tourId && tourId !== 'new'\n ? tr('tourBuilder.updateTour')\n : tr('tourBuilder.createAndAddSteps')}\n </Button>\n </PermissionGate>\n {tourId && tourId !== 'new' && secondaryActions.length > 0 && (\n <CTAOverflowMenu actions={secondaryActions} />\n )}\n </div>\n </form>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAsCA,IAAM,KAAgC,KAEhC,KAA+B,CAAG;;;;;;;;;;;;;;;;;GAmBlC,KAA4B,CAAG;;;;;;;;;;;;;;;;;GAmB/B,KAA6B,CAAG;;;;;;;;;;;;;;;;;;AAgDtC,SAAS,EAAwB,GAAiB,GAAwC;AACxF,KAAI,CAAC,KAAU,OAAO,KAAW,SAAU,QAAO;CAElD,IAAM,IAAS,GACT,IAAc,EAAO;AAC3B,KAAI,aAAuB,MAAO,QAAO,EAAY;CAErD,IAAM,IAAS,EAAO;AACtB,KAAI,CAAC,MAAM,QAAQ,EAAO,IAAI,EAAO,WAAW,EAAG,QAAO;CAE1D,IAAM,IAAW,EACd,KAAK,MAAU;AACd,MAAI,aAAiB,MAAO,QAAO,EAAM;AACzC,MAAI,CAAC,KAAS,OAAO,KAAU,SAAU,QAAO;EAEhD,IAAM,IAAW,EAAgC;AACjD,SAAO,OAAO,KAAY,WAAW,IAAU;GAC/C,CACD,QAAQ,MAA+B,EAAQ,EAAS;AAE3D,QAAO,EAAS,SAAS,IAAI,EAAS,KAAK,KAAK,GAAG;;AAyBrD,IAAM,IAAuD;CAC3D;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACP;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CAID;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EACN,cAAc;EACf;CACD;EACE,WAAW;EACX,UAAU;EACV,aAAa;EACb,QAAQ;EACR,WAAW;EACX,MAAM;EA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"}
|
|
@@ -126,7 +126,7 @@ function j() {
|
|
|
126
126
|
children: [/* @__PURE__ */ w("select", {
|
|
127
127
|
value: ue,
|
|
128
128
|
onChange: (e) => pe(e.target.value),
|
|
129
|
-
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface text-text-primary",
|
|
129
|
+
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
|
|
130
130
|
"aria-label": j("toursList.statusAll"),
|
|
131
131
|
children: [
|
|
132
132
|
/* @__PURE__ */ C("option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToursListPage.js","names":[],"sources":["../../../src/tours/pages/ToursListPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Plus, Search, Grid, List as ListIcon, ListOrdered, Eye, Settings } from 'lucide-react';\nimport { Markdown, PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport {\n AuroraBackground,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n CTAOverflowMenu,\n type CTAAction,\n} from '@burdenoff/fe-libs/ui';\nimport { useToursList } from '../hooks/useToursList';\nimport { useTourLifecycle } from '../hooks/useTourLifecycle';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursStore } from '../store/toursStore';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useDeleteTourMutation } from '../../generated/global-operations';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { TourStatus } from '../types';\nimport type { Tour } from '../../generated/global-types';\nimport { useToursI18n } from '../i18n';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../utils/nativeBridge';\n\n/**\n * Tours list page\n * Displays all tours with filtering and search\n */\nexport function ToursListPage() {\n const { tr } = useToursI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useToursContext();\n const {\n viewMode,\n tourFilter,\n toursListRevision,\n setViewMode,\n updateTourFilter,\n notifyToursListChanged,\n } = useToursStore();\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n\n const canUpdateTour = hasPermission(TOUR_PERMISSIONS.TOUR_UPDATE);\n const canUpdateTourSteps = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_UPDATE);\n const canPreviewTour = hasPermission(TOUR_PERMISSIONS.TOUR_READ);\n const canDeleteTour = hasPermission(TOUR_PERMISSIONS.TOUR_DELETE);\n const canPublishTour = hasPermission(TOUR_PERMISSIONS.TOUR_PUBLISH);\n const canArchiveTour = hasPermission(TOUR_PERMISSIONS.TOUR_ARCHIVE);\n const canDuplicateTour = hasPermission(TOUR_PERMISSIONS.TOUR_DUPLICATE);\n\n // Fetch tours (includes both workspace-specific and global tours)\n const { tours, loading, fetchTours } = useToursList();\n\n const { publishTour, unpublishTour, archiveTour, duplicateTour } = useTourLifecycle();\n const [deleteTourMutation] = useDeleteTourMutation();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<TourStatus | 'all'>('all');\n\n // Fetch tours when filter changes. The list page is also the user-facing\n // tour browser, so backend permissions remain authoritative for actions while\n // normal users can still see published tours returned by the API.\n useEffect(() => {\n if (permissionsLoading) return;\n fetchTours({ ...tourFilter });\n }, [fetchTours, tourFilter, toursListRevision, permissionsLoading]);\n\n const handleSearch = (query: string) => {\n setSearchQuery(query);\n updateTourFilter({ search: query });\n };\n\n const handleStatusFilter = (status: TourStatus | 'all') => {\n setStatusFilter(status);\n updateTourFilter({ status });\n };\n\n const handleCreateTour = () => {\n const newPath = basePath.endsWith('/') ? `${basePath}new` : `${basePath}/new`;\n navigate(newPath);\n };\n\n const getStatusLabel = (status: TourStatus) => {\n switch (status) {\n case 'DRAFT':\n return tr('toursList.statusDraft');\n case 'PUBLISHED':\n return tr('toursList.statusPublished');\n case 'ARCHIVED':\n return tr('toursList.statusArchived');\n case 'SCHEDULED':\n return tr('toursList.statusScheduled');\n }\n };\n\n const handlePublish = async (id: string) => {\n nativeImpact('light');\n try {\n await publishTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorPublish'));\n else alert(tr('toursList.errorPublish'));\n }\n };\n\n const handleUnpublish = async (id: string) => {\n nativeImpact('light');\n try {\n await unpublishTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorUnpublish'));\n else alert(tr('toursList.errorUnpublish'));\n }\n };\n\n const handleArchive = async (id: string) => {\n nativeImpact('light');\n try {\n await archiveTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorArchive'));\n else alert(tr('toursList.errorArchive'));\n }\n };\n\n const handleDuplicate = async (id: string) => {\n nativeImpact('light');\n try {\n await duplicateTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDuplicate'));\n else alert(tr('toursList.errorDuplicate'));\n }\n };\n\n const handleDelete = async (id: string) => {\n nativeImpact('medium');\n const confirmMsg = tr('toursList.confirmDelete');\n const nativeResult = await nativeConfirm({\n title: tr('toursList.actionDelete'),\n message: confirmMsg,\n okButtonTitle: tr('toursList.actionDelete'),\n });\n const confirmed = nativeResult === null ? window.confirm(confirmMsg) : nativeResult;\n if (!confirmed) return;\n try {\n await deleteTourMutation({ variables: { id } });\n notifyToursListChanged();\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDelete'));\n else alert(tr('toursList.errorDelete'));\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-xl\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 p-1 sm:p-2\">\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n <GradientText>{tr('toursList.title')}</GradientText>\n </h1>\n <p className=\"text-sm sm:text-base text-text-secondary\">{tr('toursList.subtitle')}</p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_CREATE}>\n <Button\n type=\"button\"\n onClick={handleCreateTour}\n className=\"w-full sm:w-auto min-h-[44px]\"\n >\n <Plus size={20} />\n {tr('toursList.createTour')}\n </Button>\n </PermissionGate>\n </div>\n </div>\n\n <PagePurpose>{tr('toursList.purpose')}</PagePurpose>\n\n {/* Filters and Search */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n {/* Search */}\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('toursList.searchPlaceholder')}\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n aria-label={tr('toursList.searchPlaceholder')}\n />\n </div>\n\n {/* Status Filter */}\n <div className=\"flex gap-2\">\n <select\n value={statusFilter}\n onChange={(e) => handleStatusFilter(e.target.value as TourStatus | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface text-text-primary\"\n aria-label={tr('toursList.statusAll')}\n >\n <option value=\"all\">{tr('toursList.statusAll')}</option>\n <option value=\"DRAFT\">{tr('toursList.statusDraft')}</option>\n <option value=\"PUBLISHED\">{tr('toursList.statusPublished')}</option>\n <option value=\"ARCHIVED\">{tr('toursList.statusArchived')}</option>\n <option value=\"SCHEDULED\">{tr('toursList.statusScheduled')}</option>\n </select>\n\n {/* View Mode Toggle */}\n <div className=\"flex border border-border-subtle rounded-md overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'grid'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n aria-label={tr('toursList.gridView')}\n title={tr('toursList.gridView')}\n >\n <Grid size={20} />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'list'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n aria-label={tr('toursList.listView')}\n title={tr('toursList.listView')}\n >\n <ListIcon size={20} />\n </button>\n </div>\n </div>\n </div>\n\n {/* Tours List */}\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-sm sm:text-base text-text-secondary\">{tr('toursList.loading')}</p>\n </div>\n ) : tours.length > 0 ? (\n <div\n className={\n viewMode === 'grid'\n ? 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4'\n : 'space-y-3'\n }\n >\n {tours.map((tour: Tour) => {\n const status = tour.status as TourStatus;\n const canShowPublishAction = status === 'DRAFT' && canPublishTour;\n const canShowUnpublishAction = status === 'PUBLISHED' && canPublishTour;\n const canShowArchiveAction = status !== 'ARCHIVED' && canArchiveTour;\n\n const overflowActions: CTAAction[] = [];\n if (canShowPublishAction) {\n overflowActions.push({\n label: tr('toursList.actionPublish'),\n onSelect: () => void handlePublish(tour.id),\n });\n }\n if (canShowUnpublishAction) {\n overflowActions.push({\n label: tr('toursList.actionUnpublish'),\n onSelect: () => void handleUnpublish(tour.id),\n });\n }\n if (canDuplicateTour) {\n overflowActions.push({\n label: tr('toursList.actionDuplicate'),\n onSelect: () => void handleDuplicate(tour.id),\n });\n }\n if (canShowArchiveAction) {\n overflowActions.push({\n label: tr('toursList.actionArchive'),\n onSelect: () => void handleArchive(tour.id),\n });\n }\n if (canDeleteTour) {\n overflowActions.push({\n label: tr('toursList.actionDelete'),\n onSelect: () => void handleDelete(tour.id),\n intent: 'destructive',\n });\n }\n\n return (\n <GlassCard\n key={tour.id}\n treatment=\"plain\"\n className=\"p-4 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start justify-between mb-2\">\n <h3 className=\"font-semibold text-text-primary\">\n {canUpdateTour ? (\n <button\n type=\"button\"\n className=\"cursor-pointer hover:text-primary bg-transparent border-0 p-0 text-left font-inherit\"\n onClick={() => navigate(`${basePath}/${tour.id}/edit`)}\n >\n {tour.name}\n </button>\n ) : (\n tour.name\n )}\n </h3>\n <span\n className={`px-2 py-1 text-xs rounded-full ${\n tour.status === 'PUBLISHED'\n ? 'bg-success/10 text-success motion-safe:animate-vc-glow-pulse'\n : tour.status === 'DRAFT'\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-warning/10 text-warning'\n }`}\n >\n {getStatusLabel(status)}\n </span>\n </div>\n {tour.description && (\n <Markdown inline className=\"text-sm text-text-secondary mb-3\">\n {tour.description}\n </Markdown>\n )}\n <div className=\"flex items-center justify-between text-xs text-text-secondary mb-3\">\n <span>\n {tour.steps?.length || 0} {tr('toursList.steps')}\n </span>\n <span>\n {tour.viewCount} {tr('toursList.views')}\n </span>\n </div>\n {/* Quick Actions */}\n <div className=\"flex flex-wrap items-center gap-2 pt-3 border-t border-border-seam\">\n {canUpdateTourSteps && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/steps`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 flex items-center justify-center gap-1.5\"\n title={tr('toursList.editStepsTitle')}\n >\n <ListOrdered size={14} />\n {tr('toursList.stepsButton')}\n </button>\n )}\n {canPreviewTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/preview`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] text-xs border border-border-seam rounded hover:bg-accent flex items-center justify-center gap-1.5\"\n title={tr('toursList.previewTitle')}\n >\n <Eye size={14} />\n {tr('toursList.previewButton')}\n </button>\n )}\n {canUpdateTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/edit`);\n }}\n className=\"p-2 min-h-[44px] min-w-[44px] text-xs border border-border-seam rounded hover:bg-accent\"\n title={tr('toursList.settingsTitle')}\n aria-label={tr('toursList.settingsTitle')}\n >\n <Settings size={14} />\n </button>\n )}\n {/* Lifecycle actions collapsed into the overflow menu */}\n {overflowActions.length > 0 && <CTAOverflowMenu actions={overflowActions} />}\n </div>\n </GlassCard>\n );\n })}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={tr('toursList.emptyTitle')}\n description={tr('toursList.emptySubtitle')}\n action={\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_CREATE}>\n <Button type=\"button\" onClick={handleCreateTour} className=\"min-h-[44px]\">\n <Plus size={20} />\n {tr('toursList.createTour')}\n </Button>\n </PermissionGate>\n }\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToursListPage.js","names":[],"sources":["../../../src/tours/pages/ToursListPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Plus, Search, Grid, List as ListIcon, ListOrdered, Eye, Settings } from 'lucide-react';\nimport { Markdown, PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport {\n AuroraBackground,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n CTAOverflowMenu,\n type CTAAction,\n} from '@burdenoff/fe-libs/ui';\nimport { useToursList } from '../hooks/useToursList';\nimport { useTourLifecycle } from '../hooks/useTourLifecycle';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursStore } from '../store/toursStore';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useDeleteTourMutation } from '../../generated/global-operations';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { TourStatus } from '../types';\nimport type { Tour } from '../../generated/global-types';\nimport { useToursI18n } from '../i18n';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../utils/nativeBridge';\n\n/**\n * Tours list page\n * Displays all tours with filtering and search\n */\nexport function ToursListPage() {\n const { tr } = useToursI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useToursContext();\n const {\n viewMode,\n tourFilter,\n toursListRevision,\n setViewMode,\n updateTourFilter,\n notifyToursListChanged,\n } = useToursStore();\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n\n const canUpdateTour = hasPermission(TOUR_PERMISSIONS.TOUR_UPDATE);\n const canUpdateTourSteps = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_UPDATE);\n const canPreviewTour = hasPermission(TOUR_PERMISSIONS.TOUR_READ);\n const canDeleteTour = hasPermission(TOUR_PERMISSIONS.TOUR_DELETE);\n const canPublishTour = hasPermission(TOUR_PERMISSIONS.TOUR_PUBLISH);\n const canArchiveTour = hasPermission(TOUR_PERMISSIONS.TOUR_ARCHIVE);\n const canDuplicateTour = hasPermission(TOUR_PERMISSIONS.TOUR_DUPLICATE);\n\n // Fetch tours (includes both workspace-specific and global tours)\n const { tours, loading, fetchTours } = useToursList();\n\n const { publishTour, unpublishTour, archiveTour, duplicateTour } = useTourLifecycle();\n const [deleteTourMutation] = useDeleteTourMutation();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<TourStatus | 'all'>('all');\n\n // Fetch tours when filter changes. The list page is also the user-facing\n // tour browser, so backend permissions remain authoritative for actions while\n // normal users can still see published tours returned by the API.\n useEffect(() => {\n if (permissionsLoading) return;\n fetchTours({ ...tourFilter });\n }, [fetchTours, tourFilter, toursListRevision, permissionsLoading]);\n\n const handleSearch = (query: string) => {\n setSearchQuery(query);\n updateTourFilter({ search: query });\n };\n\n const handleStatusFilter = (status: TourStatus | 'all') => {\n setStatusFilter(status);\n updateTourFilter({ status });\n };\n\n const handleCreateTour = () => {\n const newPath = basePath.endsWith('/') ? `${basePath}new` : `${basePath}/new`;\n navigate(newPath);\n };\n\n const getStatusLabel = (status: TourStatus) => {\n switch (status) {\n case 'DRAFT':\n return tr('toursList.statusDraft');\n case 'PUBLISHED':\n return tr('toursList.statusPublished');\n case 'ARCHIVED':\n return tr('toursList.statusArchived');\n case 'SCHEDULED':\n return tr('toursList.statusScheduled');\n }\n };\n\n const handlePublish = async (id: string) => {\n nativeImpact('light');\n try {\n await publishTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorPublish'));\n else alert(tr('toursList.errorPublish'));\n }\n };\n\n const handleUnpublish = async (id: string) => {\n nativeImpact('light');\n try {\n await unpublishTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorUnpublish'));\n else alert(tr('toursList.errorUnpublish'));\n }\n };\n\n const handleArchive = async (id: string) => {\n nativeImpact('light');\n try {\n await archiveTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorArchive'));\n else alert(tr('toursList.errorArchive'));\n }\n };\n\n const handleDuplicate = async (id: string) => {\n nativeImpact('light');\n try {\n await duplicateTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDuplicate'));\n else alert(tr('toursList.errorDuplicate'));\n }\n };\n\n const handleDelete = async (id: string) => {\n nativeImpact('medium');\n const confirmMsg = tr('toursList.confirmDelete');\n const nativeResult = await nativeConfirm({\n title: tr('toursList.actionDelete'),\n message: confirmMsg,\n okButtonTitle: tr('toursList.actionDelete'),\n });\n const confirmed = nativeResult === null ? window.confirm(confirmMsg) : nativeResult;\n if (!confirmed) return;\n try {\n await deleteTourMutation({ variables: { id } });\n notifyToursListChanged();\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDelete'));\n else alert(tr('toursList.errorDelete'));\n }\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"relative overflow-hidden rounded-xl\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 p-1 sm:p-2\">\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-text-primary\">\n <GradientText>{tr('toursList.title')}</GradientText>\n </h1>\n <p className=\"text-sm sm:text-base text-text-secondary\">{tr('toursList.subtitle')}</p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_CREATE}>\n <Button\n type=\"button\"\n onClick={handleCreateTour}\n className=\"w-full sm:w-auto min-h-[44px]\"\n >\n <Plus size={20} />\n {tr('toursList.createTour')}\n </Button>\n </PermissionGate>\n </div>\n </div>\n\n <PagePurpose>{tr('toursList.purpose')}</PagePurpose>\n\n {/* Filters and Search */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n {/* Search */}\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('toursList.searchPlaceholder')}\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n aria-label={tr('toursList.searchPlaceholder')}\n />\n </div>\n\n {/* Status Filter */}\n <div className=\"flex gap-2\">\n <select\n value={statusFilter}\n onChange={(e) => handleStatusFilter(e.target.value as TourStatus | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n aria-label={tr('toursList.statusAll')}\n >\n <option value=\"all\">{tr('toursList.statusAll')}</option>\n <option value=\"DRAFT\">{tr('toursList.statusDraft')}</option>\n <option value=\"PUBLISHED\">{tr('toursList.statusPublished')}</option>\n <option value=\"ARCHIVED\">{tr('toursList.statusArchived')}</option>\n <option value=\"SCHEDULED\">{tr('toursList.statusScheduled')}</option>\n </select>\n\n {/* View Mode Toggle */}\n <div className=\"flex border border-border-subtle rounded-md overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'grid'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n aria-label={tr('toursList.gridView')}\n title={tr('toursList.gridView')}\n >\n <Grid size={20} />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'list'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-accent'\n }`}\n aria-label={tr('toursList.listView')}\n title={tr('toursList.listView')}\n >\n <ListIcon size={20} />\n </button>\n </div>\n </div>\n </div>\n\n {/* Tours List */}\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-sm sm:text-base text-text-secondary\">{tr('toursList.loading')}</p>\n </div>\n ) : tours.length > 0 ? (\n <div\n className={\n viewMode === 'grid'\n ? 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4'\n : 'space-y-3'\n }\n >\n {tours.map((tour: Tour) => {\n const status = tour.status as TourStatus;\n const canShowPublishAction = status === 'DRAFT' && canPublishTour;\n const canShowUnpublishAction = status === 'PUBLISHED' && canPublishTour;\n const canShowArchiveAction = status !== 'ARCHIVED' && canArchiveTour;\n\n const overflowActions: CTAAction[] = [];\n if (canShowPublishAction) {\n overflowActions.push({\n label: tr('toursList.actionPublish'),\n onSelect: () => void handlePublish(tour.id),\n });\n }\n if (canShowUnpublishAction) {\n overflowActions.push({\n label: tr('toursList.actionUnpublish'),\n onSelect: () => void handleUnpublish(tour.id),\n });\n }\n if (canDuplicateTour) {\n overflowActions.push({\n label: tr('toursList.actionDuplicate'),\n onSelect: () => void handleDuplicate(tour.id),\n });\n }\n if (canShowArchiveAction) {\n overflowActions.push({\n label: tr('toursList.actionArchive'),\n onSelect: () => void handleArchive(tour.id),\n });\n }\n if (canDeleteTour) {\n overflowActions.push({\n label: tr('toursList.actionDelete'),\n onSelect: () => void handleDelete(tour.id),\n intent: 'destructive',\n });\n }\n\n return (\n <GlassCard\n key={tour.id}\n treatment=\"plain\"\n className=\"p-4 border-border-seam shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start justify-between mb-2\">\n <h3 className=\"font-semibold text-text-primary\">\n {canUpdateTour ? (\n <button\n type=\"button\"\n className=\"cursor-pointer hover:text-primary bg-transparent border-0 p-0 text-left font-inherit\"\n onClick={() => navigate(`${basePath}/${tour.id}/edit`)}\n >\n {tour.name}\n </button>\n ) : (\n tour.name\n )}\n </h3>\n <span\n className={`px-2 py-1 text-xs rounded-full ${\n tour.status === 'PUBLISHED'\n ? 'bg-success/10 text-success motion-safe:animate-vc-glow-pulse'\n : tour.status === 'DRAFT'\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-warning/10 text-warning'\n }`}\n >\n {getStatusLabel(status)}\n </span>\n </div>\n {tour.description && (\n <Markdown inline className=\"text-sm text-text-secondary mb-3\">\n {tour.description}\n </Markdown>\n )}\n <div className=\"flex items-center justify-between text-xs text-text-secondary mb-3\">\n <span>\n {tour.steps?.length || 0} {tr('toursList.steps')}\n </span>\n <span>\n {tour.viewCount} {tr('toursList.views')}\n </span>\n </div>\n {/* Quick Actions */}\n <div className=\"flex flex-wrap items-center gap-2 pt-3 border-t border-border-seam\">\n {canUpdateTourSteps && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/steps`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 flex items-center justify-center gap-1.5\"\n title={tr('toursList.editStepsTitle')}\n >\n <ListOrdered size={14} />\n {tr('toursList.stepsButton')}\n </button>\n )}\n {canPreviewTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/preview`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] text-xs border border-border-seam rounded hover:bg-accent flex items-center justify-center gap-1.5\"\n title={tr('toursList.previewTitle')}\n >\n <Eye size={14} />\n {tr('toursList.previewButton')}\n </button>\n )}\n {canUpdateTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/edit`);\n }}\n className=\"p-2 min-h-[44px] min-w-[44px] text-xs border border-border-seam rounded hover:bg-accent\"\n title={tr('toursList.settingsTitle')}\n aria-label={tr('toursList.settingsTitle')}\n >\n <Settings size={14} />\n </button>\n )}\n {/* Lifecycle actions collapsed into the overflow menu */}\n {overflowActions.length > 0 && <CTAOverflowMenu actions={overflowActions} />}\n </div>\n </GlassCard>\n );\n })}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={tr('toursList.emptyTitle')}\n description={tr('toursList.emptySubtitle')}\n action={\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_CREATE}>\n <Button type=\"button\" onClick={handleCreateTour} className=\"min-h-[44px]\">\n <Plus size={20} />\n {tr('toursList.createTour')}\n </Button>\n </PermissionGate>\n }\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|