@burdenoff/microfe-vibecontrols 2026.521.3 → 2026.522.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/VibeControlsRoot.js +86 -78
  2. package/dist/VibeControlsRoot.js.map +1 -1
  3. package/dist/VibeControlsRoutes.js +168 -125
  4. package/dist/VibeControlsRoutes.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js +174 -0
  6. package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -0
  7. package/dist/components/agent-manager/AgentManagerChatPane.js +149 -94
  8. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerMessageItem.js +52 -41
  10. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerMessageList.js +22 -15
  12. package/dist/components/agent-manager/AgentManagerMessageList.js.map +1 -1
  13. package/dist/components/agents/OpenInEditorMenu.js +241 -0
  14. package/dist/components/agents/OpenInEditorMenu.js.map +1 -0
  15. package/dist/components/agents/VibeOpenInEditorMenu.js +20 -0
  16. package/dist/components/agents/VibeOpenInEditorMenu.js.map +1 -0
  17. package/dist/components/agents/idePresets.js +148 -0
  18. package/dist/components/agents/idePresets.js.map +1 -0
  19. package/dist/components/agents/index.js +3 -0
  20. package/dist/components/ai/AIPlanPanel.js +332 -0
  21. package/dist/components/ai/AIPlanPanel.js.map +1 -0
  22. package/dist/components/analytics/index.js +2 -2
  23. package/dist/components/assistant/AssistantMessageItem.js +55 -47
  24. package/dist/components/assistant/AssistantMessageItem.js.map +1 -1
  25. package/dist/components/audit/AuditExportButton.js +87 -41
  26. package/dist/components/audit/AuditExportButton.js.map +1 -1
  27. package/dist/components/gitops/CIDurationChart.js +11 -11
  28. package/dist/components/security/SecurityEmptyState.js +33 -0
  29. package/dist/components/security/SecurityEmptyState.js.map +1 -0
  30. package/dist/components/security/SecurityEvidenceList.js +42 -0
  31. package/dist/components/security/SecurityEvidenceList.js.map +1 -0
  32. package/dist/components/security/SecurityFindingsTable.js +201 -0
  33. package/dist/components/security/SecurityFindingsTable.js.map +1 -0
  34. package/dist/components/security/SecurityHeaderStrip.js +67 -0
  35. package/dist/components/security/SecurityHeaderStrip.js.map +1 -0
  36. package/dist/components/security/SecurityLifecycleStepper.js +66 -0
  37. package/dist/components/security/SecurityLifecycleStepper.js.map +1 -0
  38. package/dist/components/security/SecurityProviderDrawer.js +112 -0
  39. package/dist/components/security/SecurityProviderDrawer.js.map +1 -0
  40. package/dist/components/security/SecurityTabPanel.js +105 -0
  41. package/dist/components/security/SecurityTabPanel.js.map +1 -0
  42. package/dist/components/security/gql.js +128 -0
  43. package/dist/components/security/gql.js.map +1 -0
  44. package/dist/components/security/index.js +8 -0
  45. package/dist/components/security/types.js +80 -0
  46. package/dist/components/security/types.js.map +1 -0
  47. package/dist/components/settings/IdePreferencesSection.js +66 -0
  48. package/dist/components/settings/IdePreferencesSection.js.map +1 -0
  49. package/dist/components/shared/PageSkeleton.js +88 -0
  50. package/dist/components/shared/PageSkeleton.js.map +1 -0
  51. package/dist/constants/pluginCatalog.js +20 -8
  52. package/dist/constants/pluginCatalog.js.map +1 -1
  53. package/dist/generated/wspace-operations.js +1350 -1262
  54. package/dist/generated/wspace-operations.js.map +1 -1
  55. package/dist/generated/wspace-types.js.map +1 -1
  56. package/dist/hooks/useToggleAgentBookmark.js +32 -0
  57. package/dist/hooks/useToggleAgentBookmark.js.map +1 -0
  58. package/dist/index.js +10 -10
  59. package/dist/pages/AgentDetailsPage.js +25 -25
  60. package/dist/pages/AgentDetailsPage.js.map +1 -1
  61. package/dist/pages/AgentsPage.js +326 -327
  62. package/dist/pages/AgentsPage.js.map +1 -1
  63. package/dist/pages/AnalyticsPage.js +425 -420
  64. package/dist/pages/AnalyticsPage.js.map +1 -1
  65. package/dist/pages/CatalogDetailPage.js +258 -249
  66. package/dist/pages/CatalogDetailPage.js.map +1 -1
  67. package/dist/pages/CatalogPage.js +22 -22
  68. package/dist/pages/CatalogPage.js.map +1 -1
  69. package/dist/pages/ComponentDetailPage.js +30 -30
  70. package/dist/pages/ComponentDetailPage.js.map +1 -1
  71. package/dist/pages/ComponentsPage.js +23 -23
  72. package/dist/pages/ComponentsPage.js.map +1 -1
  73. package/dist/pages/LogsPage.js +107 -0
  74. package/dist/pages/LogsPage.js.map +1 -0
  75. package/dist/pages/NoteDetailsPage.js +12 -12
  76. package/dist/pages/NoteDetailsPage.js.map +1 -1
  77. package/dist/pages/SessionDetailsPage.js +12 -12
  78. package/dist/pages/SessionDetailsPage.js.map +1 -1
  79. package/dist/pages/SessionsPage.js +158 -136
  80. package/dist/pages/SessionsPage.js.map +1 -1
  81. package/dist/pages/SettingsPage.js +60 -52
  82. package/dist/pages/SettingsPage.js.map +1 -1
  83. package/dist/pages/TargetDetailsPage.js +18 -18
  84. package/dist/pages/TargetDetailsPage.js.map +1 -1
  85. package/dist/pages/TargetsPage.js +328 -331
  86. package/dist/pages/TargetsPage.js.map +1 -1
  87. package/dist/pages/TemplateDetailPage.js +17 -17
  88. package/dist/pages/TemplateDetailPage.js.map +1 -1
  89. package/dist/pages/TemplatesPage.js +17 -17
  90. package/dist/pages/TemplatesPage.js.map +1 -1
  91. package/dist/pages/VibeDetailsPage.js +552 -535
  92. package/dist/pages/VibeDetailsPage.js.map +1 -1
  93. package/dist/pages/VibesPage.js +321 -296
  94. package/dist/pages/VibesPage.js.map +1 -1
  95. package/dist/pages/ai/AIBookmarksPage.js +184 -0
  96. package/dist/pages/ai/AIBookmarksPage.js.map +1 -0
  97. package/dist/pages/ai/AIPage.js +138 -109
  98. package/dist/pages/ai/AIPage.js.map +1 -1
  99. package/dist/services/planApi.js +18 -0
  100. package/dist/services/planApi.js.map +1 -0
  101. package/dist/store/vibeControlsStore.js +20 -2
  102. package/dist/store/vibeControlsStore.js.map +1 -1
  103. package/dist/translations/en.js +30 -1
  104. package/dist/translations/en.js.map +1 -1
  105. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../src/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { motion } from 'framer-motion';\nimport {\n ArrowLeft,\n FileCode,\n Copy,\n Layers,\n Tag,\n FolderTree,\n Clock,\n RefreshCw,\n Trash2,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetTemplateQuery,\n useInstantiateTemplateMutation,\n useDeleteTemplateMutation,\n} from '@/generated/wspace-operations';\n\nconst categoryStyles: Record<string, string> = {\n STARTER: 'bg-bg-sunken text-text-secondary',\n MICROSERVICE: 'bg-action-primary-bg/10 text-action-primary-bg',\n FRONTEND: 'bg-status-info-bg text-status-info-text',\n BACKEND: 'bg-action-primary-bg/10 text-action-primary-bg',\n FULLSTACK: 'bg-status-success-bg text-status-success-text',\n INFRASTRUCTURE: 'bg-status-warning-bg text-status-warning-text',\n LIBRARY: 'bg-status-warning-bg text-status-warning-text',\n CUSTOM: 'bg-bg-sunken text-text-secondary',\n};\n\nexport function TemplateDetailPage() {\n const { t } = useI18n();\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\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 [showCloneDialog, setShowCloneDialog] = useState(false);\n const [cloneLoading, setCloneLoading] = useState(false);\n const [cloneError, setCloneError] = useState<string | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const cloneNameRef = useRef<HTMLInputElement>(null);\n const cloneDescRef = useRef<HTMLInputElement>(null);\n\n const { data, loading, error, refetch } = useGetTemplateQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [instantiateTemplate] = useInstantiateTemplateMutation();\n const [deleteTemplate] = useDeleteTemplateMutation({ refetchQueries: ['ListTemplates'] });\n\n const template = data?.vibecontrolsTemplate ?? null;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/templates');\n };\n\n const handleClone = async () => {\n const name = cloneNameRef.current?.value?.trim();\n if (!name) {\n setCloneError(tr('template.cloneNameRequired', 'Name is required'));\n return;\n }\n\n setCloneLoading(true);\n setCloneError(null);\n try {\n const result = await instantiateTemplate({\n variables: {\n templateId: id,\n input: {\n name,\n description: cloneDescRef.current?.value?.trim() || undefined,\n },\n },\n });\n setShowCloneDialog(false);\n const newId = result.data?.instantiateVibecontrolsTemplate?.id;\n setSuccessMessage(tr('template.clonedSuccess', 'Cloned successfully'));\n setTimeout(() => {\n setSuccessMessage(null);\n if (newId) navigate(`/components/${newId}`);\n }, 1500);\n } catch (err) {\n setCloneError(\n err instanceof Error ? err.message : tr('template.cloneFailed', 'Failed to clone')\n );\n } finally {\n setCloneLoading(false);\n }\n };\n\n const handleDeleteTemplate = async () => {\n if (!id) return;\n setIsDeleting(true);\n try {\n await deleteTemplate({ variables: { id } });\n setShowDeleteConfirm(false);\n navigate('/templates');\n } finally {\n setIsDeleting(false);\n }\n };\n\n // Guard states\n if (loading && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <LoadingState message={tr('template.loadingDetails', 'Loading template details...')} />\n </MotionPage>\n );\n }\n\n if (error && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load template')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('template.notFound', 'Template not found')}\n description={`No template with ID \"${id}\" exists`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed top-4 left-4 right-4 sm:left-auto z-50 bg-status-success-bg text-status-success-text px-4 py-3 rounded-md shadow-lg\">\n {successMessage}\n </div>\n )}\n\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={template.name}\n description={template.description || tr('template.noDescription', 'No description')}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCloneDialog(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Copy className=\"w-4 h-4\" />\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </button>\n <button\n data-testid=\"template-delete-button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-status-error-bg text-status-error-text rounded-md hover:opacity-90 transition-opacity\"\n aria-label={tr('template.deleteTemplate', 'Delete Template')}\n >\n <Trash2 className=\"w-4 h-4\" aria-hidden=\"true\" />\n {tr('common.delete', 'Delete')}\n </button>\n </div>\n }\n />\n\n {/* Badges */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n {template.category && (\n <span\n className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${categoryStyles[template.category] || 'bg-bg-sunken text-text-secondary'}`}\n >\n <Layers className=\"w-3.5 h-3.5\" />\n {template.category}\n </span>\n )}\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Copy className=\"w-3.5 h-3.5\" />\n {template.cloneCount} {tr('template.clones', 'clones')}\n </span>\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Clock className=\"w-3.5 h-3.5\" />\n {new Date(template.createdAt).toLocaleDateString()}\n </span>\n </div>\n\n {/* Tags */}\n {template.tags?.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {template.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n <Tag className=\"w-3 h-3\" />\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Universal tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('template.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_TEMPLATE\" entityId={template.id} />\n </div>\n </Card>\n\n {/* Source Component */}\n {template.sourceComponent && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.sourceComponent', 'Source Component')}\n </h3>\n </div>\n <div className=\"p-4\">\n <button\n onClick={() => navigate(`/components/${template.sourceComponent.id}`)}\n className=\"flex items-center gap-3 text-left hover:bg-bg-sunken p-2 -m-2 rounded-md transition-colors\"\n >\n <div className=\"w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0\">\n <FileCode className=\"w-5 h-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{template.sourceComponent.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {template.sourceComponent.type} &middot; {template.sourceComponent.lifecycle}\n </p>\n </div>\n </button>\n </div>\n </Card>\n )}\n\n {/* Setup Instructions */}\n {template.setupInstructions && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.setupInstructions', 'Setup Instructions')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"prose prose-sm max-w-none text-text-primary\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto whitespace-pre-wrap font-mono text-text-primary\">\n {template.setupInstructions}\n </pre>\n </div>\n </div>\n </Card>\n )}\n\n {/* File Tree */}\n {template.fileTree && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <FolderTree className=\"w-5 h-5\" />\n {tr('template.fileTree', 'File Tree')}\n </h3>\n </div>\n <div className=\"p-4\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto font-mono text-text-primary\">\n {typeof template.fileTree === 'string'\n ? template.fileTree\n : JSON.stringify(template.fileTree, null, 2)}\n </pre>\n </div>\n </Card>\n )}\n\n {/* Clone Dialog */}\n {showCloneDialog && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n className=\"bg-bg-surface rounded-lg p-6 w-full max-w-md\"\n >\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </h2>\n <div className=\"space-y-4\">\n {cloneError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {cloneError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneName', 'Name *')}\n </label>\n <input\n ref={cloneNameRef}\n type=\"text\"\n placeholder={tr('template.cloneNamePlaceholder', 'my-new-component')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneDescription', 'Description')}\n </label>\n <input\n ref={cloneDescRef}\n type=\"text\"\n placeholder={tr('template.cloneDescPlaceholder', 'Optional description')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCloneDialog(false)}\n disabled={cloneLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleClone}\n disabled={cloneLoading}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Copy className=\"w-4 h-4\" />\n {cloneLoading\n ? tr('template.cloning', 'Cloning...')\n : tr('template.clone', 'Clone')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDeleteTemplate}\n title={tr('template.deleteTemplate', 'Delete Template')}\n message={tr(\n 'template.deleteConfirmMessage',\n `Are you sure you want to delete template \"${template.name}\"? Existing instantiated components are unaffected, but the template will no longer be available for cloning. This cannot be undone.`\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n isLoading={isDeleting}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../src/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { motion } from 'framer-motion';\nimport {\n ArrowLeft,\n FileCode,\n Copy,\n Layers,\n Tag,\n FolderTree,\n Clock,\n RefreshCw,\n Trash2,\n} from 'lucide-react';\nimport { PageHeader, Card, EmptyState, ErrorState, ConfirmDialog } from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetTemplateQuery,\n useInstantiateTemplateMutation,\n useDeleteTemplateMutation,\n} from '@/generated/wspace-operations';\n\nconst categoryStyles: Record<string, string> = {\n STARTER: 'bg-bg-sunken text-text-secondary',\n MICROSERVICE: 'bg-action-primary-bg/10 text-action-primary-bg',\n FRONTEND: 'bg-status-info-bg text-status-info-text',\n BACKEND: 'bg-action-primary-bg/10 text-action-primary-bg',\n FULLSTACK: 'bg-status-success-bg text-status-success-text',\n INFRASTRUCTURE: 'bg-status-warning-bg text-status-warning-text',\n LIBRARY: 'bg-status-warning-bg text-status-warning-text',\n CUSTOM: 'bg-bg-sunken text-text-secondary',\n};\n\nexport function TemplateDetailPage() {\n const { t } = useI18n();\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\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 [showCloneDialog, setShowCloneDialog] = useState(false);\n const [cloneLoading, setCloneLoading] = useState(false);\n const [cloneError, setCloneError] = useState<string | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const cloneNameRef = useRef<HTMLInputElement>(null);\n const cloneDescRef = useRef<HTMLInputElement>(null);\n\n const { data, loading, error, refetch } = useGetTemplateQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [instantiateTemplate] = useInstantiateTemplateMutation();\n const [deleteTemplate] = useDeleteTemplateMutation({ refetchQueries: ['ListTemplates'] });\n\n const template = data?.vibecontrolsTemplate ?? null;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/templates');\n };\n\n const handleClone = async () => {\n const name = cloneNameRef.current?.value?.trim();\n if (!name) {\n setCloneError(tr('template.cloneNameRequired', 'Name is required'));\n return;\n }\n\n setCloneLoading(true);\n setCloneError(null);\n try {\n const result = await instantiateTemplate({\n variables: {\n templateId: id,\n input: {\n name,\n description: cloneDescRef.current?.value?.trim() || undefined,\n },\n },\n });\n setShowCloneDialog(false);\n const newId = result.data?.instantiateVibecontrolsTemplate?.id;\n setSuccessMessage(tr('template.clonedSuccess', 'Cloned successfully'));\n setTimeout(() => {\n setSuccessMessage(null);\n if (newId) navigate(`/components/${newId}`);\n }, 1500);\n } catch (err) {\n setCloneError(\n err instanceof Error ? err.message : tr('template.cloneFailed', 'Failed to clone')\n );\n } finally {\n setCloneLoading(false);\n }\n };\n\n const handleDeleteTemplate = async () => {\n if (!id) return;\n setIsDeleting(true);\n try {\n await deleteTemplate({ variables: { id } });\n setShowDeleteConfirm(false);\n navigate('/templates');\n } finally {\n setIsDeleting(false);\n }\n };\n\n // Guard states\n if (loading && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load template')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('template.notFound', 'Template not found')}\n description={`No template with ID \"${id}\" exists`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed top-4 left-4 right-4 sm:left-auto z-50 bg-status-success-bg text-status-success-text px-4 py-3 rounded-md shadow-lg\">\n {successMessage}\n </div>\n )}\n\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={template.name}\n description={template.description || tr('template.noDescription', 'No description')}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCloneDialog(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Copy className=\"w-4 h-4\" />\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </button>\n <button\n data-testid=\"template-delete-button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-status-error-bg text-status-error-text rounded-md hover:opacity-90 transition-opacity\"\n aria-label={tr('template.deleteTemplate', 'Delete Template')}\n >\n <Trash2 className=\"w-4 h-4\" aria-hidden=\"true\" />\n {tr('common.delete', 'Delete')}\n </button>\n </div>\n }\n />\n\n {/* Badges */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n {template.category && (\n <span\n className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${categoryStyles[template.category] || 'bg-bg-sunken text-text-secondary'}`}\n >\n <Layers className=\"w-3.5 h-3.5\" />\n {template.category}\n </span>\n )}\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Copy className=\"w-3.5 h-3.5\" />\n {template.cloneCount} {tr('template.clones', 'clones')}\n </span>\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Clock className=\"w-3.5 h-3.5\" />\n {new Date(template.createdAt).toLocaleDateString()}\n </span>\n </div>\n\n {/* Tags */}\n {template.tags?.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {template.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n <Tag className=\"w-3 h-3\" />\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Universal tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('template.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_TEMPLATE\" entityId={template.id} />\n </div>\n </Card>\n\n {/* Source Component */}\n {template.sourceComponent && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.sourceComponent', 'Source Component')}\n </h3>\n </div>\n <div className=\"p-4\">\n <button\n onClick={() => navigate(`/components/${template.sourceComponent.id}`)}\n className=\"flex items-center gap-3 text-left hover:bg-bg-sunken p-2 -m-2 rounded-md transition-colors\"\n >\n <div className=\"w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0\">\n <FileCode className=\"w-5 h-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{template.sourceComponent.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {template.sourceComponent.type} &middot; {template.sourceComponent.lifecycle}\n </p>\n </div>\n </button>\n </div>\n </Card>\n )}\n\n {/* Setup Instructions */}\n {template.setupInstructions && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.setupInstructions', 'Setup Instructions')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"prose prose-sm max-w-none text-text-primary\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto whitespace-pre-wrap font-mono text-text-primary\">\n {template.setupInstructions}\n </pre>\n </div>\n </div>\n </Card>\n )}\n\n {/* File Tree */}\n {template.fileTree && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <FolderTree className=\"w-5 h-5\" />\n {tr('template.fileTree', 'File Tree')}\n </h3>\n </div>\n <div className=\"p-4\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto font-mono text-text-primary\">\n {typeof template.fileTree === 'string'\n ? template.fileTree\n : JSON.stringify(template.fileTree, null, 2)}\n </pre>\n </div>\n </Card>\n )}\n\n {/* Clone Dialog */}\n {showCloneDialog && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n className=\"bg-bg-surface rounded-lg p-6 w-full max-w-md\"\n >\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </h2>\n <div className=\"space-y-4\">\n {cloneError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {cloneError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneName', 'Name *')}\n </label>\n <input\n ref={cloneNameRef}\n type=\"text\"\n placeholder={tr('template.cloneNamePlaceholder', 'my-new-component')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneDescription', 'Description')}\n </label>\n <input\n ref={cloneDescRef}\n type=\"text\"\n placeholder={tr('template.cloneDescPlaceholder', 'Optional description')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCloneDialog(false)}\n disabled={cloneLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleClone}\n disabled={cloneLoading}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Copy className=\"w-4 h-4\" />\n {cloneLoading\n ? tr('template.cloning', 'Cloning...')\n : tr('template.clone', 'Clone')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDeleteTemplate}\n title={tr('template.deleteTemplate', 'Delete Template')}\n message={tr(\n 'template.deleteConfirmMessage',\n `Are you sure you want to delete template \"${template.name}\"? Existing instantiated components are unaffected, but the template will no longer be available for cloning. This cannot be undone.`\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n isLoading={isDeleting}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -1,14 +1,14 @@
1
1
  import { useListTemplatesQuery as e } from "../generated/wspace-operations.js";
2
2
  import { useVibeNavigate as t } from "../providers/VibeControlsProvider.js";
3
3
  import { PageHeader as n } from "../components/shared/PageHeader.js";
4
- import { LoadingState as r } from "../components/shared/LoadingState.js";
5
- import { ErrorState as i } from "../components/shared/ErrorState.js";
6
- import { EmptyState as a } from "../components/shared/EmptyState.js";
7
- import { SearchInput as o } from "../components/shared/SearchInput.js";
8
- import { FilterDropdown as s } from "../components/shared/FilterDropdown.js";
9
- import { GetHelpButton as c } from "../components/shared/GetHelpButton.js";
4
+ import { ErrorState as r } from "../components/shared/ErrorState.js";
5
+ import { EmptyState as i } from "../components/shared/EmptyState.js";
6
+ import { SearchInput as a } from "../components/shared/SearchInput.js";
7
+ import { FilterDropdown as o } from "../components/shared/FilterDropdown.js";
8
+ import { GetHelpButton as s } from "../components/shared/GetHelpButton.js";
10
9
  import "../components/shared/index.js";
11
- import { VIBE_PERMISSIONS as l } from "../constants/permissions.js";
10
+ import { VIBE_PERMISSIONS as c } from "../constants/permissions.js";
11
+ import { PageSkeleton as l } from "../components/shared/PageSkeleton.js";
12
12
  import { TemplateCard as u } from "../components/templates/TemplateCard.js";
13
13
  import "../components/templates/index.js";
14
14
  import { useCallback as d, useMemo as f, useState as p } from "react";
@@ -88,12 +88,9 @@ function w() {
88
88
  className: "text-text-secondary",
89
89
  children: "Loading permissions..."
90
90
  })
91
- }) : E(l.VIBE_LIST) ? F && R.length === 0 ? /* @__PURE__ */ y(C, {
91
+ }) : E(c.VIBE_LIST) ? I && R.length === 0 ? /* @__PURE__ */ y(C, {
92
92
  className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
93
- children: /* @__PURE__ */ y(r, { message: O("template.loading", "Loading templates...") })
94
- }) : I && R.length === 0 ? /* @__PURE__ */ y(C, {
95
- className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
96
- children: /* @__PURE__ */ y(i, {
93
+ children: /* @__PURE__ */ y(r, {
97
94
  title: O("template.failedLoad", "Failed to load templates"),
98
95
  message: I.message,
99
96
  onRetry: () => L()
@@ -114,12 +111,12 @@ function w() {
114
111
  }),
115
112
  /* @__PURE__ */ b("div", {
116
113
  className: "flex items-center gap-2 sm:gap-3 flex-wrap",
117
- children: [/* @__PURE__ */ y(o, {
114
+ children: [/* @__PURE__ */ y(a, {
118
115
  value: A,
119
116
  onChange: j,
120
117
  placeholder: O("template.searchPlaceholder", "Search templates..."),
121
118
  className: "flex-1 sm:w-64 sm:flex-none"
122
- }), /* @__PURE__ */ y(s, {
119
+ }), /* @__PURE__ */ y(o, {
123
120
  label: O("template.filterCategory", "Category"),
124
121
  value: M,
125
122
  options: k,
@@ -136,7 +133,10 @@ function w() {
136
133
  children: O("common.refreshing", "(refreshing...)")
137
134
  })]
138
135
  }),
139
- z.length > 0 ? /* @__PURE__ */ y(v, {
136
+ F && R.length === 0 ? /* @__PURE__ */ y(l, {
137
+ variant: "cards",
138
+ count: 6
139
+ }) : z.length > 0 ? /* @__PURE__ */ y(v, {
140
140
  mode: "wait",
141
141
  children: /* @__PURE__ */ y(S, {
142
142
  stagger: .04,
@@ -146,11 +146,11 @@ function w() {
146
146
  onOpen: B
147
147
  }) }, e.id))
148
148
  }, "template-grid")
149
- }) : /* @__PURE__ */ y(a, {
149
+ }) : /* @__PURE__ */ y(i, {
150
150
  icon: /* @__PURE__ */ y(h, { className: "w-8 h-8 text-text-secondary" }),
151
151
  title: A || M !== "all" ? O("template.emptyNoResults", "No templates found") : O("template.emptyNoTemplates", "No templates yet"),
152
152
  description: A || M !== "all" ? O("template.emptyAdjustSearch", "Try adjusting your search or filters") : O("template.emptyCreate", "Templates will appear here when components are published as templates"),
153
- action: !A && M === "all" ? /* @__PURE__ */ y(c, { prompt: "What is a Template in VibeControls and how do I create one?" }) : void 0
153
+ action: !A && M === "all" ? /* @__PURE__ */ y(s, { prompt: "What is a Template in VibeControls and how do I create one?" }) : void 0
154
154
  })
155
155
  ]
156
156
  }) : /* @__PURE__ */ y("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../src/pages/TemplatesPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { FileCode, RefreshCw } from 'lucide-react';\nimport { TemplateCard } from '@/components/templates';\nimport type { TemplateCardData } from '@/components/templates';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListTemplatesQuery } from '@/generated/wspace-operations';\n\nexport function TemplatesPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const categoryOptions = [\n { value: 'all', label: tr('template.allCategories', 'All Categories') },\n { value: 'STARTER', label: tr('template.categoryStarter', 'Starter') },\n { value: 'MICROSERVICE', label: tr('template.categoryMicroservice', 'Microservice') },\n { value: 'FRONTEND', label: tr('template.categoryFrontend', 'Frontend') },\n { value: 'BACKEND', label: tr('template.categoryBackend', 'Backend') },\n { value: 'FULLSTACK', label: tr('template.categoryFullstack', 'Fullstack') },\n { value: 'INFRASTRUCTURE', label: tr('template.categoryInfrastructure', 'Infrastructure') },\n { value: 'LIBRARY', label: tr('template.categoryLibrary', 'Library') },\n { value: 'CUSTOM', label: tr('template.categoryCustom', 'Custom') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [categoryFilter, setCategoryFilter] = useState('all');\n\n const { data, loading, error, refetch } = useListTemplatesQuery({\n variables: {\n filter: {\n category: categoryFilter !== 'all' ? categoryFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const templates: TemplateCardData[] = useMemo(() => {\n const items = data?.vibecontrolsTemplates?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n category: (item.category as string) || null,\n cloneCount: (item.cloneCount as number) || 0,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsTemplates?.items]);\n\n const filteredTemplates = useMemo(() => {\n if (!searchQuery) return templates;\n const query = searchQuery.toLowerCase();\n return templates.filter(\n (tmpl) =>\n tmpl.name.toLowerCase().includes(query) ||\n tmpl.description?.toLowerCase().includes(query) ||\n tmpl.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [templates, searchQuery]);\n\n const handleTemplateSelect = (tmpl: TemplateCardData) => {\n navigate(`/templates/${tmpl.id}`);\n };\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n if (loading && templates.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('template.loading', 'Loading templates...')} />\n </MotionPage>\n );\n }\n\n if (error && templates.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load templates')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('template.title', 'Templates')}\n description={tr('template.description', '{{total}} templates available', {\n total: templates.length,\n })}\n actions={\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n }\n />\n\n {/* Filters */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('template.searchPlaceholder', 'Search templates...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <FilterDropdown\n label={tr('template.filterCategory', 'Category')}\n value={categoryFilter}\n options={categoryOptions}\n onChange={setCategoryFilter}\n />\n </div>\n\n {/* Results count */}\n {(searchQuery || categoryFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('template.showingResults', 'Showing {{filtered}} of {{total}} templates', {\n filtered: filteredTemplates.length,\n total: templates.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {filteredTemplates.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"template-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredTemplates.map((tmpl) => (\n <MotionItem key={tmpl.id}>\n <TemplateCard template={tmpl} onOpen={handleTemplateSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyNoResults', 'No templates found')\n : tr('template.emptyNoTemplates', 'No templates yet')\n }\n description={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'template.emptyCreate',\n 'Templates will appear here when components are published as templates'\n )\n }\n action={\n !searchQuery && categoryFilter === 'all' ? (\n <GetHelpButton prompt=\"What is a Template in VibeControls and how do I create one?\" />\n ) : undefined\n }\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,IAAgB;CAC9B,IAAM,IAAW,GAAiB,EAC5B,EAAE,SAAM,GAAS,EACjB,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EAEnE,IAAK,GACR,GAAa,GAAkB,MAAqD;EACnF,IAAM,IAAa,EAAE,GAAK,EAAO,EAC3B,IAAW,MAAe,IAAM,IAAW;AAEjD,SADK,IACE,OAAO,QAAQ,EAAO,CAAC,QAC3B,GAAK,CAAC,GAAU,OAAgB,EAAI,QAAQ,KAAK,EAAS,KAAK,OAAO,EAAW,CAAC,EACnF,EACD,GAJmB;IAMtB,CAAC,EAAE,CACJ,EAEK,IAAkB;EACtB;GAAE,OAAO;GAAO,OAAO,EAAG,0BAA0B,iBAAiB;GAAE;EACvE;GAAE,OAAO;GAAW,OAAO,EAAG,4BAA4B,UAAU;GAAE;EACtE;GAAE,OAAO;GAAgB,OAAO,EAAG,iCAAiC,eAAe;GAAE;EACrF;GAAE,OAAO;GAAY,OAAO,EAAG,6BAA6B,WAAW;GAAE;EACzE;GAAE,OAAO;GAAW,OAAO,EAAG,4BAA4B,UAAU;GAAE;EACtE;GAAE,OAAO;GAAa,OAAO,EAAG,8BAA8B,YAAY;GAAE;EAC5E;GAAE,OAAO;GAAkB,OAAO,EAAG,mCAAmC,iBAAiB;GAAE;EAC3F;GAAE,OAAO;GAAW,OAAO,EAAG,4BAA4B,UAAU;GAAE;EACtE;GAAE,OAAO;GAAU,OAAO,EAAG,2BAA2B,SAAS;GAAE;EACpE,EAEK,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAgB,KAAqB,EAAS,MAAM,EAErD,EAAE,SAAM,YAAS,UAAO,eAAY,EAAsB;EAC9D,WAAW;GACT,QAAQ,EACN,UAAU,MAAmB,QAAyB,KAAA,IAAjB,GACtC;GACD,GAAI,IAAc,EAAE,YAAY,EAAE,OAAO,KAAM,EAAE,GAAG,EAAE;GACvD;EACD,aAAa;EACd,CAAC,EAEI,IAAgC,SACtB,GAAM,uBAAuB,SAAS,EAAE,EACzC,KAAK,OAAmC;EACnD,IAAI,EAAK;EACT,MAAM,EAAK;EACX,aAAc,EAAK,eAA0B;EAC7C,UAAW,EAAK,YAAuB;EACvC,YAAa,EAAK,cAAyB;EAC3C,MAAO,EAAK,QAAqB,EAAE;EACnC,WAAW,EAAK;EAChB,WAAW,EAAK;EACjB,EAAE,EACF,CAAC,GAAM,uBAAuB,MAAM,CAAC,EAElC,IAAoB,QAAc;AACtC,MAAI,CAAC,EAAa,QAAO;EACzB,IAAM,IAAQ,EAAY,aAAa;AACvC,SAAO,EAAU,QACd,MACC,EAAK,KAAK,aAAa,CAAC,SAAS,EAAM,IACvC,EAAK,aAAa,aAAa,CAAC,SAAS,EAAM,IAC/C,EAAK,MAAM,MAAM,MAAQ,EAAI,aAAa,CAAC,SAAS,EAAM,CAAC,CAC9D;IACA,CAAC,GAAW,EAAY,CAAC,EAEtB,KAAwB,MAA2B;AACvD,IAAS,cAAc,EAAK,KAAK;;AAyCnC,QAtCI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAAsB;GAA0B,CAAA;EACzD,CAAA,GAEL,EAAc,EAAiB,UAAU,GAY1C,KAAW,EAAU,WAAW,IAEhC,kBAAC,GAAD;EAAY,WAAU;YACpB,kBAAC,GAAD,EAAc,SAAS,EAAG,oBAAoB,uBAAuB,EAAI,CAAA;EAC9D,CAAA,GAIb,KAAS,EAAU,WAAW,IAE9B,kBAAC,GAAD;EAAY,WAAU;YACpB,kBAAC,GAAD;GACE,OAAO,EAAG,uBAAuB,2BAA2B;GAC5D,SAAS,EAAM;GACf,eAAe,GAAS;GACxB,CAAA;EACS,CAAA,GAKf,kBAAC,GAAD;EAAY,WAAU;YAAtB;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,kBAAkB,YAAY;IACxC,aAAa,EAAG,wBAAwB,iCAAiC,EACvE,OAAO,EAAU,QAClB,CAAC;IACF,SACE,kBAAC,UAAD;KACE,eAAe,GAAS;KACxB,UAAU;KACV,WAAU;KACV,OAAO,EAAG,kBAAkB,UAAU;eAEtC,kBAAC,GAAD,EAAW,WAAW,WAAW,IAAU,iBAAiB,MAAQ,CAAA;KAC7D,CAAA;IAEX,CAAA;GAGF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,OAAO;KACP,UAAU;KACV,aAAa,EAAG,8BAA8B,sBAAsB;KACpE,WAAU;KACV,CAAA,EACF,kBAAC,GAAD;KACE,OAAO,EAAG,2BAA2B,WAAW;KAChD,OAAO;KACP,SAAS;KACT,UAAU;KACV,CAAA,CACE;;IAGJ,KAAe,MAAmB,UAClC,kBAAC,KAAD;IAAG,WAAU;cAAb,CACG,EAAG,2BAA2B,+CAA+C;KAC5E,UAAU,EAAkB;KAC5B,OAAO,EAAU;KAClB,CAAC,EACD,KACC,kBAAC,QAAD;KAAM,WAAU;eACb,EAAG,qBAAqB,kBAAkB;KACtC,CAAA,CAEP;;GAIL,EAAkB,SAAS,IAC1B,kBAAC,GAAD;IAAiB,MAAK;cACpB,kBAAC,GAAD;KAEE,SAAS;KACT,WAAU;eAET,EAAkB,KAAK,MACtB,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAc,UAAU;MAAM,QAAQ;MAAwB,CAAA,EACnD,EAFI,EAAK,GAET,CACb;KACS,EATP,gBASO;IACG,CAAA,GAElB,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAU,WAAU,+BAAgC,CAAA;IAC1D,OACE,KAAe,MAAmB,QAC9B,EAAG,2BAA2B,qBAAqB,GACnD,EAAG,6BAA6B,mBAAmB;IAEzD,aACE,KAAe,MAAmB,QAC9B,EAAG,8BAA8B,uCAAuC,GACxE,EACE,wBACA,wEACD;IAEP,QACE,CAAC,KAAe,MAAmB,QACjC,kBAAC,GAAD,EAAe,QAAO,+DAAgE,CAAA,GACpF,KAAA;IAEN,CAAA;GAEO;MAtHX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAwC;IAAkB,CAAA,EACxE,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,CACA;;EACF,CAAA"}
1
+ {"version":3,"file":"TemplatesPage.js","names":[],"sources":["../../src/pages/TemplatesPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { FileCode, RefreshCw } from 'lucide-react';\nimport { TemplateCard } from '@/components/templates';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport type { TemplateCardData } from '@/components/templates';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListTemplatesQuery } from '@/generated/wspace-operations';\n\nexport function TemplatesPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const categoryOptions = [\n { value: 'all', label: tr('template.allCategories', 'All Categories') },\n { value: 'STARTER', label: tr('template.categoryStarter', 'Starter') },\n { value: 'MICROSERVICE', label: tr('template.categoryMicroservice', 'Microservice') },\n { value: 'FRONTEND', label: tr('template.categoryFrontend', 'Frontend') },\n { value: 'BACKEND', label: tr('template.categoryBackend', 'Backend') },\n { value: 'FULLSTACK', label: tr('template.categoryFullstack', 'Fullstack') },\n { value: 'INFRASTRUCTURE', label: tr('template.categoryInfrastructure', 'Infrastructure') },\n { value: 'LIBRARY', label: tr('template.categoryLibrary', 'Library') },\n { value: 'CUSTOM', label: tr('template.categoryCustom', 'Custom') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [categoryFilter, setCategoryFilter] = useState('all');\n\n const { data, loading, error, refetch } = useListTemplatesQuery({\n variables: {\n filter: {\n category: categoryFilter !== 'all' ? categoryFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const templates: TemplateCardData[] = useMemo(() => {\n const items = data?.vibecontrolsTemplates?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n category: (item.category as string) || null,\n cloneCount: (item.cloneCount as number) || 0,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsTemplates?.items]);\n\n const filteredTemplates = useMemo(() => {\n if (!searchQuery) return templates;\n const query = searchQuery.toLowerCase();\n return templates.filter(\n (tmpl) =>\n tmpl.name.toLowerCase().includes(query) ||\n tmpl.description?.toLowerCase().includes(query) ||\n tmpl.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [templates, searchQuery]);\n\n const handleTemplateSelect = (tmpl: TemplateCardData) => {\n navigate(`/templates/${tmpl.id}`);\n };\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n if (error && templates.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load templates')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('template.title', 'Templates')}\n description={tr('template.description', '{{total}} templates available', {\n total: templates.length,\n })}\n actions={\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n }\n />\n\n {/* Filters */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('template.searchPlaceholder', 'Search templates...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <FilterDropdown\n label={tr('template.filterCategory', 'Category')}\n value={categoryFilter}\n options={categoryOptions}\n onChange={setCategoryFilter}\n />\n </div>\n\n {/* Results count */}\n {(searchQuery || categoryFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('template.showingResults', 'Showing {{filtered}} of {{total}} templates', {\n filtered: filteredTemplates.length,\n total: templates.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {loading && templates.length === 0 ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredTemplates.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"template-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredTemplates.map((tmpl) => (\n <MotionItem key={tmpl.id}>\n <TemplateCard template={tmpl} onOpen={handleTemplateSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyNoResults', 'No templates found')\n : tr('template.emptyNoTemplates', 'No templates yet')\n }\n description={\n searchQuery || categoryFilter !== 'all'\n ? tr('template.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'template.emptyCreate',\n 'Templates will appear here when components are published as templates'\n )\n }\n action={\n !searchQuery && categoryFilter === 'all' ? (\n <GetHelpButton prompt=\"What is a Template in VibeControls and how do I create one?\" />\n ) : undefined\n }\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}