@burdenoff/microfe-tours 2026.519.1 → 2026.523.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/index.js +2 -1
  2. package/dist/tours/ToursAdminRoot.d.ts.map +1 -1
  3. package/dist/tours/ToursAdminRoot.js +23 -20
  4. package/dist/tours/ToursAdminRoot.js.map +1 -1
  5. package/dist/tours/ToursUserRoot.d.ts.map +1 -1
  6. package/dist/tours/ToursUserRoot.js +25 -22
  7. package/dist/tours/ToursUserRoot.js.map +1 -1
  8. package/dist/tours/hooks/useMediaLibrary.js +3 -3
  9. package/dist/tours/hooks/useTourBuilder.js +10 -10
  10. package/dist/tours/hooks/useTourLifecycle.js +3 -3
  11. package/dist/tours/i18n.d.ts +5 -1
  12. package/dist/tours/i18n.d.ts.map +1 -1
  13. package/dist/tours/i18n.js +4 -0
  14. package/dist/tours/i18n.js.map +1 -1
  15. package/dist/tours/index.d.ts +1 -0
  16. package/dist/tours/index.d.ts.map +1 -1
  17. package/dist/tours/index.js +1 -0
  18. package/dist/tours/pages/AnalyticsDashboardPage.d.ts.map +1 -1
  19. package/dist/tours/pages/AnalyticsDashboardPage.js +5 -5
  20. package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
  21. package/dist/tours/pages/MediaLibraryPage.d.ts.map +1 -1
  22. package/dist/tours/pages/MediaLibraryPage.js +97 -96
  23. package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
  24. package/dist/tours/pages/SchedulerPage.d.ts.map +1 -1
  25. package/dist/tours/pages/SchedulerPage.js +129 -126
  26. package/dist/tours/pages/SchedulerPage.js.map +1 -1
  27. package/dist/tours/pages/StepEditorPage.d.ts.map +1 -1
  28. package/dist/tours/pages/StepEditorPage.js +133 -128
  29. package/dist/tours/pages/StepEditorPage.js.map +1 -1
  30. package/dist/tours/pages/SystemAnalyticsPage.d.ts.map +1 -1
  31. package/dist/tours/pages/SystemAnalyticsPage.js +3 -3
  32. package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
  33. package/dist/tours/pages/TemplateLibraryPage.d.ts.map +1 -1
  34. package/dist/tours/pages/TemplateLibraryPage.js +97 -94
  35. package/dist/tours/pages/TemplateLibraryPage.js.map +1 -1
  36. package/dist/tours/pages/TourBuilderPage.d.ts.map +1 -1
  37. package/dist/tours/pages/TourBuilderPage.js +232 -230
  38. package/dist/tours/pages/TourBuilderPage.js.map +1 -1
  39. package/dist/tours/pages/TourPreviewPage.d.ts.map +1 -1
  40. package/dist/tours/pages/TourPreviewPage.js +20 -18
  41. package/dist/tours/pages/TourPreviewPage.js.map +1 -1
  42. package/dist/tours/pages/ToursListPage.js +14 -14
  43. package/dist/tours/pages/ToursListPage.js.map +1 -1
  44. package/dist/tours/store/toursStore.d.ts +1 -0
  45. package/dist/tours/store/toursStore.d.ts.map +1 -1
  46. package/dist/tours/store/toursStore.js +14 -0
  47. package/dist/tours/store/toursStore.js.map +1 -1
  48. package/dist/tours/telemetry.d.ts +9 -0
  49. package/dist/tours/telemetry.d.ts.map +1 -0
  50. package/dist/tours/telemetry.js +19 -0
  51. package/dist/tours/telemetry.js.map +1 -0
  52. package/package.json +1 -1
@@ -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-dom';\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 { 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';\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 { basePath = '/' } = useToursContext();\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(false);\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 checkMobile();\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 (error) {\n console.error('Failed to save steps:', error);\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('Save your steps before attaching media to them.');\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 (attachError) {\n console.error('Failed to attach media:', attachError);\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-background\">\n {/* Header */}\n <div className=\"border-b border-border p-3 sm:p-4 flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4 bg-card\">\n <div className=\"flex items-center gap-3\">\n <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-muted-foreground\">\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 onClick={() => setIsSidebarOpen(!isSidebarOpen)}\n className=\"px-3 py-2 border border-border 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-primary/20 rounded-full\">\n {steps.length}\n </span>\n )}\n </button>\n )}\n\n {canCreateStep && (\n <button\n onClick={handleAddStep}\n className=\"px-3 sm:px-4 py-2 border border-border 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 onClick={handleSaveSteps}\n disabled={saving || !canUpdateStep}\n className=\"px-3 sm:px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50 flex items-center gap-2 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=\"flex-1 flex overflow-hidden relative\">\n {/* Mobile Sidebar Overlay */}\n {isMobile && isSidebarOpen && (\n <div\n className=\"fixed inset-0 bg-black/50 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-72 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 overflow-y-auto bg-card\n `}\n >\n {/* Mobile sidebar header */}\n {isMobile && (\n <div className=\"flex items-center justify-between p-4 border-b border-border sticky top-0 bg-card z-10\">\n <h2 className=\"font-semibold\">{tr('stepEditor.tourSteps')}</h2>\n <button\n onClick={() => setIsSidebarOpen(false)}\n className=\"p-2 hover:bg-accent rounded-md\"\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-muted-foreground\">\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 onClick={() => handleSelectStep(step)}\n className={`p-3 border rounded-md cursor-pointer transition-colors ${\n selectedStep?.id === step.id\n ? 'border-primary bg-primary/10'\n : 'border-border 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-muted-foreground 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-muted-foreground 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 onClick={(e) => {\n e.stopPropagation();\n if (index > 0) {\n moveStep(index, index - 1);\n }\n }}\n className=\"p-1.5 hover:bg-accent rounded\"\n title=\"Move step up\"\n aria-label=\"Move step up\"\n disabled={index === 0}\n >\n <ChevronUp size={14} />\n </button>\n <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 hover:bg-accent rounded\"\n title=\"Move step down\"\n aria-label=\"Move step down\"\n disabled={index === steps.length - 1}\n >\n <ChevronDown size={14} />\n </button>\n <button\n onClick={(e) => {\n e.stopPropagation();\n deleteStep(step.id);\n }}\n className=\"p-1.5 hover:bg-destructive/10 rounded text-destructive\"\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-muted/50 rounded-lg p-2 mb-4\">\n <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 hover:bg-accent rounded-md disabled:opacity-30\"\n >\n <ChevronLeft size={20} />\n </button>\n <span className=\"text-sm text-muted-foreground\">\n {tr('stepEditor.stepNumber')}{' '}\n {steps.findIndex((s) => s.id === selectedStep.id) + 1} {tr('stepEditor.of')}{' '}\n {steps.length}\n </span>\n <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 hover:bg-accent rounded-md disabled:opacity-30\"\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\">\n {tr('stepEditor.stepTitle')} *\n </label>\n <input\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 rounded-md bg-background text-sm sm:text-base\"\n placeholder={tr('stepEditor.stepTitlePlaceholder')}\n />\n </div>\n\n {/* Content */}\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('stepEditor.content')} *\n </label>\n <textarea\n value={selectedStep.content}\n onChange={(e) => updateStepField(selectedStep.id, 'content', e.target.value)}\n rows={4}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-sm sm:text-base resize-y min-h-[100px]\"\n placeholder={tr('stepEditor.contentPlaceholder')}\n />\n </div>\n\n {/* Selector */}\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('stepEditor.cssSelector')} *\n <span className=\"text-muted-foreground font-normal ml-2\">\n ({tr('stepEditor.targetElement')})\n </span>\n </label>\n <input\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 rounded-md bg-background font-mono text-sm\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr('stepEditor.dataTourHint')}\n </p>\n </div>\n\n {/* Placement */}\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('stepEditor.tooltipPlacement')}\n </label>\n <select\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 rounded-md bg-background text-sm sm:text-base\"\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 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\">\n <p className=\"text-xs text-muted-foreground mb-3\">\n {tr('stepEditor.deviceSelectorHint')}\n </p>\n <div>\n <label className=\"block text-xs font-medium mb-1\">\n {tr('stepEditor.mobileSelector')}\n </label>\n <input\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 rounded-md bg-background text-sm font-mono\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">\n {tr('stepEditor.tabletSelector')}\n </label>\n <input\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 rounded-md bg-background text-sm font-mono\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">\n {tr('stepEditor.desktopSelector')}\n </label>\n <input\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 rounded-md bg-background text-sm font-mono\"\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 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=\"w-4 h-4 rounded\"\n />\n <div>\n <div className=\"text-sm font-medium\">Require interaction</div>\n <div className=\"text-xs text-muted-foreground\">\n User must interact with the highlighted element before advancing.\n </div>\n </div>\n </label>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\">Wait for timeout (ms)</label>\n <input\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 rounded-md bg-background text-sm\"\n placeholder=\"1500\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium mb-2\">Wait for selector</label>\n <input\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 rounded-md bg-background text-sm font-mono\"\n placeholder=\"[data-tour='agents-tabs']\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\">Wait for event</label>\n <input\n type=\"text\"\n value={selectedStep.waitForEvent || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'waitForEvent', e.target.value || undefined)\n }\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-sm\"\n placeholder=\"tour:ready\"\n />\n </div>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\">Skip conditions (JSON)</label>\n <textarea\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 rounded-md bg-background text-sm font-mono\"\n placeholder='{\"selectorMissing\":\"[data-tour=\\\"missing\\\"]\"}'\n />\n </div>\n\n <div className=\"border border-border rounded-md p-4 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Paperclip size={18} />\n <h3 className=\"text-sm font-semibold\">Step media</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 p-2 text-sm\"\n >\n <div>\n <div className=\"font-medium\">{item.media.name}</div>\n <div className=\"text-xs text-muted-foreground\">{item.position}</div>\n </div>\n <span className=\"text-xs text-muted-foreground\">{item.media.type}</span>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n 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 rounded-md bg-background text-sm\"\n >\n <option value=\"\">\n {mediaLoading ? 'Loading media...' : '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 rounded-md bg-background text-sm\"\n >\n <option value=\"header\">Header</option>\n <option value=\"body\">Body</option>\n <option value=\"footer\">Footer</option>\n </select>\n\n <button\n type=\"button\"\n onClick={handleAttachMedia}\n disabled={attachingMedia || !attachmentMediaId}\n className=\"px-4 py-2 border border-border rounded-md hover:bg-accent disabled:opacity-50\"\n >\n Attach\n </button>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center h-full text-center px-4\">\n <div className=\"w-16 h-16 bg-muted rounded-full flex items-center justify-center mb-4\">\n <Plus size={24} className=\"text-muted-foreground\" />\n </div>\n <p className=\"text-muted-foreground mb-2\">{tr('stepEditor.noStepSelected')}</p>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {steps.length > 0\n ? tr('stepEditor.selectStepToEdit')\n : tr('stepEditor.createFirstStep')}\n </p>\n {steps.length === 0 && canCreateStep && (\n <button\n onClick={handleAddStep}\n className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 flex items-center gap-2\"\n >\n <Plus size={18} />\n {tr('stepEditor.addFirstStep')}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"StepEditorPage.js","names":[],"sources":["../../../src/tours/pages/StepEditorPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\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 { 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(false);\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 checkMobile();\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('Save your steps before attaching media to them.');\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-background\">\n {/* Header */}\n <div className=\"border-b border-border p-3 sm:p-4 flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-4 bg-card\">\n <div className=\"flex items-center gap-3\">\n <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-muted-foreground\">\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 onClick={() => setIsSidebarOpen(!isSidebarOpen)}\n className=\"px-3 py-2 border border-border 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-primary/20 rounded-full\">\n {steps.length}\n </span>\n )}\n </button>\n )}\n\n {canCreateStep && (\n <button\n onClick={handleAddStep}\n className=\"px-3 sm:px-4 py-2 border border-border 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 onClick={handleSaveSteps}\n disabled={saving || !canUpdateStep}\n className=\"px-3 sm:px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50 flex items-center gap-2 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=\"flex-1 flex overflow-hidden relative\">\n {/* Mobile Sidebar Overlay */}\n {isMobile && isSidebarOpen && (\n <div\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 overflow-y-auto bg-card\n `}\n >\n {/* Mobile sidebar header */}\n {isMobile && (\n <div className=\"flex items-center justify-between p-4 border-b border-border sticky top-0 bg-card z-10\">\n <h2 className=\"font-semibold\">{tr('stepEditor.tourSteps')}</h2>\n <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-muted-foreground\">\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 onClick={() => handleSelectStep(step)}\n className={`p-3 border rounded-md cursor-pointer transition-colors ${\n selectedStep?.id === step.id\n ? 'border-primary bg-primary/10'\n : 'border-border 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-muted-foreground 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-muted-foreground 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 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=\"Move step up\"\n aria-label=\"Move step up\"\n disabled={index === 0}\n >\n <ChevronUp size={14} />\n </button>\n <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=\"Move step down\"\n aria-label=\"Move step down\"\n disabled={index === steps.length - 1}\n >\n <ChevronDown size={14} />\n </button>\n <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-destructive/10 rounded text-destructive\"\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-muted/50 rounded-lg p-2 mb-4\">\n <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-muted-foreground\">\n {tr('stepEditor.stepNumber')}{' '}\n {steps.findIndex((s) => s.id === selectedStep.id) + 1} {tr('stepEditor.of')}{' '}\n {steps.length}\n </span>\n <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\">\n {tr('stepEditor.stepTitle')} *\n </label>\n <input\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 rounded-md bg-background text-sm sm:text-base\"\n placeholder={tr('stepEditor.stepTitlePlaceholder')}\n />\n </div>\n\n {/* Content */}\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('stepEditor.content')} *\n </label>\n <textarea\n value={selectedStep.content}\n onChange={(e) => updateStepField(selectedStep.id, 'content', e.target.value)}\n rows={4}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-sm sm:text-base resize-y min-h-[100px]\"\n placeholder={tr('stepEditor.contentPlaceholder')}\n />\n </div>\n\n {/* Selector */}\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('stepEditor.cssSelector')} *\n <span className=\"text-muted-foreground font-normal ml-2\">\n ({tr('stepEditor.targetElement')})\n </span>\n </label>\n <input\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 rounded-md bg-background font-mono text-sm\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr('stepEditor.dataTourHint')}\n </p>\n </div>\n\n {/* Placement */}\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('stepEditor.tooltipPlacement')}\n </label>\n <select\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 rounded-md bg-background text-sm sm:text-base\"\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 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\">\n <p className=\"text-xs text-muted-foreground mb-3\">\n {tr('stepEditor.deviceSelectorHint')}\n </p>\n <div>\n <label className=\"block text-xs font-medium mb-1\">\n {tr('stepEditor.mobileSelector')}\n </label>\n <input\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 rounded-md bg-background text-sm font-mono\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">\n {tr('stepEditor.tabletSelector')}\n </label>\n <input\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 rounded-md bg-background text-sm font-mono\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">\n {tr('stepEditor.desktopSelector')}\n </label>\n <input\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 rounded-md bg-background text-sm font-mono\"\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 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=\"w-4 h-4 rounded\"\n />\n <div>\n <div className=\"text-sm font-medium\">Require interaction</div>\n <div className=\"text-xs text-muted-foreground\">\n User must interact with the highlighted element before advancing.\n </div>\n </div>\n </label>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\">Wait for timeout (ms)</label>\n <input\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 rounded-md bg-background text-sm\"\n placeholder=\"1500\"\n />\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium mb-2\">Wait for selector</label>\n <input\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 rounded-md bg-background text-sm font-mono\"\n placeholder=\"[data-tour='agents-tabs']\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\">Wait for event</label>\n <input\n type=\"text\"\n value={selectedStep.waitForEvent || ''}\n onChange={(e) =>\n updateStepField(selectedStep.id, 'waitForEvent', e.target.value || undefined)\n }\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-sm\"\n placeholder=\"tour:ready\"\n />\n </div>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-2\">Skip conditions (JSON)</label>\n <textarea\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 rounded-md bg-background text-sm font-mono\"\n placeholder='{\"selectorMissing\":\"[data-tour=\\\"missing\\\"]\"}'\n />\n </div>\n\n <div className=\"border border-border rounded-md p-4 space-y-4\">\n <div className=\"flex items-center gap-2\">\n <Paperclip size={18} />\n <h3 className=\"text-sm font-semibold\">Step media</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 p-2 text-sm\"\n >\n <div>\n <div className=\"font-medium\">{item.media.name}</div>\n <div className=\"text-xs text-muted-foreground\">{item.position}</div>\n </div>\n <span className=\"text-xs text-muted-foreground\">{item.media.type}</span>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n 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 rounded-md bg-background text-sm\"\n >\n <option value=\"\">\n {mediaLoading ? 'Loading media...' : '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 rounded-md bg-background text-sm\"\n >\n <option value=\"header\">Header</option>\n <option value=\"body\">Body</option>\n <option value=\"footer\">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 rounded-md hover:bg-accent disabled:opacity-50\"\n >\n Attach\n </button>\n </div>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center h-full text-center px-4\">\n <div className=\"w-16 h-16 bg-muted rounded-full flex items-center justify-center mb-4\">\n <Plus size={24} className=\"text-muted-foreground\" />\n </div>\n <p className=\"text-muted-foreground mb-2\">{tr('stepEditor.noStepSelected')}</p>\n <p className=\"text-sm text-muted-foreground mb-4\">\n {steps.length > 0\n ? tr('stepEditor.selectStepToEdit')\n : tr('stepEditor.createFirstStep')}\n </p>\n {steps.length === 0 && canCreateStep && (\n <button\n onClick={handleAddStep}\n className=\"px-4 py-2 min-h-[44px] bg-primary text-primary-foreground rounded-md hover:bg-primary/90 flex items-center gap-2\"\n >\n <Plus size={18} />\n {tr('stepEditor.addFirstStep')}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SystemAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../src/tours/pages/SystemAnalyticsPage.tsx"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAgB,mBAAmB,4CAqIlC"}
1
+ {"version":3,"file":"SystemAnalyticsPage.d.ts","sourceRoot":"","sources":["../../../src/tours/pages/SystemAnalyticsPage.tsx"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAgB,mBAAmB,4CAuIlC"}
@@ -13,7 +13,7 @@ function m() {
13
13
  return n === e ? t : n;
14
14
  }, [m]), { hasPermission: g, isLoading: _ } = t(), v = g(n.TOUR_ANALYTICS_READ), { data: y, loading: b } = e({ skip: _ || !v });
15
15
  if (!_ && !v) return /* @__PURE__ */ d("div", {
16
- className: "flex items-center justify-center h-full p-6",
16
+ className: "flex items-center justify-center h-full p-3 sm:p-6",
17
17
  children: /* @__PURE__ */ f("div", {
18
18
  className: "text-center",
19
19
  children: [/* @__PURE__ */ d("h2", {
@@ -34,9 +34,9 @@ function m() {
34
34
  });
35
35
  let x = y?.workspaceAnalyticsSummary;
36
36
  return x ? /* @__PURE__ */ f("div", {
37
- className: "space-y-6 p-6",
37
+ className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
38
38
  children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
39
- className: "text-2xl font-bold",
39
+ className: "text-xl sm:text-2xl font-bold",
40
40
  children: h("systemAnalytics.title", "System Analytics")
41
41
  }), /* @__PURE__ */ d("p", {
42
42
  className: "text-sm text-muted-foreground",
@@ -1 +1 @@
1
- {"version":3,"file":"SystemAnalyticsPage.js","names":[],"sources":["../../../src/tours/pages/SystemAnalyticsPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { Compass, Globe, Eye, TrendingUp, BarChart3 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useGetWorkspaceAnalyticsSummaryQuery } from '../../generated/global-operations';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { formatNumber, formatPercentage } 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 { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\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\n if (!permissionsLoading && !canViewAnalytics) {\n return (\n <div className=\"flex items-center justify-center h-full p-6\">\n <div className=\"text-center\">\n <h2 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('systemAnalytics.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\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-muted-foreground\">\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=\"flex items-center justify-center h-full\">\n <p className=\"text-muted-foreground\">\n {tr('systemAnalytics.noData', 'No analytics data available')}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-2xl font-bold\">{tr('systemAnalytics.title', 'System Analytics')}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {tr('systemAnalytics.subtitle', 'Workspace-wide tour performance overview')}\n </p>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4\">\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-primary/10 rounded-md\">\n <Compass className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('systemAnalytics.totalTours', 'Total Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.totalTours)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\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-muted-foreground\">\n {tr('systemAnalytics.publishedTours', 'Published Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.publishedTours)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\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-muted-foreground\">\n {tr('systemAnalytics.totalViews', 'Total Views')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.totalViews)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\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-muted-foreground\">\n {tr('systemAnalytics.totalCompletions', 'Total Completions')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.totalCompletions)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-primary/10 rounded-md\">\n <BarChart3 className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('systemAnalytics.completionRate', 'Completion Rate')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">\n {formatPercentage(summary.overallCompletionRate)}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SystemAnalyticsPage.js","names":[],"sources":["../../../src/tours/pages/SystemAnalyticsPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { Compass, Globe, Eye, TrendingUp, BarChart3 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useGetWorkspaceAnalyticsSummaryQuery } from '../../generated/global-operations';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { formatNumber, formatPercentage } 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 { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\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\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-foreground mb-2\">\n {tr('systemAnalytics.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\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-muted-foreground\">\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=\"flex items-center justify-center h-full\">\n <p className=\"text-muted-foreground\">\n {tr('systemAnalytics.noData', 'No analytics data available')}\n </p>\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-muted-foreground\">\n {tr('systemAnalytics.subtitle', 'Workspace-wide tour performance overview')}\n </p>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4\">\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-primary/10 rounded-md\">\n <Compass className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('systemAnalytics.totalTours', 'Total Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.totalTours)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\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-muted-foreground\">\n {tr('systemAnalytics.publishedTours', 'Published Tours')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.publishedTours)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\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-muted-foreground\">\n {tr('systemAnalytics.totalViews', 'Total Views')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.totalViews)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\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-muted-foreground\">\n {tr('systemAnalytics.totalCompletions', 'Total Completions')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(summary.totalCompletions)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-primary/10 rounded-md\">\n <BarChart3 className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('systemAnalytics.completionRate', 'Completion Rate')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">\n {formatPercentage(summary.overallCompletionRate)}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateLibraryPage.d.ts","sourceRoot":"","sources":["../../../src/tours/pages/TemplateLibraryPage.tsx"],"names":[],"mappings":"AAeA;;;GAGG;AACH,wBAAgB,mBAAmB,4CAyMlC"}
1
+ {"version":3,"file":"TemplateLibraryPage.d.ts","sourceRoot":"","sources":["../../../src/tours/pages/TemplateLibraryPage.tsx"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,mBAAmB,4CA4MlC"}
@@ -4,197 +4,200 @@ import { useToursPermissionAccess as i } from "../hooks/useToursPermissionAccess
4
4
  import { TOUR_PERMISSIONS as a } from "../../constants/permissions.js";
5
5
  import { useToursI18n as o } from "../i18n.js";
6
6
  import { nativeConfirm as s, nativeImpact as c, nativeNotify as l } from "../../utils/nativeBridge.js";
7
- import { useState as u } from "react";
8
- import { Globe as d, LayoutTemplate as f, Lock as p, Search as m, Star as h, Trash2 as g } from "lucide-react";
9
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
10
- import { useNavigate as y } from "react-router-dom";
7
+ import { emitToursApiFailed as u } from "../telemetry.js";
8
+ import { useState as d } from "react";
9
+ import { Globe as f, LayoutTemplate as p, Lock as m, Search as h, Star as g, Trash2 as _ } from "lucide-react";
10
+ import { jsx as v, jsxs as y } from "react/jsx-runtime";
11
+ import { useNavigate as b } from "react-router-dom";
12
+ import { useEventBus as x } from "@burdenoff/fe-libs/shared/events";
11
13
  //#region src/tours/pages/TemplateLibraryPage.tsx
12
- function b() {
13
- let { tr: b } = o(), x = y(), { basePath: S = "/" } = e(), { hasPermission: C } = i(), w = C(a.TOUR_TEMPLATE_LIST), T = C(a.TOUR_TEMPLATE_USE), E = C(a.TOUR_TEMPLATE_DELETE), [D, O] = u(""), [k, A] = u("all"), [j, M] = u(!1), { data: N, loading: P } = r({
14
- skip: !w,
14
+ function S() {
15
+ let { tr: S } = o(), C = b(), w = e(), { basePath: T = "/" } = w, E = x(), { hasPermission: D } = i(), O = D(a.TOUR_TEMPLATE_LIST), k = D(a.TOUR_TEMPLATE_USE), A = D(a.TOUR_TEMPLATE_DELETE), [j, M] = d(""), [N, P] = d("all"), [F, I] = d(!1), { data: L, loading: R } = r({
16
+ skip: !O,
15
17
  variables: {
16
- isPublic: j || void 0,
17
- category: k === "all" ? void 0 : k
18
+ isPublic: F || void 0,
19
+ category: N === "all" ? void 0 : N
18
20
  }
19
- }), F = (N?.tourTemplates ?? []).filter((e) => !D || e.name.toLowerCase().includes(D.toLowerCase())), [I] = t(), L = async (e) => {
21
+ }), z = (L?.tourTemplates ?? []).filter((e) => !j || e.name.toLowerCase().includes(j.toLowerCase())), [B] = t(), V = async (e) => {
20
22
  c("light");
21
23
  try {
22
- let { data: t } = await I({ variables: { templateId: e } });
23
- t?.createTourFromTemplate && (l("success"), x(`${S}/${t.createTourFromTemplate.id}/edit`));
24
- } catch (e) {
25
- l("error"), console.error("Failed to create tour from template:", e);
24
+ let { data: t } = await B({ variables: { templateId: e } });
25
+ t?.createTourFromTemplate && (l("success"), C(`${T}/${t.createTourFromTemplate.id}/edit`));
26
+ } catch {
27
+ l("error"), u(E, "template.create_from_template", w);
26
28
  }
27
- }, [R] = n(), z = async (e) => {
29
+ }, [H] = n(), U = async (e) => {
28
30
  try {
29
- await R({
31
+ await H({
30
32
  variables: { id: e },
31
33
  refetchQueries: ["GetTourTemplates"]
32
34
  }), l("success");
33
- } catch (e) {
34
- l("error"), console.error("Failed to delete template:", e);
35
+ } catch {
36
+ l("error"), u(E, "template.delete", w);
35
37
  }
36
- }, B = async (e) => {
38
+ }, W = async (e) => {
37
39
  c("medium");
38
- let t = b("templateLibrary.deleteConfirm"), n = await s({
39
- title: b("templateLibrary.deleteConfirm"),
40
+ let t = S("templateLibrary.deleteConfirm"), n = await s({
41
+ title: S("templateLibrary.deleteConfirm"),
40
42
  message: t
41
43
  });
42
- (n === null ? window.confirm(t) : n) && await z(e);
44
+ (n === null ? window.confirm(t) : n) && await U(e);
43
45
  };
44
- return /* @__PURE__ */ v("div", {
45
- className: "space-y-6 p-6",
46
+ return /* @__PURE__ */ y("div", {
47
+ className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
46
48
  children: [
47
- /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("h1", {
48
- className: "text-2xl font-bold",
49
- children: b("templateLibrary.title")
50
- }), /* @__PURE__ */ _("p", {
49
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h1", {
50
+ className: "text-xl sm:text-2xl font-bold",
51
+ children: S("templateLibrary.title")
52
+ }), /* @__PURE__ */ v("p", {
51
53
  className: "text-sm text-muted-foreground",
52
- children: b("templateLibrary.subtitle")
54
+ children: S("templateLibrary.subtitle")
53
55
  })] }),
54
- /* @__PURE__ */ v("div", {
56
+ /* @__PURE__ */ y("div", {
55
57
  className: "flex flex-col sm:flex-row gap-3",
56
58
  children: [
57
- /* @__PURE__ */ v("div", {
59
+ /* @__PURE__ */ y("div", {
58
60
  className: "flex-1 relative",
59
- children: [/* @__PURE__ */ _(m, {
61
+ children: [/* @__PURE__ */ v(h, {
60
62
  className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground",
61
63
  size: 20
62
- }), /* @__PURE__ */ _("input", {
64
+ }), /* @__PURE__ */ v("input", {
63
65
  type: "text",
64
- placeholder: b("templateLibrary.searchPlaceholder"),
65
- value: D,
66
- onChange: (e) => O(e.target.value),
67
- className: "w-full pl-[3.25rem] pr-4 py-2 border border-border rounded-md bg-background"
66
+ placeholder: S("templateLibrary.searchPlaceholder"),
67
+ value: j,
68
+ onChange: (e) => M(e.target.value),
69
+ className: "w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border rounded-md bg-background"
68
70
  })]
69
71
  }),
70
- /* @__PURE__ */ v("select", {
71
- value: k,
72
- onChange: (e) => A(e.target.value),
73
- className: "px-4 py-2 border border-border rounded-md bg-background",
72
+ /* @__PURE__ */ y("select", {
73
+ value: N,
74
+ onChange: (e) => P(e.target.value),
75
+ className: "px-4 py-2 min-h-[44px] border border-border rounded-md bg-background",
74
76
  children: [
75
- /* @__PURE__ */ _("option", {
77
+ /* @__PURE__ */ v("option", {
76
78
  value: "all",
77
- children: b("templateLibrary.allCategories")
79
+ children: S("templateLibrary.allCategories")
78
80
  }),
79
- /* @__PURE__ */ _("option", {
81
+ /* @__PURE__ */ v("option", {
80
82
  value: "ONBOARDING",
81
- children: b("templateLibrary.categoryOnboarding")
83
+ children: S("templateLibrary.categoryOnboarding")
82
84
  }),
83
- /* @__PURE__ */ _("option", {
85
+ /* @__PURE__ */ v("option", {
84
86
  value: "FEATURE_ANNOUNCEMENT",
85
- children: b("templateLibrary.categoryFeatureAnnouncement")
87
+ children: S("templateLibrary.categoryFeatureAnnouncement")
86
88
  }),
87
- /* @__PURE__ */ _("option", {
89
+ /* @__PURE__ */ v("option", {
88
90
  value: "HELP",
89
- children: b("templateLibrary.categoryHelp")
91
+ children: S("templateLibrary.categoryHelp")
90
92
  }),
91
- /* @__PURE__ */ _("option", {
93
+ /* @__PURE__ */ v("option", {
92
94
  value: "CUSTOM",
93
- children: b("templateLibrary.categoryCustom")
95
+ children: S("templateLibrary.categoryCustom")
94
96
  })
95
97
  ]
96
98
  }),
97
- /* @__PURE__ */ v("label", {
98
- className: "flex items-center gap-2 px-4 py-2 border border-border rounded-md bg-background cursor-pointer",
99
- children: [/* @__PURE__ */ _("input", {
99
+ /* @__PURE__ */ y("label", {
100
+ className: "flex items-center gap-2 px-4 py-2 min-h-[44px] border border-border rounded-md bg-background cursor-pointer",
101
+ children: [/* @__PURE__ */ v("input", {
100
102
  type: "checkbox",
101
- checked: j,
102
- onChange: (e) => M(e.target.checked),
103
+ checked: F,
104
+ onChange: (e) => I(e.target.checked),
103
105
  className: "rounded"
104
- }), /* @__PURE__ */ _("span", {
106
+ }), /* @__PURE__ */ v("span", {
105
107
  className: "text-sm",
106
- children: b("templateLibrary.publicOnly")
108
+ children: S("templateLibrary.publicOnly")
107
109
  })]
108
110
  })
109
111
  ]
110
112
  }),
111
- P ? /* @__PURE__ */ _("div", {
113
+ R ? /* @__PURE__ */ v("div", {
112
114
  className: "text-center py-12",
113
- children: /* @__PURE__ */ _("p", {
115
+ children: /* @__PURE__ */ v("p", {
114
116
  className: "text-muted-foreground",
115
- children: b("templateLibrary.loading")
117
+ children: S("templateLibrary.loading")
116
118
  })
117
- }) : F.length > 0 ? /* @__PURE__ */ _("div", {
119
+ }) : z.length > 0 ? /* @__PURE__ */ v("div", {
118
120
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
119
- children: F.map((e) => /* @__PURE__ */ v("div", {
121
+ children: z.map((e) => /* @__PURE__ */ y("div", {
120
122
  className: "border border-border rounded-md overflow-hidden hover:shadow-md transition-shadow",
121
- children: [/* @__PURE__ */ _("div", {
123
+ children: [/* @__PURE__ */ v("div", {
122
124
  className: "aspect-video bg-muted flex items-center justify-center",
123
- children: e.thumbnail ? /* @__PURE__ */ _("img", {
125
+ children: e.thumbnail ? /* @__PURE__ */ v("img", {
124
126
  src: e.thumbnail,
125
127
  alt: e.name,
126
128
  className: "w-full h-full object-cover"
127
- }) : /* @__PURE__ */ _(f, {
129
+ }) : /* @__PURE__ */ v(p, {
128
130
  className: "text-muted-foreground",
129
131
  size: 48
130
132
  })
131
- }), /* @__PURE__ */ v("div", {
133
+ }), /* @__PURE__ */ y("div", {
132
134
  className: "p-4",
133
135
  children: [
134
- /* @__PURE__ */ v("div", {
136
+ /* @__PURE__ */ y("div", {
135
137
  className: "flex items-start justify-between mb-2",
136
- children: [/* @__PURE__ */ _("h3", {
138
+ children: [/* @__PURE__ */ v("h3", {
137
139
  className: "font-semibold",
138
140
  children: e.name
139
- }), /* @__PURE__ */ v("div", {
141
+ }), /* @__PURE__ */ y("div", {
140
142
  className: "flex gap-1",
141
- children: [e.isPremium && /* @__PURE__ */ _(h, {
143
+ children: [e.isPremium && /* @__PURE__ */ v(g, {
142
144
  className: "text-warning",
143
145
  size: 16
144
- }), e.isPublic ? /* @__PURE__ */ _(d, {
146
+ }), e.isPublic ? /* @__PURE__ */ v(f, {
145
147
  className: "text-muted-foreground",
146
148
  size: 16
147
- }) : /* @__PURE__ */ _(p, {
149
+ }) : /* @__PURE__ */ v(m, {
148
150
  className: "text-muted-foreground",
149
151
  size: 16
150
152
  })]
151
153
  })]
152
154
  }),
153
- e.description && /* @__PURE__ */ _("p", {
155
+ e.description && /* @__PURE__ */ v("p", {
154
156
  className: "text-sm text-muted-foreground mb-3 line-clamp-2",
155
157
  children: e.description
156
158
  }),
157
- /* @__PURE__ */ v("div", {
159
+ /* @__PURE__ */ y("div", {
158
160
  className: "flex items-center justify-between",
159
- children: [/* @__PURE__ */ v("span", {
161
+ children: [/* @__PURE__ */ y("span", {
160
162
  className: "text-xs text-muted-foreground",
161
163
  children: [
162
- b("templateLibrary.usedTimes"),
164
+ S("templateLibrary.usedTimes"),
163
165
  " ",
164
166
  e.useCount,
165
167
  " ",
166
- b("templateLibrary.times")
168
+ S("templateLibrary.times")
167
169
  ]
168
- }), /* @__PURE__ */ v("div", {
170
+ }), /* @__PURE__ */ y("div", {
169
171
  className: "flex items-center gap-2",
170
- children: [E && /* @__PURE__ */ _("button", {
171
- onClick: () => void B(e.id),
172
- className: "p-1 text-muted-foreground hover:text-destructive rounded-md",
173
- children: /* @__PURE__ */ _(g, { size: 16 })
174
- }), T && /* @__PURE__ */ _("button", {
175
- onClick: () => L(e.id),
176
- className: "px-3 py-1 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 text-sm",
177
- children: b("templateLibrary.useTemplate")
172
+ children: [A && /* @__PURE__ */ v("button", {
173
+ onClick: () => void W(e.id),
174
+ className: "p-2 min-h-[44px] min-w-[44px] text-muted-foreground hover:text-destructive rounded-md",
175
+ "aria-label": S("templateLibrary.deleteTemplate"),
176
+ children: /* @__PURE__ */ v(_, { size: 16 })
177
+ }), k && /* @__PURE__ */ v("button", {
178
+ onClick: () => V(e.id),
179
+ className: "px-3 py-2 min-h-[44px] bg-primary text-primary-foreground rounded-md hover:bg-primary/90 text-sm",
180
+ children: S("templateLibrary.useTemplate")
178
181
  })]
179
182
  })]
180
183
  })
181
184
  ]
182
185
  })]
183
186
  }, e.id))
184
- }) : /* @__PURE__ */ v("div", {
187
+ }) : /* @__PURE__ */ y("div", {
185
188
  className: "text-center py-12 border border-dashed border-border rounded-md",
186
189
  children: [
187
- /* @__PURE__ */ _(f, {
190
+ /* @__PURE__ */ v(p, {
188
191
  className: "mx-auto mb-4 text-muted-foreground",
189
192
  size: 48
190
193
  }),
191
- /* @__PURE__ */ _("p", {
194
+ /* @__PURE__ */ v("p", {
192
195
  className: "text-muted-foreground mb-2",
193
- children: b("templateLibrary.noTemplatesFound")
196
+ children: S("templateLibrary.noTemplatesFound")
194
197
  }),
195
- /* @__PURE__ */ _("p", {
198
+ /* @__PURE__ */ v("p", {
196
199
  className: "text-sm text-muted-foreground",
197
- children: b("templateLibrary.adjustFilters")
200
+ children: S("templateLibrary.adjustFilters")
198
201
  })
199
202
  ]
200
203
  })
@@ -202,6 +205,6 @@ function b() {
202
205
  });
203
206
  }
204
207
  //#endregion
205
- export { b as TemplateLibraryPage };
208
+ export { S as TemplateLibraryPage };
206
209
 
207
210
  //# sourceMappingURL=TemplateLibraryPage.js.map