@burdenoff/microfe-tours 2026.1014.1 → 2026.1114.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/featureFlags.d.ts +24 -0
- package/dist/constants/featureFlags.js +1 -2
- package/dist/constants/featureFlags.js.map +1 -1
- package/dist/constants/permissions.d.ts +48 -0
- package/dist/constants/permissions.js +1 -2
- package/dist/constants/permissions.js.map +1 -1
- package/dist/dev/main.d.ts +0 -0
- package/dist/generated/global-operations.d.ts +1318 -0
- package/dist/generated/global-operations.js +2 -3
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.d.ts +26469 -0
- package/dist/generated/wspace-operations.d.ts +1 -0
- package/dist/generated/wspace-types.d.ts +108647 -0
- package/dist/index.d.ts +5 -0
- package/dist/test/happydom.d.ts +1 -0
- package/dist/test/testing-library.d.ts +1 -0
- package/dist/tours/ToursAdminRoot.d.ts +14 -0
- package/dist/tours/ToursAdminRoot.js +14 -13
- package/dist/tours/ToursAdminRoot.js.map +1 -1
- package/dist/tours/ToursAdminRoutes.d.ts +11 -0
- package/dist/tours/ToursAdminRoutes.js +12 -13
- package/dist/tours/ToursAdminRoutes.js.map +1 -1
- package/dist/tours/ToursRoot.d.ts +9 -0
- package/dist/tours/ToursRoot.js +1 -1
- package/dist/tours/ToursUserRoot.d.ts +13 -0
- package/dist/tours/ToursUserRoot.js +14 -13
- package/dist/tours/ToursUserRoot.js.map +1 -1
- package/dist/tours/ToursUserRoutes.d.ts +14 -0
- package/dist/tours/ToursUserRoutes.js +13 -13
- package/dist/tours/ToursUserRoutes.js.map +1 -1
- package/dist/tours/components/MarkdownEditor.d.ts +12 -0
- package/dist/tours/components/MarkdownEditor.js +1 -2
- package/dist/tours/components/MarkdownEditor.js.map +1 -1
- package/dist/tours/components/RouteHelpers.d.ts +31 -0
- package/dist/tours/components/RouteHelpers.js +1 -2
- package/dist/tours/components/RouteHelpers.js.map +1 -1
- package/dist/tours/components/ToursAdminNavRegistrar.d.ts +23 -0
- package/dist/tours/components/ToursAdminNavRegistrar.js +4 -3
- package/dist/tours/components/ToursAdminNavRegistrar.js.map +1 -1
- package/dist/tours/hooks/useMediaLibrary.d.ts +20 -0
- package/dist/tours/hooks/useMediaLibrary.js +1 -2
- package/dist/tours/hooks/useMediaLibrary.js.map +1 -1
- package/dist/tours/hooks/useStepSequencer.d.ts +18 -0
- package/dist/tours/hooks/useStepSequencer.js +16 -13
- package/dist/tours/hooks/useStepSequencer.js.map +1 -1
- package/dist/tours/hooks/useTourAnalytics.d.ts +30 -0
- package/dist/tours/hooks/useTourAnalytics.js +1 -2
- package/dist/tours/hooks/useTourAnalytics.js.map +1 -1
- package/dist/tours/hooks/useTourBuilder.d.ts +59 -0
- package/dist/tours/hooks/useTourBuilder.js +10 -6
- package/dist/tours/hooks/useTourBuilder.js.map +1 -1
- package/dist/tours/hooks/useTourLifecycle.d.ts +21 -0
- package/dist/tours/hooks/useTourLifecycle.js +4 -5
- package/dist/tours/hooks/useTourLifecycle.js.map +1 -1
- package/dist/tours/hooks/useTourPreview.d.ts +31 -0
- package/dist/tours/hooks/useTourPreview.js +5 -6
- package/dist/tours/hooks/useTourPreview.js.map +1 -1
- package/dist/tours/hooks/useToursList.d.ts +15 -0
- package/dist/tours/hooks/useToursList.js +1 -2
- package/dist/tours/hooks/useToursList.js.map +1 -1
- package/dist/tours/hooks/useToursPermissionAccess.d.ts +15 -0
- package/dist/tours/hooks/useToursPermissionAccess.js +1 -2
- package/dist/tours/hooks/useToursPermissionAccess.js.map +1 -1
- package/dist/tours/i18n.d.ts +413 -0
- package/dist/tours/i18n.js +1 -2
- package/dist/tours/i18n.js.map +1 -1
- package/dist/tours/index.d.ts +21 -0
- package/dist/tours/index.js +6 -5
- package/dist/tours/pages/AnalyticsDashboardPage.d.ts +5 -0
- package/dist/tours/pages/AnalyticsDashboardPage.js +6 -7
- package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/tours/pages/MediaLibraryPage.d.ts +5 -0
- package/dist/tours/pages/MediaLibraryPage.js +10 -11
- package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
- package/dist/tours/pages/MyToursPage.d.ts +10 -0
- package/dist/tours/pages/MyToursPage.js +5 -6
- package/dist/tours/pages/MyToursPage.js.map +1 -1
- package/dist/tours/pages/MyToursProgressPage.d.ts +5 -0
- package/dist/tours/pages/MyToursProgressPage.js +5 -6
- package/dist/tours/pages/MyToursProgressPage.js.map +1 -1
- package/dist/tours/pages/SchedulerPage.d.ts +5 -0
- package/dist/tours/pages/SchedulerPage.js +10 -11
- package/dist/tours/pages/SchedulerPage.js.map +1 -1
- package/dist/tours/pages/StepEditorPage.d.ts +6 -0
- package/dist/tours/pages/StepEditorPage.js +302 -309
- package/dist/tours/pages/StepEditorPage.js.map +1 -1
- package/dist/tours/pages/SystemAnalyticsPage.d.ts +5 -0
- package/dist/tours/pages/SystemAnalyticsPage.js +1 -2
- package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
- package/dist/tours/pages/TemplateLibraryPage.d.ts +5 -0
- package/dist/tours/pages/TemplateLibraryPage.js +15 -16
- package/dist/tours/pages/TemplateLibraryPage.js.map +1 -1
- package/dist/tours/pages/TourBuilderPage.d.ts +1 -0
- package/dist/tours/pages/TourBuilderPage.js +273 -300
- package/dist/tours/pages/TourBuilderPage.js.map +1 -1
- package/dist/tours/pages/TourPreviewPage.d.ts +5 -0
- package/dist/tours/pages/TourPreviewPage.js +9 -10
- package/dist/tours/pages/TourPreviewPage.js.map +1 -1
- package/dist/tours/pages/ToursListPage.d.ts +5 -0
- package/dist/tours/pages/ToursListPage.js +157 -157
- package/dist/tours/pages/ToursListPage.js.map +1 -1
- package/dist/tours/providers/ToursProvider.d.ts +25 -0
- package/dist/tours/providers/ToursProvider.js +1 -2
- package/dist/tours/providers/ToursProvider.js.map +1 -1
- package/dist/tours/store/toursStore.d.ts +40 -0
- package/dist/tours/store/toursStore.js +5 -6
- package/dist/tours/store/toursStore.js.map +1 -1
- package/dist/tours/telemetry.d.ts +7 -0
- package/dist/tours/telemetry.js +1 -2
- package/dist/tours/telemetry.js.map +1 -1
- package/dist/tours/types/index.d.ts +348 -0
- package/dist/tours/utils/index.d.ts +28 -0
- package/dist/tours/utils/index.js +3 -4
- package/dist/tours/utils/index.js.map +1 -1
- package/dist/tours/utils/joinPath.d.ts +8 -0
- package/dist/tours/utils/joinPath.js +1 -2
- package/dist/tours/utils/joinPath.js.map +1 -1
- package/dist/utils/nativeBridge.d.ts +2 -0
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +62 -51
|
@@ -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 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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"}
|
|
1
|
+
{"version":3,"file":"StepEditorPage.js","names":["StepEditorPage","useToursI18n","useParams","navigate","useNavigate","toursContext","useToursContext","eventBus","useEventBus","useToursPermissionAccess","canCreateStep","hasPermission","TOUR_PERMISSIONS","canUpdateStep","canDeleteStep","useTourBuilder","tourId","useStepSequencer","mediaLoading","useMediaLibrary","isSidebarOpen","setIsSidebarOpen","useState","isMobile","setIsMobile","attachmentMediaId","setAttachmentMediaId","attachmentPosition","setAttachmentPosition","attachingMedia","setAttachingMedia","useEffect","checkMobile","mobile","loadTour","fetchMedia","selectedStepId","stepId","selectedStep","steps","step","s","selectStep","handleAddStep","handleSaveSteps","syncSteps","handleBack","basePath","handleAttachMedia","tr","attachToStep","handleSelectStep","ArrowLeft","tour","Menu","Plus","PermissionGate","Button","saving","Save","PagePurpose","X","e","index","moveStep","ChevronUp","ChevronDown","deleteStep","Trash2","currentIndex","ChevronLeft","ChevronRight","updateStepField","MarkdownEditor","content","OptionalFields","rawValue","GlassCard","Paperclip","item","media","IllustratedEmptyState"],"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":";;;;;;;;;;;;;;;;;AAuCA,SAAgBA,IAAiB;CAC/B,IAAM,EAAE,UAAOC,EAAa,GAEtB,EAAE,WAAQ,cAAWC,GAAU,GAC/BC,IAAWC,GAAY,GACvBC,IAAeC,GAAgB,GAC/B,EAAE,cAAW,QAAQD,GACrBE,IAAWC,GAAY,GACvB,EAAE,qBAAkBC,GAAyB,GAC7CC,IAAgBC,EAAcC,EAAiB,gBAAgB,GAC/DC,IAAgBF,EAAcC,EAAiB,gBAAgB,GAC/DE,IAAgBH,EAAcC,EAAiB,gBAAgB,GAE/D,EAAE,SAAM,aAAU,cAAW,cAAWG,EAAeC,CAAM,GAC7D,EAAE,UAAO,iBAAc,eAAY,gBAAY,eAAY,oBAAiB,gBAChFC,EAAiB,GACb,EAAE,WAAO,eAAY,kBAAc,SAASC,OAAiBC,EAAgB,GAG7E,CAACC,GAAeC,KAAoBC,EAAS,EAAK,GAClD,CAACC,GAAUC,MAAeF,QACxB,OAAO,SAAW,OAAe,OAAO,aAAa,GAC7D,GACM,CAACG,GAAmBC,KAAwBJ,EAAS,EAAE,GACvD,CAACK,GAAoBC,MAAyBN,EAClD,MACF,GACM,CAACO,IAAgBC,KAAqBR,EAAS,EAAK;CA0B1D,AAvBAS,QAAgB;EACd,IAAMC,UAAoB;GACxB,IAAMC,IAAS,OAAO,aAAa;GAGnC,AAFAT,GAAYS,CAAM,GAEbA,KACHZ,EAAiB,EAAK;EAE1B;EAEA,OADA,OAAO,iBAAiB,UAAUW,CAAW,SAChC,OAAO,oBAAoB,UAAUA,CAAW;CAC/D,GAAG,CAAC,CAAC,GAELD,QAAgB;EACd,AAAIf,KACFkB,EAASlB,CAAM;CAEnB,GAAG,CAACA,GAAQkB,CAAQ,CAAC,GAErBH,QAAgB;EACd,EAAgB;CAClB,GAAG,CAACI,CAAU,CAAC,GAEfJ,QAAgB;EACd,IAAMK,IAAiBC,KAAUC,GAAc;EAC/C,IAAIF,KAAkBG,EAAM,SAAS,GAAG;GACtC,IAAMC,IAAOD,EAAM,MAAM,MAAME,EAAE,OAAOL,CAAc;GACtD,AAAII,KACFE,EAAWF,CAAI;EAEnB;CACF,GAAG;EAACH;EAAQE;EAAOG;EAAYJ,GAAc;CAAE,CAAC;CAEhD,IAAMK,UAAsB;EAC1B,GAAW;CACb,GAEMC,KAAkB,YAAY;EAClC,IAAI5B,GACF,IAAI;GACF,MAAM6B,EAAU7B,GAAQuB,CAAK;EAC/B,QAAQ;GACN,EAAmBhC,GAAU,aAAaF,CAAY;EACxD;CAEJ,GAEMyC,WAAmB;EACvB,EAAS,GAAGC,EAAS,GAAG/B,EAAO,MAAM;CACvC,GAEMgC,KAAoB,YAAY;EACpC,IAAKV,KAAiBtB,KAAWS,GACjC;OAAIa,EAAa,GAAG,WAAW,OAAO,GACpC,OAAO,MACLW,EAAG,+BAA+B,iDAAiD,CACrF;QAIF,IAAI;IASF,AARAnB,EAAkB,EAAI,GACtB,MAAMoB,GACJZ,EAAa,IACbb,GACAE,IACCW,EAAa,OAAO,UAAU,KAAK,CACtC,GACA,MAAMJ,EAASlB,CAAM,GACrBU,EAAqB,EAAE;GACzB,QAAQ;IACN,EAAmBnB,GAAU,gBAAgBF,CAAY;GAC3D,UAAU;IACR,EAAkB,EAAK;GACzB;EAhBA;CAiBF,GAEM8C,KAAoB,MAA4B;EAGpD,AAFAT,EAAWF,CAAI,GAEXjB,KACFF,EAAiB,EAAK;CAE1B;CAEA,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GAEE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAASyB;MACT,WAAU;MACV,cAAYG,EAAG,mBAAmB;MAElC,UAAA,kBAACG,GAAD,EAAW,MAAM,GAAK,CAAA;KAChB,CAAA,GACR,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;OAAd,UAAA;QACGC,GAAM,QAAQJ,EAAG,yBAAyB;QAAE;QAAIA,EAAG,uBAAuB;OACzE;MACJ,CAAA,GAAA,kBAAC,KAAD;OAAG,WAAU;OAAb,UAAA;QACGV,EAAM;QAAO;QAAEU,EAAG,uBAAuB;OACzC;MACA,CAAA,CAAA;KACF,CAAA,CAAA;IAGL,CAAA,GAAA,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MAEG1B,KACC,kBAAC,UAAD;OACE,MAAK;OACL,eAAeF,EAAiB,CAACD,CAAa;OAC9C,WAAU;OAHZ,UAAA;QAKE,kBAACkC,GAAD,EAAM,MAAM,GAAK,CAAA;QACjB,kBAAC,QAAD;SAAM,WAAU;SAAW,UAAAL,EAAG,kBAAkB;QAAQ,CAAA;QACvDV,EAAM,SAAS,KACd,kBAAC,QAAD;SAAM,WAAU;SACb,UAAAA,EAAM;QACH,CAAA;OAEF;;MAGT7B,KACC,kBAAC,UAAD;OACE,MAAK;OACL,SAASiC;OACT,WAAU;OAHZ,UAAA,CAKE,kBAACY,GAAD,EAAM,MAAM,GAAK,CAAA,GACjB,kBAAC,QAAD;QAAM,WAAU;QAAoB,UAAAN,EAAG,oBAAoB;OAAQ,CAAA,CAC7D;;MAEV,kBAACO,IAAD;OAAgB,YAAY5C,EAAiB;OAC3C,UAAA,kBAAC6C,GAAD;QACE,MAAK;QACL,SAASb;QACT,UAAUc,KAAU,CAAC7C;QACrB,WAAU;QAJZ,UAAA,CAME,kBAAC8C,GAAD,EAAM,MAAM,GAAK,CAAA,GACjB,kBAAC,QAAD;SAAM,WAAU;SACb,UAASV,EAATS,IAAY,sBAA0B,iBAAiB;QACpD,CAAA,CACA;;MACM,CAAA;KACb;IACF,CAAA,CAAA;;GAEL,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAACE,IAAD,EAAA,UAAcX,EAAG,oBAAoB,EAAe,CAAA;GACjD,CAAA;GAEL,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KAEG1B,KAAYH,KACX,kBAAC,UAAD;MACE,MAAK;MACL,cAAY6B,EAAG,uBAAuB;MACtC,WAAU;MACV,eAAe5B,EAAiB,EAAK;KACtC,CAAA;KAIH,kBAAC,OAAD;MACE,WAAW;cACPE,IAAW,4GAA4G,wBAAwB;cAC/IA,KAAY,CAACH,IAAgB,sBAAsB,gBAAgB;;;MAHzE,UAAA,CAQGG,KACC,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;QAAiB,UAAA0B,EAAG,sBAAsB;OAAM,CAAA,GAC9D,kBAAC,UAAD;QACE,MAAK;QACL,eAAe5B,EAAiB,EAAK;QACrC,WAAU;QACV,cAAY4B,EAAG,uBAAuB;QAEtC,UAAA,kBAACY,GAAD,EAAG,MAAM,GAAK,CAAA;OACR,CAAA,CACL;MAGP,CAAA,GAAA,kBAAC,OAAD;OAAK,WAAU;OACZ,UAAAtB,EAAM,WAAW,IAChB,kBAAC,OAAD;QAAK,WAAU;QAAf,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;SAAW,UAAAU,EAAG,uBAAuB;QAAK,CAAA,GACvD,kBAAC,KAAD;SAAG,WAAU;SAAgB,UAAAA,EAAG,yBAAyB;QAAK,CAAA,CAC3D;OAEL,CAAA,IAAAV,EAAM,KAAK,GAAM,MACf,kBAAC,OAAD;QAEE,MAAK;QACL,UAAU;QACV,eAAeY,EAAiBX,CAAI;QACpC,YAAY,MAAM;SAChB,CAAIsB,EAAE,QAAQ,WAAWA,EAAE,QAAQ,SACjCA,EAAE,eAAe,GACjBX,EAAiBX,CAAI;QAEzB;QACA,WAAW,0DACTF,GAAc,OAAOE,EAAK,KACtB,sEACA;QAGN,UAAA,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA;WACE,kBAAC,OAAD;YAAK,WAAU;YAAf,UAAA;aACGS,EAAG,uBAAuB;aAAE;aAAEc,IAAQ;YACpC;;WACL,kBAAC,OAAD;YAAK,WAAU;YACZ,UAAAvB,EAAK,SAASS,EAAG,qBAAqB;WACpC,CAAA;WACJT,EAAK,kBACJ,kBAAC,OAAD;YAAK,WAAU;YACZ,UAAAA,EAAK;WACH,CAAA;UAEJ;SACJ,CAAA,GAAA1B,KACC,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA;WACE,kBAAC,UAAD;YACE,MAAK;YACL,UAAU,MAAM;aAEd,AADA,EAAE,gBAAgB,GACdiD,IAAQ,KACVC,EAASD,GAAOA,IAAQ,CAAC;YAE7B;YACA,WAAU;YACV,OAAOd,EAAG,yBAAyB,cAAc;YACjD,cAAYA,EAAG,yBAAyB,cAAc;YACtD,UAAUc,MAAU;YAEpB,UAAA,kBAACE,GAAD,EAAW,MAAM,GAAK,CAAA;WAChB,CAAA;WACR,kBAAC,UAAD;YACE,MAAK;YACL,UAAU,MAAM;aAEd,AADA,EAAE,gBAAgB,GACdF,IAAQxB,EAAM,SAAS,KACzByB,EAASD,GAAOA,IAAQ,CAAC;YAE7B;YACA,WAAU;YACV,OAAOd,EAAG,2BAA2B,gBAAgB;YACrD,cAAYA,EAAG,2BAA2B,gBAAgB;YAC1D,UAAUc,MAAUxB,EAAM,SAAS;YAEnC,UAAA,kBAAC2B,GAAD,EAAa,MAAM,GAAK,CAAA;WAClB,CAAA;WACR,kBAAC,UAAD;YACE,MAAK;YACL,UAAU,MAAM;aAEd,AADAJ,EAAE,gBAAgB,GAClBK,EAAW3B,EAAK,EAAE;YACpB;YACA,WAAU;YACV,OAAOS,EAAG,uBAAuB;YACjC,cAAYA,EAAG,uBAAuB;YAEtC,UAAA,kBAACmB,GAAD,EAAQ,MAAM,GAAK,CAAA;WACb,CAAA;UACL;SAEJ,CAAA,CAAA;;OACF,GA7EE5B,EAAK,EA6EP,CACN;MAEA,CAAA,CACF;;KAGL,kBAAC,OAAD;MAAK,WAAU;MACZ,UAAAF,IACC,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QAEGf,KAAYgB,EAAM,SAAS,KAC1B,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA;UACE,kBAAC,UAAD;WACE,MAAK;WACL,eAAe;YACb,IAAM8B,IAAe9B,EAAM,WAAW,MAAME,EAAE,OAAOH,EAAa,EAAE;YACpE,AAAI+B,IAAe,KACjB3B,EAAWH,EAAM8B,IAAe,EAAE;WAEtC;WACA,UAAU9B,EAAM,WAAW,MAAME,EAAE,OAAOH,EAAa,EAAE,MAAM;WAC/D,WAAU;WACV,cAAYW,EAAG,yBAAyB;WAExC,UAAA,kBAACqB,GAAD,EAAa,MAAM,GAAK,CAAA;UAClB,CAAA;UACR,kBAAC,QAAD;WAAM,WAAU;WAAhB,UAAA;YACGrB,EAAG,uBAAuB;YAAG;YAC7BV,EAAM,WAAW,MAAME,EAAE,OAAOH,EAAa,EAAE,IAAI;YAAE;YAAEW,EAAG,eAAe;YAAG;YAC5EV,EAAM;WACH;;UACN,kBAAC,UAAD;WACE,MAAK;WACL,eAAe;YACb,IAAM8B,IAAe9B,EAAM,WAAW,MAAME,EAAE,OAAOH,EAAa,EAAE;YACpE,AAAI+B,IAAe9B,EAAM,SAAS,KAChCG,EAAWH,EAAM8B,IAAe,EAAE;WAEtC;WACA,UAAU9B,EAAM,WAAW,MAAME,EAAE,OAAOH,EAAa,EAAE,MAAMC,EAAM,SAAS;WAC9E,WAAU;WACV,cAAYU,EAAG,qBAAqB;WAEpC,UAAA,kBAACsB,GAAD,EAAc,MAAM,GAAK,CAAA;UACnB,CAAA;SACL;;QAGP,kBAAC,MAAD;SAAI,WAAU;SAAoC,UAAAtB,EAAG,qBAAqB;QAAM,CAAA;QAGhF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SAAiC,SAAQ;SAA1D,UAAA,CACGA,EAAG,sBAAsB,GAAE,IACvB;QACP,CAAA,GAAA,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,OAAOX,EAAa;SACpB,WAAW,MAAMkC,EAAgBlC,EAAa,IAAI,SAAS,EAAE,OAAO,KAAK;SACzE,WAAU;SACV,aAAaW,EAAG,iCAAiC;SACjD,cAAYA,EAAG,sBAAsB;QACtC,CAAA,CACE,EAAA,CAAA;QAGL,kBAACwB,GAAD;SACE,IAAG;SACH,OAAO,GAAGxB,EAAG,oBAAoB,EAAE;SACnC,OAAOX,EAAa;SACpB,WAAW,MAAYkC,EAAgBlC,EAAa,IAAI,WAAWoC,CAAO;SAC1E,MAAM;SACN,aAAazB,EAAG,+BAA+B;SAC/C,cAAYA,EAAG,oBAAoB;QACpC,CAAA;QAGD,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UACE,WAAU;UACV,SAAQ;UAFV,UAAA;WAIGA,EAAG,wBAAwB;WAAE;WAC9B,kBAAC,QAAD;YAAM,WAAU;YAAhB,UAAA;aAAuD;aACnDA,EAAG,0BAA0B;aAAE;YAC7B;;UACD;;SACP,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAOX,EAAa,kBAAkB;UACtC,WAAW,MACTkC,EAAgBlC,EAAa,IAAI,kBAAkB,EAAE,OAAO,KAAK;UAEnE,aAAaW,EAAG,mCAAmC;UACnD,WAAU;UACV,cAAY,GAAGA,EAAG,wBAAwB,EAAE,MAAMA,EAAG,0BAA0B,EAAE;SAClF,CAAA;SACD,kBAAC,KAAD;UAAG,WAAU;UAAoC,UAAAA,EAAG,yBAAyB;SAAK,CAAA;QAC/E,EAAA,CAAA;QAGL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SAAiC,SAAQ;SACvD,UAAAA,EAAG,6BAA6B;QAC5B,CAAA,GACP,kBAAC,UAAD;SACE,IAAG;SACH,OAAOX,EAAa;SACpB,WAAW,MAAMkC,EAAgBlC,EAAa,IAAI,aAAa,EAAE,OAAO,KAAK;SAC7E,WAAU;SACV,cAAYW,EAAG,6BAA6B;SAL9C,UAAA;UAOE,kBAAC,UAAD;WAAQ,OAAM;WAAQ,UAAAA,EAAG,0BAA0B;UAAU,CAAA;UAC7D,kBAAC,UAAD;WAAQ,OAAM;WAAO,UAAAA,EAAG,yBAAyB;UAAU,CAAA;UAC3D,kBAAC,UAAD;WAAQ,OAAM;WAAU,UAAAA,EAAG,4BAA4B;UAAU,CAAA;UACjE,kBAAC,UAAD;WAAQ,OAAM;WAAQ,UAAAA,EAAG,0BAA0B;UAAU,CAAA;UAC7D,kBAAC,UAAD;WAAQ,OAAM;WAAS,UAAAA,EAAG,2BAA2B;UAAU,CAAA;UAC/D,kBAAC,UAAD;WAAQ,OAAM;WAAU,UAAAA,EAAG,4BAA4B;UAAU,CAAA;SAC3D;QACL,CAAA,CAAA,EAAA,CAAA;QAGL,kBAAC,WAAD;SAAS,WAAU;SAAnB,UAAA,CACE,kBAAC,WAAD;UAAS,WAAU;UAChB,UAAAA,EAAG,oCAAoC;SACjC,CAAA,GACT,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA;WACE,kBAAC,KAAD;YAAG,WAAU;YACV,UAAAA,EAAG,+BAA+B;WAClC,CAAA;WACH,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;YACE,WAAU;YACV,SAAQ;YAEP,UAAAA,EAAG,2BAA2B;WAC1B,CAAA,GACP,kBAAC,SAAD;YACE,IAAG;YACH,MAAK;YACL,OAAOX,EAAa,wBAAwB;YAC5C,WAAW,MACTkC,EAAgBlC,EAAa,IAAI,wBAAwB,EAAE,OAAO,KAAK;YAEzE,aAAaW,EAAG,8BAA8B;YAC9C,WAAU;YACV,cAAYA,EAAG,2BAA2B;WAC3C,CAAA,CACE,EAAA,CAAA;WACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;YACE,WAAU;YACV,SAAQ;YAEP,UAAAA,EAAG,2BAA2B;WAC1B,CAAA,GACP,kBAAC,SAAD;YACE,IAAG;YACH,MAAK;YACL,OAAOX,EAAa,wBAAwB;YAC5C,WAAW,MACTkC,EAAgBlC,EAAa,IAAI,wBAAwB,EAAE,OAAO,KAAK;YAEzE,aAAaW,EAAG,8BAA8B;YAC9C,WAAU;YACV,cAAYA,EAAG,2BAA2B;WAC3C,CAAA,CACE,EAAA,CAAA;WACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;YACE,WAAU;YACV,SAAQ;YAEP,UAAAA,EAAG,4BAA4B;WAC3B,CAAA,GACP,kBAAC,SAAD;YACE,IAAG;YACH,MAAK;YACL,OAAOX,EAAa,yBAAyB;YAC7C,WAAW,MACTkC,EAAgBlC,EAAa,IAAI,yBAAyB,EAAE,OAAO,KAAK;YAE1E,aAAaW,EAAG,8BAA8B;YAC9C,WAAU;YACV,cAAYA,EAAG,4BAA4B;WAC5C,CAAA,CACE,EAAA,CAAA;UACF;SACE,CAAA,CAAA;;QAET,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,SAAD;UAAO,WAAU;UAAjB,UAAA,CACE,kBAAC,SAAD;WACE,MAAK;WACL,SAASX,EAAa,sBAAsB;WAC5C,WAAW,MACTkC,EAAgBlC,EAAa,IAAI,sBAAsB,EAAE,OAAO,OAAO;WAEzE,WAAU;WACV,cAAYW,EAAG,iCAAiC,qBAAqB;UACtE,CAAA,GACD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;WAAK,WAAU;WACZ,UAAAA,EAAG,iCAAiC,qBAAqB;UACvD,CAAA,GACL,kBAAC,OAAD;WAAK,WAAU;WACZ,UAAAA,EACC,4CACA,mEACF;UACG,CAAA,CACF,EAAA,CAAA,CACA;SAEP,CAAA,GAAA,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UACE,WAAU;UACV,SAAQ;UAEP,UAAAA,EAAG,6BAA6B,uBAAuB;SACnD,CAAA,GACP,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,KAAI;UACJ,OAAOX,EAAa,kBAAkB;UACtC,WAAW,MACTkC,EACElC,EAAa,IACb,kBACA,EAAE,OAAO,QAAQ,OAAO,EAAE,OAAO,KAAK,IAAI,KAAA,CAC5C;UAEF,WAAU;UACV,aAAY;UACZ,cAAYW,EAAG,6BAA6B,uBAAuB;SACpE,CAAA,CACE,EAAA,CAAA,CACF;;QAGL,kBAAC0B,IAAD;SACE,OAAO1B,EAAG,+BAA+B,mBAAmB;SAC5D,aAAaA,EACX,0CACA,yDACF;SALF,UAAA,CAOE,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;WACE,WAAU;WACV,SAAQ;WAEP,UAAAA,EAAG,8BAA8B,mBAAmB;UAChD,CAAA,GACP,kBAAC,SAAD;WACE,IAAG;WACH,MAAK;WACL,OAAOX,EAAa,mBAAmB;WACvC,WAAW,MACTkC,EACElC,EAAa,IACb,mBACA,EAAE,OAAO,SAAS,KAAA,CACpB;WAEF,WAAU;WACV,aAAY;WACZ,cAAYW,EAAG,8BAA8B,mBAAmB;UACjE,CAAA,CACE,EAAA,CAAA,GAEL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;WACE,WAAU;WACV,SAAQ;WAEP,UAAAA,EAAG,2BAA2B,gBAAgB;UAC1C,CAAA,GACP,kBAAC,SAAD;WACE,IAAG;WACH,MAAK;WACL,OAAOX,EAAa,gBAAgB;WACpC,WAAW,MACTkC,EACElC,EAAa,IACb,gBACA,EAAE,OAAO,SAAS,KAAA,CACpB;WAEF,WAAU;WACV,aAAY;WACZ,cAAYW,EAAG,2BAA2B,gBAAgB;UAC3D,CAAA,CACE,EAAA,CAAA,CACF;SAEL,CAAA,GAAA,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA,CACE,kBAAC,SAAD;WACE,WAAU;WACV,SAAQ;WAEP,UAAAA,EAAG,6BAA6B,wBAAwB;UACpD,CAAA,GACP,kBAAC,YAAD;WACE,IAAG;WACH,OACEX,EAAa,iBACT,KAAK,UAAUA,EAAa,gBAAgB,MAAM,CAAC,IACnD;WAEN,WAAW,MAAM;YACf,IAAMsC,IAAW,EAAE,OAAO,MAAM,KAAK;YACrC,IAAI,CAACA,GACH,EAAgBtC,EAAa,IAAI,kBAAkB,KAAA,CAAS;iBAI9D,IAAI;aACF,EAAgBA,EAAa,IAAI,kBAAkB,KAAK,MAAMsC,CAAQ,CAAC;YACzE,QAAQ;aACN,EAAgBtC,EAAa,IAAI,kBAAkBsC,CAAQ;YAC7D;WACF;WACA,MAAM;WACN,WAAU;WACV,aAAY;WACZ,cAAY3B,EAAG,6BAA6B,wBAAwB;UACrE,CAAA,CACE;SACS,CAAA,CAAA;;QAEhB,kBAAC4B,IAAD;SAAW,WAAU;SAAQ,WAAU;SAAvC,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;WAAf,UAAA,CACE,kBAACC,GAAD,EAAW,MAAM,GAAK,CAAA,GACtB,kBAAC,MAAD;YAAI,WAAU;YACX,UAAA7B,EAAG,wBAAwB,YAAY;WACtC,CAAA,CACD;;UAEJX,EAAa,SAASA,EAAa,MAAM,SAAS,IACjD,kBAAC,OAAD;WAAK,WAAU;WACZ,UAAAA,EAAa,MAAM,KAAK,MACvB,kBAAC,OAAD;YAEE,WAAU;YAFZ,UAAA,CAIE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;aAAK,WAAU;aAAe,UAAAyC,EAAK,MAAM;YAAU,CAAA,GACnD,kBAAC,OAAD;aAAK,WAAU;aAA+B,UAAAA,EAAK;YAAc,CAAA,CAC9D,EAAA,CAAA,GACL,kBAAC,QAAD;aAAM,WAAU;aAA+B,UAAAA,EAAK,MAAM;YAAW,CAAA,CAClE;WARE,GAAAA,EAAK,EAQP,CACN;UACE,CAAA,IAEL,kBAAC,KAAD;WAAG,WAAU;WACV,UAAA9B,EAAG,8BAA8B,qCAAqC;UACtE,CAAA;UAGL,kBAAC,OAAD;WAAK,WAAU;WAAf,UAAA;YACE,kBAAC,UAAD;aACE,OAAOxB;aACP,WAAW,MAAMC,EAAqB,EAAE,OAAO,KAAK;aACpD,WAAU;aACV,cAAYuB,EAAG,kCAAkC,uBAAuB;aAJ1E,UAAA,CAME,kBAAC,UAAD;cAAQ,OAAM;cACX,UAAA/B,KACG+B,EAAG,2BAA2B,kBAAkB,IAChDA,EAAG,kCAAkC,uBAAuB;aAC1D,CAAA,GACP+B,GAAM,KAAK,MACV,kBAAC,UAAD;cAAsB,OAAOD,EAAK;cAAlC,UAAA;eACGA,EAAK;eAAK;eAAGA,EAAK;eAAK;cAClB;aAFK,GAAAA,EAAK,EAEV,CACT,CACK;;YAER,kBAAC,UAAD;aACE,OAAOpD;aACP,WAAW,MACTC,GAAsB,EAAE,OAAO,KAAqC;aAEtE,WAAU;aACV,cAAYqB,EAAG,6BAA6B,QAAQ;aANtD,UAAA;cAQE,kBAAC,UAAD;eAAQ,OAAM;eAAU,UAAAA,EAAG,6BAA6B,QAAQ;cAAU,CAAA;cAC1E,kBAAC,UAAD;eAAQ,OAAM;eAAQ,UAAAA,EAAG,2BAA2B,MAAM;cAAU,CAAA;cACpE,kBAAC,UAAD;eAAQ,OAAM;eAAU,UAAAA,EAAG,6BAA6B,QAAQ;cAAU,CAAA;aACpE;;YAER,kBAAC,UAAD;aACE,MAAK;aACL,SAASD;aACT,UAAUnB,MAAkB,CAACJ;aAC7B,WAAU;aAET,UAAAwB,EAAG,qBAAqB,QAAQ;YAC3B,CAAA;WACL;;SACI;;OACR;MAEL,CAAA,IAAA,kBAAC,OAAD;OAAK,WAAU;OACb,UAAA,kBAACgC,IAAD;QACE,cAAc1C,EAAM,SAAS,IAAI,kBAAkB;QACnD,OAAOU,EAAG,uBAAuB;QACjC,aACEV,EAAM,SAAS,IACXU,EAAG,6BAA6B,IAChCA,EAAG,4BAA4B;QAErC,QACEV,EAAM,WAAW,KAAK7B,IACpB,kBAAC+C,GAAD;SAAQ,MAAK;SAAS,SAASd;SAAe,WAAU;SAAxD,UAAA,CACE,kBAACY,GAAD,EAAM,MAAM,GAAK,CAAA,GAChBN,EAAG,yBAAyB,CACvB;QACN,CAAA,IAAA,KAAA;OAEP,CAAA;MACE,CAAA;KAEJ,CAAA;IACF;;EACF;;AAET"}
|
|
@@ -6,7 +6,7 @@ import { useToursFormatters as i } from "../utils/index.js";
|
|
|
6
6
|
import { BarChart3 as a, Compass as o, Eye as s, Globe as c, TrendingUp as l } from "lucide-react";
|
|
7
7
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
8
8
|
import { EmphasisPanel as f, GlassCard as p, IllustratedEmptyState as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
|
|
9
|
-
|
|
9
|
+
// src/tours/pages/SystemAnalyticsPage.tsx
|
|
10
10
|
function g() {
|
|
11
11
|
let { tr: g } = e(), { hasPermission: _, isLoading: v } = n(), y = _(r.TOUR_ANALYTICS_READ), { data: b, loading: x } = t({ skip: v || !y }), { formatNumber: S, formatPercentage: C } = i();
|
|
12
12
|
if (!v && !y) return /* @__PURE__ */ u("div", {
|
|
@@ -161,7 +161,6 @@ function g() {
|
|
|
161
161
|
})
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
-
//#endregion
|
|
165
164
|
export { g as SystemAnalyticsPage };
|
|
166
165
|
|
|
167
166
|
//# sourceMappingURL=SystemAnalyticsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SystemAnalyticsPage.js","names":[],"sources":["../../../src/tours/pages/SystemAnalyticsPage.tsx"],"sourcesContent":["import { Compass, Globe, Eye, TrendingUp, BarChart3 } from 'lucide-react';\nimport {\n EmphasisPanel,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useToursI18n } from '../i18n';\nimport { useGetWorkspaceAnalyticsSummaryQuery } from '../../generated/global-operations';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursFormatters } from '../utils';\n\n/**\n * System-wide analytics page\n * Displays workspace-level tour analytics summary (no tourId needed)\n */\nexport function SystemAnalyticsPage() {\n const { tr } = useToursI18n();\n\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canViewAnalytics = hasPermission(TOUR_PERMISSIONS.TOUR_ANALYTICS_READ);\n\n const { data, loading } = useGetWorkspaceAnalyticsSummaryQuery({\n skip: permissionsLoading || !canViewAnalytics,\n });\n const { formatNumber, formatPercentage } = useToursFormatters();\n\n if (!permissionsLoading && !canViewAnalytics) {\n return (\n <div className=\"flex items-center justify-center h-full p-3 sm:p-6\">\n <div className=\"text-center\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('systemAnalytics.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'systemAnalytics.noPermission',\n 'You do not have permission to view system analytics.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-text-secondary\">\n {tr('systemAnalytics.loading', 'Loading analytics...')}\n </p>\n </div>\n );\n }\n\n const summary = data?.workspaceAnalyticsSummary;\n\n if (!summary) {\n return (\n <div className=\"p-3 sm:p-6\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('systemAnalytics.noData', 'No analytics data available')}\n description={tr(\n 'systemAnalytics.noDataHint',\n 'Workspace-wide tour metrics appear once you have published tours and users have started seeing them. Publish a tour to begin collecting data.'\n )}\n />\n </div>\n );\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\">\n {tr('systemAnalytics.title', 'System Analytics')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.subtitle', 'Workspace-wide tour performance overview')}\n </p>\n </div>\n\n <PagePurpose>\n {tr(\n 'systemAnalytics.purpose',\n 'Get the big-picture health of onboarding across the whole workspace: how many tours exist, how many are live, total reach, and the overall completion rate. Use it to judge whether your guided experiences are actually landing.'\n )}\n </PagePurpose>\n\n {/* Headline KPI — the single emphasis zone on this page. */}\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-md\">\n <BarChart3 className=\"text-primary\" size={24} />\n </div>\n <div>\n <div className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.completionRate', 'Completion Rate')}\n </div>\n <div className=\"text-3xl font-bold tabular-nums\">\n {formatPercentage(summary.overallCompletionRate)}\n </div>\n </div>\n </div>\n <p className=\"text-sm text-text-secondary sm:max-w-xs\">\n {tr(\n 'systemAnalytics.completionRateHint',\n 'The share of started tours that users finished across this workspace.'\n )}\n </p>\n </div>\n </EmphasisPanel>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <GlassCard treatment=\"glass\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-md\">\n <Compass className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.totalTours', 'Total Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{formatNumber(summary.totalTours)}</div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-success/10 rounded-md\">\n <Globe className=\"text-success\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.publishedTours', 'Published Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">\n {formatNumber(summary.publishedTours)}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-info/10 rounded-md\">\n <Eye className=\"text-info\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.totalViews', 'Total Views')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{formatNumber(summary.totalViews)}</div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-warning/10 rounded-md\">\n <TrendingUp className=\"text-warning\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.totalCompletions', 'Total Completions')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">\n {formatNumber(summary.totalCompletions)}\n </div>\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,SAAgB,IAAsB;CACpC,IAAM,EAAE,UAAO,GAAc,EAEvB,EAAE,kBAAe,WAAW,MAAuB,GAA0B,EAC7E,IAAmB,EAAc,EAAiB,oBAAoB,EAEtE,EAAE,SAAM,eAAY,EAAqC,EAC7D,MAAM,KAAsB,CAAC,GAC9B,CAAC,EACI,EAAE,iBAAc,wBAAqB,GAAoB;AAE/D,KAAI,CAAC,KAAsB,CAAC,EAC1B,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,gCAAgC,gBAAgB;IACjD,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,gCACA,uDACD;IACC,CAAA,CACA;;EACF,CAAA;AAIV,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aACV,EAAG,2BAA2B,uBAAuB;GACpD,CAAA;EACA,CAAA;CAIV,IAAM,IAAU,GAAM;AAiBtB,QAfK,IAgBH,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,yBAAyB,mBAAmB;IAC7C,CAAA,EACL,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,4BAA4B,2CAA2C;IACzE,CAAA,CACA,EAAA,CAAA;GAEN,kBAAC,GAAD,EAAA,UACG,EACC,2BACA,oOACD,EACW,CAAA;GAGd,kBAAC,GAAD;IAAe,WAAU;cACvB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAW,WAAU;QAAe,MAAM;QAAM,CAAA;OAC5C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAG,kCAAkC,kBAAkB;OACpD,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAiB,EAAQ,sBAAsB;OAC5C,CAAA,CACF,EAAA,CAAA,CACF;SACN,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,sCACA,wEACD;MACC,CAAA,CACA;;IACQ,CAAA;GAGhB,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SAAS,WAAU;SAAe,MAAM;SAAM,CAAA;QAC1C,CAAA,EACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,8BAA8B,cAAc;QAC3C,CAAA,CACH;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAmC,EAAa,EAAQ,WAAW;OAAO,CAAA,CAC/E;;KAEZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SAAO,WAAU;SAAe,MAAM;SAAM,CAAA;QACxC,CAAA,EACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,kCAAkC,kBAAkB;QACnD,CAAA,CACH;UACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAa,EAAQ,eAAe;OACjC,CAAA,CACI;;KAEZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SAAK,WAAU;SAAY,MAAM;SAAM,CAAA;QACnC,CAAA,EACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,8BAA8B,cAAc;QAC3C,CAAA,CACH;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAmC,EAAa,EAAQ,WAAW;OAAO,CAAA,CAC/E;;KAEZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SAAY,WAAU;SAAe,MAAM;SAAM,CAAA;QAC7C,CAAA,EACN,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,oCAAoC,oBAAoB;QACvD,CAAA,CACH;UACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAa,EAAQ,iBAAiB;OACnC,CAAA,CACI;;KACR;;GACF;MA/GJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAG,0BAA0B,8BAA8B;GAClE,aAAa,EACX,8BACA,gJACD;GACD,CAAA;EACE,CAAA"}
|
|
1
|
+
{"version":3,"file":"SystemAnalyticsPage.js","names":["SystemAnalyticsPage","useToursI18n","permissionsLoading","useToursPermissionAccess","canViewAnalytics","hasPermission","TOUR_PERMISSIONS","useGetWorkspaceAnalyticsSummaryQuery","useToursFormatters","tr","loading","summary","data","PagePurpose","EmphasisPanel","BarChart3","formatPercentage","GlassCard","Compass","formatNumber","Globe","Eye","TrendingUp","IllustratedEmptyState"],"sources":["../../../src/tours/pages/SystemAnalyticsPage.tsx"],"sourcesContent":["import { Compass, Globe, Eye, TrendingUp, BarChart3 } from 'lucide-react';\nimport {\n EmphasisPanel,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useToursI18n } from '../i18n';\nimport { useGetWorkspaceAnalyticsSummaryQuery } from '../../generated/global-operations';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { useToursFormatters } from '../utils';\n\n/**\n * System-wide analytics page\n * Displays workspace-level tour analytics summary (no tourId needed)\n */\nexport function SystemAnalyticsPage() {\n const { tr } = useToursI18n();\n\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canViewAnalytics = hasPermission(TOUR_PERMISSIONS.TOUR_ANALYTICS_READ);\n\n const { data, loading } = useGetWorkspaceAnalyticsSummaryQuery({\n skip: permissionsLoading || !canViewAnalytics,\n });\n const { formatNumber, formatPercentage } = useToursFormatters();\n\n if (!permissionsLoading && !canViewAnalytics) {\n return (\n <div className=\"flex items-center justify-center h-full p-3 sm:p-6\">\n <div className=\"text-center\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('systemAnalytics.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'systemAnalytics.noPermission',\n 'You do not have permission to view system analytics.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-text-secondary\">\n {tr('systemAnalytics.loading', 'Loading analytics...')}\n </p>\n </div>\n );\n }\n\n const summary = data?.workspaceAnalyticsSummary;\n\n if (!summary) {\n return (\n <div className=\"p-3 sm:p-6\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('systemAnalytics.noData', 'No analytics data available')}\n description={tr(\n 'systemAnalytics.noDataHint',\n 'Workspace-wide tour metrics appear once you have published tours and users have started seeing them. Publish a tour to begin collecting data.'\n )}\n />\n </div>\n );\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\">\n {tr('systemAnalytics.title', 'System Analytics')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.subtitle', 'Workspace-wide tour performance overview')}\n </p>\n </div>\n\n <PagePurpose>\n {tr(\n 'systemAnalytics.purpose',\n 'Get the big-picture health of onboarding across the whole workspace: how many tours exist, how many are live, total reach, and the overall completion rate. Use it to judge whether your guided experiences are actually landing.'\n )}\n </PagePurpose>\n\n {/* Headline KPI — the single emphasis zone on this page. */}\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-md\">\n <BarChart3 className=\"text-primary\" size={24} />\n </div>\n <div>\n <div className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.completionRate', 'Completion Rate')}\n </div>\n <div className=\"text-3xl font-bold tabular-nums\">\n {formatPercentage(summary.overallCompletionRate)}\n </div>\n </div>\n </div>\n <p className=\"text-sm text-text-secondary sm:max-w-xs\">\n {tr(\n 'systemAnalytics.completionRateHint',\n 'The share of started tours that users finished across this workspace.'\n )}\n </p>\n </div>\n </EmphasisPanel>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <GlassCard treatment=\"glass\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-md\">\n <Compass className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.totalTours', 'Total Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{formatNumber(summary.totalTours)}</div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-success/10 rounded-md\">\n <Globe className=\"text-success\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.publishedTours', 'Published Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">\n {formatNumber(summary.publishedTours)}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-info/10 rounded-md\">\n <Eye className=\"text-info\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.totalViews', 'Total Views')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{formatNumber(summary.totalViews)}</div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-warning/10 rounded-md\">\n <TrendingUp className=\"text-warning\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('systemAnalytics.totalCompletions', 'Total Completions')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">\n {formatNumber(summary.totalCompletions)}\n </div>\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useToursContext as e } from "../providers/ToursProvider.js";
|
|
2
|
+
import { useToursPermissionAccess as t } from "../hooks/useToursPermissionAccess.js";
|
|
3
3
|
import { useCreateTourFromTemplateMutation as n, useDeleteTourTemplateMutation as r, useGetTourTemplatesQuery as i } from "../../generated/global-operations.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { TOUR_PERMISSIONS as a } from "../../constants/permissions.js";
|
|
5
|
+
import { useToursI18n as o } from "../i18n.js";
|
|
6
|
+
import { emitToursApiFailed as s } from "../telemetry.js";
|
|
7
|
+
import { nativeConfirm as c, nativeImpact as l, nativeNotify as u } from "../../utils/nativeBridge.js";
|
|
8
8
|
import { useState as d } from "react";
|
|
9
9
|
import { Globe as f, LayoutTemplate as p, Lock as m, Search as h, Star as g, Trash2 as _ } from "lucide-react";
|
|
10
10
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
11
11
|
import { useNavigate as b } from "react-router";
|
|
12
12
|
import { GlassCard as x, IllustratedEmptyState as S, PagePurpose as C } from "@burdenoff/fe-libs/ui";
|
|
13
13
|
import { useEventBus as w } from "@burdenoff/fe-libs/shared/events";
|
|
14
|
-
|
|
14
|
+
// src/tours/pages/TemplateLibraryPage.tsx
|
|
15
15
|
function T() {
|
|
16
|
-
let { tr: T } =
|
|
16
|
+
let { tr: T } = o(), E = b(), D = e(), { basePath: O = "/" } = D, k = w(), { hasPermission: A } = t(), j = A(a.TOUR_TEMPLATE_LIST), M = A(a.TOUR_TEMPLATE_USE), N = A(a.TOUR_TEMPLATE_DELETE), [P, F] = d(""), [I, L] = d("all"), [R, z] = d(!1), { data: B, loading: V } = i({
|
|
17
17
|
skip: !j,
|
|
18
18
|
variables: {
|
|
19
19
|
isPublic: R || void 0,
|
|
20
20
|
category: I === "all" ? void 0 : I
|
|
21
21
|
}
|
|
22
22
|
}), H = (B?.tourTemplates ?? []).filter((e) => !P || e.name.toLowerCase().includes(P.toLowerCase())), [U] = n(), W = async (e) => {
|
|
23
|
-
|
|
23
|
+
l("light");
|
|
24
24
|
try {
|
|
25
25
|
let { data: t } = await U({ variables: { templateId: e } });
|
|
26
|
-
t?.createTourFromTemplate && (
|
|
26
|
+
t?.createTourFromTemplate && (u("success"), E(`${O}/${t.createTourFromTemplate.id}/edit`));
|
|
27
27
|
} catch {
|
|
28
|
-
|
|
28
|
+
u("error"), s(k, "template.create_from_template", D);
|
|
29
29
|
}
|
|
30
30
|
}, [G] = r(), K = async (e) => {
|
|
31
31
|
try {
|
|
32
32
|
await G({
|
|
33
33
|
variables: { id: e },
|
|
34
34
|
refetchQueries: ["GetTourTemplates"]
|
|
35
|
-
}),
|
|
35
|
+
}), u("success");
|
|
36
36
|
} catch {
|
|
37
|
-
|
|
37
|
+
u("error"), s(k, "template.delete", D);
|
|
38
38
|
}
|
|
39
39
|
}, q = async (e) => {
|
|
40
|
-
|
|
41
|
-
let t = T("templateLibrary.deleteConfirm"), n = await
|
|
40
|
+
l("medium");
|
|
41
|
+
let t = T("templateLibrary.deleteConfirm"), n = await c({
|
|
42
42
|
title: T("templateLibrary.deleteConfirm"),
|
|
43
43
|
message: t
|
|
44
44
|
});
|
|
@@ -200,7 +200,6 @@ function T() {
|
|
|
200
200
|
]
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
|
-
//#endregion
|
|
204
203
|
export { T as TemplateLibraryPage };
|
|
205
204
|
|
|
206
205
|
//# sourceMappingURL=TemplateLibraryPage.js.map
|
|
@@ -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 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":";;;;;;;;;;;;;;AAsBA,SAAgB,IAAsB;CACpC,IAAM,EAAE,UAAO,GAAc,EAEvB,IAAW,GAAa,EACxB,IAAe,GAAiB,EAChC,EAAE,cAAW,QAAQ,GACrB,IAAW,GAAa,EACxB,EAAE,qBAAkB,GAA0B,EAC9C,IAAmB,EAAc,EAAiB,mBAAmB,EACrE,IAAiB,EAAc,EAAiB,kBAAkB,EAClE,IAAoB,EAAc,EAAiB,qBAAqB,EAExE,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAgB,KAAqB,EAAmC,MAAM,EAC/E,CAAC,GAAY,KAAiB,EAAS,GAAM,EAE7C,EAAE,SAAM,eAAY,EAAyB;EACjD,MAAM,CAAC;EACP,WAAW;GACT,UAAU,KAAc,KAAA;GACxB,UAAU,MAAmB,QAAQ,KAAA,IAAY;GAClD;EACF,CAAC,EAGI,KAFY,GAAM,iBAAiB,EAAE,EAEP,QACjC,MACC,CAAC,KAAe,EAAE,KAAK,aAAa,CAAC,SAAS,EAAY,aAAa,CAAC,CAC3E,EAEK,CAAC,KAAsB,GAAmC,EAE1D,IAAoB,OAAO,MAAuB;AACtD,IAAa,QAAQ;AACrB,MAAI;GACF,IAAM,EAAE,YAAS,MAAM,EAAmB,EACxC,WAAW,EAAE,eAAY,EAC1B,CAAC;AACF,GAAI,GAAM,2BACH,EAAa,UAAU,EAC5B,EAAS,GAAG,EAAS,GAAG,EAAK,uBAAuB,GAAG,OAAO;UAE1D;AAEN,GADK,EAAa,QAAQ,EAC1B,EAAmB,GAAU,iCAAiC,EAAa;;IAIzE,CAAC,KAAkB,GAA+B,EAElD,IAAuB,OAAO,MAAuB;AACzD,MAAI;AAKG,GAJL,MAAM,EAAe;IACnB,WAAW,EAAE,IAAI,GAAY;IAC7B,gBAAgB,CAAC,mBAAmB;IACrC,CAAC,EACG,EAAa,UAAU;UACtB;AAEN,GADK,EAAa,QAAQ,EAC1B,EAAmB,GAAU,mBAAmB,EAAa;;IAI3D,IAA4B,OAAO,MAAuB;AAC9D,IAAa,SAAS;EACtB,IAAM,IAAU,EAAG,gCAAgC,EAC7C,IAAe,MAAM,EAAc;GACvC,OAAO,EAAG,gCAAgC;GAC1C;GACD,CAAC;AAEF,GADkB,MAAiB,OAAO,OAAO,QAAQ,EAAQ,GAAG,MACrD,MAAM,EAAqB,EAAW;;AAGvD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAAiC,EAAG,wBAAwB;IAAM,CAAA,EAChF,kBAAC,KAAD;IAAG,WAAU;cAA+B,EAAG,2BAA2B;IAAK,CAAA,CAC3E,EAAA,CAAA;GAEN,kBAAC,GAAD,EAAA,UAAc,EAAG,0BAA0B,EAAe,CAAA;GAG1D,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,WAAU;OACV,MAAM;OACN,CAAA,EACF,kBAAC,SAAD;OACE,MAAK;OACL,aAAa,EAAG,oCAAoC;OACpD,OAAO;OACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;OAC/C,WAAU;OACV,cAAY,EAAG,oCAAoC;OACnD,CAAA,CACE;;KACN,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAkC;MAC9E,WAAU;MACV,cAAY,EAAG,gCAAgC;gBAJjD;OAME,kBAAC,UAAD;QAAQ,OAAM;kBAAO,EAAG,gCAAgC;QAAU,CAAA;OAClE,kBAAC,UAAD;QAAQ,OAAM;kBAAc,EAAG,qCAAqC;QAAU,CAAA;OAC9E,kBAAC,UAAD;QAAQ,OAAM;kBACX,EAAG,8CAA8C;QAC3C,CAAA;OACT,kBAAC,UAAD;QAAQ,OAAM;kBAAQ,EAAG,+BAA+B;QAAU,CAAA;OAClE,kBAAC,UAAD;QAAQ,OAAM;kBAAU,EAAG,iCAAiC;QAAU,CAAA;OAC/D;;KACT,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD;OACE,MAAK;OACL,SAAS;OACT,WAAW,MAAM,EAAc,EAAE,OAAO,QAAQ;OAChD,WAAU;OACV,cAAY,EAAG,6BAA6B;OAC5C,CAAA,EACF,kBAAC,QAAD;OAAM,WAAU;iBAAW,EAAG,6BAA6B;OAAQ,CAAA,CAC7D;;KACJ;;GAGL,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,KAAD;KAAG,WAAU;eAAuB,EAAG,0BAA0B;KAAK,CAAA;IAClE,CAAA,GACJ,EAAkB,SAAS,IAC7B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAkB,KAAK,MACtB,kBAAC,GAAD;KAEE,WAAU;KACV,WAAU;eAHZ,CAME,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAS,YACR,kBAAC,OAAD;OACE,KAAK,EAAS;OACd,KAAK,EAAS;OACd,WAAU;OACV,CAAA,GAEF,kBAAC,GAAD;OAAgB,WAAU;OAAsB,MAAM;OAAM,CAAA;MAE1D,CAAA,EAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAiB,EAAS;SAAU,CAAA,EAClD,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACG,EAAS,aAAa,kBAAC,GAAD;UAAM,WAAU;UAAe,MAAM;UAAM,CAAA,EACjE,EAAS,WACR,kBAAC,GAAD;UAAO,WAAU;UAAsB,MAAM;UAAM,CAAA,GAEnD,kBAAC,GAAD;UAAM,WAAU;UAAsB,MAAM;UAAM,CAAA,CAEhD;WACF;;OAEL,EAAS,eACR,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAS;QACR,CAAA;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACG,EAAG,4BAA4B;UAAC;UAAE,EAAS;UAAU;UACrD,EAAG,wBAAwB;UACvB;YACP,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACG,KACC,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,KAAK,EAA0B,EAAS,GAAG;UAC1D,WAAU;UACV,cAAY,EAAG,iCAAiC;oBAEhD,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;UACb,CAAA,EAEV,KACC,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAkB,EAAS,GAAG;UAC7C,WAAU;oBAET,EAAG,8BAA8B;UAC3B,CAAA,CAEP;WACF;;OACF;QACI;OAjEL,EAAS,GAiEJ,CACZ;IACE,CAAA,GAEN,kBAAC,GAAD;IACE,cAAa;IACb,OAAO,EAAG,mCAAmC;IAC7C,aAAa,EAAG,gCAAgC;IAChD,CAAA;GAEA"}
|
|
1
|
+
{"version":3,"file":"TemplateLibraryPage.js","names":["TemplateLibraryPage","useToursI18n","navigate","useNavigate","toursContext","useToursContext","eventBus","useEventBus","useToursPermissionAccess","canListTemplates","hasPermission","TOUR_PERMISSIONS","canUseTemplate","canDeleteTemplate","searchQuery","setSearchQuery","useState","categoryFilter","setCategoryFilter","publicOnly","setPublicOnly","useGetTourTemplatesQuery","filteredTemplates","data","t","createFromTemplate","useCreateTourFromTemplateMutation","handleUseTemplate","nativeNotify","basePath","emitToursApiFailed","deleteTemplate","useDeleteTourTemplateMutation","handleDeleteTemplate","templateId","handleDeleteTemplateClick","message","tr","nativeResult","nativeConfirm","PagePurpose","Search","loading","GlassCard","template","LayoutTemplate","Star","Globe","Lock","Trash2","IllustratedEmptyState"],"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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function TourBuilderPage(): import('react').JSX.Element;
|