@burdenoff/microfe-vibecontrols 2026.522.1 → 2026.522.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"TargetDetailsPage.js","names":[],"sources":["../../src/pages/TargetDetailsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useParams, Link, useNavigate } from 'react-router-dom';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport {\n ArrowLeft,\n Server,\n CircleCheck,\n CircleX,\n Cpu,\n ExternalLink,\n Activity,\n Tag,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatusBadge,\n EmptyState,\n ErrorState,\n} from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useGetTargetQuery } from '@/generated/wspace-operations';\nimport type { GetTargetQuery } from '@/generated/wspace-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype TargetType = NonNullable<GetTargetQuery['target']>;\ntype AgentSummary = TargetType['agents'][number];\ntype ConnectionSummary = TargetType['inboundConnections'][number];\n\n/**\n * TargetDetailsPage — `/vibecontrols/targets/:id`\n *\n * Mirrors AgentDetailsPage shell. Three sections:\n * 1. Identity card (name, type, status, host, last updated)\n * 2. Agents on this target (table with link to AgentDetailsPage)\n * 3. Inbound agent connections\n *\n * Reuses the existing `GetTarget` query — no schema changes needed; the\n * `TargetFields` fragment already includes the agent list and metadata.\n */\nexport function TargetDetailsPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const { data, loading, error, refetch } = useGetTargetQuery({\n variables: { id: id ?? '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const target = data?.target ?? null;\n const agents = useMemo(() => target?.agents ?? [], [target]);\n const connections = useMemo(() => target?.inboundConnections ?? [], [target]);\n\n if (!id) {\n return (\n <ErrorState title={tr('targetDetails.missingId', 'Target id missing from URL')} message=\"\" />\n );\n }\n\n if (loading && !data) {\n return <PageSkeleton variant=\"detail\" />;\n }\n\n if (error && !target) {\n return (\n <ErrorState\n title={tr('targetDetails.errorTitle', 'Failed to load target')}\n message={error.message}\n onRetry={() => void refetch()}\n />\n );\n }\n\n if (!target) {\n return (\n <EmptyState\n icon={<Server size={48} className=\"text-text-tertiary\" />}\n title={tr('targetDetails.notFoundTitle', 'Target not found')}\n description={tr(\n 'targetDetails.notFoundBody',\n 'The target you requested may have been deleted or is in a different workspace.'\n )}\n />\n );\n }\n\n const lastUpdatedAt = target.updatedAt\n ? new Date(target.updatedAt as string).toLocaleString()\n : '—';\n\n return (\n <MotionPage>\n <PageHeader\n title={target.name}\n description={`${target.type} · ${target.host ?? 'no host'}`}\n breadcrumb={[\n { label: tr('breadcrumbs.targetsAgents', 'Targets & Agents'), path: '../agents' },\n { label: tr('breadcrumbs.targets', 'Targets'), path: '../agents?tab=targets' },\n { label: target.name },\n ]}\n actions={\n <button\n onClick={() => navigate('../agents?tab=targets')}\n className=\"flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border border-border-default hover:bg-bg-sunken transition-colors\"\n >\n <ArrowLeft size={14} />\n {tr('common.back', 'Back')}\n </button>\n }\n />\n\n {/* Identity */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Server className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('targetDetails.identity', 'Identity')}\n </h3>\n </div>\n <div className=\"p-4 grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.name', 'Name')}</p>\n <p className=\"text-text-primary font-medium\">{target.name}</p>\n </div>\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.type', 'Type')}</p>\n <p className=\"text-text-primary font-medium\">{target.type}</p>\n </div>\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.status', 'Status')}</p>\n <StatusBadge status={target.status} />\n </div>\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.host', 'Host')}</p>\n <p className=\"text-text-primary font-mono text-xs\">{target.host ?? '—'}</p>\n </div>\n {target.username && (\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.username', 'Username')}</p>\n <p className=\"text-text-primary font-mono text-xs\">{target.username}</p>\n </div>\n )}\n {target.port != null && (\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.port', 'Port')}</p>\n <p className=\"text-text-primary font-mono text-xs\">{target.port}</p>\n </div>\n )}\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.updatedAt', 'Last updated')}</p>\n <p className=\"text-text-primary text-xs\">{lastUpdatedAt}</p>\n </div>\n {target.tags && target.tags.length > 0 && (\n <div className=\"sm:col-span-2\">\n <p className=\"text-text-tertiary mb-1\">{tr('targetDetails.tags', 'Tags')}</p>\n <div className=\"flex flex-wrap gap-1\">\n {target.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-2 py-0.5 text-xs rounded bg-bg-sunken border border-border-default\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n </Card>\n\n {/* Tags */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('targetDetails.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_TARGET\" entityId={target.id} />\n </div>\n </Card>\n\n {/* Agents on this target */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Cpu className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('targetDetails.agents', 'Agents on this target')} ({agents.length})\n </h3>\n </div>\n <Link\n to=\"../agents?tab=agents\"\n className=\"text-sm text-action-primary-text hover:underline flex items-center gap-1\"\n >\n {tr('targetDetails.allAgents', 'All agents')}\n <ExternalLink size={12} />\n </Link>\n </div>\n {agents.length === 0 ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Cpu size={40} className=\"text-text-tertiary\" />}\n title={tr('targetDetails.noAgents', 'No agents on this target yet')}\n description={tr(\n 'targetDetails.noAgentsBody',\n 'Use the Agents tab to onboard an agent and select this target during setup.'\n )}\n />\n </div>\n ) : (\n <div className=\"p-4 space-y-2\">\n {agents.map((agent: AgentSummary) => (\n <Link\n key={agent.id}\n to={`../agents/${agent.id}`}\n className=\"flex items-center justify-between gap-3 p-3 rounded-md border border-border-default hover:bg-bg-sunken/40 transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"w-8 h-8 rounded-md bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n {agent.isActive ? (\n <CircleCheck className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <CircleX className=\"w-4 h-4 text-text-tertiary\" />\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{agent.name}</p>\n <p className=\"text-xs text-text-tertiary truncate\">\n v{agent.version}\n {agent.tunnelUrl ? ` · ${agent.tunnelUrl}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`text-xs px-2 py-0.5 rounded ${\n agent.isActive\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-bg-sunken text-text-tertiary'\n }`}\n >\n {agent.isActive\n ? tr('targetDetails.active', 'Active')\n : tr('targetDetails.offline', 'Offline')}\n </span>\n <ExternalLink size={14} className=\"text-text-tertiary\" />\n </div>\n </Link>\n ))}\n </div>\n )}\n </Card>\n\n {/* Inbound connections */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Activity className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('targetDetails.connections', 'Inbound connections')} ({connections.length})\n </h3>\n </div>\n {connections.length === 0 ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Activity size={40} className=\"text-text-tertiary\" />}\n title={tr('targetDetails.noConnections', 'No inbound connections')}\n description={tr(\n 'targetDetails.noConnectionsBody',\n 'No agents are currently connected to this target.'\n )}\n />\n </div>\n ) : (\n <div className=\"p-4 space-y-2\">\n {connections.map((conn: ConnectionSummary) => (\n <div\n key={conn.id}\n className=\"flex items-center justify-between gap-3 p-3 rounded-md border border-border-default\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {conn.label || `${conn.protocol} connection`}\n </p>\n <p className=\"text-xs text-text-tertiary truncate\">\n {tr('targetDetails.from', 'From')}{' '}\n <Link\n to={`../agents/${conn.sourceAgent.id}`}\n className=\"text-action-primary-text hover:underline\"\n >\n {conn.sourceAgent.name}\n </Link>{' '}\n · {conn.protocol} · {conn.status}\n </p>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded bg-bg-sunken\">{conn.status}</span>\n </div>\n ))}\n </div>\n )}\n </Card>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TargetDetailsPage.js","names":[],"sources":["../../src/pages/TargetDetailsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useParams, Link, useNavigate } from 'react-router-dom';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport {\n ArrowLeft,\n Server,\n CircleCheck,\n CircleX,\n Cpu,\n ExternalLink,\n Activity,\n Tag,\n} from 'lucide-react';\nimport { PageHeader, Card, StatusBadge, EmptyState, ErrorState } from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useGetTargetQuery } from '@/generated/wspace-operations';\nimport type { GetTargetQuery } from '@/generated/wspace-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype TargetType = NonNullable<GetTargetQuery['target']>;\ntype AgentSummary = TargetType['agents'][number];\ntype ConnectionSummary = TargetType['inboundConnections'][number];\n\n/**\n * TargetDetailsPage — `/vibecontrols/targets/:id`\n *\n * Mirrors AgentDetailsPage shell. Three sections:\n * 1. Identity card (name, type, status, host, last updated)\n * 2. Agents on this target (table with link to AgentDetailsPage)\n * 3. Inbound agent connections\n *\n * Reuses the existing `GetTarget` query — no schema changes needed; the\n * `TargetFields` fragment already includes the agent list and metadata.\n */\nexport function TargetDetailsPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const { data, loading, error, refetch } = useGetTargetQuery({\n variables: { id: id ?? '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const target = data?.target ?? null;\n const agents = useMemo(() => target?.agents ?? [], [target]);\n const connections = useMemo(() => target?.inboundConnections ?? [], [target]);\n\n if (!id) {\n return (\n <ErrorState title={tr('targetDetails.missingId', 'Target id missing from URL')} message=\"\" />\n );\n }\n\n if (loading && !data) {\n return <PageSkeleton variant=\"detail\" />;\n }\n\n if (error && !target) {\n return (\n <ErrorState\n title={tr('targetDetails.errorTitle', 'Failed to load target')}\n message={error.message}\n onRetry={() => void refetch()}\n />\n );\n }\n\n if (!target) {\n return (\n <EmptyState\n icon={<Server size={48} className=\"text-text-tertiary\" />}\n title={tr('targetDetails.notFoundTitle', 'Target not found')}\n description={tr(\n 'targetDetails.notFoundBody',\n 'The target you requested may have been deleted or is in a different workspace.'\n )}\n />\n );\n }\n\n const lastUpdatedAt = target.updatedAt\n ? new Date(target.updatedAt as string).toLocaleString()\n : '—';\n\n return (\n <MotionPage>\n <PageHeader\n title={target.name}\n description={`${target.type} · ${target.host ?? 'no host'}`}\n breadcrumb={[\n { label: tr('breadcrumbs.targetsAgents', 'Targets & Agents'), path: '../agents' },\n { label: tr('breadcrumbs.targets', 'Targets'), path: '../agents?tab=targets' },\n { label: target.name },\n ]}\n actions={\n <button\n onClick={() => navigate('../agents?tab=targets')}\n className=\"flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border border-border-default hover:bg-bg-sunken transition-colors\"\n >\n <ArrowLeft size={14} />\n {tr('common.back', 'Back')}\n </button>\n }\n />\n\n {/* Identity */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Server className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('targetDetails.identity', 'Identity')}\n </h3>\n </div>\n <div className=\"p-4 grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.name', 'Name')}</p>\n <p className=\"text-text-primary font-medium\">{target.name}</p>\n </div>\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.type', 'Type')}</p>\n <p className=\"text-text-primary font-medium\">{target.type}</p>\n </div>\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.status', 'Status')}</p>\n <StatusBadge status={target.status} />\n </div>\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.host', 'Host')}</p>\n <p className=\"text-text-primary font-mono text-xs\">{target.host ?? '—'}</p>\n </div>\n {target.username && (\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.username', 'Username')}</p>\n <p className=\"text-text-primary font-mono text-xs\">{target.username}</p>\n </div>\n )}\n {target.port != null && (\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.port', 'Port')}</p>\n <p className=\"text-text-primary font-mono text-xs\">{target.port}</p>\n </div>\n )}\n <div>\n <p className=\"text-text-tertiary\">{tr('targetDetails.updatedAt', 'Last updated')}</p>\n <p className=\"text-text-primary text-xs\">{lastUpdatedAt}</p>\n </div>\n {target.tags && target.tags.length > 0 && (\n <div className=\"sm:col-span-2\">\n <p className=\"text-text-tertiary mb-1\">{tr('targetDetails.tags', 'Tags')}</p>\n <div className=\"flex flex-wrap gap-1\">\n {target.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-2 py-0.5 text-xs rounded bg-bg-sunken border border-border-default\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n </Card>\n\n {/* Tags */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('targetDetails.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_TARGET\" entityId={target.id} />\n </div>\n </Card>\n\n {/* Agents on this target */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Cpu className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('targetDetails.agents', 'Agents on this target')} ({agents.length})\n </h3>\n </div>\n <Link\n to=\"../agents?tab=agents\"\n className=\"text-sm text-action-primary-text hover:underline flex items-center gap-1\"\n >\n {tr('targetDetails.allAgents', 'All agents')}\n <ExternalLink size={12} />\n </Link>\n </div>\n {agents.length === 0 ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Cpu size={40} className=\"text-text-tertiary\" />}\n title={tr('targetDetails.noAgents', 'No agents on this target yet')}\n description={tr(\n 'targetDetails.noAgentsBody',\n 'Use the Agents tab to onboard an agent and select this target during setup.'\n )}\n />\n </div>\n ) : (\n <div className=\"p-4 space-y-2\">\n {agents.map((agent: AgentSummary) => (\n <Link\n key={agent.id}\n to={`../agents/${agent.id}`}\n className=\"flex items-center justify-between gap-3 p-3 rounded-md border border-border-default hover:bg-bg-sunken/40 transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"w-8 h-8 rounded-md bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n {agent.isActive ? (\n <CircleCheck className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <CircleX className=\"w-4 h-4 text-text-tertiary\" />\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{agent.name}</p>\n <p className=\"text-xs text-text-tertiary truncate\">\n v{agent.version}\n {agent.tunnelUrl ? ` · ${agent.tunnelUrl}` : ''}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={`text-xs px-2 py-0.5 rounded ${\n agent.isActive\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-bg-sunken text-text-tertiary'\n }`}\n >\n {agent.isActive\n ? tr('targetDetails.active', 'Active')\n : tr('targetDetails.offline', 'Offline')}\n </span>\n <ExternalLink size={14} className=\"text-text-tertiary\" />\n </div>\n </Link>\n ))}\n </div>\n )}\n </Card>\n\n {/* Inbound connections */}\n <Card className=\"mt-4\">\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Activity className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('targetDetails.connections', 'Inbound connections')} ({connections.length})\n </h3>\n </div>\n {connections.length === 0 ? (\n <div className=\"p-6\">\n <EmptyState\n icon={<Activity size={40} className=\"text-text-tertiary\" />}\n title={tr('targetDetails.noConnections', 'No inbound connections')}\n description={tr(\n 'targetDetails.noConnectionsBody',\n 'No agents are currently connected to this target.'\n )}\n />\n </div>\n ) : (\n <div className=\"p-4 space-y-2\">\n {connections.map((conn: ConnectionSummary) => (\n <div\n key={conn.id}\n className=\"flex items-center justify-between gap-3 p-3 rounded-md border border-border-default\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {conn.label || `${conn.protocol} connection`}\n </p>\n <p className=\"text-xs text-text-tertiary truncate\">\n {tr('targetDetails.from', 'From')}{' '}\n <Link\n to={`../agents/${conn.sourceAgent.id}`}\n className=\"text-action-primary-text hover:underline\"\n >\n {conn.sourceAgent.name}\n </Link>{' '}\n · {conn.protocol} · {conn.status}\n </p>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded bg-bg-sunken\">{conn.status}</span>\n </div>\n ))}\n </div>\n )}\n </Card>\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmCA,SAAgB,IAAoB;CAClC,IAAM,EAAE,UAAO,GAA2B,EACpC,IAAW,GAAa,EACxB,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAI,EAAE,EAAI;AAChB,SAAO,MAAM,IAAM,IAAW;IAG1B,EAAE,SAAM,YAAS,UAAO,eAAY,EAAkB;EAC1D,WAAW,EAAE,IAAI,KAAM,IAAI;EAC3B,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,IAAS,GAAM,UAAU,MACzB,IAAS,QAAc,GAAQ,UAAU,EAAE,EAAE,CAAC,EAAO,CAAC,EACtD,IAAc,QAAc,GAAQ,sBAAsB,EAAE,EAAE,CAAC,EAAO,CAAC;AAE7E,KAAI,CAAC,EACH,QACE,kBAAC,GAAD;EAAY,OAAO,EAAG,2BAA2B,6BAA6B;EAAE,SAAQ;EAAK,CAAA;AAIjG,KAAI,KAAW,CAAC,EACd,QAAO,kBAAC,GAAD,EAAc,SAAQ,UAAW,CAAA;AAG1C,KAAI,KAAS,CAAC,EACZ,QACE,kBAAC,GAAD;EACE,OAAO,EAAG,4BAA4B,wBAAwB;EAC9D,SAAS,EAAM;EACf,eAAe,KAAK,GAAS;EAC7B,CAAA;AAIN,KAAI,CAAC,EACH,QACE,kBAAC,GAAD;EACE,MAAM,kBAAC,GAAD;GAAQ,MAAM;GAAI,WAAU;GAAuB,CAAA;EACzD,OAAO,EAAG,+BAA+B,mBAAmB;EAC5D,aAAa,EACX,8BACA,iFACD;EACD,CAAA;CAIN,IAAM,IAAgB,EAAO,YACzB,IAAI,KAAK,EAAO,UAAoB,CAAC,gBAAgB,GACrD;AAEJ,QACE,kBAAC,GAAD,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,OAAO,EAAO;GACd,aAAa,GAAG,EAAO,KAAK,KAAK,EAAO,QAAQ;GAChD,YAAY;IACV;KAAE,OAAO,EAAG,6BAA6B,mBAAmB;KAAE,MAAM;KAAa;IACjF;KAAE,OAAO,EAAG,uBAAuB,UAAU;KAAE,MAAM;KAAyB;IAC9E,EAAE,OAAO,EAAO,MAAM;IACvB;GACD,SACE,kBAAC,UAAD;IACE,eAAe,EAAS,wBAAwB;IAChD,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAG,eAAe,OAAO,CACnB;;GAEX,CAAA;EAGF,kBAAC,GAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC9B,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,0BAA0B,WAAW;KACtC,CAAA,CACD;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,sBAAsB,OAAO;MAAK,CAAA,EACxE,kBAAC,KAAD;MAAG,WAAU;gBAAiC,EAAO;MAAS,CAAA,CAC1D,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,sBAAsB,OAAO;MAAK,CAAA,EACxE,kBAAC,KAAD;MAAG,WAAU;gBAAiC,EAAO;MAAS,CAAA,CAC1D,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,wBAAwB,SAAS;MAAK,CAAA,EAC5E,kBAAC,GAAD,EAAa,QAAQ,EAAO,QAAU,CAAA,CAClC,EAAA,CAAA;KACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,sBAAsB,OAAO;MAAK,CAAA,EACxE,kBAAC,KAAD;MAAG,WAAU;gBAAuC,EAAO,QAAQ;MAAQ,CAAA,CACvE,EAAA,CAAA;KACL,EAAO,YACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,0BAA0B,WAAW;MAAK,CAAA,EAChF,kBAAC,KAAD;MAAG,WAAU;gBAAuC,EAAO;MAAa,CAAA,CACpE,EAAA,CAAA;KAEP,EAAO,QAAQ,QACd,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,sBAAsB,OAAO;MAAK,CAAA,EACxE,kBAAC,KAAD;MAAG,WAAU;gBAAuC,EAAO;MAAS,CAAA,CAChE,EAAA,CAAA;KAER,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAsB,EAAG,2BAA2B,eAAe;MAAK,CAAA,EACrF,kBAAC,KAAD;MAAG,WAAU;gBAA6B;MAAkB,CAAA,CACxD,EAAA,CAAA;KACL,EAAO,QAAQ,EAAO,KAAK,SAAS,KACnC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA2B,EAAG,sBAAsB,OAAO;OAAK,CAAA,EAC7E,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAO,KAAK,KAAK,MAChB,kBAAC,QAAD;QAEE,WAAU;kBAET;QACI,EAJA,EAIA,CACP;OACE,CAAA,CACF;;KAEJ;MACD;;EAGP,kBAAC,GAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAC3B,kBAAC,MAAD;KAAI,WAAU;eAAmC,EAAG,sBAAsB,OAAO;KAAM,CAAA,CACnF;OACN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAiB,YAAW;KAAsB,UAAU,EAAO;KAAM,CAAA;IACrE,CAAA,CACD;;EAGP,kBAAC,GAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAC3B,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACG,EAAG,wBAAwB,wBAAwB;OAAC;OAAG,EAAO;OAAO;OACnE;QACD;QACN,kBAAC,GAAD;KACE,IAAG;KACH,WAAU;eAFZ,CAIG,EAAG,2BAA2B,aAAa,EAC5C,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA,CACrB;OACH;OACL,EAAO,WAAW,IACjB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD;MAAK,MAAM;MAAI,WAAU;MAAuB,CAAA;KACtD,OAAO,EAAG,0BAA0B,+BAA+B;KACnE,aAAa,EACX,8BACA,8EACD;KACD,CAAA;IACE,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MACX,kBAAC,GAAD;KAEE,IAAI,aAAa,EAAM;KACvB,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAM,WACL,kBAAC,GAAD,EAAa,WAAU,oCAAqC,CAAA,GAE5D,kBAAC,GAAD,EAAS,WAAU,8BAA+B,CAAA;OAEhD,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAkD,EAAM;QAAS,CAAA,EAC9E,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAAmD;SAC/C,EAAM;SACP,EAAM,YAAY,MAAM,EAAM,cAAc;SAC3C;UACA;SACF;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OACE,WAAW,+BACT,EAAM,WACF,kDACA;iBAGL,EAAM,WACH,EAAG,wBAAwB,SAAS,GACpC,EAAG,yBAAyB,UAAU;OACrC,CAAA,EACP,kBAAC,GAAD;OAAc,MAAM;OAAI,WAAU;OAAuB,CAAA,CACrD;QACD;OAlCA,EAAM,GAkCN,CACP;IACE,CAAA,CAEH;;EAGP,kBAAC,GAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAChC,kBAAC,MAAD;KAAI,WAAU;eAAd;MACG,EAAG,6BAA6B,sBAAsB;MAAC;MAAG,EAAY;MAAO;MAC3E;OACD;OACL,EAAY,WAAW,IACtB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD;MAAU,MAAM;MAAI,WAAU;MAAuB,CAAA;KAC3D,OAAO,EAAG,+BAA+B,yBAAyB;KAClE,aAAa,EACX,mCACA,oDACD;KACD,CAAA;IACE,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAY,KAAK,MAChB,kBAAC,OAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAK,SAAS,GAAG,EAAK,SAAS;OAC9B,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAG,sBAAsB,OAAO;QAAE;QACnC,kBAAC,GAAD;SACE,IAAI,aAAa,EAAK,YAAY;SAClC,WAAU;mBAET,EAAK,YAAY;SACb,CAAA;QAAC;QAAI;QACT,EAAK;QAAS;QAAI,EAAK;QACxB;SACA;SACN,kBAAC,QAAD;MAAM,WAAU;gBAA4C,EAAK;MAAc,CAAA,CAC3E;OAnBC,EAAK,GAmBN,CACN;IACE,CAAA,CAEH;;EACI,EAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../src/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { motion } from 'framer-motion';\nimport {\n ArrowLeft,\n FileCode,\n Copy,\n Layers,\n Tag,\n FolderTree,\n Clock,\n RefreshCw,\n Trash2,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n EmptyState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetTemplateQuery,\n useInstantiateTemplateMutation,\n useDeleteTemplateMutation,\n} from '@/generated/wspace-operations';\n\nconst categoryStyles: Record<string, string> = {\n STARTER: 'bg-bg-sunken text-text-secondary',\n MICROSERVICE: 'bg-action-primary-bg/10 text-action-primary-bg',\n FRONTEND: 'bg-status-info-bg text-status-info-text',\n BACKEND: 'bg-action-primary-bg/10 text-action-primary-bg',\n FULLSTACK: 'bg-status-success-bg text-status-success-text',\n INFRASTRUCTURE: 'bg-status-warning-bg text-status-warning-text',\n LIBRARY: 'bg-status-warning-bg text-status-warning-text',\n CUSTOM: 'bg-bg-sunken text-text-secondary',\n};\n\nexport function TemplateDetailPage() {\n const { t } = useI18n();\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [showCloneDialog, setShowCloneDialog] = useState(false);\n const [cloneLoading, setCloneLoading] = useState(false);\n const [cloneError, setCloneError] = useState<string | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const cloneNameRef = useRef<HTMLInputElement>(null);\n const cloneDescRef = useRef<HTMLInputElement>(null);\n\n const { data, loading, error, refetch } = useGetTemplateQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [instantiateTemplate] = useInstantiateTemplateMutation();\n const [deleteTemplate] = useDeleteTemplateMutation({ refetchQueries: ['ListTemplates'] });\n\n const template = data?.vibecontrolsTemplate ?? null;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/templates');\n };\n\n const handleClone = async () => {\n const name = cloneNameRef.current?.value?.trim();\n if (!name) {\n setCloneError(tr('template.cloneNameRequired', 'Name is required'));\n return;\n }\n\n setCloneLoading(true);\n setCloneError(null);\n try {\n const result = await instantiateTemplate({\n variables: {\n templateId: id,\n input: {\n name,\n description: cloneDescRef.current?.value?.trim() || undefined,\n },\n },\n });\n setShowCloneDialog(false);\n const newId = result.data?.instantiateVibecontrolsTemplate?.id;\n setSuccessMessage(tr('template.clonedSuccess', 'Cloned successfully'));\n setTimeout(() => {\n setSuccessMessage(null);\n if (newId) navigate(`/components/${newId}`);\n }, 1500);\n } catch (err) {\n setCloneError(\n err instanceof Error ? err.message : tr('template.cloneFailed', 'Failed to clone')\n );\n } finally {\n setCloneLoading(false);\n }\n };\n\n const handleDeleteTemplate = async () => {\n if (!id) return;\n setIsDeleting(true);\n try {\n await deleteTemplate({ variables: { id } });\n setShowDeleteConfirm(false);\n navigate('/templates');\n } finally {\n setIsDeleting(false);\n }\n };\n\n // Guard states\n if (loading && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load template')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('template.notFound', 'Template not found')}\n description={`No template with ID \"${id}\" exists`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed top-4 left-4 right-4 sm:left-auto z-50 bg-status-success-bg text-status-success-text px-4 py-3 rounded-md shadow-lg\">\n {successMessage}\n </div>\n )}\n\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={template.name}\n description={template.description || tr('template.noDescription', 'No description')}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCloneDialog(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Copy className=\"w-4 h-4\" />\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </button>\n <button\n data-testid=\"template-delete-button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-status-error-bg text-status-error-text rounded-md hover:opacity-90 transition-opacity\"\n aria-label={tr('template.deleteTemplate', 'Delete Template')}\n >\n <Trash2 className=\"w-4 h-4\" aria-hidden=\"true\" />\n {tr('common.delete', 'Delete')}\n </button>\n </div>\n }\n />\n\n {/* Badges */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n {template.category && (\n <span\n className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${categoryStyles[template.category] || 'bg-bg-sunken text-text-secondary'}`}\n >\n <Layers className=\"w-3.5 h-3.5\" />\n {template.category}\n </span>\n )}\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Copy className=\"w-3.5 h-3.5\" />\n {template.cloneCount} {tr('template.clones', 'clones')}\n </span>\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Clock className=\"w-3.5 h-3.5\" />\n {new Date(template.createdAt).toLocaleDateString()}\n </span>\n </div>\n\n {/* Tags */}\n {template.tags?.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {template.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n <Tag className=\"w-3 h-3\" />\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Universal tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('template.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_TEMPLATE\" entityId={template.id} />\n </div>\n </Card>\n\n {/* Source Component */}\n {template.sourceComponent && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.sourceComponent', 'Source Component')}\n </h3>\n </div>\n <div className=\"p-4\">\n <button\n onClick={() => navigate(`/components/${template.sourceComponent.id}`)}\n className=\"flex items-center gap-3 text-left hover:bg-bg-sunken p-2 -m-2 rounded-md transition-colors\"\n >\n <div className=\"w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0\">\n <FileCode className=\"w-5 h-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{template.sourceComponent.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {template.sourceComponent.type} &middot; {template.sourceComponent.lifecycle}\n </p>\n </div>\n </button>\n </div>\n </Card>\n )}\n\n {/* Setup Instructions */}\n {template.setupInstructions && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.setupInstructions', 'Setup Instructions')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"prose prose-sm max-w-none text-text-primary\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto whitespace-pre-wrap font-mono text-text-primary\">\n {template.setupInstructions}\n </pre>\n </div>\n </div>\n </Card>\n )}\n\n {/* File Tree */}\n {template.fileTree && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <FolderTree className=\"w-5 h-5\" />\n {tr('template.fileTree', 'File Tree')}\n </h3>\n </div>\n <div className=\"p-4\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto font-mono text-text-primary\">\n {typeof template.fileTree === 'string'\n ? template.fileTree\n : JSON.stringify(template.fileTree, null, 2)}\n </pre>\n </div>\n </Card>\n )}\n\n {/* Clone Dialog */}\n {showCloneDialog && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n className=\"bg-bg-surface rounded-lg p-6 w-full max-w-md\"\n >\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </h2>\n <div className=\"space-y-4\">\n {cloneError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {cloneError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneName', 'Name *')}\n </label>\n <input\n ref={cloneNameRef}\n type=\"text\"\n placeholder={tr('template.cloneNamePlaceholder', 'my-new-component')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneDescription', 'Description')}\n </label>\n <input\n ref={cloneDescRef}\n type=\"text\"\n placeholder={tr('template.cloneDescPlaceholder', 'Optional description')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCloneDialog(false)}\n disabled={cloneLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleClone}\n disabled={cloneLoading}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Copy className=\"w-4 h-4\" />\n {cloneLoading\n ? tr('template.cloning', 'Cloning...')\n : tr('template.clone', 'Clone')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDeleteTemplate}\n title={tr('template.deleteTemplate', 'Delete Template')}\n message={tr(\n 'template.deleteConfirmMessage',\n `Are you sure you want to delete template \"${template.name}\"? Existing instantiated components are unaffected, but the template will no longer be available for cloning. This cannot be undone.`\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n isLoading={isDeleting}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TemplateDetailPage.js","names":[],"sources":["../../src/pages/TemplateDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { motion } from 'framer-motion';\nimport {\n ArrowLeft,\n FileCode,\n Copy,\n Layers,\n Tag,\n FolderTree,\n Clock,\n RefreshCw,\n Trash2,\n} from 'lucide-react';\nimport { PageHeader, Card, EmptyState, ErrorState, ConfirmDialog } from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetTemplateQuery,\n useInstantiateTemplateMutation,\n useDeleteTemplateMutation,\n} from '@/generated/wspace-operations';\n\nconst categoryStyles: Record<string, string> = {\n STARTER: 'bg-bg-sunken text-text-secondary',\n MICROSERVICE: 'bg-action-primary-bg/10 text-action-primary-bg',\n FRONTEND: 'bg-status-info-bg text-status-info-text',\n BACKEND: 'bg-action-primary-bg/10 text-action-primary-bg',\n FULLSTACK: 'bg-status-success-bg text-status-success-text',\n INFRASTRUCTURE: 'bg-status-warning-bg text-status-warning-text',\n LIBRARY: 'bg-status-warning-bg text-status-warning-text',\n CUSTOM: 'bg-bg-sunken text-text-secondary',\n};\n\nexport function TemplateDetailPage() {\n const { t } = useI18n();\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [showCloneDialog, setShowCloneDialog] = useState(false);\n const [cloneLoading, setCloneLoading] = useState(false);\n const [cloneError, setCloneError] = useState<string | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const cloneNameRef = useRef<HTMLInputElement>(null);\n const cloneDescRef = useRef<HTMLInputElement>(null);\n\n const { data, loading, error, refetch } = useGetTemplateQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [instantiateTemplate] = useInstantiateTemplateMutation();\n const [deleteTemplate] = useDeleteTemplateMutation({ refetchQueries: ['ListTemplates'] });\n\n const template = data?.vibecontrolsTemplate ?? null;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/templates');\n };\n\n const handleClone = async () => {\n const name = cloneNameRef.current?.value?.trim();\n if (!name) {\n setCloneError(tr('template.cloneNameRequired', 'Name is required'));\n return;\n }\n\n setCloneLoading(true);\n setCloneError(null);\n try {\n const result = await instantiateTemplate({\n variables: {\n templateId: id,\n input: {\n name,\n description: cloneDescRef.current?.value?.trim() || undefined,\n },\n },\n });\n setShowCloneDialog(false);\n const newId = result.data?.instantiateVibecontrolsTemplate?.id;\n setSuccessMessage(tr('template.clonedSuccess', 'Cloned successfully'));\n setTimeout(() => {\n setSuccessMessage(null);\n if (newId) navigate(`/components/${newId}`);\n }, 1500);\n } catch (err) {\n setCloneError(\n err instanceof Error ? err.message : tr('template.cloneFailed', 'Failed to clone')\n );\n } finally {\n setCloneLoading(false);\n }\n };\n\n const handleDeleteTemplate = async () => {\n if (!id) return;\n setIsDeleting(true);\n try {\n await deleteTemplate({ variables: { id } });\n setShowDeleteConfirm(false);\n navigate('/templates');\n } finally {\n setIsDeleting(false);\n }\n };\n\n // Guard states\n if (loading && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <ErrorState\n title={tr('template.failedLoad', 'Failed to load template')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!template) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n <EmptyState\n icon={<FileCode className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('template.notFound', 'Template not found')}\n description={`No template with ID \"${id}\" exists`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed top-4 left-4 right-4 sm:left-auto z-50 bg-status-success-bg text-status-success-text px-4 py-3 rounded-md shadow-lg\">\n {successMessage}\n </div>\n )}\n\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('template.backToTemplates', 'Back to Templates')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={template.name}\n description={template.description || tr('template.noDescription', 'No description')}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCloneDialog(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Copy className=\"w-4 h-4\" />\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </button>\n <button\n data-testid=\"template-delete-button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-status-error-bg text-status-error-text rounded-md hover:opacity-90 transition-opacity\"\n aria-label={tr('template.deleteTemplate', 'Delete Template')}\n >\n <Trash2 className=\"w-4 h-4\" aria-hidden=\"true\" />\n {tr('common.delete', 'Delete')}\n </button>\n </div>\n }\n />\n\n {/* Badges */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n {template.category && (\n <span\n className={`inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-medium rounded ${categoryStyles[template.category] || 'bg-bg-sunken text-text-secondary'}`}\n >\n <Layers className=\"w-3.5 h-3.5\" />\n {template.category}\n </span>\n )}\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Copy className=\"w-3.5 h-3.5\" />\n {template.cloneCount} {tr('template.clones', 'clones')}\n </span>\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <Clock className=\"w-3.5 h-3.5\" />\n {new Date(template.createdAt).toLocaleDateString()}\n </span>\n </div>\n\n {/* Tags */}\n {template.tags?.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {template.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n <Tag className=\"w-3 h-3\" />\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Universal tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"w-5 h-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('template.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_TEMPLATE\" entityId={template.id} />\n </div>\n </Card>\n\n {/* Source Component */}\n {template.sourceComponent && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.sourceComponent', 'Source Component')}\n </h3>\n </div>\n <div className=\"p-4\">\n <button\n onClick={() => navigate(`/components/${template.sourceComponent.id}`)}\n className=\"flex items-center gap-3 text-left hover:bg-bg-sunken p-2 -m-2 rounded-md transition-colors\"\n >\n <div className=\"w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center flex-shrink-0\">\n <FileCode className=\"w-5 h-5 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{template.sourceComponent.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {template.sourceComponent.type} &middot; {template.sourceComponent.lifecycle}\n </p>\n </div>\n </button>\n </div>\n </Card>\n )}\n\n {/* Setup Instructions */}\n {template.setupInstructions && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('template.setupInstructions', 'Setup Instructions')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"prose prose-sm max-w-none text-text-primary\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto whitespace-pre-wrap font-mono text-text-primary\">\n {template.setupInstructions}\n </pre>\n </div>\n </div>\n </Card>\n )}\n\n {/* File Tree */}\n {template.fileTree && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <FolderTree className=\"w-5 h-5\" />\n {tr('template.fileTree', 'File Tree')}\n </h3>\n </div>\n <div className=\"p-4\">\n <pre className=\"bg-bg-sunken rounded-md p-4 text-xs sm:text-sm overflow-x-auto font-mono text-text-primary\">\n {typeof template.fileTree === 'string'\n ? template.fileTree\n : JSON.stringify(template.fileTree, null, 2)}\n </pre>\n </div>\n </Card>\n )}\n\n {/* Clone Dialog */}\n {showCloneDialog && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n className=\"bg-bg-surface rounded-lg p-6 w-full max-w-md\"\n >\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('template.cloneFromTemplate', 'Clone from Template')}\n </h2>\n <div className=\"space-y-4\">\n {cloneError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {cloneError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneName', 'Name *')}\n </label>\n <input\n ref={cloneNameRef}\n type=\"text\"\n placeholder={tr('template.cloneNamePlaceholder', 'my-new-component')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('template.cloneDescription', 'Description')}\n </label>\n <input\n ref={cloneDescRef}\n type=\"text\"\n placeholder={tr('template.cloneDescPlaceholder', 'Optional description')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCloneDialog(false)}\n disabled={cloneLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleClone}\n disabled={cloneLoading}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Copy className=\"w-4 h-4\" />\n {cloneLoading\n ? tr('template.cloning', 'Cloning...')\n : tr('template.clone', 'Clone')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDeleteTemplate}\n title={tr('template.deleteTemplate', 'Delete Template')}\n message={tr(\n 'template.deleteConfirmMessage',\n `Are you sure you want to delete template \"${template.name}\"? Existing instantiated components are unaffected, but the template will no longer be available for cloning. This cannot be undone.`\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n isLoading={isDeleting}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}