@burdenoff/microfe-vibecontrols 2026.518.2 → 2026.519.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pages/AgentsPage.js +7 -4
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +333 -289
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +243 -199
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +104 -71
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentDetailPage.js","names":[],"sources":["../../src/pages/ComponentDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n ArrowLeft,\n Box,\n BookOpen,\n BarChart3,\n FileText,\n Settings,\n RefreshCw,\n FileCode,\n Layers,\n Upload,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatCard,\n EmptyState,\n LoadingState,\n ErrorState,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { ScorecardPanel } from '@/components/scorecard';\nimport { DocsTabPanel } from '@/components/docs';\nimport { CatalogCard } from '@/components/catalog';\nimport type { CatalogCardData } from '@/components/catalog';\nimport { ComponentForm } from '@/components/idp-components';\nimport type { ComponentFormData } from '@/components/idp-components';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useQueryBackedTab } from '@/hooks/useQueryBackedTab';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport {\n useGetComponentQuery,\n useUpdateComponentMutation,\n usePublishComponentAsTemplateMutation,\n} from '@/generated/wspace-operations';\n\ntype ComponentTab = 'overview' | 'catalogs' | 'docs' | 'scorecard' | 'template' | 'settings';\nconst COMPONENT_TABS: readonly ComponentTab[] = [\n 'overview',\n 'catalogs',\n 'docs',\n 'scorecard',\n 'template',\n 'settings',\n];\n\nexport function ComponentDetailPage() {\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 [activeTab, setActiveTab] = useQueryBackedTab<ComponentTab>(COMPONENT_TABS, 'overview');\n const [showEditForm, setShowEditForm] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n const { data, loading, error, refetch } = useGetComponentQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateComponent] = useUpdateComponentMutation({ refetchQueries: ['GetComponent'] });\n const [publishAsTemplate] = usePublishComponentAsTemplateMutation({\n refetchQueries: ['GetComponent'],\n });\n\n const component = data?.vibecontrolsComponent ?? null;\n\n const catalogs: CatalogCardData[] = useMemo(() => {\n return (component?.catalogs ?? []).map((entry: Record<string, any>) => {\n const c = entry.catalog || {};\n return {\n id: c.id as string,\n name: c.name as string,\n description: (c.description as string) || null,\n visibility: c.visibility as CatalogCardData['visibility'],\n componentCount: (c.componentCount as number) || 0,\n vibeCount: (c.vibeCount as number) || 0,\n tags: (c.tags as string[]) || [],\n createdAt: c.createdAt as string,\n updatedAt: c.updatedAt as string,\n };\n });\n }, [component?.catalogs]);\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('/components');\n };\n\n const handleCatalogClick = (catalog: CatalogCardData) => {\n navigate(`/catalogs/${catalog.id}`);\n };\n\n const handleUpdateComponent = async (formData: ComponentFormData) => {\n if (!id) return;\n await updateComponent({\n variables: {\n id,\n input: {\n name: formData.name,\n description: formData.description || undefined,\n type: formData.type,\n lifecycle: formData.lifecycle,\n language: formData.language || undefined,\n framework: formData.framework || undefined,\n version: formData.version || undefined,\n ownerName: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n setSuccessMessage(tr('component.updatedSuccess', 'Component updated successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n const handlePublishAsTemplate = async () => {\n if (!id) return;\n try {\n await publishAsTemplate({\n variables: { componentId: id, input: { name: component?.name || 'Template' } },\n });\n setSuccessMessage(tr('component.publishedAsTemplate', 'Published as template successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n setSuccessMessage(\n err instanceof Error ? err.message : tr('component.failedPublish', 'Failed to publish')\n );\n setTimeout(() => setSuccessMessage(null), 3000);\n }\n };\n\n const lifecycleStyles: Record<string, string> = {\n EXPERIMENTAL: 'bg-status-warning-bg text-status-warning-text',\n PRODUCTION: 'bg-status-success-bg text-status-success-text',\n DEPRECATED: 'bg-status-error-bg text-status-error-text',\n };\n\n const tabs: { id: ComponentTab; label: string; icon: typeof Box; badge?: string }[] = [\n { id: 'overview', label: tr('nav.overview', 'Overview'), icon: Box },\n {\n id: 'catalogs',\n label: tr('component.catalogs', 'Catalogs'),\n icon: BookOpen,\n badge: `${catalogs.length}`,\n },\n { id: 'docs', label: tr('component.docs', 'Docs'), icon: FileText },\n { id: 'scorecard', label: tr('component.scorecard', 'Scorecard'), icon: BarChart3 },\n { id: 'template', label: tr('component.template', 'Template'), icon: FileCode },\n { id: 'settings', label: tr('common.settings', 'Settings'), icon: Settings },\n ];\n\n // Guard states\n if (loading && !component) {\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('component.backToComponents', 'Back to Components')}\n </button>\n <LoadingState message={tr('component.loadingDetails', 'Loading component details...')} />\n </MotionPage>\n );\n }\n\n if (error && !component) {\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('component.backToComponents', 'Back to Components')}\n </button>\n <ErrorState\n title={tr('component.failedLoad', 'Failed to load component')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!component) {\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('component.backToComponents', 'Back to Components')}\n </button>\n <EmptyState\n icon={<Box className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('component.notFound', 'Component not found')}\n description={`No component 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('component.backToComponents', 'Back to Components')}\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('component.backToComponents', 'Back to Components')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={component.name}\n description={component.description || tr('component.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 <span className=\"inline-flex items-center px-2.5 py-1 text-xs font-medium bg-bg-sunken text-text-secondary rounded\">\n {component.type}\n </span>\n <span\n className={`inline-flex items-center px-2.5 py-1 text-xs font-medium rounded ${lifecycleStyles[component.lifecycle] || 'bg-bg-sunken text-text-secondary'}`}\n >\n {component.lifecycle}\n </span>\n </div>\n }\n />\n\n {/* Stat Cards */}\n <MotionList stagger={0.04} className=\"grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4\">\n <MotionItem>\n <StatCard\n title={tr('component.type', 'Type')}\n value={component.type}\n icon={<Box className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('component.lifecycle', 'Lifecycle')}\n value={component.lifecycle}\n icon={<Layers className=\"w-4 h-4\" />}\n variant={\n component.lifecycle === 'PRODUCTION'\n ? 'success'\n : component.lifecycle === 'DEPRECATED'\n ? 'error'\n : 'warning'\n }\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('component.version', 'Version')}\n value={component.version || '-'}\n icon={<FileCode className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('component.catalogs', 'Catalogs')}\n value={catalogs.length}\n icon={<BookOpen className=\"w-4 h-4\" />}\n />\n </MotionItem>\n </MotionList>\n\n {/* Tab Navigation */}\n <div className=\"border-b border-border-default\">\n <nav\n className=\"flex gap-1 -mb-px overflow-x-auto pb-1\"\n aria-label={tr('component.tabsAriaLabel', 'Component tabs')}\n >\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActiveTab = activeTab === tab.id;\n return (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={`flex shrink-0 items-center gap-1.5 px-3 py-2.5 text-xs sm:gap-2 sm:px-4 sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${\n isActiveTab\n ? 'border-action-primary-bg text-action-primary-text'\n : 'border-transparent text-text-secondary hover:text-text-primary hover:border-border-default'\n }`}\n aria-selected={isActiveTab}\n role=\"tab\"\n >\n <Icon className=\"w-4 h-4\" />\n {tab.label}\n {tab.badge && (\n <span\n className={`hidden rounded-full px-1.5 py-0.5 text-[10px] sm:inline-flex sm:text-xs ${\n isActiveTab\n ? 'bg-action-primary-bg/10 text-action-primary-bg'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.badge}\n </span>\n )}\n </button>\n );\n })}\n </nav>\n </div>\n\n {/* Tab Panels */}\n <AnimatePresence mode=\"wait\">\n {activeTab === 'overview' && (\n <motion.div\n key=\"overview\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n {component.description && (\n <Card>\n <div className=\"p-4\">\n <p className=\"text-text-primary\">{component.description}</p>\n </div>\n </Card>\n )}\n\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('component.metadata', 'Metadata')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">{tr('component.ownerNameLabel', 'Owner')}</p>\n <p className=\"font-medium text-text-primary\">\n {component.ownerName || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('component.languageLabel', 'Language')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {component.language || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('component.frameworkLabel', 'Framework')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {component.framework || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('component.versionLabel', 'Version')}</p>\n <p className=\"font-medium text-text-primary\">\n {component.version || tr('common.unset', 'Unset')}\n </p>\n </div>\n {component.repositoryUrl && (\n <div className=\"sm:col-span-2\">\n <p className=\"text-text-secondary\">\n {tr('component.repository', 'Repository')}\n </p>\n <a\n href={component.repositoryUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"font-medium text-action-primary-bg hover:underline break-all\"\n >\n {component.repositoryUrl}\n </a>\n </div>\n )}\n <div>\n <p className=\"text-text-secondary\">{tr('common.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(component.createdAt).toLocaleDateString()}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('common.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(component.updatedAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n </div>\n </Card>\n\n {component.tags?.length > 0 && (\n <Card>\n <div className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('component.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {component.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n </Card>\n )}\n </motion.div>\n )}\n\n {activeTab === 'catalogs' && (\n <motion.div\n key=\"catalogs\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <p className=\"text-sm text-text-secondary\">\n {catalogs.length}{' '}\n {tr('component.catalogsContaining', 'catalogs containing this component')}\n </p>\n {catalogs.length > 0 ? (\n <MotionList\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {catalogs.map((catalog) => (\n <MotionItem key={catalog.id}>\n <CatalogCard catalog={catalog} onOpen={handleCatalogClick} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <EmptyState\n icon={<BookOpen className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('component.noCatalogs', 'Not in any catalogs')}\n description={tr(\n 'component.noCatalogsDescription',\n 'This component has not been added to any catalogs yet'\n )}\n />\n )}\n </motion.div>\n )}\n\n {activeTab === 'docs' && (\n <motion.div\n key=\"docs\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n <DocsTabPanel ownerType=\"COMPONENT\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'scorecard' && (\n <motion.div\n key=\"scorecard\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <ScorecardPanel ownerType=\"COMPONENT\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'template' && (\n <motion.div\n key=\"template\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <Card padding=\"lg\">\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <FileCode className=\"w-12 h-12 text-text-muted mb-4\" />\n <h3 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('component.template', 'Template')}\n </h3>\n <p className=\"text-sm text-text-secondary max-w-md mb-4\">\n {(component.templates?.length ?? 0) > 0\n ? tr(\n 'component.alreadyPublished',\n 'This component has been published as a template.'\n )\n : tr(\n 'component.publishDescription',\n 'Publish this component as a reusable template that others can clone.'\n )}\n </p>\n {(component.templates?.length ?? 0) === 0 && (\n <button\n onClick={handlePublishAsTemplate}\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 <Upload className=\"w-4 h-4\" />\n {tr('component.publishAsTemplate', 'Publish as Template')}\n </button>\n )}\n {(component.templates?.length ?? 0) > 0 && (\n <button\n onClick={() => navigate(`/templates/${component.templates[0].id}`)}\n className=\"flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n {tr('component.viewTemplate', 'View Template')}\n </button>\n )}\n </div>\n </Card>\n </motion.div>\n )}\n\n {activeTab === 'settings' && (\n <motion.div\n key=\"settings\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <Card padding=\"lg\">\n <h3 className=\"font-semibold text-text-primary mb-4\">\n {tr('component.editComponent', 'Edit Component')}\n </h3>\n <button\n onClick={() => setShowEditForm(true)}\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('component.editDetails', 'Edit Details')}\n </button>\n </Card>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Edit Form */}\n <ComponentForm\n isOpen={showEditForm}\n onClose={() => setShowEditForm(false)}\n onSubmit={handleUpdateComponent}\n initialData={{\n name: component.name,\n description: component.description || '',\n type: component.type,\n lifecycle: component.lifecycle,\n language: component.language || '',\n framework: component.framework || '',\n version: component.version || '',\n owner: component.ownerName || '',\n tags: component.tags || [],\n }}\n title={tr('component.editComponent', 'Edit Component')}\n />\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Template Publish Quota Reached\"\n message=\"You have used the template publishing quota for your current plan. Upgrade to publish more templates.\"\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ComponentDetailPage.js","names":[],"sources":["../../src/pages/ComponentDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n ArrowLeft,\n Box,\n BookOpen,\n BarChart3,\n FileText,\n Settings,\n RefreshCw,\n FileCode,\n Layers,\n Upload,\n Trash2,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatCard,\n EmptyState,\n LoadingState,\n ErrorState,\n QuotaExhaustedDialog,\n ConfirmDialog,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { ScorecardPanel } from '@/components/scorecard';\nimport { DocsTabPanel } from '@/components/docs';\nimport { CatalogCard } from '@/components/catalog';\nimport type { CatalogCardData } from '@/components/catalog';\nimport { ComponentForm } from '@/components/idp-components';\nimport type { ComponentFormData } from '@/components/idp-components';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useQueryBackedTab } from '@/hooks/useQueryBackedTab';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport {\n useGetComponentQuery,\n useUpdateComponentMutation,\n useDeleteComponentMutation,\n usePublishComponentAsTemplateMutation,\n} from '@/generated/wspace-operations';\n\ntype ComponentTab = 'overview' | 'catalogs' | 'docs' | 'scorecard' | 'template' | 'settings';\nconst COMPONENT_TABS: readonly ComponentTab[] = [\n 'overview',\n 'catalogs',\n 'docs',\n 'scorecard',\n 'template',\n 'settings',\n];\n\nexport function ComponentDetailPage() {\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 [activeTab, setActiveTab] = useQueryBackedTab<ComponentTab>(COMPONENT_TABS, 'overview');\n const [showEditForm, setShowEditForm] = useState(false);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n const { data, loading, error, refetch } = useGetComponentQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateComponent] = useUpdateComponentMutation({ refetchQueries: ['GetComponent'] });\n const [deleteComponent] = useDeleteComponentMutation({ refetchQueries: ['ListComponents'] });\n const [publishAsTemplate] = usePublishComponentAsTemplateMutation({\n refetchQueries: ['GetComponent'],\n });\n\n const component = data?.vibecontrolsComponent ?? null;\n\n const catalogs: CatalogCardData[] = useMemo(() => {\n return (component?.catalogs ?? []).map((entry: Record<string, any>) => {\n const c = entry.catalog || {};\n return {\n id: c.id as string,\n name: c.name as string,\n description: (c.description as string) || null,\n visibility: c.visibility as CatalogCardData['visibility'],\n componentCount: (c.componentCount as number) || 0,\n vibeCount: (c.vibeCount as number) || 0,\n tags: (c.tags as string[]) || [],\n createdAt: c.createdAt as string,\n updatedAt: c.updatedAt as string,\n };\n });\n }, [component?.catalogs]);\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('/components');\n };\n\n const handleCatalogClick = (catalog: CatalogCardData) => {\n navigate(`/catalogs/${catalog.id}`);\n };\n\n const handleUpdateComponent = async (formData: ComponentFormData) => {\n if (!id) return;\n await updateComponent({\n variables: {\n id,\n input: {\n name: formData.name,\n description: formData.description || undefined,\n type: formData.type,\n lifecycle: formData.lifecycle,\n language: formData.language || undefined,\n framework: formData.framework || undefined,\n version: formData.version || undefined,\n ownerName: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n setSuccessMessage(tr('component.updatedSuccess', 'Component updated successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n const handleDeleteComponent = async () => {\n if (!id) return;\n setIsDeleting(true);\n try {\n await deleteComponent({ variables: { id } });\n setShowDeleteConfirm(false);\n navigate('/components');\n } finally {\n setIsDeleting(false);\n }\n };\n\n const handlePublishAsTemplate = async () => {\n if (!id) return;\n try {\n await publishAsTemplate({\n variables: { componentId: id, input: { name: component?.name || 'Template' } },\n });\n setSuccessMessage(tr('component.publishedAsTemplate', 'Published as template successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowQuotaExhausted(true);\n return;\n }\n\n setSuccessMessage(\n err instanceof Error ? err.message : tr('component.failedPublish', 'Failed to publish')\n );\n setTimeout(() => setSuccessMessage(null), 3000);\n }\n };\n\n const lifecycleStyles: Record<string, string> = {\n EXPERIMENTAL: 'bg-status-warning-bg text-status-warning-text',\n PRODUCTION: 'bg-status-success-bg text-status-success-text',\n DEPRECATED: 'bg-status-error-bg text-status-error-text',\n };\n\n const tabs: { id: ComponentTab; label: string; icon: typeof Box; badge?: string }[] = [\n { id: 'overview', label: tr('nav.overview', 'Overview'), icon: Box },\n {\n id: 'catalogs',\n label: tr('component.catalogs', 'Catalogs'),\n icon: BookOpen,\n badge: `${catalogs.length}`,\n },\n { id: 'docs', label: tr('component.docs', 'Docs'), icon: FileText },\n { id: 'scorecard', label: tr('component.scorecard', 'Scorecard'), icon: BarChart3 },\n { id: 'template', label: tr('component.template', 'Template'), icon: FileCode },\n { id: 'settings', label: tr('common.settings', 'Settings'), icon: Settings },\n ];\n\n // Guard states\n if (loading && !component) {\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('component.backToComponents', 'Back to Components')}\n </button>\n <LoadingState message={tr('component.loadingDetails', 'Loading component details...')} />\n </MotionPage>\n );\n }\n\n if (error && !component) {\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('component.backToComponents', 'Back to Components')}\n </button>\n <ErrorState\n title={tr('component.failedLoad', 'Failed to load component')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!component) {\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('component.backToComponents', 'Back to Components')}\n </button>\n <EmptyState\n icon={<Box className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('component.notFound', 'Component not found')}\n description={`No component 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('component.backToComponents', 'Back to Components')}\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('component.backToComponents', 'Back to Components')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={component.name}\n description={component.description || tr('component.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 <span className=\"inline-flex items-center px-2.5 py-1 text-xs font-medium bg-bg-sunken text-text-secondary rounded\">\n {component.type}\n </span>\n <span\n className={`inline-flex items-center px-2.5 py-1 text-xs font-medium rounded ${lifecycleStyles[component.lifecycle] || 'bg-bg-sunken text-text-secondary'}`}\n >\n {component.lifecycle}\n </span>\n </div>\n }\n />\n\n {/* Stat Cards */}\n <MotionList stagger={0.04} className=\"grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4\">\n <MotionItem>\n <StatCard\n title={tr('component.type', 'Type')}\n value={component.type}\n icon={<Box className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('component.lifecycle', 'Lifecycle')}\n value={component.lifecycle}\n icon={<Layers className=\"w-4 h-4\" />}\n variant={\n component.lifecycle === 'PRODUCTION'\n ? 'success'\n : component.lifecycle === 'DEPRECATED'\n ? 'error'\n : 'warning'\n }\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('component.version', 'Version')}\n value={component.version || '-'}\n icon={<FileCode className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('component.catalogs', 'Catalogs')}\n value={catalogs.length}\n icon={<BookOpen className=\"w-4 h-4\" />}\n />\n </MotionItem>\n </MotionList>\n\n {/* Tab Navigation */}\n <div className=\"border-b border-border-default\">\n <nav\n className=\"flex gap-1 -mb-px overflow-x-auto pb-1\"\n aria-label={tr('component.tabsAriaLabel', 'Component tabs')}\n >\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActiveTab = activeTab === tab.id;\n return (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={`flex shrink-0 items-center gap-1.5 px-3 py-2.5 text-xs sm:gap-2 sm:px-4 sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${\n isActiveTab\n ? 'border-action-primary-bg text-action-primary-text'\n : 'border-transparent text-text-secondary hover:text-text-primary hover:border-border-default'\n }`}\n aria-selected={isActiveTab}\n role=\"tab\"\n >\n <Icon className=\"w-4 h-4\" />\n {tab.label}\n {tab.badge && (\n <span\n className={`hidden rounded-full px-1.5 py-0.5 text-[10px] sm:inline-flex sm:text-xs ${\n isActiveTab\n ? 'bg-action-primary-bg/10 text-action-primary-bg'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.badge}\n </span>\n )}\n </button>\n );\n })}\n </nav>\n </div>\n\n {/* Tab Panels */}\n <AnimatePresence mode=\"wait\">\n {activeTab === 'overview' && (\n <motion.div\n key=\"overview\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n {component.description && (\n <Card>\n <div className=\"p-4\">\n <p className=\"text-text-primary\">{component.description}</p>\n </div>\n </Card>\n )}\n\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('component.metadata', 'Metadata')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">{tr('component.ownerNameLabel', 'Owner')}</p>\n <p className=\"font-medium text-text-primary\">\n {component.ownerName || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('component.languageLabel', 'Language')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {component.language || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">\n {tr('component.frameworkLabel', 'Framework')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {component.framework || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('component.versionLabel', 'Version')}</p>\n <p className=\"font-medium text-text-primary\">\n {component.version || tr('common.unset', 'Unset')}\n </p>\n </div>\n {component.repositoryUrl && (\n <div className=\"sm:col-span-2\">\n <p className=\"text-text-secondary\">\n {tr('component.repository', 'Repository')}\n </p>\n <a\n href={component.repositoryUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"font-medium text-action-primary-bg hover:underline break-all\"\n >\n {component.repositoryUrl}\n </a>\n </div>\n )}\n <div>\n <p className=\"text-text-secondary\">{tr('common.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(component.createdAt).toLocaleDateString()}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('common.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(component.updatedAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n </div>\n </Card>\n\n {component.tags?.length > 0 && (\n <Card>\n <div className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('component.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {component.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n </Card>\n )}\n </motion.div>\n )}\n\n {activeTab === 'catalogs' && (\n <motion.div\n key=\"catalogs\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <p className=\"text-sm text-text-secondary\">\n {catalogs.length}{' '}\n {tr('component.catalogsContaining', 'catalogs containing this component')}\n </p>\n {catalogs.length > 0 ? (\n <MotionList\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {catalogs.map((catalog) => (\n <MotionItem key={catalog.id}>\n <CatalogCard catalog={catalog} onOpen={handleCatalogClick} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <EmptyState\n icon={<BookOpen className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('component.noCatalogs', 'Not in any catalogs')}\n description={tr(\n 'component.noCatalogsDescription',\n 'This component has not been added to any catalogs yet'\n )}\n />\n )}\n </motion.div>\n )}\n\n {activeTab === 'docs' && (\n <motion.div\n key=\"docs\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n <DocsTabPanel ownerType=\"COMPONENT\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'scorecard' && (\n <motion.div\n key=\"scorecard\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <ScorecardPanel ownerType=\"COMPONENT\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'template' && (\n <motion.div\n key=\"template\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <Card padding=\"lg\">\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <FileCode className=\"w-12 h-12 text-text-muted mb-4\" />\n <h3 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('component.template', 'Template')}\n </h3>\n <p className=\"text-sm text-text-secondary max-w-md mb-4\">\n {(component.templates?.length ?? 0) > 0\n ? tr(\n 'component.alreadyPublished',\n 'This component has been published as a template.'\n )\n : tr(\n 'component.publishDescription',\n 'Publish this component as a reusable template that others can clone.'\n )}\n </p>\n {(component.templates?.length ?? 0) === 0 && (\n <button\n onClick={handlePublishAsTemplate}\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 <Upload className=\"w-4 h-4\" />\n {tr('component.publishAsTemplate', 'Publish as Template')}\n </button>\n )}\n {(component.templates?.length ?? 0) > 0 && (\n <button\n onClick={() => navigate(`/templates/${component.templates[0].id}`)}\n className=\"flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n {tr('component.viewTemplate', 'View Template')}\n </button>\n )}\n </div>\n </Card>\n </motion.div>\n )}\n\n {activeTab === 'settings' && (\n <motion.div\n key=\"settings\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"space-y-4\">\n <Card padding=\"lg\">\n <h3 className=\"font-semibold text-text-primary mb-4\">\n {tr('component.editComponent', 'Edit Component')}\n </h3>\n <button\n onClick={() => setShowEditForm(true)}\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('component.editDetails', 'Edit Details')}\n </button>\n </Card>\n <Card padding=\"lg\">\n <h3 className=\"font-semibold text-status-error-text mb-2\">\n {tr('component.dangerZone', 'Danger Zone')}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n 'component.deleteWarning',\n 'Deleting this component will remove it from all catalogs and break any templates derived from it. This cannot be undone.'\n )}\n </p>\n <button\n data-testid=\"component-delete-button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"inline-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 >\n <Trash2 className=\"w-4 h-4\" aria-hidden=\"true\" />\n {tr('component.deleteComponent', 'Delete Component')}\n </button>\n </Card>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Edit Form */}\n <ComponentForm\n isOpen={showEditForm}\n onClose={() => setShowEditForm(false)}\n onSubmit={handleUpdateComponent}\n initialData={{\n name: component.name,\n description: component.description || '',\n type: component.type,\n lifecycle: component.lifecycle,\n language: component.language || '',\n framework: component.framework || '',\n version: component.version || '',\n owner: component.ownerName || '',\n tags: component.tags || [],\n }}\n title={tr('component.editComponent', 'Edit Component')}\n />\n\n <ConfirmDialog\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDeleteComponent}\n title={tr('component.deleteComponent', 'Delete Component')}\n message={tr(\n 'component.deleteConfirmMessage',\n `Are you sure you want to delete \"${component.name}\"? This action cannot be undone.`\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n isLoading={isDeleting}\n />\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Template Publish Quota Reached\"\n message=\"You have used the template publishing quota for your current plan. Upgrade to publish more templates.\"\n />\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,IAAM,IAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,IAAsB;CACpC,IAAM,EAAE,SAAM,IAAS,EACjB,EAAE,UAAO,IAA2B,EACpC,IAAW,GAAiB,EAC5B,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EAEnE,IAAK,GACR,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAE,CACJ,EAEK,CAAC,GAAW,KAAgB,EAAgC,GAAgB,WAAW,EACvF,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,IAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,IAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAgB,KAAqB,EAAwB,KAAK,EACnE,CAAC,IAAoB,KAAyB,EAAS,GAAM,EAE7D,EAAE,UAAM,YAAS,UAAO,eAAY,EAAqB;EAC7D,WAAW,EAAE,IAAI,KAAM,IAAI;EAC3B,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,CAAC,KAAmB,EAA2B,EAAE,gBAAgB,CAAC,eAAe,EAAE,CAAC,EACpF,CAAC,MAAmB,EAA2B,EAAE,gBAAgB,CAAC,iBAAiB,EAAE,CAAC,EACtF,CAAC,MAAqB,EAAsC,EAChE,gBAAgB,CAAC,eAAe,EACjC,CAAC,EAEI,IAAY,IAAM,yBAAyB,MAE3C,IAA8B,SAC1B,GAAW,YAAY,EAAE,EAAE,KAAK,MAA+B;EACrE,IAAM,IAAI,EAAM,WAAW,EAAE;AAC7B,SAAO;GACL,IAAI,EAAE;GACN,MAAM,EAAE;GACR,aAAc,EAAE,eAA0B;GAC1C,YAAY,EAAE;GACd,gBAAiB,EAAE,kBAA6B;GAChD,WAAY,EAAE,aAAwB;GACtC,MAAO,EAAE,QAAqB,EAAE;GAChC,WAAW,EAAE;GACb,WAAW,EAAE;GACd;GACD,EACD,CAAC,GAAW,SAAS,CAAC;AAEzB,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAAsB;GAA0B,CAAA;EACzD,CAAA;AAEV,KAAI,CAAC,EAAc,EAAiB,UAAU,CAC5C,QACE,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;CAGV,IAAM,UAAmB;AACvB,IAAS,cAAc;IAGnB,MAAsB,MAA6B;AACvD,IAAS,aAAa,EAAQ,KAAK;IAG/B,KAAwB,OAAO,MAAgC;AAC9D,QACL,MAAM,EAAgB,EACpB,WAAW;GACT;GACA,OAAO;IACL,MAAM,EAAS;IACf,aAAa,EAAS,eAAe,KAAA;IACrC,MAAM,EAAS;IACf,WAAW,EAAS;IACpB,UAAU,EAAS,YAAY,KAAA;IAC/B,WAAW,EAAS,aAAa,KAAA;IACjC,SAAS,EAAS,WAAW,KAAA;IAC7B,WAAW,EAAS,SAAS,KAAA;IAC7B,MAAM,EAAS,KAAK,SAAS,IAAI,EAAS,OAAO,KAAA;IAClD;GACF,EACF,CAAC,EACF,EAAkB,EAAG,4BAA4B,iCAAiC,CAAC,EACnF,iBAAiB,EAAkB,KAAK,EAAE,IAAK;IAG3C,KAAwB,YAAY;AACnC,SACL;KAAc,GAAK;AACnB,OAAI;AAGF,IAFA,MAAM,GAAgB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC,EAC5C,EAAqB,GAAM,EAC3B,EAAS,cAAc;aACf;AACR,MAAc,GAAM;;;IAIlB,KAA0B,YAAY;AACrC,QACL,KAAI;AAKF,GAJA,MAAM,GAAkB,EACtB,WAAW;IAAE,aAAa;IAAI,OAAO,EAAE,MAAM,GAAW,QAAQ,YAAY;IAAE,EAC/E,CAAC,EACF,EAAkB,EAAG,iCAAiC,qCAAqC,CAAC,EAC5F,iBAAiB,EAAkB,KAAK,EAAE,IAAK;WACxC,GAAK;AACZ,OAAI,GAAsB,EAAI,EAAE;AAC9B,MAAsB,GAAK;AAC3B;;AAMF,GAHA,EACE,aAAe,QAAQ,EAAI,UAAU,EAAG,2BAA2B,oBAAoB,CACxF,EACD,iBAAiB,EAAkB,KAAK,EAAE,IAAK;;IAI7C,KAA0C;EAC9C,cAAc;EACd,YAAY;EACZ,YAAY;EACb,EAEK,KAAgF;EACpF;GAAE,IAAI;GAAY,OAAO,EAAG,gBAAgB,WAAW;GAAE,MAAM;GAAK;EACpE;GACE,IAAI;GACJ,OAAO,EAAG,sBAAsB,WAAW;GAC3C,MAAM;GACN,OAAO,GAAG,EAAS;GACpB;EACD;GAAE,IAAI;GAAQ,OAAO,EAAG,kBAAkB,OAAO;GAAE,MAAM;GAAU;EACnE;GAAE,IAAI;GAAa,OAAO,EAAG,uBAAuB,YAAY;GAAE,MAAM;GAAW;EACnF;GAAE,IAAI;GAAY,OAAO,EAAG,sBAAsB,WAAW;GAAE,MAAM;GAAU;EAC/E;GAAE,IAAI;GAAY,OAAO,EAAG,mBAAmB,WAAW;GAAE,MAAM;GAAU;EAC7E;AAgED,QA7DI,KAAW,CAAC,IAEZ,kBAAC,GAAD;EAAY,WAAU;YAAtB,CACE,kBAAC,UAAD;GACE,SAAS;GACT,WAAU;aAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAG,8BAA8B,qBAAqB,CAChD;MACT,kBAAC,GAAD,EAAc,SAAS,EAAG,4BAA4B,+BAA+B,EAAI,CAAA,CAC9E;MAIb,KAAS,CAAC,IAEV,kBAAC,GAAD;EAAY,WAAU;YAAtB,CACE,kBAAC,UAAD;GACE,SAAS;GACT,WAAU;aAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAG,8BAA8B,qBAAqB,CAChD;MACT,kBAAC,IAAD;GACE,OAAO,EAAG,wBAAwB,2BAA2B;GAC7D,SAAS,EAAM;GACf,eAAe,GAAS;GACxB,CAAA,CACS;MAIZ,IA4BH,kBAAC,GAAD;EAAY,WAAU;YAAtB;GAEG,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ;IACG,CAAA;GAIR,kBAAC,UAAD;IACE,SAAS;IACT,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAG,8BAA8B,qBAAqB,CAChD;;GAGT,kBAAC,GAAD;IACE,OAAO,EAAU;IACjB,aAAa,EAAU,eAAe,EAAG,2BAA2B,iBAAiB;IACrF,SACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,eAAe,GAAS;OACxB,UAAU;OACV,WAAU;OACV,OAAO,EAAG,kBAAkB,UAAU;iBAEtC,kBAAC,IAAD,EAAW,WAAW,WAAW,IAAU,iBAAiB,MAAQ,CAAA;OAC7D,CAAA;MACT,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAU;OACN,CAAA;MACP,kBAAC,QAAD;OACE,WAAW,oEAAoE,GAAgB,EAAU,cAAc;iBAEtH,EAAU;OACN,CAAA;MACH;;IAER,CAAA;GAGF,kBAAC,GAAD;IAAY,SAAS;IAAM,WAAU;cAArC;KACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,OAAO,EAAG,kBAAkB,OAAO;MACnC,OAAO,EAAU;MACjB,MAAM,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;MACjC,CAAA,EACS,CAAA;KACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,OAAO,EAAG,uBAAuB,YAAY;MAC7C,OAAO,EAAU;MACjB,MAAM,kBAAC,IAAD,EAAQ,WAAU,WAAY,CAAA;MACpC,SACE,EAAU,cAAc,eACpB,YACA,EAAU,cAAc,eACtB,UACA;MAER,CAAA,EACS,CAAA;KACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,OAAO,EAAG,qBAAqB,UAAU;MACzC,OAAO,EAAU,WAAW;MAC5B,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;MACtC,CAAA,EACS,CAAA;KACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,OAAO,EAAG,sBAAsB,WAAW;MAC3C,OAAO,EAAS;MAChB,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;MACtC,CAAA,EACS,CAAA;KACF;;GAGb,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KACE,WAAU;KACV,cAAY,EAAG,2BAA2B,iBAAiB;eAE1D,GAAK,KAAK,MAAQ;MACjB,IAAM,IAAO,EAAI,MACX,IAAc,MAAc,EAAI;AACtC,aACE,kBAAC,UAAD;OAEE,eAAe,EAAa,EAAI,GAAG;OACnC,WAAW,iJACT,IACI,sDACA;OAEN,iBAAe;OACf,MAAK;iBATP;QAWE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;QAC3B,EAAI;QACJ,EAAI,SACH,kBAAC,QAAD;SACE,WAAW,2EACT,IACI,mDACA;mBAGL,EAAI;SACA,CAAA;QAEF;SAvBF,EAAI,GAuBF;OAEX;KACE,CAAA;IACF,CAAA;GAGN,kBAAC,GAAD;IAAiB,MAAK;cAAtB;KACG,MAAc,cACb,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,MAAM;OAAE,SAAS;OAAG,GAAG;OAAI;MAC3B,YAAY,EAAE,UAAU,IAAK;MAC7B,WAAU;gBANZ;OAQG,EAAU,eACT,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,KAAD;SAAG,WAAU;mBAAqB,EAAU;SAAgB,CAAA;QACxD,CAAA,EACD,CAAA;OAGT,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,sBAAsB,WAAW;SAClC,CAAA;QACD,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAuB,EAAG,4BAA4B,QAAQ;WAAK,CAAA,EAChF,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAU,aAAa,EAAG,gBAAgB,QAAQ;WACjD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,2BAA2B,WAAW;WACxC,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAU,YAAY,EAAG,gBAAgB,QAAQ;WAChD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAG,4BAA4B,YAAY;WAC1C,CAAA,EACJ,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAU,aAAa,EAAG,gBAAgB,QAAQ;WACjD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAuB,EAAG,0BAA0B,UAAU;WAAK,CAAA,EAChF,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAU,WAAW,EAAG,gBAAgB,QAAQ;WAC/C,CAAA,CACA,EAAA,CAAA;UACL,EAAU,iBACT,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAG,wBAAwB,aAAa;YACvC,CAAA,EACJ,kBAAC,KAAD;YACE,MAAM,EAAU;YAChB,QAAO;YACP,KAAI;YACJ,WAAU;sBAET,EAAU;YACT,CAAA,CACA;;UAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAuB,EAAG,kBAAkB,UAAU;WAAK,CAAA,EACxE,kBAAC,KAAD;WAAG,WAAU;qBACV,IAAI,KAAK,EAAU,UAAU,CAAC,oBAAoB;WACjD,CAAA,CACA,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAuB,EAAG,kBAAkB,UAAU;WAAK,CAAA,EACxE,kBAAC,KAAD;WAAG,WAAU;qBACV,IAAI,KAAK,EAAU,UAAU,CAAC,oBAAoB;WACjD,CAAA,CACA,EAAA,CAAA;UACF;;QACF,CAAA,CACD,EAAA,CAAA;OAEN,EAAU,MAAM,SAAS,KACxB,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,kBAAkB,OAAO;SAC1B,CAAA,EACL,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAU,KAAK,KAAK,MACnB,kBAAC,QAAD;UAEE,WAAU;oBAET;UACI,EAJA,EAIA,CACP;SACE,CAAA,CACF;WACD,CAAA;OAEE;QArGP,WAqGO;KAGd,MAAc,cACb,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,MAAM;OAAE,SAAS;OAAG,GAAG;OAAI;MAC3B,YAAY,EAAE,UAAU,IAAK;MAC7B,WAAU;gBANZ,CAQE,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAS;QAAQ;QACjB,EAAG,gCAAgC,qCAAqC;QACvE;UACH,EAAS,SAAS,IACjB,kBAAC,GAAD;OACE,SAAS;OACT,WAAU;iBAET,EAAS,KAAK,MACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;QAAsB;QAAS,QAAQ;QAAsB,CAAA,EAClD,EAFI,EAAQ,GAEZ,CACb;OACS,CAAA,GAEb,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,+BAAgC,CAAA;OAC1D,OAAO,EAAG,wBAAwB,sBAAsB;OACxD,aAAa,EACX,mCACA,wDACD;OACD,CAAA,CAEO;QAhCP,WAgCO;KAGd,MAAc,UACb,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,MAAM;OAAE,SAAS;OAAG,GAAG;OAAI;MAC3B,YAAY,EAAE,UAAU,IAAK;MAC7B,WAAU;gBAEV,kBAAC,GAAD;OAAc,WAAU;OAAY,SAAS,KAAM;OAAM,CAAA;MAC9C,EARP,OAQO;KAGd,MAAc,eACb,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,MAAM;OAAE,SAAS;OAAG,GAAG;OAAI;MAC3B,YAAY,EAAE,UAAU,IAAK;gBAE7B,kBAAC,GAAD;OAAgB,WAAU;OAAY,SAAS,KAAM;OAAM,CAAA;MAChD,EAPP,YAOO;KAGd,MAAc,cACb,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,MAAM;OAAE,SAAS;OAAG,GAAG;OAAI;MAC3B,YAAY,EAAE,UAAU,IAAK;gBAE7B,kBAAC,GAAD;OAAM,SAAQ;iBACZ,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAU,WAAU,kCAAmC,CAAA;SACvD,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,sBAAsB,WAAW;UAClC,CAAA;SACL,kBAAC,KAAD;UAAG,WAAU;qBACT,EAAU,WAAW,UAAU,KAAK,IAClC,EACE,8BACA,mDACD,GACD,EACE,gCACA,uEACD;UACH,CAAA;UACF,EAAU,WAAW,UAAU,OAAO,KACtC,kBAAC,UAAD;UACE,SAAS;UACT,WAAU;oBAFZ,CAIE,kBAAC,IAAD,EAAQ,WAAU,WAAY,CAAA,EAC7B,EAAG,+BAA+B,sBAAsB,CAClD;;UAET,EAAU,WAAW,UAAU,KAAK,KACpC,kBAAC,UAAD;UACE,eAAe,EAAS,cAAc,EAAU,UAAU,GAAG,KAAK;UAClE,WAAU;oBAET,EAAG,0BAA0B,gBAAgB;UACvC,CAAA;SAEP;;OACD,CAAA;MACI,EA1CP,WA0CO;KAGd,MAAc,cACb,kBAAC,EAAO,KAAR;MAEE,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,SAAS;OAAE,SAAS;OAAG,GAAG;OAAG;MAC7B,MAAM;OAAE,SAAS;OAAG,GAAG;OAAI;MAC3B,YAAY,EAAE,UAAU,IAAK;gBAE7B,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAM,SAAQ;kBAAd,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,2BAA2B,iBAAiB;SAC7C,CAAA,EACL,kBAAC,UAAD;SACE,eAAe,EAAgB,GAAK;SACpC,WAAU;mBAET,EAAG,yBAAyB,eAAe;SACrC,CAAA,CACJ;WACP,kBAAC,GAAD;QAAM,SAAQ;kBAAd;SACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAG,wBAAwB,cAAc;UACvC,CAAA;SACL,kBAAC,KAAD;UAAG,WAAU;oBACV,EACC,2BACA,2HACD;UACC,CAAA;SACJ,kBAAC,UAAD;UACE,eAAY;UACZ,eAAe,EAAqB,GAAK;UACzC,WAAU;oBAHZ,CAKE,kBAAC,IAAD;WAAQ,WAAU;WAAU,eAAY;WAAS,CAAA,EAChD,EAAG,6BAA6B,mBAAmB,CAC7C;;SACJ;UACH;;MACK,EAtCP,WAsCO;KAEC;;GAGlB,kBAAC,GAAD;IACE,QAAQ;IACR,eAAe,EAAgB,GAAM;IACrC,UAAU;IACV,aAAa;KACX,MAAM,EAAU;KAChB,aAAa,EAAU,eAAe;KACtC,MAAM,EAAU;KAChB,WAAW,EAAU;KACrB,UAAU,EAAU,YAAY;KAChC,WAAW,EAAU,aAAa;KAClC,SAAS,EAAU,WAAW;KAC9B,OAAO,EAAU,aAAa;KAC9B,MAAM,EAAU,QAAQ,EAAE;KAC3B;IACD,OAAO,EAAG,2BAA2B,iBAAiB;IACtD,CAAA;GAEF,kBAAC,IAAD;IACE,QAAQ;IACR,eAAe,EAAqB,GAAM;IAC1C,WAAW;IACX,OAAO,EAAG,6BAA6B,mBAAmB;IAC1D,SAAS,EACP,kCACA,oCAAoC,EAAU,KAAK,kCACpD;IACD,cAAc,EAAG,iBAAiB,SAAS;IAC3C,SAAQ;IACR,WAAW;IACX,CAAA;GAEF,kBAAC,IAAD;IACE,QAAQ;IACR,eAAe,EAAsB,GAAM;IAC3C,iBAAiB;AAEf,KADA,EAAsB,GAAM,EAC5B,EAAS,WAAW;;IAEtB,OAAM;IACN,SAAQ;IACR,CAAA;GACS;MApcX,kBAAC,GAAD;EAAY,WAAU;YAAtB,CACE,kBAAC,UAAD;GACE,SAAS;GACT,WAAU;aAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAG,8BAA8B,qBAAqB,CAChD;MACT,kBAAC,GAAD;GACE,MAAM,kBAAC,GAAD,EAAK,WAAU,+BAAgC,CAAA;GACrD,OAAO,EAAG,sBAAsB,sBAAsB;GACtD,aAAa,yBAAyB,EAAG;GACzC,QACE,kBAAC,UAAD;IACE,SAAS;IACT,WAAU;cAET,EAAG,8BAA8B,qBAAqB;IAChD,CAAA;GAEX,CAAA,CACS"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useVibeNavigate as
|
|
3
|
-
import { PageHeader as
|
|
4
|
-
import { LoadingState as
|
|
1
|
+
import { useDeleteTemplateMutation as e, useGetTemplateQuery as t, useInstantiateTemplateMutation as n } from "../generated/wspace-operations.js";
|
|
2
|
+
import { useVibeNavigate as r } from "../providers/VibeControlsProvider.js";
|
|
3
|
+
import { PageHeader as i } from "../components/shared/PageHeader.js";
|
|
4
|
+
import { LoadingState as ee } from "../components/shared/LoadingState.js";
|
|
5
5
|
import { ErrorState as a } from "../components/shared/ErrorState.js";
|
|
6
|
+
import { ConfirmDialog as te } from "../components/shared/ConfirmDialog.js";
|
|
6
7
|
import { EmptyState as o } from "../components/shared/EmptyState.js";
|
|
7
8
|
import { Card as s } from "../components/shared/Card.js";
|
|
8
9
|
import "../components/shared/index.js";
|
|
9
10
|
import { VIBE_PERMISSIONS as c } from "../constants/permissions.js";
|
|
10
|
-
import { useCallback as
|
|
11
|
-
import { useI18n as
|
|
12
|
-
import { ArrowLeft as
|
|
11
|
+
import { useCallback as ne, useRef as l, useState as u } from "react";
|
|
12
|
+
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
|
+
import { ArrowLeft as f, Clock as p, Copy as m, FileCode as h, FolderTree as g, Layers as _, RefreshCw as v, Tag as y, Trash2 as b } from "lucide-react";
|
|
13
14
|
import { useParams as x } from "react-router-dom";
|
|
14
15
|
import { usePermissions as S } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
15
16
|
import { motion as C } from "framer-motion";
|
|
@@ -27,14 +28,14 @@ var D = {
|
|
|
27
28
|
CUSTOM: "bg-bg-sunken text-text-secondary"
|
|
28
29
|
};
|
|
29
30
|
function O() {
|
|
30
|
-
let { t: O } =
|
|
31
|
+
let { t: O } = d(), { id: k } = x(), A = r(), { hasPermission: j, isLoading: M } = S(), N = ne((e, t) => {
|
|
31
32
|
let n = O(e);
|
|
32
33
|
return n === e ? t : n;
|
|
33
|
-
}, [O]), [P, F] =
|
|
34
|
+
}, [O]), [P, F] = u(!1), [I, L] = u(!1), [R, z] = u(null), [B, V] = u(!1), [H, U] = u(!1), [W, G] = u(null), K = l(null), q = l(null), { data: J, loading: Y, error: X, refetch: Z } = t({
|
|
34
35
|
variables: { id: k || "" },
|
|
35
36
|
skip: !k,
|
|
36
37
|
fetchPolicy: "cache-and-network"
|
|
37
|
-
}), [
|
|
38
|
+
}), [re] = n(), [ie] = e({ refetchQueries: ["ListTemplates"] }), Q = J?.vibecontrolsTemplate ?? null;
|
|
38
39
|
if (M) return /* @__PURE__ */ w("div", {
|
|
39
40
|
className: "flex items-center justify-center min-h-[400px]",
|
|
40
41
|
children: /* @__PURE__ */ w("p", {
|
|
@@ -55,87 +56,100 @@ function O() {
|
|
|
55
56
|
})]
|
|
56
57
|
})
|
|
57
58
|
});
|
|
58
|
-
let
|
|
59
|
+
let $ = () => {
|
|
59
60
|
A("/templates");
|
|
60
61
|
};
|
|
61
|
-
return
|
|
62
|
+
return Y && !Q ? /* @__PURE__ */ T(E, {
|
|
62
63
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
63
64
|
children: [/* @__PURE__ */ T("button", {
|
|
64
|
-
onClick:
|
|
65
|
+
onClick: $,
|
|
65
66
|
className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
|
|
66
|
-
children: [/* @__PURE__ */ w(
|
|
67
|
-
}), /* @__PURE__ */ w(
|
|
68
|
-
}) :
|
|
67
|
+
children: [/* @__PURE__ */ w(f, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
|
|
68
|
+
}), /* @__PURE__ */ w(ee, { message: N("template.loadingDetails", "Loading template details...") })]
|
|
69
|
+
}) : X && !Q ? /* @__PURE__ */ T(E, {
|
|
69
70
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
70
71
|
children: [/* @__PURE__ */ T("button", {
|
|
71
|
-
onClick:
|
|
72
|
+
onClick: $,
|
|
72
73
|
className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
|
|
73
|
-
children: [/* @__PURE__ */ w(
|
|
74
|
+
children: [/* @__PURE__ */ w(f, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
|
|
74
75
|
}), /* @__PURE__ */ w(a, {
|
|
75
76
|
title: N("template.failedLoad", "Failed to load template"),
|
|
76
|
-
message:
|
|
77
|
-
onRetry: () =>
|
|
77
|
+
message: X.message,
|
|
78
|
+
onRetry: () => Z()
|
|
78
79
|
})]
|
|
79
|
-
}) :
|
|
80
|
+
}) : Q ? /* @__PURE__ */ T(E, {
|
|
80
81
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
81
82
|
children: [
|
|
82
|
-
|
|
83
|
+
W && /* @__PURE__ */ w("div", {
|
|
83
84
|
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",
|
|
84
|
-
children:
|
|
85
|
+
children: W
|
|
85
86
|
}),
|
|
86
87
|
/* @__PURE__ */ T("button", {
|
|
87
|
-
onClick:
|
|
88
|
+
onClick: $,
|
|
88
89
|
className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
|
|
89
|
-
children: [/* @__PURE__ */ w(
|
|
90
|
+
children: [/* @__PURE__ */ w(f, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
|
|
90
91
|
}),
|
|
91
|
-
/* @__PURE__ */ w(
|
|
92
|
-
title:
|
|
93
|
-
description:
|
|
92
|
+
/* @__PURE__ */ w(i, {
|
|
93
|
+
title: Q.name,
|
|
94
|
+
description: Q.description || N("template.noDescription", "No description"),
|
|
94
95
|
actions: /* @__PURE__ */ T("div", {
|
|
95
96
|
className: "flex items-center gap-2 flex-wrap",
|
|
96
|
-
children: [
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
children: [
|
|
98
|
+
/* @__PURE__ */ w("button", {
|
|
99
|
+
onClick: () => Z(),
|
|
100
|
+
disabled: Y,
|
|
101
|
+
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",
|
|
102
|
+
title: N("common.refresh", "Refresh"),
|
|
103
|
+
children: /* @__PURE__ */ w(v, { className: `w-4 h-4 ${Y ? "animate-spin" : ""}` })
|
|
104
|
+
}),
|
|
105
|
+
/* @__PURE__ */ T("button", {
|
|
106
|
+
onClick: () => F(!0),
|
|
107
|
+
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",
|
|
108
|
+
children: [/* @__PURE__ */ w(m, { className: "w-4 h-4" }), N("template.cloneFromTemplate", "Clone from Template")]
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ T("button", {
|
|
111
|
+
"data-testid": "template-delete-button",
|
|
112
|
+
onClick: () => V(!0),
|
|
113
|
+
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",
|
|
114
|
+
"aria-label": N("template.deleteTemplate", "Delete Template"),
|
|
115
|
+
children: [/* @__PURE__ */ w(b, {
|
|
116
|
+
className: "w-4 h-4",
|
|
117
|
+
"aria-hidden": "true"
|
|
118
|
+
}), N("common.delete", "Delete")]
|
|
119
|
+
})
|
|
120
|
+
]
|
|
107
121
|
})
|
|
108
122
|
}),
|
|
109
123
|
/* @__PURE__ */ T("div", {
|
|
110
124
|
className: "flex items-center gap-3 flex-wrap",
|
|
111
125
|
children: [
|
|
112
|
-
|
|
113
|
-
className: `inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${D[
|
|
114
|
-
children: [/* @__PURE__ */ w(
|
|
126
|
+
Q.category && /* @__PURE__ */ T("span", {
|
|
127
|
+
className: `inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${D[Q.category] || "bg-bg-sunken text-text-secondary"}`,
|
|
128
|
+
children: [/* @__PURE__ */ w(_, { className: "w-3.5 h-3.5" }), Q.category]
|
|
115
129
|
}),
|
|
116
130
|
/* @__PURE__ */ T("span", {
|
|
117
131
|
className: "inline-flex items-center gap-1.5 text-sm text-text-secondary",
|
|
118
132
|
children: [
|
|
119
|
-
/* @__PURE__ */ w(
|
|
120
|
-
|
|
133
|
+
/* @__PURE__ */ w(m, { className: "w-3.5 h-3.5" }),
|
|
134
|
+
Q.cloneCount,
|
|
121
135
|
" ",
|
|
122
136
|
N("template.clones", "clones")
|
|
123
137
|
]
|
|
124
138
|
}),
|
|
125
139
|
/* @__PURE__ */ T("span", {
|
|
126
140
|
className: "inline-flex items-center gap-1.5 text-sm text-text-secondary",
|
|
127
|
-
children: [/* @__PURE__ */ w(
|
|
141
|
+
children: [/* @__PURE__ */ w(p, { className: "w-3.5 h-3.5" }), new Date(Q.createdAt).toLocaleDateString()]
|
|
128
142
|
})
|
|
129
143
|
]
|
|
130
144
|
}),
|
|
131
|
-
|
|
145
|
+
Q.tags?.length > 0 && /* @__PURE__ */ w("div", {
|
|
132
146
|
className: "flex flex-wrap gap-2",
|
|
133
|
-
children:
|
|
147
|
+
children: Q.tags.map((e) => /* @__PURE__ */ T("span", {
|
|
134
148
|
className: "inline-flex items-center gap-1 px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md",
|
|
135
|
-
children: [/* @__PURE__ */ w(
|
|
149
|
+
children: [/* @__PURE__ */ w(y, { className: "w-3 h-3" }), e]
|
|
136
150
|
}, e))
|
|
137
151
|
}),
|
|
138
|
-
|
|
152
|
+
Q.sourceComponent && /* @__PURE__ */ T(s, { children: [/* @__PURE__ */ w("div", {
|
|
139
153
|
className: "p-4 border-b border-border-default",
|
|
140
154
|
children: /* @__PURE__ */ w("h3", {
|
|
141
155
|
className: "font-semibold text-text-primary",
|
|
@@ -144,25 +158,25 @@ function O() {
|
|
|
144
158
|
}), /* @__PURE__ */ w("div", {
|
|
145
159
|
className: "p-4",
|
|
146
160
|
children: /* @__PURE__ */ T("button", {
|
|
147
|
-
onClick: () => A(`/components/${
|
|
161
|
+
onClick: () => A(`/components/${Q.sourceComponent.id}`),
|
|
148
162
|
className: "flex items-center gap-3 text-left hover:bg-bg-sunken p-2 -m-2 rounded-md transition-colors",
|
|
149
163
|
children: [/* @__PURE__ */ w("div", {
|
|
150
164
|
className: "w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0",
|
|
151
|
-
children: /* @__PURE__ */ w(
|
|
165
|
+
children: /* @__PURE__ */ w(h, { className: "w-5 h-5 text-action-primary-bg" })
|
|
152
166
|
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
|
|
153
167
|
className: "font-medium text-text-primary",
|
|
154
|
-
children:
|
|
168
|
+
children: Q.sourceComponent.name
|
|
155
169
|
}), /* @__PURE__ */ T("p", {
|
|
156
170
|
className: "text-xs text-text-secondary",
|
|
157
171
|
children: [
|
|
158
|
-
|
|
172
|
+
Q.sourceComponent.type,
|
|
159
173
|
" · ",
|
|
160
|
-
|
|
174
|
+
Q.sourceComponent.lifecycle
|
|
161
175
|
]
|
|
162
176
|
})] })]
|
|
163
177
|
})
|
|
164
178
|
})] }),
|
|
165
|
-
|
|
179
|
+
Q.setupInstructions && /* @__PURE__ */ T(s, { children: [/* @__PURE__ */ w("div", {
|
|
166
180
|
className: "p-4 border-b border-border-default",
|
|
167
181
|
children: /* @__PURE__ */ w("h3", {
|
|
168
182
|
className: "font-semibold text-text-primary",
|
|
@@ -174,21 +188,21 @@ function O() {
|
|
|
174
188
|
className: "prose prose-sm max-w-none text-text-primary",
|
|
175
189
|
children: /* @__PURE__ */ w("pre", {
|
|
176
190
|
className: "bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto whitespace-pre-wrap font-mono text-text-primary",
|
|
177
|
-
children:
|
|
191
|
+
children: Q.setupInstructions
|
|
178
192
|
})
|
|
179
193
|
})
|
|
180
194
|
})] }),
|
|
181
|
-
|
|
195
|
+
Q.fileTree && /* @__PURE__ */ T(s, { children: [/* @__PURE__ */ w("div", {
|
|
182
196
|
className: "p-4 border-b border-border-default",
|
|
183
197
|
children: /* @__PURE__ */ T("h3", {
|
|
184
198
|
className: "font-semibold text-text-primary flex items-center gap-2",
|
|
185
|
-
children: [/* @__PURE__ */ w(
|
|
199
|
+
children: [/* @__PURE__ */ w(g, { className: "w-5 h-5" }), N("template.fileTree", "File Tree")]
|
|
186
200
|
})
|
|
187
201
|
}), /* @__PURE__ */ w("div", {
|
|
188
202
|
className: "p-4",
|
|
189
203
|
children: /* @__PURE__ */ w("pre", {
|
|
190
204
|
className: "bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto font-mono text-text-primary",
|
|
191
|
-
children: typeof
|
|
205
|
+
children: typeof Q.fileTree == "string" ? Q.fileTree : JSON.stringify(Q.fileTree, null, 2)
|
|
192
206
|
})
|
|
193
207
|
})] }),
|
|
194
208
|
P && /* @__PURE__ */ w("div", {
|
|
@@ -219,7 +233,7 @@ function O() {
|
|
|
219
233
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
220
234
|
children: N("template.cloneName", "Name *")
|
|
221
235
|
}), /* @__PURE__ */ w("input", {
|
|
222
|
-
ref:
|
|
236
|
+
ref: K,
|
|
223
237
|
type: "text",
|
|
224
238
|
placeholder: N("template.cloneNamePlaceholder", "my-new-component"),
|
|
225
239
|
className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
|
|
@@ -228,7 +242,7 @@ function O() {
|
|
|
228
242
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
229
243
|
children: N("template.cloneDescription", "Description")
|
|
230
244
|
}), /* @__PURE__ */ w("input", {
|
|
231
|
-
ref:
|
|
245
|
+
ref: q,
|
|
232
246
|
type: "text",
|
|
233
247
|
placeholder: N("template.cloneDescPlaceholder", "Optional description"),
|
|
234
248
|
className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
|
|
@@ -244,24 +258,24 @@ function O() {
|
|
|
244
258
|
children: N("common.cancel", "Cancel")
|
|
245
259
|
}), /* @__PURE__ */ T("button", {
|
|
246
260
|
onClick: async () => {
|
|
247
|
-
let e =
|
|
261
|
+
let e = K.current?.value?.trim();
|
|
248
262
|
if (!e) {
|
|
249
263
|
z(N("template.cloneNameRequired", "Name is required"));
|
|
250
264
|
return;
|
|
251
265
|
}
|
|
252
266
|
L(!0), z(null);
|
|
253
267
|
try {
|
|
254
|
-
let t = await
|
|
268
|
+
let t = await re({ variables: {
|
|
255
269
|
templateId: k,
|
|
256
270
|
input: {
|
|
257
271
|
name: e,
|
|
258
|
-
description:
|
|
272
|
+
description: q.current?.value?.trim() || void 0
|
|
259
273
|
}
|
|
260
274
|
} });
|
|
261
275
|
F(!1);
|
|
262
276
|
let n = t.data?.instantiateVibecontrolsTemplate?.id;
|
|
263
|
-
|
|
264
|
-
|
|
277
|
+
G(N("template.clonedSuccess", "Cloned successfully")), setTimeout(() => {
|
|
278
|
+
G(null), n && A(`/components/${n}`);
|
|
265
279
|
}, 1500);
|
|
266
280
|
} catch (e) {
|
|
267
281
|
z(e instanceof Error ? e.message : N("template.cloneFailed", "Failed to clone"));
|
|
@@ -271,25 +285,44 @@ function O() {
|
|
|
271
285
|
},
|
|
272
286
|
disabled: I,
|
|
273
287
|
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",
|
|
274
|
-
children: [/* @__PURE__ */ w(
|
|
288
|
+
children: [/* @__PURE__ */ w(m, { className: "w-4 h-4" }), I ? N("template.cloning", "Cloning...") : N("template.clone", "Clone")]
|
|
275
289
|
})]
|
|
276
290
|
})
|
|
277
291
|
]
|
|
278
292
|
})
|
|
293
|
+
}),
|
|
294
|
+
/* @__PURE__ */ w(te, {
|
|
295
|
+
isOpen: B,
|
|
296
|
+
onClose: () => V(!1),
|
|
297
|
+
onConfirm: async () => {
|
|
298
|
+
if (k) {
|
|
299
|
+
U(!0);
|
|
300
|
+
try {
|
|
301
|
+
await ie({ variables: { id: k } }), V(!1), A("/templates");
|
|
302
|
+
} finally {
|
|
303
|
+
U(!1);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
title: N("template.deleteTemplate", "Delete Template"),
|
|
308
|
+
message: N("template.deleteConfirmMessage", `Are you sure you want to delete template "${Q.name}"? Existing instantiated components are unaffected, but the template will no longer be available for cloning. This cannot be undone.`),
|
|
309
|
+
confirmLabel: N("common.delete", "Delete"),
|
|
310
|
+
variant: "danger",
|
|
311
|
+
isLoading: H
|
|
279
312
|
})
|
|
280
313
|
]
|
|
281
314
|
}) : /* @__PURE__ */ T(E, {
|
|
282
315
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
283
316
|
children: [/* @__PURE__ */ T("button", {
|
|
284
|
-
onClick:
|
|
317
|
+
onClick: $,
|
|
285
318
|
className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
|
|
286
|
-
children: [/* @__PURE__ */ w(
|
|
319
|
+
children: [/* @__PURE__ */ w(f, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
|
|
287
320
|
}), /* @__PURE__ */ w(o, {
|
|
288
|
-
icon: /* @__PURE__ */ w(
|
|
321
|
+
icon: /* @__PURE__ */ w(h, { className: "w-8 h-8 text-text-secondary" }),
|
|
289
322
|
title: N("template.notFound", "Template not found"),
|
|
290
323
|
description: `No template with ID "${k}" exists`,
|
|
291
324
|
action: /* @__PURE__ */ w("button", {
|
|
292
|
-
onClick:
|
|
325
|
+
onClick: $,
|
|
293
326
|
className: "px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
294
327
|
children: N("template.backToTemplates", "Back to Templates")
|
|
295
328
|
})
|