@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.
@@ -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":"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"}
@@ -1,15 +1,16 @@
1
- import { useGetTemplateQuery as e, useInstantiateTemplateMutation as t } from "../generated/wspace-operations.js";
2
- import { useVibeNavigate as n } from "../providers/VibeControlsProvider.js";
3
- import { PageHeader as r } from "../components/shared/PageHeader.js";
4
- import { LoadingState as i } from "../components/shared/LoadingState.js";
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 l, useRef as u, useState as d } from "react";
11
- import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
- import { ArrowLeft as p, Clock as m, Copy as h, FileCode as g, FolderTree as _, Layers as v, RefreshCw as y, Tag as b } from "lucide-react";
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 } = f(), { id: k } = x(), A = n(), { hasPermission: j, isLoading: M } = S(), N = l((e, t) => {
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] = d(!1), [I, L] = d(!1), [R, z] = d(null), [B, V] = d(null), H = u(null), U = u(null), { data: W, loading: G, error: K, refetch: q } = e({
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
- }), [J] = t(), Y = W?.vibecontrolsTemplate ?? null;
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 X = () => {
59
+ let $ = () => {
59
60
  A("/templates");
60
61
  };
61
- return G && !Y ? /* @__PURE__ */ T(E, {
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: X,
65
+ onClick: $,
65
66
  className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
66
- children: [/* @__PURE__ */ w(p, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
67
- }), /* @__PURE__ */ w(i, { message: N("template.loadingDetails", "Loading template details...") })]
68
- }) : K && !Y ? /* @__PURE__ */ T(E, {
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: X,
72
+ onClick: $,
72
73
  className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
73
- children: [/* @__PURE__ */ w(p, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
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: K.message,
77
- onRetry: () => q()
77
+ message: X.message,
78
+ onRetry: () => Z()
78
79
  })]
79
- }) : Y ? /* @__PURE__ */ T(E, {
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
- B && /* @__PURE__ */ w("div", {
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: B
85
+ children: W
85
86
  }),
86
87
  /* @__PURE__ */ T("button", {
87
- onClick: X,
88
+ onClick: $,
88
89
  className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
89
- children: [/* @__PURE__ */ w(p, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
90
+ children: [/* @__PURE__ */ w(f, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
90
91
  }),
91
- /* @__PURE__ */ w(r, {
92
- title: Y.name,
93
- description: Y.description || N("template.noDescription", "No 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: [/* @__PURE__ */ w("button", {
97
- onClick: () => q(),
98
- disabled: G,
99
- 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",
100
- title: N("common.refresh", "Refresh"),
101
- children: /* @__PURE__ */ w(y, { className: `w-4 h-4 ${G ? "animate-spin" : ""}` })
102
- }), /* @__PURE__ */ T("button", {
103
- onClick: () => F(!0),
104
- 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",
105
- children: [/* @__PURE__ */ w(h, { className: "w-4 h-4" }), N("template.cloneFromTemplate", "Clone from Template")]
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
- Y.category && /* @__PURE__ */ T("span", {
113
- className: `inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${D[Y.category] || "bg-bg-sunken text-text-secondary"}`,
114
- children: [/* @__PURE__ */ w(v, { className: "w-3.5 h-3.5" }), Y.category]
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(h, { className: "w-3.5 h-3.5" }),
120
- Y.cloneCount,
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(m, { className: "w-3.5 h-3.5" }), new Date(Y.createdAt).toLocaleDateString()]
141
+ children: [/* @__PURE__ */ w(p, { className: "w-3.5 h-3.5" }), new Date(Q.createdAt).toLocaleDateString()]
128
142
  })
129
143
  ]
130
144
  }),
131
- Y.tags?.length > 0 && /* @__PURE__ */ w("div", {
145
+ Q.tags?.length > 0 && /* @__PURE__ */ w("div", {
132
146
  className: "flex flex-wrap gap-2",
133
- children: Y.tags.map((e) => /* @__PURE__ */ T("span", {
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(b, { className: "w-3 h-3" }), e]
149
+ children: [/* @__PURE__ */ w(y, { className: "w-3 h-3" }), e]
136
150
  }, e))
137
151
  }),
138
- Y.sourceComponent && /* @__PURE__ */ T(s, { children: [/* @__PURE__ */ w("div", {
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/${Y.sourceComponent.id}`),
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(g, { className: "w-5 h-5 text-action-primary-bg" })
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: Y.sourceComponent.name
168
+ children: Q.sourceComponent.name
155
169
  }), /* @__PURE__ */ T("p", {
156
170
  className: "text-xs text-text-secondary",
157
171
  children: [
158
- Y.sourceComponent.type,
172
+ Q.sourceComponent.type,
159
173
  " · ",
160
- Y.sourceComponent.lifecycle
174
+ Q.sourceComponent.lifecycle
161
175
  ]
162
176
  })] })]
163
177
  })
164
178
  })] }),
165
- Y.setupInstructions && /* @__PURE__ */ T(s, { children: [/* @__PURE__ */ w("div", {
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: Y.setupInstructions
191
+ children: Q.setupInstructions
178
192
  })
179
193
  })
180
194
  })] }),
181
- Y.fileTree && /* @__PURE__ */ T(s, { children: [/* @__PURE__ */ w("div", {
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(_, { className: "w-5 h-5" }), N("template.fileTree", "File Tree")]
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 Y.fileTree == "string" ? Y.fileTree : JSON.stringify(Y.fileTree, null, 2)
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: H,
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: U,
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 = H.current?.value?.trim();
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 J({ variables: {
268
+ let t = await re({ variables: {
255
269
  templateId: k,
256
270
  input: {
257
271
  name: e,
258
- description: U.current?.value?.trim() || void 0
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
- V(N("template.clonedSuccess", "Cloned successfully")), setTimeout(() => {
264
- V(null), n && A(`/components/${n}`);
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(h, { className: "w-4 h-4" }), I ? N("template.cloning", "Cloning...") : N("template.clone", "Clone")]
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: X,
317
+ onClick: $,
285
318
  className: "flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors",
286
- children: [/* @__PURE__ */ w(p, { className: "w-4 h-4" }), N("template.backToTemplates", "Back to Templates")]
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(g, { className: "w-8 h-8 text-text-secondary" }),
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: X,
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
  })