@burdenoff/microfe-vibecontrols 2026.623.2 → 2026.623.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/components/actions/ActionTypeBadge.js +1 -1
  2. package/dist/components/actions/ActionTypeBadge.js.map +1 -1
  3. package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
  4. package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerChatPane.js +1 -1
  6. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  7. package/dist/components/agent-manager/AgentManagerInputArea.js +1 -1
  8. package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
  9. package/dist/components/agent-manager/BridleConfigPanel.js +2 -2
  10. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  11. package/dist/components/agent-manager/ProfilesPanel.js +1 -1
  12. package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
  13. package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
  14. package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
  15. package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
  16. package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
  17. package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
  18. package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
  19. package/dist/components/agent-manager/detail/SessionLogsPanel.js +2 -2
  20. package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
  21. package/dist/components/ai/AIContextsTab.js +1 -1
  22. package/dist/components/ai/AIContextsTab.js.map +1 -1
  23. package/dist/components/ai/AIOverviewTab.js +1 -1
  24. package/dist/components/ai/AIOverviewTab.js.map +1 -1
  25. package/dist/components/ai/AIPromptsTab.js +1 -1
  26. package/dist/components/ai/AIPromptsTab.js.map +1 -1
  27. package/dist/components/ai/AIQueueTab.js +1 -1
  28. package/dist/components/ai/AIQueueTab.js.map +1 -1
  29. package/dist/components/ai/AISessionsTab.js +1 -1
  30. package/dist/components/ai/AISessionsTab.js.map +1 -1
  31. package/dist/components/audit/AuditLogTable.js +1 -1
  32. package/dist/components/audit/AuditLogTable.js.map +1 -1
  33. package/dist/components/docs/DocsPageTree.js +1 -1
  34. package/dist/components/docs/DocsPageTree.js.map +1 -1
  35. package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
  36. package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
  37. package/dist/components/graph/MeshDeployWizard.js +3 -3
  38. package/dist/components/graph/MeshDeployWizard.js.map +1 -1
  39. package/dist/components/graph/MeshRequestPanel.js +2 -2
  40. package/dist/components/graph/MeshRequestPanel.js.map +1 -1
  41. package/dist/components/notes/NoteCard.js +1 -1
  42. package/dist/components/notes/NoteCard.js.map +1 -1
  43. package/dist/components/notes/NoteEditor.js +1 -1
  44. package/dist/components/notes/NoteEditor.js.map +1 -1
  45. package/dist/components/notes/NoteList.js +1 -1
  46. package/dist/components/notes/NoteList.js.map +1 -1
  47. package/dist/components/shared/AgentSetupBanner.js +3 -3
  48. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  49. package/dist/components/templates/TemplateCard.js +1 -1
  50. package/dist/components/templates/TemplateCard.js.map +1 -1
  51. package/dist/components/vibecalendar/types.js +1 -1
  52. package/dist/components/vibecalendar/types.js.map +1 -1
  53. package/dist/components/vibedeck/VibeDeckButtonForm.js +1 -1
  54. package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
  55. package/dist/components/vibedeck/VibeDeckExecutionLog.js +2 -2
  56. package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
  57. package/dist/components/vibedeck/VibeDeckForm.js +1 -1
  58. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  59. package/dist/components/vibedeck/VibeDeckGrid.js +1 -1
  60. package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
  61. package/dist/pages/AgentsPage.js +2 -2
  62. package/dist/pages/AgentsPage.js.map +1 -1
  63. package/dist/pages/CatalogDetailPage.js +1 -1
  64. package/dist/pages/CatalogDetailPage.js.map +1 -1
  65. package/dist/pages/ComponentDetailPage.js +1 -1
  66. package/dist/pages/ComponentDetailPage.js.map +1 -1
  67. package/dist/pages/OverviewPage.js +10 -10
  68. package/dist/pages/OverviewPage.js.map +1 -1
  69. package/dist/pages/PluginsCatalogPage.js +1 -1
  70. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  71. package/dist/pages/SessionsPage.js +3 -3
  72. package/dist/pages/SessionsPage.js.map +1 -1
  73. package/dist/pages/SharedSessionPage.js +2 -2
  74. package/dist/pages/SharedSessionPage.js.map +1 -1
  75. package/dist/pages/TargetsPage.js +2 -2
  76. package/dist/pages/TargetsPage.js.map +1 -1
  77. package/dist/pages/TemplateDetailPage.js +2 -2
  78. package/dist/pages/TemplateDetailPage.js.map +1 -1
  79. package/dist/pages/VibesPage.js +2 -2
  80. package/dist/pages/VibesPage.js.map +1 -1
  81. package/dist/pages/WebhooksPage.js +2 -2
  82. package/dist/pages/WebhooksPage.js.map +1 -1
  83. package/dist/pages/ai/AISessionDetailPage.js +2 -2
  84. package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
  85. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"TargetsPage.js","names":[],"sources":["../../src/pages/TargetsPage.tsx"],"sourcesContent":["/**\n * Targets Page — Manage all targets (servers, workstations, environments).\n *\n * Targets are the first-class entity. Each target can have agents installed,\n * and supports two access types: Direct and SSH.\n */\n\nimport { useState, useMemo, useCallback, useEffect, useId } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport {\n Plus,\n RefreshCw,\n Trash2,\n Server,\n Grid,\n List,\n CheckCircle,\n XCircle,\n Loader2,\n Check,\n Pencil,\n CheckSquare,\n XSquare,\n} from 'lucide-react';\nimport {\n Button,\n Checkbox,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n AgentSetupBanner,\n SearchableSelect,\n GetHelpButton,\n ActionMenu,\n} from '@/components/shared';\nimport type { ActionMenuItem } from '@/components/shared';\nimport { TargetCard } from '@/components/targets';\nimport type { TargetCardTarget } from '@/components/targets';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useSearchParams } from 'react-router-dom';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { useField } from '@/shared/hooks/useField';\nimport { notify } from '@/lib/toast';\nimport {\n useListTargetsQuery,\n useCreateTargetMutation,\n useUpdateTargetMutation,\n useTestTargetMutation,\n useStartTerminalMutation,\n useDeleteTargetMutation,\n useInstallAgentOnTargetMutation,\n useUninstallAgentFromTargetMutation,\n useGetInstallAgentJobStatusLazyQuery,\n} from '@/generated/wspace-operations';\nimport {\n getQuotaDialogContent,\n markQuotaHandledLocally,\n isQuotaExhaustedError,\n type QuotaKind,\n} from '@/utils/quotaUtils';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype TargetStatus = 'connected' | 'disconnected' | 'unknown';\n\ntype TargetKind = 'DIRECT' | 'SSH';\n\ninterface SshTarget {\n id: string;\n type: TargetKind;\n name: string;\n label: string | null;\n host: string | null;\n port: number | null;\n username: string | null;\n status: TargetStatus;\n connectionId: string | null;\n tags: string[];\n metadata: Record<string, unknown> | null;\n createdAt: string;\n agents: Array<{ id: string; name: string; version: string; isActive: boolean }>;\n}\n\ninterface AgentOption {\n id: string;\n name: string;\n isActive: boolean;\n tunnelUrl: string | null;\n agentApiKey: string | null;\n}\n\ninterface InstallJob {\n id: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n steps: Array<{ name: string; status: string; message?: string }>;\n currentStep: number;\n result?: { agentUrl?: string; backendAgentId?: string };\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction mapStatus(s: string): TargetStatus {\n if (s === 'connected') return 'connected';\n if (s === 'disconnected') return 'disconnected';\n return 'unknown';\n}\n\nconst stepLabels: Record<string, string> = {\n connect: 'Connecting to server',\n detect_os: 'Detecting OS',\n install_bun: 'Installing Bun runtime',\n install_agent: 'Installing VibeControls Agent',\n configure: 'Configuring agent',\n start: 'Starting agent',\n verify: 'Verifying health',\n retrieve: 'Retrieving agent details',\n register: 'Registering with backend',\n};\n\ntype ViewMode = 'grid' | 'list';\n\nconst statusConfig: Record<TargetStatus, { label: string }> = {\n connected: { label: 'Connected' },\n disconnected: { label: 'Disconnected' },\n unknown: { label: 'Unknown' },\n};\n\nconst typeBadgeClass: Record<TargetKind, string> = {\n DIRECT: 'bg-bg-sunken text-text-secondary border border-border-default',\n SSH: 'bg-bg-sunken text-text-secondary border border-border-default',\n};\n\nconst targetStatusBadgeClass: Record<TargetStatus, string> = {\n connected: 'bg-status-success-bg-subtle text-status-success-text',\n disconnected: 'bg-status-error-bg-subtle text-status-error-text',\n unknown: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction isAgentInstalled(target: SshTarget): boolean {\n return target.agents.length > 0;\n}\n\n// ---------------------------------------------------------------------------\n// Form field helpers (accessible label/input/hint wiring via useField)\n// ---------------------------------------------------------------------------\n\ninterface TextFieldProps {\n label: string;\n value: string | number;\n onChange: (value: string) => void;\n type?: 'text' | 'password' | 'number';\n placeholder?: string;\n hint?: string;\n requiredField?: boolean;\n autoComplete?: string;\n}\n\nfunction TextField({\n label,\n value,\n onChange,\n type = 'text',\n placeholder,\n hint,\n requiredField = false,\n autoComplete,\n}: TextFieldProps) {\n const field = useField({ hint: hint ?? null });\n return (\n <div>\n <label htmlFor={field.id} className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {label}\n </label>\n <Input\n {...field.inputProps}\n type={type}\n value={value}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}\n placeholder={placeholder}\n aria-required={requiredField || undefined}\n autoComplete={autoComplete}\n className=\"text-base md:text-sm\"\n />\n {hint ? (\n <p id={field.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {hint}\n </p>\n ) : null}\n </div>\n );\n}\n\ninterface AuthMethodPickerProps {\n legend: string;\n name: string;\n value: 'password' | 'key';\n onChange: (value: 'password' | 'key') => void;\n passwordLabel: string;\n keyLabel: string;\n hint?: string;\n}\n\nfunction AuthMethodPicker({\n legend,\n name,\n value,\n onChange,\n passwordLabel,\n keyLabel,\n hint,\n}: AuthMethodPickerProps) {\n return (\n <fieldset>\n <legend className=\"text-sm font-medium text-text-primary\">{legend}</legend>\n <div className=\"mt-1.5 flex flex-wrap gap-x-4 gap-y-2\">\n <label className=\"flex min-h-9 cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"radio\"\n name={name}\n checked={value === 'password'}\n onChange={() => onChange('password')}\n />\n {passwordLabel}\n </label>\n <label className=\"flex min-h-9 cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"radio\"\n name={name}\n checked={value === 'key'}\n onChange={() => onChange('key')}\n />\n {keyLabel}\n </label>\n </div>\n {hint ? <p className=\"mt-1 text-xs text-text-muted\">{hint}</p> : null}\n </fieldset>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Targets Page\n// ---------------------------------------------------------------------------\n\nexport function TargetsPage() {\n const tr = useTr();\n const { workspaceId } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const [searchParams] = useSearchParams();\n const selectedTargetId = searchParams.get('selectedTargetId');\n const baseId = useId();\n\n // UI state\n const [searchQuery, setSearchQuery] = useState('');\n const [agentFilter, setAgentFilter] = useState<string | null>(null);\n const [typeFilter, setTypeFilter] = useState('all');\n const [statusFilter, setStatusFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showAddForm, setShowAddForm] = useState(false);\n const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);\n const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false);\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n // Per-kind so the dialog labels switch between \"Target Quota Reached\"\n // (create flows) and \"Session/Environment Quota Reached\" (Open Terminal),\n // both of which set this state on the same page.\n const [quotaExhaustedKind, setQuotaExhaustedKind] = useState<QuotaKind | null>(null);\n\n // Install progress state\n const [installJobTarget, setInstallJobTarget] = useState<SshTarget | null>(null);\n const [installJob, setInstallJob] = useState<InstallJob | null>(null);\n const [installJobId, setInstallJobId] = useState<string | null>(null);\n\n // Edit target modal state\n const [editTarget, setEditTarget] = useState<SshTarget | null>(null);\n const [editLoading, setEditLoading] = useState(false);\n const [editError, setEditError] = useState<string | null>(null);\n const [editName, setEditName] = useState('');\n const [editLabel, setEditLabel] = useState('');\n const [editHost, setEditHost] = useState('');\n const [editPort, setEditPort] = useState(22);\n const [editUsername, setEditUsername] = useState('');\n const [editAuthMethod, setEditAuthMethod] = useState<'password' | 'key'>('key');\n const [editPassword, setEditPassword] = useState('');\n const [editKeyPath, setEditKeyPath] = useState('');\n const [editTags, setEditTags] = useState('');\n\n // Form state\n const [formType, setFormType] = useState<'DIRECT' | 'SSH'>('DIRECT');\n const [formName, setFormName] = useState('');\n const [formDescription, setFormDescription] = useState('');\n const [formAgent, setFormAgent] = useState('');\n const [formHost, setFormHost] = useState('');\n const [formPort, setFormPort] = useState(22);\n const [formUsername, setFormUsername] = useState('');\n const [formAuthMethod, setFormAuthMethod] = useState<'password' | 'key'>('key');\n const [formPassword, setFormPassword] = useState('');\n const [formKeyPath, setFormKeyPath] = useState('');\n const [formLabel, setFormLabel] = useState('');\n const [formTags, setFormTags] = useState('');\n\n // Queries - use cache-and-network for silent background refresh\n const {\n data: targetsData,\n loading,\n refetch: refetchTargets,\n } = useListTargetsQuery({\n variables: { workspaceId: workspaceId || '', limit: 50, offset: 0 },\n skip: !workspaceId,\n fetchPolicy: 'cache-first',\n });\n\n // Use cached initial data for related entities already prefetched on initial load.\n const { agents: cachedAgents, refetch: refetchInitialData } = useCachedInitialData();\n\n // Mutations\n const [createTarget] = useCreateTargetMutation({\n refetchQueries: ['VibeControlsInitialData', 'ListTargets'],\n awaitRefetchQueries: true,\n });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_TARGET');\n const [updateTarget] = useUpdateTargetMutation();\n const [testTarget] = useTestTargetMutation();\n const [startTerminal] = useStartTerminalMutation();\n const [deleteTarget] = useDeleteTargetMutation();\n const [installAgentOnTarget] = useInstallAgentOnTargetMutation();\n const [uninstallAgent] = useUninstallAgentFromTargetMutation();\n const [fetchInstallJobStatus] = useGetInstallAgentJobStatusLazyQuery({\n fetchPolicy: 'network-only',\n });\n\n // Derived data\n const targets: SshTarget[] = useMemo(() => {\n const raw = targetsData?.targets?.items ?? [];\n return raw.map((t: (typeof raw)[number]) => ({\n id: t.id,\n type: (t.type || 'DIRECT') as TargetKind,\n name: t.name,\n label: t.label || null,\n host: t.host || null,\n port: t.port || null,\n username: t.username || null,\n status: mapStatus(t.status),\n connectionId: t.connectionId || null,\n tags: t.tags,\n metadata: (t.metadata as Record<string, unknown> | null) ?? null,\n createdAt: t.createdAt,\n agents: (t.agents || []).map((a: (typeof t.agents)[number]) => ({\n id: a.id,\n name: a.name,\n version: a.version,\n isActive: a.isActive,\n })),\n }));\n }, [targetsData]);\n\n const agents: AgentOption[] = useMemo(() => {\n return cachedAgents.map((a) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n tunnelUrl: a.tunnelUrl ?? null,\n agentApiKey: null,\n }));\n }, [cachedAgents]);\n\n const filtered = useMemo(() => {\n let items = targets;\n if (searchQuery) {\n const q = searchQuery.toLowerCase();\n items = items.filter(\n (t: SshTarget) =>\n t.name.toLowerCase().includes(q) ||\n t.host?.toLowerCase().includes(q) ||\n t.label?.toLowerCase().includes(q)\n );\n }\n if (agentFilter) {\n items = items.filter((t: SshTarget) => t.agents.some((a) => a.id === agentFilter));\n }\n if (typeFilter !== 'all') {\n items = items.filter((t: SshTarget) => t.type === typeFilter);\n }\n if (statusFilter !== 'all') {\n items = items.filter((t: SshTarget) => t.status === statusFilter);\n }\n return items;\n }, [targets, searchQuery, agentFilter, typeFilter, statusFilter]);\n\n useEffect(() => {\n if (!selectedTargetId) return;\n\n const target = filtered.find((item) => item.id === selectedTargetId);\n if (!target) return;\n\n const timeout = window.setTimeout(() => {\n document\n .getElementById(`target-row-${selectedTargetId}`)\n ?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }, 150);\n\n return () => window.clearTimeout(timeout);\n }, [filtered, selectedTargetId]);\n\n const connectedCount = targets.filter((t: SshTarget) => t.status === 'connected').length;\n const allFilteredSelected =\n filtered.length > 0 && filtered.every((t: SshTarget) => selectedIds.has(t.id));\n const hasSelection = selectedIds.size > 0;\n\n // Install job polling\n useEffect(() => {\n if (!installJobId || !installJobTarget) return;\n\n let cancelled = false;\n\n const pollJob = () => {\n void fetchInstallJobStatus({\n variables: { targetId: installJobTarget.id, jobId: installJobId },\n })\n .then((result) => {\n if (cancelled) return;\n const data = result.data?.installAgentJobStatus;\n if (!data) return;\n if (!data.steps) data.steps = [];\n setInstallJob({\n id: data.jobId,\n status: data.status,\n currentStep: data.currentStep ?? 0,\n steps: data.steps ?? [],\n result: data.result ?? undefined,\n error: data.error ?? undefined,\n });\n if (data.status === 'completed' || data.status === 'failed') {\n if (data.status === 'completed') {\n void refetchTargets();\n refetchInitialData();\n }\n }\n })\n .catch(() => {\n // Polling error — will retry on next interval\n });\n };\n\n pollJob();\n const interval = setInterval(pollJob, 2000);\n\n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n }, [installJobId, installJobTarget, fetchInstallJobStatus, refetchInitialData, refetchTargets]);\n\n // Handlers\n const handleCreate = useCallback(async () => {\n if (!formName || !workspaceId) return;\n if (formType !== 'DIRECT' && (!formAgent || !formHost || !formUsername)) return;\n try {\n const res = await createTarget({\n variables: {\n workspaceId,\n input: {\n type: formType as string,\n name: formName,\n sourceAgentId: formType !== 'DIRECT' ? formAgent : undefined,\n host: formType !== 'DIRECT' ? formHost : undefined,\n port: formType !== 'DIRECT' ? formPort : undefined,\n username: formType !== 'DIRECT' ? formUsername : undefined,\n password:\n formType === 'SSH' && formAuthMethod === 'password' ? formPassword : undefined,\n privateKeyPath:\n formType === 'SSH' && formAuthMethod === 'key' ? formKeyPath : undefined,\n label: formLabel || undefined,\n tags: formTags ? formTags.split(',').map((t: string) => t.trim()) : undefined,\n metadata: formDescription ? { description: formDescription } : undefined,\n },\n },\n });\n\n // Apollo's `errorPolicy: 'all'` makes the mutation promise resolve\n // even when the server returns GraphQL errors, so we have to check\n // `res.error` here — the `catch` block below would never fire for\n // server-side validation errors like ALREADY_EXISTS (BOFF-2626).\n if (res.error) {\n if (isQuotaExhaustedError(res.error)) {\n setShowAddForm(false);\n markQuotaHandledLocally();\n setQuotaExhaustedKind('target');\n return;\n }\n notify.error(\n tr('targets.createFailed', 'Failed to create target'),\n res.error.message || undefined\n );\n return;\n }\n\n const newTargetId = res.data?.createTarget?.id;\n setShowAddForm(false);\n setFormName('');\n setFormHost('');\n setFormUsername('');\n setFormPassword('');\n setFormKeyPath('');\n setFormLabel('');\n setFormTags('');\n if (newTargetId) await flushTags(newTargetId);\n void refetchTargets();\n // Refresh shared initial-data cache so AgentsPage (and other pages\n // reading targets via useCachedInitialData) see the new row without\n // a full page reload.\n refetchInitialData();\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowAddForm(false);\n markQuotaHandledLocally();\n setQuotaExhaustedKind('target');\n return;\n }\n // Unexpected throw (network error, etc.) — surface via toast rather\n // than letting it bubble silently into the React error boundary.\n notify.error(\n tr('targets.createFailed', 'Failed to create target'),\n err instanceof Error ? err.message : undefined\n );\n }\n }, [\n formName,\n formAgent,\n formHost,\n formPort,\n formUsername,\n formAuthMethod,\n formPassword,\n formKeyPath,\n formLabel,\n formTags,\n formDescription,\n formType,\n workspaceId,\n createTarget,\n flushTags,\n refetchInitialData,\n refetchTargets,\n tr,\n ]);\n\n const handleTest = useCallback(\n async (id: string) => {\n const result = await testTarget({ variables: { id } });\n const data = result.data?.testTarget;\n if (data) {\n if (data.success) {\n notify.success(\n tr('targets.connectionSuccessful', 'Connection successful'),\n data.message || undefined\n );\n } else {\n notify.error(\n tr('targets.connectionFailed', 'Connection failed'),\n data.message || undefined\n );\n }\n void refetchTargets();\n refetchInitialData();\n }\n },\n [testTarget, refetchInitialData, refetchTargets, tr]\n );\n\n // Per-target loading state for the Open Terminal action — startTerminal\n // shells into the remote box and spawns ttyd, which routinely takes 3-8s.\n // Without a hint the user thinks the click did nothing and clicks again.\n const [startingTerminalIds, setStartingTerminalIds] = useState<Set<string>>(new Set());\n\n const handleStartTerminal = useCallback(\n async (id: string) => {\n setStartingTerminalIds((prev) => {\n const next = new Set(prev);\n next.add(id);\n return next;\n });\n try {\n const result = await startTerminal({ variables: { targetId: id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setQuotaExhaustedKind('environment');\n return;\n }\n\n const data = result.data?.startTerminal;\n if (data?.sessionId) {\n navigate(`/sessions/${data.sessionId}`);\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setQuotaExhaustedKind('environment');\n return;\n }\n\n throw err;\n } finally {\n setStartingTerminalIds((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n },\n [startTerminal, navigate]\n );\n\n const handleDeleteSingle = useCallback(async () => {\n if (!deleteTargetId) return;\n try {\n await deleteTarget({ variables: { id: deleteTargetId } });\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n setDeleteTargetId(null);\n setSelectedIds((prev) => {\n const next = new Set(prev);\n next.delete(deleteTargetId);\n return next;\n });\n void refetchTargets();\n refetchInitialData();\n }, [deleteTargetId, deleteTarget, refetchInitialData, refetchTargets]);\n\n const handleBulkDelete = useCallback(async () => {\n const ids = Array.from(selectedIds);\n await Promise.all(ids.map((id: string) => deleteTarget({ variables: { id } })));\n setSelectedIds(new Set());\n setBulkDeleteOpen(false);\n void refetchTargets();\n refetchInitialData();\n }, [selectedIds, deleteTarget, refetchInitialData, refetchTargets]);\n\n const handleInstallAgent = useCallback(\n async (target: SshTarget) => {\n setInstallJobTarget(target);\n setInstallJob(null);\n setInstallJobId(null);\n\n const sourceAgent = agents.find((a: AgentOption) => a.isActive);\n if (!sourceAgent) return;\n\n const result = await installAgentOnTarget({\n variables: { targetId: target.id, sourceAgentId: sourceAgent.id },\n });\n const data = result.data?.installAgentOnTarget;\n if (data?.jobId) {\n setInstallJobId(data.jobId);\n }\n },\n [installAgentOnTarget, agents]\n );\n\n const handleUninstallAgent = useCallback(\n async (target: SshTarget) => {\n await uninstallAgent({ variables: { targetId: target.id } });\n void refetchTargets();\n refetchInitialData();\n },\n [uninstallAgent, refetchInitialData, refetchTargets]\n );\n\n const handleOpenEdit = useCallback((target: SshTarget) => {\n setEditTarget(target);\n setEditName(target.name);\n setEditLabel(target.label ?? '');\n setEditHost(target.host ?? '');\n setEditPort(target.port ?? 22);\n setEditUsername(target.username ?? '');\n setEditAuthMethod('key');\n setEditPassword('');\n setEditKeyPath('');\n setEditTags(target.tags.join(', '));\n setEditError(null);\n }, []);\n\n const handleSaveEdit = useCallback(async () => {\n if (!editTarget) return;\n if (!editName.trim()) {\n setEditError('Name is required');\n return;\n }\n setEditLoading(true);\n setEditError(null);\n try {\n const res = await updateTarget({\n variables: {\n id: editTarget.id,\n input: {\n name: editName.trim(),\n label: editLabel.trim() || null,\n host: editHost.trim() || undefined,\n port: editPort || undefined,\n username: editUsername.trim() || undefined,\n password:\n editTarget.type === 'SSH' && editAuthMethod === 'password' && editPassword\n ? editPassword\n : undefined,\n privateKeyPath:\n editTarget.type === 'SSH' && editAuthMethod === 'key' && editKeyPath\n ? editKeyPath\n : undefined,\n tags: editTags\n ? editTags\n .split(',')\n .map((t: string) => t.trim())\n .filter(Boolean)\n : [],\n },\n },\n });\n // Same `errorPolicy: 'all'` pitfall as handleCreate — surface server\n // errors (e.g. ALREADY_EXISTS on rename collision) inline in the\n // modal rather than letting them vanish silently. BOFF-2626.\n //\n // Quota intentionally not checked here: updateTarget is a metadata\n // edit on an already-counted row, so the svc never raises\n // QUOTA_EXHAUSTED on this path. If that ever changes, mirror the\n // pattern from handleCreate (open the quota modal, return).\n if (res.error) {\n setEditError(res.error.message || 'Failed to update target');\n return;\n }\n setEditTarget(null);\n void refetchTargets();\n refetchInitialData();\n } catch (err) {\n setEditError(err instanceof Error ? err.message : 'Failed to update target');\n } finally {\n setEditLoading(false);\n }\n }, [\n editTarget,\n editName,\n editLabel,\n editHost,\n editPort,\n editUsername,\n editAuthMethod,\n editPassword,\n editKeyPath,\n editTags,\n updateTarget,\n refetchInitialData,\n refetchTargets,\n ]);\n\n const closeInstallDialog = useCallback(() => {\n setInstallJobTarget(null);\n setInstallJob(null);\n setInstallJobId(null);\n }, []);\n\n const toggleSelectAll = useCallback(() => {\n if (allFilteredSelected) {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n filtered.forEach((t: SshTarget) => next.delete(t.id));\n return next;\n });\n } else {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n filtered.forEach((t: SshTarget) => next.add(t.id));\n return next;\n });\n }\n }, [allFilteredSelected, filtered]);\n\n const toggleSelect = useCallback((id: string) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n // Stats\n const stats = {\n total: targets.length,\n connected: connectedCount,\n };\n\n const localizedTypeOptions = useMemo(\n () => [\n { value: 'all', label: tr('targets.filterTypeAll', 'All Types') },\n { value: 'DIRECT', label: tr('targets.typeDirect', 'Direct') },\n { value: 'SSH', label: 'SSH' },\n ],\n [tr]\n );\n\n const localizedStatusOptions = useMemo(\n () => [\n { value: 'all', label: tr('targets.filterStatusAll', 'All Statuses') },\n { value: 'connected', label: tr('targets.connected', 'Connected') },\n { value: 'disconnected', label: tr('targets.disconnected', 'Disconnected') },\n { value: 'unknown', label: tr('targets.unknown', 'Unknown') },\n ],\n [tr]\n );\n\n const isFiltered = searchQuery || agentFilter || typeFilter !== 'all' || statusFilter !== 'all';\n\n // Add-form source-agent select (accessible wiring shared with the hint).\n const sourceAgentHint = `${tr('targets.agentConnectionHintPrefix', 'The agent that will establish the')} ${formType} ${tr('targets.agentConnectionHintSuffix', 'connection to this target.')}`;\n const sourceAgentField = useField({ hint: sourceAgentHint });\n const addTypeLabelId = `${baseId}-add-type-label`;\n\n if (permissionsLoading)\n return <LoadingState message={tr('targets.loadingPermissions', 'Loading permissions…')} />;\n if (!hasPermission(VIBE_PERMISSIONS.TARGET_LIST))\n return (\n <ErrorState\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n message={tr('targets.accessDeniedBody', \"You don't have permission to access this page.\")}\n />\n );\n\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-full\">\n {/* Test Connection results and create-target errors surface via the\n module-wide `notify` toasts. Update-target errors surface inline in\n the edit dialog's `editError` banner instead. BOFF-2626. */}\n\n <PageHeader\n title={tr('nav.targets', 'Targets')}\n description={tr('targets.onlineSummary', '{{connected}} of {{total}} targets online', {\n connected: stats.connected,\n total: stats.total,\n })}\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => void refetchTargets()}\n disabled={loading}\n aria-label={tr('targets.refreshTitle', 'Refresh targets')}\n title={tr('targets.refreshTitle', 'Refresh targets')}\n className=\"max-md:min-h-11 max-md:min-w-11\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} aria-hidden=\"true\" />\n </Button>\n <Button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"max-md:min-h-11 active:scale-[0.98]\"\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('targets.addTarget', 'Add Target')}\n </Button>\n </div>\n }\n />\n\n {/* Agent setup banner */}\n {agents.length === 0 && !loading && (\n <AgentSetupBanner variant=\"full\" dismissKey=\"vibecontrols-targets-setup-banner\" />\n )}\n\n {/* Filters Bar */}\n <div className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('targets.searchPlaceholder', 'Search targets...')}\n className=\"flex-1 min-w-0 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div\n role=\"group\"\n aria-label={tr('targets.viewMode', 'View mode')}\n className=\"flex items-center gap-1 bg-bg-sunken rounded-button p-1 flex-shrink-0 border border-border-subtle\"\n >\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n aria-pressed={viewMode === 'grid'}\n aria-label={tr('targets.gridViewTitle', 'Grid view')}\n title={tr('targets.gridViewTitle', 'Grid view')}\n className={`flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${viewMode === 'grid' ? 'bg-[var(--color-accent-soft)] text-action-primary-bg' : 'text-text-secondary hover:bg-bg-surface/60'}`}\n >\n <Grid className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n aria-pressed={viewMode === 'list'}\n aria-label={tr('targets.listViewTitle', 'List view')}\n title={tr('targets.listViewTitle', 'List view')}\n className={`flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${viewMode === 'list' ? 'bg-[var(--color-accent-soft)] text-action-primary-bg' : 'text-text-secondary hover:bg-bg-surface/60'}`}\n >\n <List className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('sessions.filterType', 'Type')}\n value={typeFilter}\n options={localizedTypeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterStatus', 'Status')}\n value={statusFilter}\n options={localizedStatusOptions}\n onChange={setStatusFilter}\n />\n <SearchableSelect\n value={agentFilter ?? ''}\n onChange={(v: string) => setAgentFilter(v || null)}\n options={agents.map((a: AgentOption) => ({ value: a.id, label: a.name }))}\n placeholder={tr('targets.allAgents', 'All agents')}\n className=\"w-full sm:min-w-[180px] sm:w-auto\"\n dropdownAlign=\"left\"\n clearable\n />\n </div>\n </div>\n\n {/* Select All & Bulk Actions */}\n {filtered.length > 0 && (\n <div className=\"flex items-center gap-2 flex-wrap\">\n <div className=\"ml-auto flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={toggleSelectAll}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors rounded-button focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <CheckSquare className=\"size-3.5\" aria-hidden=\"true\" />\n {allFilteredSelected\n ? tr('targets.deselectAll', 'Deselect All')\n : tr('targets.selectAll', 'Select All')}\n </button>\n </div>\n </div>\n )}\n\n {/* Bulk Action Bar */}\n {hasSelection && (\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-3 px-4 py-2 border border-action-primary-bg bg-action-primary-bg/5 rounded-card\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedIds.size} {tr('nav.targets', 'target')}\n {selectedIds.size !== 1 ? 's' : ''} {tr('sessions.selected', 'selected')}\n </span>\n <div className=\"flex items-center gap-1.5 sm:ml-auto\">\n <button\n type=\"button\"\n onClick={() => setBulkDeleteOpen(true)}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-button hover:bg-status-error-bg-subtle/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Trash2 className=\"size-3\" aria-hidden=\"true\" /> {tr('common.delete', 'Delete')}\n </button>\n <button\n type=\"button\"\n onClick={() => setSelectedIds(new Set())}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors rounded-button focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <XSquare className=\"size-3\" aria-hidden=\"true\" />{' '}\n {tr('sessions.deselect', 'Deselect')}\n </button>\n </div>\n </div>\n )}\n\n {/* Results Count */}\n {isFiltered ? (\n <p className=\"text-sm text-text-secondary\" role=\"status\">\n {tr('targets.showingResults', 'Showing {{filtered}} of {{total}} targets')\n .replace('{{filtered}}', String(filtered.length))\n .replace('{{total}}', String(targets.length))}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n ({tr('sessions.refreshing', 'refreshing...')})\n </span>\n )}\n </p>\n ) : null}\n\n {/* Targets Display */}\n {loading && targets.length === 0 ? (\n <PageSkeleton variant=\"rows\" count={6} />\n ) : filtered.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n {viewMode === 'grid' ? (\n <MotionList\n key=\"grid-view\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filtered.map((target: SshTarget) => (\n <MotionItem key={target.id} className=\"h-full\">\n <TargetCard\n target={target as TargetCardTarget}\n onTest={(id) => void handleTest(id)}\n onTerminal={(id) => void handleStartTerminal(id)}\n onOpenDetail={(id) => navigate(`/targets/${id}`)}\n onEdit={(t) => handleOpenEdit(t as SshTarget)}\n onInstallAgent={(t) => void handleInstallAgent(t as SshTarget)}\n onUninstallAgent={(t) => void handleUninstallAgent(t as SshTarget)}\n onDelete={(id) => setDeleteTargetId(id)}\n selected={selectedIds.has(target.id)}\n onToggleSelect={() => toggleSelect(target.id)}\n highlighted={selectedTargetId === target.id}\n isStartingTerminal={startingTerminalIds.has(target.id)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionList key=\"list-view\" stagger={0.04} className=\"space-y-2\">\n {filtered.map((target: SshTarget) => {\n const installed = isAgentInstalled(target);\n const isSelectedTarget = selectedTargetId === target.id;\n const overflowItems: ActionMenuItem[] = [\n {\n key: 'edit',\n label: tr('common.edit', 'Edit'),\n icon: Pencil,\n onSelect: () => handleOpenEdit(target),\n },\n ...(target.type !== 'DIRECT' && installed\n ? [\n {\n key: 'uninstall',\n label: tr('targets.uninstall', 'Uninstall'),\n icon: XCircle,\n variant: 'danger' as const,\n onSelect: () => void handleUninstallAgent(target),\n },\n ]\n : []),\n {\n key: 'delete',\n label: tr('common.delete', 'Delete'),\n icon: Trash2,\n variant: 'danger' as const,\n separatorBefore: true,\n onSelect: () => setDeleteTargetId(target.id),\n },\n ];\n return (\n <MotionItem key={target.id} variant=\"fadeRight\">\n <div\n id={`target-row-${target.id}`}\n className={`p-4 rounded-card border transition-colors ${\n isSelectedTarget\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default hover:bg-bg-sunken/30'\n }`}\n >\n {/* Row 1: Info */}\n <div className=\"flex items-start gap-3\">\n <Checkbox\n checked={selectedIds.has(target.id)}\n onCheckedChange={() => toggleSelect(target.id)}\n aria-label={tr('targets.selectTarget', 'Select {{name}}', {\n name: target.name,\n })}\n className=\"mt-1\"\n />\n <div className=\"flex min-w-0 flex-1 flex-wrap items-start gap-2\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-semibold rounded ${typeBadgeClass[target.type]}`}\n >\n {target.type}\n </span>\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-semibold rounded ${targetStatusBadgeClass[target.status]}`}\n >\n {tr(\n `targets.status.${target.status}`,\n statusConfig[target.status].label\n )}\n </span>\n <div className=\"min-w-0 basis-full\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-text-primary\">{target.name}</span>\n {target.label && (\n <span className=\"text-text-secondary text-sm\">{target.label}</span>\n )}\n {installed && (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-status-success-border px-2 py-0.5 text-xs bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"size-2.5\" aria-hidden=\"true\" />\n {tr('targets.agentInstalled', 'Agent Installed')}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-sm text-text-secondary sm:gap-3\">\n {target.host && (\n <code className=\"text-xs break-words\">\n {target.username ? `${target.username}@` : ''}\n {target.host}\n {target.port ? `:${target.port}` : ''}\n </code>\n )}\n {target.agents.length > 0 && (\n <span className=\"text-text-muted\">\n {tr('targets.via', 'via')} {target.agents[0].name}\n </span>\n )}\n </div>\n {target.tags.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap gap-1\">\n {target.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n {/* Row 2: Actions */}\n <div className=\"mt-3 flex flex-wrap items-center gap-2 pl-0 sm:pl-7\">\n <button\n type=\"button\"\n onClick={() => void handleTest(target.id)}\n title={tr('targets.testConnectivity', 'Test connectivity')}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-button border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {tr('targets.test', 'Test')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleStartTerminal(target.id)}\n disabled={startingTerminalIds.has(target.id)}\n aria-busy={startingTerminalIds.has(target.id)}\n title={tr('targets.openTerminal', 'Open terminal')}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-button border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors disabled:opacity-60 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {startingTerminalIds.has(target.id) ? (\n <>\n <Loader2 className=\"size-3 animate-spin\" aria-hidden=\"true\" />\n {tr('targets.starting', 'Starting…')}\n </>\n ) : (\n tr('targets.terminal', 'Terminal')\n )}\n </button>\n {target.type !== 'DIRECT' && !installed && (\n <button\n type=\"button\"\n onClick={() => void handleInstallAgent(target)}\n title={tr('targets.installAgent', 'Install agent')}\n className=\"flex min-h-9 items-center gap-1.5 rounded-button px-3 py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {tr('targets.installAgent', 'Install Agent')}\n </button>\n )}\n <div className=\"hidden flex-1 sm:block\" />\n <ActionMenu\n ariaLabel={tr('targets.targetActions', 'Target actions')}\n title={tr('targets.targetActions', 'Target actions')}\n align=\"end\"\n items={overflowItems}\n />\n </div>\n </div>\n </MotionItem>\n );\n })}\n </MotionList>\n )}\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Server className=\"size-8 text-text-secondary\" aria-hidden=\"true\" />}\n title={\n isFiltered\n ? tr('targets.emptyNoTargetsFound', 'No targets found')\n : tr('targets.emptyNoTargetsConfigured', 'No targets configured')\n }\n description={\n isFiltered\n ? tr('targets.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'targets.emptyDescription',\n 'Add a target to start managing servers, workstations, and environments.'\n )\n }\n action={\n !isFiltered ? (\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <Button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"max-md:min-h-11 active:scale-[0.98]\"\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('targets.addTarget', 'Add Target')}\n </Button>\n <GetHelpButton prompt=\"What is a Target in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Active sessions live on the dedicated /vibecontrols/sessions page —\n intentionally not surfaced here so the Targets tab stays focused on\n target lifecycle (add/edit/delete + drill into a target's agents). */}\n\n {/* Add Target dialog */}\n <ResponsiveDialog\n open={showAddForm}\n onOpenChange={(open) => {\n if (!open) setShowAddForm(false);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{tr('targets.addTarget', 'Add Target')}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr('targets.addTargetBody', 'Register a machine or server you want to manage.')}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"max-h-[min(60vh,34rem)] space-y-4 overflow-y-auto px-0.5 py-1\">\n {/* Target Type */}\n <div>\n <span\n id={addTypeLabelId}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('targets.targetType', 'Target Type')}\n </span>\n <div role=\"group\" aria-labelledby={addTypeLabelId} className=\"grid grid-cols-2 gap-2\">\n {(['DIRECT', 'SSH'] as const).map((t) => (\n <button\n type=\"button\"\n key={t}\n onClick={() => setFormType(t)}\n aria-pressed={formType === t}\n className={`min-h-9 px-3 py-2 text-sm font-medium rounded-button border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${\n formType === t\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default hover:bg-bg-sunken text-text-secondary'\n }`}\n >\n {t}\n </button>\n ))}\n </div>\n <p className=\"mt-1 text-xs text-text-muted\">\n {formType === 'DIRECT'\n ? tr(\n 'targets.targetTypeDirectHint',\n 'A local machine where you will install the agent manually.'\n )\n : tr('targets.targetTypeSshHint', 'A remote server accessible via SSH.')}\n </p>\n </div>\n\n <TextField\n label={tr('webhooks.name', 'Name')}\n value={formName}\n onChange={setFormName}\n requiredField\n placeholder={\n formType === 'DIRECT'\n ? tr('targets.namePlaceholderDirect', 'My Laptop')\n : tr('targets.namePlaceholderServer', 'Production Server')\n }\n />\n\n <TextField\n label={tr('targets.descriptionOptional', 'Description (optional)')}\n value={formDescription}\n onChange={setFormDescription}\n placeholder={tr('targets.descriptionPlaceholder', 'Brief description of this target')}\n />\n\n {/* Connection fields — only for SSH */}\n {formType !== 'DIRECT' && (\n <>\n <div>\n <label\n htmlFor={sourceAgentField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('targets.sourceAgent', 'Source Agent')}\n </label>\n <select\n {...sourceAgentField.inputProps}\n value={formAgent}\n onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>\n setFormAgent(e.target.value)\n }\n aria-required=\"true\"\n className=\"w-full rounded-input border border-border-default bg-bg-elevated px-3 py-2 text-base md:text-sm\"\n >\n <option value=\"\">\n {tr('targets.selectAgentConnectFrom', 'Select an agent to connect from')}\n </option>\n {agents.map((a: AgentOption) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n <p id={sourceAgentField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {sourceAgentHint}\n </p>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <TextField\n label={tr('targets.host', 'Host')}\n value={formHost}\n onChange={setFormHost}\n requiredField\n placeholder={tr('targets.hostPlaceholder', '192.168.1.100')}\n />\n <TextField\n label={tr('tunnels.port', 'Port')}\n value={formPort}\n onChange={(v) => setFormPort(Number(v))}\n type=\"number\"\n />\n </div>\n <TextField\n label={tr('targets.username', 'Username')}\n value={formUsername}\n onChange={setFormUsername}\n requiredField\n placeholder={tr('targets.usernamePlaceholder', 'root')}\n />\n {formType === 'SSH' && (\n <>\n <AuthMethodPicker\n legend={tr('targets.authentication', 'Authentication')}\n name={`${baseId}-add-auth`}\n value={formAuthMethod}\n onChange={setFormAuthMethod}\n passwordLabel={tr('targets.password', 'Password')}\n keyLabel={tr('targets.privateKey', 'Private Key')}\n />\n {formAuthMethod === 'password' ? (\n <TextField\n label={tr('targets.password', 'Password')}\n value={formPassword}\n onChange={setFormPassword}\n type=\"password\"\n autoComplete=\"new-password\"\n />\n ) : (\n <TextField\n label={tr('targets.privateKeyPath', 'Private Key Path')}\n value={formKeyPath}\n onChange={setFormKeyPath}\n placeholder={tr('targets.privateKeyPathPlaceholder', '~/.ssh/id_rsa')}\n />\n )}\n </>\n )}\n </>\n )}\n\n {/* Label + Tags (all types) */}\n <TextField\n label={tr('targets.labelOptional', 'Label (optional)')}\n value={formLabel}\n onChange={setFormLabel}\n placeholder={tr('targets.labelPlaceholder', 'web-server-01')}\n />\n <TextField\n label={tr('targets.tagsCommaSeparated', 'Tags (comma-separated)')}\n value={formTags}\n onChange={setFormTags}\n placeholder={tr('targets.tagsPlaceholder', 'production, web')}\n />\n\n <div className=\"border-t border-border-default pt-3\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('targets.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_TARGET\" onDraftChange={setDraftTagIds} />\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setShowAddForm(false)}\n className=\"max-md:min-h-11\"\n >\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleCreate()}\n className=\"max-md:min-h-11 active:scale-[0.98]\"\n >\n {tr('targets.addTarget', 'Add Target')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit Target dialog */}\n <ResponsiveDialog\n open={editTarget !== null}\n onOpenChange={(open) => {\n if (!open && !editLoading) setEditTarget(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n {editTarget && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('targets.editTarget', 'Edit Target')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>{editTarget.name}</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"max-h-[min(60vh,34rem)] space-y-4 overflow-y-auto px-0.5 py-1\">\n {editError && (\n <div\n role=\"alert\"\n className=\"rounded-card border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {editError}\n </div>\n )}\n\n <TextField\n label={tr('webhooks.name', 'Name')}\n value={editName}\n onChange={setEditName}\n requiredField\n placeholder={tr('targets.namePlaceholderServer', 'Production Server')}\n />\n\n <TextField\n label={tr('targets.labelOptional', 'Label (optional)')}\n value={editLabel}\n onChange={setEditLabel}\n placeholder={tr('targets.labelPlaceholder', 'web-server-01')}\n />\n\n {/* Connection fields — only for non-DIRECT targets */}\n {editTarget.type !== 'DIRECT' && (\n <>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <TextField\n label={tr('targets.host', 'Host')}\n value={editHost}\n onChange={setEditHost}\n placeholder={tr('targets.hostPlaceholder', '192.168.1.100')}\n />\n <TextField\n label={tr('tunnels.port', 'Port')}\n value={editPort}\n onChange={(v) => setEditPort(Number(v))}\n type=\"number\"\n />\n </div>\n\n <TextField\n label={tr('targets.username', 'Username')}\n value={editUsername}\n onChange={setEditUsername}\n placeholder={tr('targets.usernamePlaceholder', 'root')}\n />\n\n {editTarget.type === 'SSH' && (\n <>\n <AuthMethodPicker\n legend={tr(\n 'targets.updateCredentialsOptional',\n 'Update credentials (optional)'\n )}\n name={`${baseId}-edit-auth`}\n value={editAuthMethod}\n onChange={setEditAuthMethod}\n passwordLabel={tr('targets.password', 'Password')}\n keyLabel={tr('targets.privateKey', 'Private Key')}\n hint={tr(\n 'targets.keepCredentialsHint',\n 'Leave blank to keep existing credentials.'\n )}\n />\n {editAuthMethod === 'password' ? (\n <TextField\n label={tr('targets.newPassword', 'New Password')}\n value={editPassword}\n onChange={setEditPassword}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={tr(\n 'targets.keepCurrentPlaceholder',\n 'Leave blank to keep current'\n )}\n />\n ) : (\n <TextField\n label={tr('targets.newPrivateKeyPath', 'New Private Key Path')}\n value={editKeyPath}\n onChange={setEditKeyPath}\n placeholder={tr(\n 'targets.keepCurrentKeyPlaceholder',\n 'Leave blank to keep current (~/.ssh/id_rsa)'\n )}\n />\n )}\n </>\n )}\n </>\n )}\n\n <TextField\n label={tr('targets.tagsCommaSeparated', 'Tags (comma-separated)')}\n value={editTags}\n onChange={setEditTags}\n placeholder={tr('targets.tagsPlaceholder', 'production, web')}\n />\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setEditTarget(null)}\n disabled={editLoading}\n className=\"max-md:min-h-11\"\n >\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSaveEdit()}\n disabled={editLoading}\n aria-busy={editLoading}\n className=\"max-md:min-h-11\"\n >\n {editLoading\n ? tr('common.saving', 'Saving...')\n : tr('targets.saveChanges', 'Save Changes')}\n </Button>\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Single delete confirmation */}\n <ConfirmDialog\n isOpen={!!deleteTargetId}\n onClose={() => setDeleteTargetId(null)}\n onConfirm={() => void handleDeleteSingle()}\n title={tr('targets.deleteTarget', 'Delete Target')}\n message={tr(\n 'targets.deleteTargetMessage',\n 'This will remove the target and its connection from the agent. Active terminals will be disconnected.'\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n />\n\n {/* Bulk delete confirmation */}\n <ConfirmDialog\n isOpen={bulkDeleteOpen}\n onClose={() => setBulkDeleteOpen(false)}\n onConfirm={() => void handleBulkDelete()}\n title={tr('targets.deleteSelectedTargets', 'Delete Selected Targets')}\n message={`${tr('targets.confirmDeleteSelectedPrefix', 'Are you sure you want to delete')} ${selectedIds.size} ${tr('nav.targets', 'target')}${selectedIds.size === 1 ? '' : 's'}? ${tr('targets.confirmDeleteSelectedSuffix', 'This action cannot be undone.')}`}\n confirmLabel={tr('targets.deleteAll', 'Delete All')}\n variant=\"danger\"\n />\n\n {/* Install progress dialog */}\n <ResponsiveDialog\n open={installJobTarget !== null}\n onOpenChange={(open) => {\n if (!open && installJob?.status !== 'running') closeInstallDialog();\n }}\n >\n <ResponsiveDialogContent\n className=\"sm:max-w-md\"\n showCloseButton={installJob?.status !== 'running'}\n >\n {installJobTarget && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"break-words\">\n {tr('targets.installingAgentOn', 'Installing Agent on')} {installJobTarget.name}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr('targets.installProgressBody', 'Installation progress for this target.')}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-2\" role=\"status\">\n {installJob && installJob.steps ? (\n installJob.steps.map((step: InstallJob['steps'][number], idx: number) => (\n <div key={idx} className=\"flex items-center gap-3\">\n {step.status === 'running' && (\n <Loader2\n className=\"size-4 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n )}\n {step.status === 'completed' && (\n <CheckCircle\n className=\"size-4 text-status-success-text\"\n aria-hidden=\"true\"\n />\n )}\n {step.status === 'failed' && (\n <XCircle className=\"size-4 text-status-error-text\" aria-hidden=\"true\" />\n )}\n {step.status === 'skipped' && (\n <Check className=\"size-4 text-text-muted\" aria-hidden=\"true\" />\n )}\n {step.status === 'pending' && (\n <div\n className=\"size-4 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {tr(\n `targets.installStep.${step.name}`,\n stepLabels[step.name] ?? step.name\n )}\n </div>\n {step.message && (\n <div className=\"text-xs text-text-muted break-words\">{step.message}</div>\n )}\n </div>\n </div>\n ))\n ) : (\n <div className=\"flex items-center gap-3\">\n <Loader2\n className=\"size-4 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n <span className=\"text-sm text-text-primary\">\n {tr('targets.startingInstallJob', 'Starting install job...')}\n </span>\n </div>\n )}\n </div>\n\n {installJob?.status === 'completed' && (\n <div\n role=\"status\"\n className=\"rounded-card border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\"\n >\n {tr('targets.agentInstalledSuccess', 'Agent installed successfully!')}\n </div>\n )}\n {installJob?.status === 'failed' && (\n <div\n role=\"alert\"\n className=\"rounded-card border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {installJob.error ?? tr('targets.installationFailed', 'Installation failed.')}\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={closeInstallDialog}\n disabled={installJob?.status === 'running'}\n aria-busy={installJob?.status === 'running'}\n className=\"w-full max-md:min-h-11\"\n >\n {!installJob || installJob.status === 'running'\n ? tr('targets.installing', 'Installing...')\n : tr('common.close', 'Close')}\n </Button>\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n <ConfirmDialog\n isOpen={quotaExhaustedKind !== null}\n onClose={() => setQuotaExhaustedKind(null)}\n onConfirm={() => {\n setQuotaExhaustedKind(null);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent(quotaExhaustedKind ?? 'generic').title}\n message={getQuotaDialogContent(quotaExhaustedKind ?? 'generic').message}\n confirmLabel={tr('targets.viewBilling', 'View Billing')}\n cancelLabel={tr('common.dismiss', 'Dismiss')}\n variant=\"warning\"\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TargetsPage.js","names":[],"sources":["../../src/pages/TargetsPage.tsx"],"sourcesContent":["/**\n * Targets Page — Manage all targets (servers, workstations, environments).\n *\n * Targets are the first-class entity. Each target can have agents installed,\n * and supports two access types: Direct and SSH.\n */\n\nimport { useState, useMemo, useCallback, useEffect, useId } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport {\n Plus,\n RefreshCw,\n Trash2,\n Server,\n Grid,\n List,\n CheckCircle,\n XCircle,\n Loader2,\n Check,\n Pencil,\n CheckSquare,\n XSquare,\n} from 'lucide-react';\nimport {\n Button,\n Checkbox,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n AgentSetupBanner,\n SearchableSelect,\n GetHelpButton,\n ActionMenu,\n} from '@/components/shared';\nimport type { ActionMenuItem } from '@/components/shared';\nimport { TargetCard } from '@/components/targets';\nimport type { TargetCardTarget } from '@/components/targets';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useSearchParams } from 'react-router-dom';\nimport { useTr } from '@/shared/hooks/useTr';\nimport { useField } from '@/shared/hooks/useField';\nimport { notify } from '@/lib/toast';\nimport {\n useListTargetsQuery,\n useCreateTargetMutation,\n useUpdateTargetMutation,\n useTestTargetMutation,\n useStartTerminalMutation,\n useDeleteTargetMutation,\n useInstallAgentOnTargetMutation,\n useUninstallAgentFromTargetMutation,\n useGetInstallAgentJobStatusLazyQuery,\n} from '@/generated/wspace-operations';\nimport {\n getQuotaDialogContent,\n markQuotaHandledLocally,\n isQuotaExhaustedError,\n type QuotaKind,\n} from '@/utils/quotaUtils';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype TargetStatus = 'connected' | 'disconnected' | 'unknown';\n\ntype TargetKind = 'DIRECT' | 'SSH';\n\ninterface SshTarget {\n id: string;\n type: TargetKind;\n name: string;\n label: string | null;\n host: string | null;\n port: number | null;\n username: string | null;\n status: TargetStatus;\n connectionId: string | null;\n tags: string[];\n metadata: Record<string, unknown> | null;\n createdAt: string;\n agents: Array<{ id: string; name: string; version: string; isActive: boolean }>;\n}\n\ninterface AgentOption {\n id: string;\n name: string;\n isActive: boolean;\n tunnelUrl: string | null;\n agentApiKey: string | null;\n}\n\ninterface InstallJob {\n id: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n steps: Array<{ name: string; status: string; message?: string }>;\n currentStep: number;\n result?: { agentUrl?: string; backendAgentId?: string };\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction mapStatus(s: string): TargetStatus {\n if (s === 'connected') return 'connected';\n if (s === 'disconnected') return 'disconnected';\n return 'unknown';\n}\n\nconst stepLabels: Record<string, string> = {\n connect: 'Connecting to server',\n detect_os: 'Detecting OS',\n install_bun: 'Installing Bun runtime',\n install_agent: 'Installing VibeControls Agent',\n configure: 'Configuring agent',\n start: 'Starting agent',\n verify: 'Verifying health',\n retrieve: 'Retrieving agent details',\n register: 'Registering with backend',\n};\n\ntype ViewMode = 'grid' | 'list';\n\nconst statusConfig: Record<TargetStatus, { label: string }> = {\n connected: { label: 'Connected' },\n disconnected: { label: 'Disconnected' },\n unknown: { label: 'Unknown' },\n};\n\nconst typeBadgeClass: Record<TargetKind, string> = {\n DIRECT: 'bg-bg-sunken text-text-secondary border border-border-default',\n SSH: 'bg-bg-sunken text-text-secondary border border-border-default',\n};\n\nconst targetStatusBadgeClass: Record<TargetStatus, string> = {\n connected: 'bg-status-success-bg-subtle text-status-success-text',\n disconnected: 'bg-status-error-bg-subtle text-status-error-text',\n unknown: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction isAgentInstalled(target: SshTarget): boolean {\n return target.agents.length > 0;\n}\n\n// ---------------------------------------------------------------------------\n// Form field helpers (accessible label/input/hint wiring via useField)\n// ---------------------------------------------------------------------------\n\ninterface TextFieldProps {\n label: string;\n value: string | number;\n onChange: (value: string) => void;\n type?: 'text' | 'password' | 'number';\n placeholder?: string;\n hint?: string;\n requiredField?: boolean;\n autoComplete?: string;\n}\n\nfunction TextField({\n label,\n value,\n onChange,\n type = 'text',\n placeholder,\n hint,\n requiredField = false,\n autoComplete,\n}: TextFieldProps) {\n const field = useField({ hint: hint ?? null });\n return (\n <div>\n <label htmlFor={field.id} className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {label}\n </label>\n <Input\n {...field.inputProps}\n type={type}\n value={value}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}\n placeholder={placeholder}\n aria-required={requiredField || undefined}\n autoComplete={autoComplete}\n className=\"text-base md:text-sm\"\n />\n {hint ? (\n <p id={field.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {hint}\n </p>\n ) : null}\n </div>\n );\n}\n\ninterface AuthMethodPickerProps {\n legend: string;\n name: string;\n value: 'password' | 'key';\n onChange: (value: 'password' | 'key') => void;\n passwordLabel: string;\n keyLabel: string;\n hint?: string;\n}\n\nfunction AuthMethodPicker({\n legend,\n name,\n value,\n onChange,\n passwordLabel,\n keyLabel,\n hint,\n}: AuthMethodPickerProps) {\n return (\n <fieldset>\n <legend className=\"text-sm font-medium text-text-primary\">{legend}</legend>\n <div className=\"mt-1.5 flex flex-wrap gap-x-4 gap-y-2\">\n <label className=\"flex min-h-9 cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"radio\"\n name={name}\n checked={value === 'password'}\n onChange={() => onChange('password')}\n />\n {passwordLabel}\n </label>\n <label className=\"flex min-h-9 cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"radio\"\n name={name}\n checked={value === 'key'}\n onChange={() => onChange('key')}\n />\n {keyLabel}\n </label>\n </div>\n {hint ? <p className=\"mt-1 text-xs text-text-muted\">{hint}</p> : null}\n </fieldset>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Targets Page\n// ---------------------------------------------------------------------------\n\nexport function TargetsPage() {\n const tr = useTr();\n const { workspaceId } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const [searchParams] = useSearchParams();\n const selectedTargetId = searchParams.get('selectedTargetId');\n const baseId = useId();\n\n // UI state\n const [searchQuery, setSearchQuery] = useState('');\n const [agentFilter, setAgentFilter] = useState<string | null>(null);\n const [typeFilter, setTypeFilter] = useState('all');\n const [statusFilter, setStatusFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showAddForm, setShowAddForm] = useState(false);\n const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);\n const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false);\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n // Per-kind so the dialog labels switch between \"Target Quota Reached\"\n // (create flows) and \"Session/Environment Quota Reached\" (Open Terminal),\n // both of which set this state on the same page.\n const [quotaExhaustedKind, setQuotaExhaustedKind] = useState<QuotaKind | null>(null);\n\n // Install progress state\n const [installJobTarget, setInstallJobTarget] = useState<SshTarget | null>(null);\n const [installJob, setInstallJob] = useState<InstallJob | null>(null);\n const [installJobId, setInstallJobId] = useState<string | null>(null);\n\n // Edit target modal state\n const [editTarget, setEditTarget] = useState<SshTarget | null>(null);\n const [editLoading, setEditLoading] = useState(false);\n const [editError, setEditError] = useState<string | null>(null);\n const [editName, setEditName] = useState('');\n const [editLabel, setEditLabel] = useState('');\n const [editHost, setEditHost] = useState('');\n const [editPort, setEditPort] = useState(22);\n const [editUsername, setEditUsername] = useState('');\n const [editAuthMethod, setEditAuthMethod] = useState<'password' | 'key'>('key');\n const [editPassword, setEditPassword] = useState('');\n const [editKeyPath, setEditKeyPath] = useState('');\n const [editTags, setEditTags] = useState('');\n\n // Form state\n const [formType, setFormType] = useState<'DIRECT' | 'SSH'>('DIRECT');\n const [formName, setFormName] = useState('');\n const [formDescription, setFormDescription] = useState('');\n const [formAgent, setFormAgent] = useState('');\n const [formHost, setFormHost] = useState('');\n const [formPort, setFormPort] = useState(22);\n const [formUsername, setFormUsername] = useState('');\n const [formAuthMethod, setFormAuthMethod] = useState<'password' | 'key'>('key');\n const [formPassword, setFormPassword] = useState('');\n const [formKeyPath, setFormKeyPath] = useState('');\n const [formLabel, setFormLabel] = useState('');\n const [formTags, setFormTags] = useState('');\n\n // Queries - use cache-and-network for silent background refresh\n const {\n data: targetsData,\n loading,\n refetch: refetchTargets,\n } = useListTargetsQuery({\n variables: { workspaceId: workspaceId || '', limit: 50, offset: 0 },\n skip: !workspaceId,\n fetchPolicy: 'cache-first',\n });\n\n // Use cached initial data for related entities already prefetched on initial load.\n const { agents: cachedAgents, refetch: refetchInitialData } = useCachedInitialData();\n\n // Mutations\n const [createTarget] = useCreateTargetMutation({\n refetchQueries: ['VibeControlsInitialData', 'ListTargets'],\n awaitRefetchQueries: true,\n });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_TARGET');\n const [updateTarget] = useUpdateTargetMutation();\n const [testTarget] = useTestTargetMutation();\n const [startTerminal] = useStartTerminalMutation();\n const [deleteTarget] = useDeleteTargetMutation();\n const [installAgentOnTarget] = useInstallAgentOnTargetMutation();\n const [uninstallAgent] = useUninstallAgentFromTargetMutation();\n const [fetchInstallJobStatus] = useGetInstallAgentJobStatusLazyQuery({\n fetchPolicy: 'network-only',\n });\n\n // Derived data\n const targets: SshTarget[] = useMemo(() => {\n const raw = targetsData?.targets?.items ?? [];\n return raw.map((t: (typeof raw)[number]) => ({\n id: t.id,\n type: (t.type || 'DIRECT') as TargetKind,\n name: t.name,\n label: t.label || null,\n host: t.host || null,\n port: t.port || null,\n username: t.username || null,\n status: mapStatus(t.status),\n connectionId: t.connectionId || null,\n tags: t.tags,\n metadata: (t.metadata as Record<string, unknown> | null) ?? null,\n createdAt: t.createdAt,\n agents: (t.agents || []).map((a: (typeof t.agents)[number]) => ({\n id: a.id,\n name: a.name,\n version: a.version,\n isActive: a.isActive,\n })),\n }));\n }, [targetsData]);\n\n const agents: AgentOption[] = useMemo(() => {\n return cachedAgents.map((a) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n tunnelUrl: a.tunnelUrl ?? null,\n agentApiKey: null,\n }));\n }, [cachedAgents]);\n\n const filtered = useMemo(() => {\n let items = targets;\n if (searchQuery) {\n const q = searchQuery.toLowerCase();\n items = items.filter(\n (t: SshTarget) =>\n t.name.toLowerCase().includes(q) ||\n t.host?.toLowerCase().includes(q) ||\n t.label?.toLowerCase().includes(q)\n );\n }\n if (agentFilter) {\n items = items.filter((t: SshTarget) => t.agents.some((a) => a.id === agentFilter));\n }\n if (typeFilter !== 'all') {\n items = items.filter((t: SshTarget) => t.type === typeFilter);\n }\n if (statusFilter !== 'all') {\n items = items.filter((t: SshTarget) => t.status === statusFilter);\n }\n return items;\n }, [targets, searchQuery, agentFilter, typeFilter, statusFilter]);\n\n useEffect(() => {\n if (!selectedTargetId) return;\n\n const target = filtered.find((item) => item.id === selectedTargetId);\n if (!target) return;\n\n const timeout = window.setTimeout(() => {\n document\n .getElementById(`target-row-${selectedTargetId}`)\n ?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }, 150);\n\n return () => window.clearTimeout(timeout);\n }, [filtered, selectedTargetId]);\n\n const connectedCount = targets.filter((t: SshTarget) => t.status === 'connected').length;\n const allFilteredSelected =\n filtered.length > 0 && filtered.every((t: SshTarget) => selectedIds.has(t.id));\n const hasSelection = selectedIds.size > 0;\n\n // Install job polling\n useEffect(() => {\n if (!installJobId || !installJobTarget) return;\n\n let cancelled = false;\n\n const pollJob = () => {\n void fetchInstallJobStatus({\n variables: { targetId: installJobTarget.id, jobId: installJobId },\n })\n .then((result) => {\n if (cancelled) return;\n const data = result.data?.installAgentJobStatus;\n if (!data) return;\n if (!data.steps) data.steps = [];\n setInstallJob({\n id: data.jobId,\n status: data.status,\n currentStep: data.currentStep ?? 0,\n steps: data.steps ?? [],\n result: data.result ?? undefined,\n error: data.error ?? undefined,\n });\n if (data.status === 'completed' || data.status === 'failed') {\n if (data.status === 'completed') {\n void refetchTargets();\n refetchInitialData();\n }\n }\n })\n .catch(() => {\n // Polling error — will retry on next interval\n });\n };\n\n pollJob();\n const interval = setInterval(pollJob, 2000);\n\n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n }, [installJobId, installJobTarget, fetchInstallJobStatus, refetchInitialData, refetchTargets]);\n\n // Handlers\n const handleCreate = useCallback(async () => {\n if (!formName || !workspaceId) return;\n if (formType !== 'DIRECT' && (!formAgent || !formHost || !formUsername)) return;\n try {\n const res = await createTarget({\n variables: {\n workspaceId,\n input: {\n type: formType as string,\n name: formName,\n sourceAgentId: formType !== 'DIRECT' ? formAgent : undefined,\n host: formType !== 'DIRECT' ? formHost : undefined,\n port: formType !== 'DIRECT' ? formPort : undefined,\n username: formType !== 'DIRECT' ? formUsername : undefined,\n password:\n formType === 'SSH' && formAuthMethod === 'password' ? formPassword : undefined,\n privateKeyPath:\n formType === 'SSH' && formAuthMethod === 'key' ? formKeyPath : undefined,\n label: formLabel || undefined,\n tags: formTags ? formTags.split(',').map((t: string) => t.trim()) : undefined,\n metadata: formDescription ? { description: formDescription } : undefined,\n },\n },\n });\n\n // Apollo's `errorPolicy: 'all'` makes the mutation promise resolve\n // even when the server returns GraphQL errors, so we have to check\n // `res.error` here — the `catch` block below would never fire for\n // server-side validation errors like ALREADY_EXISTS (BOFF-2626).\n if (res.error) {\n if (isQuotaExhaustedError(res.error)) {\n setShowAddForm(false);\n markQuotaHandledLocally();\n setQuotaExhaustedKind('target');\n return;\n }\n notify.error(\n tr('targets.createFailed', 'Failed to create target'),\n res.error.message || undefined\n );\n return;\n }\n\n const newTargetId = res.data?.createTarget?.id;\n setShowAddForm(false);\n setFormName('');\n setFormHost('');\n setFormUsername('');\n setFormPassword('');\n setFormKeyPath('');\n setFormLabel('');\n setFormTags('');\n if (newTargetId) await flushTags(newTargetId);\n void refetchTargets();\n // Refresh shared initial-data cache so AgentsPage (and other pages\n // reading targets via useCachedInitialData) see the new row without\n // a full page reload.\n refetchInitialData();\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowAddForm(false);\n markQuotaHandledLocally();\n setQuotaExhaustedKind('target');\n return;\n }\n // Unexpected throw (network error, etc.) — surface via toast rather\n // than letting it bubble silently into the React error boundary.\n notify.error(\n tr('targets.createFailed', 'Failed to create target'),\n err instanceof Error ? err.message : undefined\n );\n }\n }, [\n formName,\n formAgent,\n formHost,\n formPort,\n formUsername,\n formAuthMethod,\n formPassword,\n formKeyPath,\n formLabel,\n formTags,\n formDescription,\n formType,\n workspaceId,\n createTarget,\n flushTags,\n refetchInitialData,\n refetchTargets,\n tr,\n ]);\n\n const handleTest = useCallback(\n async (id: string) => {\n const result = await testTarget({ variables: { id } });\n const data = result.data?.testTarget;\n if (data) {\n if (data.success) {\n notify.success(\n tr('targets.connectionSuccessful', 'Connection successful'),\n data.message || undefined\n );\n } else {\n notify.error(\n tr('targets.connectionFailed', 'Connection failed'),\n data.message || undefined\n );\n }\n void refetchTargets();\n refetchInitialData();\n }\n },\n [testTarget, refetchInitialData, refetchTargets, tr]\n );\n\n // Per-target loading state for the Open Terminal action — startTerminal\n // shells into the remote box and spawns ttyd, which routinely takes 3-8s.\n // Without a hint the user thinks the click did nothing and clicks again.\n const [startingTerminalIds, setStartingTerminalIds] = useState<Set<string>>(new Set());\n\n const handleStartTerminal = useCallback(\n async (id: string) => {\n setStartingTerminalIds((prev) => {\n const next = new Set(prev);\n next.add(id);\n return next;\n });\n try {\n const result = await startTerminal({ variables: { targetId: id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setQuotaExhaustedKind('environment');\n return;\n }\n\n const data = result.data?.startTerminal;\n if (data?.sessionId) {\n navigate(`/sessions/${data.sessionId}`);\n }\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n markQuotaHandledLocally();\n setQuotaExhaustedKind('environment');\n return;\n }\n\n throw err;\n } finally {\n setStartingTerminalIds((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n },\n [startTerminal, navigate]\n );\n\n const handleDeleteSingle = useCallback(async () => {\n if (!deleteTargetId) return;\n try {\n await deleteTarget({ variables: { id: deleteTargetId } });\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n setDeleteTargetId(null);\n setSelectedIds((prev) => {\n const next = new Set(prev);\n next.delete(deleteTargetId);\n return next;\n });\n void refetchTargets();\n refetchInitialData();\n }, [deleteTargetId, deleteTarget, refetchInitialData, refetchTargets]);\n\n const handleBulkDelete = useCallback(async () => {\n const ids = Array.from(selectedIds);\n await Promise.all(ids.map((id: string) => deleteTarget({ variables: { id } })));\n setSelectedIds(new Set());\n setBulkDeleteOpen(false);\n void refetchTargets();\n refetchInitialData();\n }, [selectedIds, deleteTarget, refetchInitialData, refetchTargets]);\n\n const handleInstallAgent = useCallback(\n async (target: SshTarget) => {\n setInstallJobTarget(target);\n setInstallJob(null);\n setInstallJobId(null);\n\n const sourceAgent = agents.find((a: AgentOption) => a.isActive);\n if (!sourceAgent) return;\n\n const result = await installAgentOnTarget({\n variables: { targetId: target.id, sourceAgentId: sourceAgent.id },\n });\n const data = result.data?.installAgentOnTarget;\n if (data?.jobId) {\n setInstallJobId(data.jobId);\n }\n },\n [installAgentOnTarget, agents]\n );\n\n const handleUninstallAgent = useCallback(\n async (target: SshTarget) => {\n await uninstallAgent({ variables: { targetId: target.id } });\n void refetchTargets();\n refetchInitialData();\n },\n [uninstallAgent, refetchInitialData, refetchTargets]\n );\n\n const handleOpenEdit = useCallback((target: SshTarget) => {\n setEditTarget(target);\n setEditName(target.name);\n setEditLabel(target.label ?? '');\n setEditHost(target.host ?? '');\n setEditPort(target.port ?? 22);\n setEditUsername(target.username ?? '');\n setEditAuthMethod('key');\n setEditPassword('');\n setEditKeyPath('');\n setEditTags(target.tags.join(', '));\n setEditError(null);\n }, []);\n\n const handleSaveEdit = useCallback(async () => {\n if (!editTarget) return;\n if (!editName.trim()) {\n setEditError('Name is required');\n return;\n }\n setEditLoading(true);\n setEditError(null);\n try {\n const res = await updateTarget({\n variables: {\n id: editTarget.id,\n input: {\n name: editName.trim(),\n label: editLabel.trim() || null,\n host: editHost.trim() || undefined,\n port: editPort || undefined,\n username: editUsername.trim() || undefined,\n password:\n editTarget.type === 'SSH' && editAuthMethod === 'password' && editPassword\n ? editPassword\n : undefined,\n privateKeyPath:\n editTarget.type === 'SSH' && editAuthMethod === 'key' && editKeyPath\n ? editKeyPath\n : undefined,\n tags: editTags\n ? editTags\n .split(',')\n .map((t: string) => t.trim())\n .filter(Boolean)\n : [],\n },\n },\n });\n // Same `errorPolicy: 'all'` pitfall as handleCreate — surface server\n // errors (e.g. ALREADY_EXISTS on rename collision) inline in the\n // modal rather than letting them vanish silently. BOFF-2626.\n //\n // Quota intentionally not checked here: updateTarget is a metadata\n // edit on an already-counted row, so the svc never raises\n // QUOTA_EXHAUSTED on this path. If that ever changes, mirror the\n // pattern from handleCreate (open the quota modal, return).\n if (res.error) {\n setEditError(res.error.message || 'Failed to update target');\n return;\n }\n setEditTarget(null);\n void refetchTargets();\n refetchInitialData();\n } catch (err) {\n setEditError(err instanceof Error ? err.message : 'Failed to update target');\n } finally {\n setEditLoading(false);\n }\n }, [\n editTarget,\n editName,\n editLabel,\n editHost,\n editPort,\n editUsername,\n editAuthMethod,\n editPassword,\n editKeyPath,\n editTags,\n updateTarget,\n refetchInitialData,\n refetchTargets,\n ]);\n\n const closeInstallDialog = useCallback(() => {\n setInstallJobTarget(null);\n setInstallJob(null);\n setInstallJobId(null);\n }, []);\n\n const toggleSelectAll = useCallback(() => {\n if (allFilteredSelected) {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n filtered.forEach((t: SshTarget) => next.delete(t.id));\n return next;\n });\n } else {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n filtered.forEach((t: SshTarget) => next.add(t.id));\n return next;\n });\n }\n }, [allFilteredSelected, filtered]);\n\n const toggleSelect = useCallback((id: string) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n // Stats\n const stats = {\n total: targets.length,\n connected: connectedCount,\n };\n\n const localizedTypeOptions = useMemo(\n () => [\n { value: 'all', label: tr('targets.filterTypeAll', 'All Types') },\n { value: 'DIRECT', label: tr('targets.typeDirect', 'Direct') },\n { value: 'SSH', label: 'SSH' },\n ],\n [tr]\n );\n\n const localizedStatusOptions = useMemo(\n () => [\n { value: 'all', label: tr('targets.filterStatusAll', 'All Statuses') },\n { value: 'connected', label: tr('targets.connected', 'Connected') },\n { value: 'disconnected', label: tr('targets.disconnected', 'Disconnected') },\n { value: 'unknown', label: tr('targets.unknown', 'Unknown') },\n ],\n [tr]\n );\n\n const isFiltered = searchQuery || agentFilter || typeFilter !== 'all' || statusFilter !== 'all';\n\n // Add-form source-agent select (accessible wiring shared with the hint).\n const sourceAgentHint = `${tr('targets.agentConnectionHintPrefix', 'The agent that will establish the')} ${formType} ${tr('targets.agentConnectionHintSuffix', 'connection to this target.')}`;\n const sourceAgentField = useField({ hint: sourceAgentHint });\n const addTypeLabelId = `${baseId}-add-type-label`;\n\n if (permissionsLoading)\n return <LoadingState message={tr('targets.loadingPermissions', 'Loading permissions…')} />;\n if (!hasPermission(VIBE_PERMISSIONS.TARGET_LIST))\n return (\n <ErrorState\n title={tr('common.accessDeniedTitle', 'Access Denied')}\n message={tr('targets.accessDeniedBody', \"You don't have permission to access this page.\")}\n />\n );\n\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-full\">\n {/* Test Connection results and create-target errors surface via the\n module-wide `notify` toasts. Update-target errors surface inline in\n the edit dialog's `editError` banner instead. BOFF-2626. */}\n\n <PageHeader\n title={tr('nav.targets', 'Targets')}\n description={tr('targets.onlineSummary', '{{connected}} of {{total}} targets online', {\n connected: stats.connected,\n total: stats.total,\n })}\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => void refetchTargets()}\n disabled={loading}\n aria-label={tr('targets.refreshTitle', 'Refresh targets')}\n title={tr('targets.refreshTitle', 'Refresh targets')}\n className=\"max-md:min-h-11 max-md:min-w-11\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} aria-hidden=\"true\" />\n </Button>\n <Button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"max-md:min-h-11 active:scale-[0.98]\"\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('targets.addTarget', 'Add Target')}\n </Button>\n </div>\n }\n />\n\n {/* Agent setup banner */}\n {agents.length === 0 && !loading && (\n <AgentSetupBanner variant=\"full\" dismissKey=\"vibecontrols-targets-setup-banner\" />\n )}\n\n {/* Filters Bar */}\n <div className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('targets.searchPlaceholder', 'Search targets...')}\n className=\"flex-1 min-w-0 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div\n role=\"group\"\n aria-label={tr('targets.viewMode', 'View mode')}\n className=\"flex items-center gap-1 bg-bg-sunken rounded-button p-1 flex-shrink-0 border border-border-subtle\"\n >\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n aria-pressed={viewMode === 'grid'}\n aria-label={tr('targets.gridViewTitle', 'Grid view')}\n title={tr('targets.gridViewTitle', 'Grid view')}\n className={`flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${viewMode === 'grid' ? 'bg-[var(--color-accent-soft)] text-text-primary' : 'text-text-secondary hover:bg-bg-surface/60'}`}\n >\n <Grid className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n aria-pressed={viewMode === 'list'}\n aria-label={tr('targets.listViewTitle', 'List view')}\n title={tr('targets.listViewTitle', 'List view')}\n className={`flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${viewMode === 'list' ? 'bg-[var(--color-accent-soft)] text-text-primary' : 'text-text-secondary hover:bg-bg-surface/60'}`}\n >\n <List className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('sessions.filterType', 'Type')}\n value={typeFilter}\n options={localizedTypeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('sessions.filterStatus', 'Status')}\n value={statusFilter}\n options={localizedStatusOptions}\n onChange={setStatusFilter}\n />\n <SearchableSelect\n value={agentFilter ?? ''}\n onChange={(v: string) => setAgentFilter(v || null)}\n options={agents.map((a: AgentOption) => ({ value: a.id, label: a.name }))}\n placeholder={tr('targets.allAgents', 'All agents')}\n className=\"w-full sm:min-w-[180px] sm:w-auto\"\n dropdownAlign=\"left\"\n clearable\n />\n </div>\n </div>\n\n {/* Select All & Bulk Actions */}\n {filtered.length > 0 && (\n <div className=\"flex items-center gap-2 flex-wrap\">\n <div className=\"ml-auto flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={toggleSelectAll}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors rounded-button focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <CheckSquare className=\"size-3.5\" aria-hidden=\"true\" />\n {allFilteredSelected\n ? tr('targets.deselectAll', 'Deselect All')\n : tr('targets.selectAll', 'Select All')}\n </button>\n </div>\n </div>\n )}\n\n {/* Bulk Action Bar */}\n {hasSelection && (\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-3 px-4 py-2 border border-action-primary-bg bg-action-primary-bg/5 rounded-card\">\n <span className=\"text-sm font-medium text-text-primary\">\n {selectedIds.size} {tr('nav.targets', 'target')}\n {selectedIds.size !== 1 ? 's' : ''} {tr('sessions.selected', 'selected')}\n </span>\n <div className=\"flex items-center gap-1.5 sm:ml-auto\">\n <button\n type=\"button\"\n onClick={() => setBulkDeleteOpen(true)}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-button hover:bg-status-error-bg-subtle/80 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Trash2 className=\"size-3\" aria-hidden=\"true\" /> {tr('common.delete', 'Delete')}\n </button>\n <button\n type=\"button\"\n onClick={() => setSelectedIds(new Set())}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors rounded-button focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <XSquare className=\"size-3\" aria-hidden=\"true\" />{' '}\n {tr('sessions.deselect', 'Deselect')}\n </button>\n </div>\n </div>\n )}\n\n {/* Results Count */}\n {isFiltered ? (\n <p className=\"text-sm text-text-secondary\" role=\"status\">\n {tr('targets.showingResults', 'Showing {{filtered}} of {{total}} targets')\n .replace('{{filtered}}', String(filtered.length))\n .replace('{{total}}', String(targets.length))}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n ({tr('sessions.refreshing', 'refreshing...')})\n </span>\n )}\n </p>\n ) : null}\n\n {/* Targets Display */}\n {loading && targets.length === 0 ? (\n <PageSkeleton variant=\"rows\" count={6} />\n ) : filtered.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n {viewMode === 'grid' ? (\n <MotionList\n key=\"grid-view\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filtered.map((target: SshTarget) => (\n <MotionItem key={target.id} className=\"h-full\">\n <TargetCard\n target={target as TargetCardTarget}\n onTest={(id) => void handleTest(id)}\n onTerminal={(id) => void handleStartTerminal(id)}\n onOpenDetail={(id) => navigate(`/targets/${id}`)}\n onEdit={(t) => handleOpenEdit(t as SshTarget)}\n onInstallAgent={(t) => void handleInstallAgent(t as SshTarget)}\n onUninstallAgent={(t) => void handleUninstallAgent(t as SshTarget)}\n onDelete={(id) => setDeleteTargetId(id)}\n selected={selectedIds.has(target.id)}\n onToggleSelect={() => toggleSelect(target.id)}\n highlighted={selectedTargetId === target.id}\n isStartingTerminal={startingTerminalIds.has(target.id)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionList key=\"list-view\" stagger={0.04} className=\"space-y-2\">\n {filtered.map((target: SshTarget) => {\n const installed = isAgentInstalled(target);\n const isSelectedTarget = selectedTargetId === target.id;\n const overflowItems: ActionMenuItem[] = [\n {\n key: 'edit',\n label: tr('common.edit', 'Edit'),\n icon: Pencil,\n onSelect: () => handleOpenEdit(target),\n },\n ...(target.type !== 'DIRECT' && installed\n ? [\n {\n key: 'uninstall',\n label: tr('targets.uninstall', 'Uninstall'),\n icon: XCircle,\n variant: 'danger' as const,\n onSelect: () => void handleUninstallAgent(target),\n },\n ]\n : []),\n {\n key: 'delete',\n label: tr('common.delete', 'Delete'),\n icon: Trash2,\n variant: 'danger' as const,\n separatorBefore: true,\n onSelect: () => setDeleteTargetId(target.id),\n },\n ];\n return (\n <MotionItem key={target.id} variant=\"fadeRight\">\n <div\n id={`target-row-${target.id}`}\n className={`p-4 rounded-card border transition-colors ${\n isSelectedTarget\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default hover:bg-bg-sunken/30'\n }`}\n >\n {/* Row 1: Info */}\n <div className=\"flex items-start gap-3\">\n <Checkbox\n checked={selectedIds.has(target.id)}\n onCheckedChange={() => toggleSelect(target.id)}\n aria-label={tr('targets.selectTarget', 'Select {{name}}', {\n name: target.name,\n })}\n className=\"mt-1\"\n />\n <div className=\"flex min-w-0 flex-1 flex-wrap items-start gap-2\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-semibold rounded ${typeBadgeClass[target.type]}`}\n >\n {target.type}\n </span>\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-semibold rounded ${targetStatusBadgeClass[target.status]}`}\n >\n {tr(\n `targets.status.${target.status}`,\n statusConfig[target.status].label\n )}\n </span>\n <div className=\"min-w-0 basis-full\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium text-text-primary\">{target.name}</span>\n {target.label && (\n <span className=\"text-text-secondary text-sm\">{target.label}</span>\n )}\n {installed && (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-status-success-border px-2 py-0.5 text-xs bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"size-2.5\" aria-hidden=\"true\" />\n {tr('targets.agentInstalled', 'Agent Installed')}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-sm text-text-secondary sm:gap-3\">\n {target.host && (\n <code className=\"text-xs break-words\">\n {target.username ? `${target.username}@` : ''}\n {target.host}\n {target.port ? `:${target.port}` : ''}\n </code>\n )}\n {target.agents.length > 0 && (\n <span className=\"text-text-muted\">\n {tr('targets.via', 'via')} {target.agents[0].name}\n </span>\n )}\n </div>\n {target.tags.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap gap-1\">\n {target.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n {/* Row 2: Actions */}\n <div className=\"mt-3 flex flex-wrap items-center gap-2 pl-0 sm:pl-7\">\n <button\n type=\"button\"\n onClick={() => void handleTest(target.id)}\n title={tr('targets.testConnectivity', 'Test connectivity')}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-button border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {tr('targets.test', 'Test')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleStartTerminal(target.id)}\n disabled={startingTerminalIds.has(target.id)}\n aria-busy={startingTerminalIds.has(target.id)}\n title={tr('targets.openTerminal', 'Open terminal')}\n className=\"flex min-h-9 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-button border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors disabled:opacity-60 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {startingTerminalIds.has(target.id) ? (\n <>\n <Loader2 className=\"size-3 animate-spin\" aria-hidden=\"true\" />\n {tr('targets.starting', 'Starting…')}\n </>\n ) : (\n tr('targets.terminal', 'Terminal')\n )}\n </button>\n {target.type !== 'DIRECT' && !installed && (\n <button\n type=\"button\"\n onClick={() => void handleInstallAgent(target)}\n title={tr('targets.installAgent', 'Install agent')}\n className=\"flex min-h-9 items-center gap-1.5 rounded-button px-3 py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {tr('targets.installAgent', 'Install Agent')}\n </button>\n )}\n <div className=\"hidden flex-1 sm:block\" />\n <ActionMenu\n ariaLabel={tr('targets.targetActions', 'Target actions')}\n title={tr('targets.targetActions', 'Target actions')}\n align=\"end\"\n items={overflowItems}\n />\n </div>\n </div>\n </MotionItem>\n );\n })}\n </MotionList>\n )}\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Server className=\"size-8 text-text-secondary\" aria-hidden=\"true\" />}\n title={\n isFiltered\n ? tr('targets.emptyNoTargetsFound', 'No targets found')\n : tr('targets.emptyNoTargetsConfigured', 'No targets configured')\n }\n description={\n isFiltered\n ? tr('targets.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'targets.emptyDescription',\n 'Add a target to start managing servers, workstations, and environments.'\n )\n }\n action={\n !isFiltered ? (\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <Button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"max-md:min-h-11 active:scale-[0.98]\"\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {tr('targets.addTarget', 'Add Target')}\n </Button>\n <GetHelpButton prompt=\"What is a Target in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Active sessions live on the dedicated /vibecontrols/sessions page —\n intentionally not surfaced here so the Targets tab stays focused on\n target lifecycle (add/edit/delete + drill into a target's agents). */}\n\n {/* Add Target dialog */}\n <ResponsiveDialog\n open={showAddForm}\n onOpenChange={(open) => {\n if (!open) setShowAddForm(false);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{tr('targets.addTarget', 'Add Target')}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr('targets.addTargetBody', 'Register a machine or server you want to manage.')}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"max-h-[min(60vh,34rem)] space-y-4 overflow-y-auto px-0.5 py-1\">\n {/* Target Type */}\n <div>\n <span\n id={addTypeLabelId}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('targets.targetType', 'Target Type')}\n </span>\n <div role=\"group\" aria-labelledby={addTypeLabelId} className=\"grid grid-cols-2 gap-2\">\n {(['DIRECT', 'SSH'] as const).map((t) => (\n <button\n type=\"button\"\n key={t}\n onClick={() => setFormType(t)}\n aria-pressed={formType === t}\n className={`min-h-9 px-3 py-2 text-sm font-medium rounded-button border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${\n formType === t\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default hover:bg-bg-sunken text-text-secondary'\n }`}\n >\n {t}\n </button>\n ))}\n </div>\n <p className=\"mt-1 text-xs text-text-muted\">\n {formType === 'DIRECT'\n ? tr(\n 'targets.targetTypeDirectHint',\n 'A local machine where you will install the agent manually.'\n )\n : tr('targets.targetTypeSshHint', 'A remote server accessible via SSH.')}\n </p>\n </div>\n\n <TextField\n label={tr('webhooks.name', 'Name')}\n value={formName}\n onChange={setFormName}\n requiredField\n placeholder={\n formType === 'DIRECT'\n ? tr('targets.namePlaceholderDirect', 'My Laptop')\n : tr('targets.namePlaceholderServer', 'Production Server')\n }\n />\n\n <TextField\n label={tr('targets.descriptionOptional', 'Description (optional)')}\n value={formDescription}\n onChange={setFormDescription}\n placeholder={tr('targets.descriptionPlaceholder', 'Brief description of this target')}\n />\n\n {/* Connection fields — only for SSH */}\n {formType !== 'DIRECT' && (\n <>\n <div>\n <label\n htmlFor={sourceAgentField.id}\n className=\"mb-1.5 block text-sm font-medium text-text-primary\"\n >\n {tr('targets.sourceAgent', 'Source Agent')}\n </label>\n <select\n {...sourceAgentField.inputProps}\n value={formAgent}\n onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>\n setFormAgent(e.target.value)\n }\n aria-required=\"true\"\n className=\"w-full rounded-input border border-border-default bg-bg-elevated px-3 py-2 text-base md:text-sm\"\n >\n <option value=\"\">\n {tr('targets.selectAgentConnectFrom', 'Select an agent to connect from')}\n </option>\n {agents.map((a: AgentOption) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n <p id={sourceAgentField.hintId} className=\"mt-1.5 text-xs text-text-muted\">\n {sourceAgentHint}\n </p>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <TextField\n label={tr('targets.host', 'Host')}\n value={formHost}\n onChange={setFormHost}\n requiredField\n placeholder={tr('targets.hostPlaceholder', '192.168.1.100')}\n />\n <TextField\n label={tr('tunnels.port', 'Port')}\n value={formPort}\n onChange={(v) => setFormPort(Number(v))}\n type=\"number\"\n />\n </div>\n <TextField\n label={tr('targets.username', 'Username')}\n value={formUsername}\n onChange={setFormUsername}\n requiredField\n placeholder={tr('targets.usernamePlaceholder', 'root')}\n />\n {formType === 'SSH' && (\n <>\n <AuthMethodPicker\n legend={tr('targets.authentication', 'Authentication')}\n name={`${baseId}-add-auth`}\n value={formAuthMethod}\n onChange={setFormAuthMethod}\n passwordLabel={tr('targets.password', 'Password')}\n keyLabel={tr('targets.privateKey', 'Private Key')}\n />\n {formAuthMethod === 'password' ? (\n <TextField\n label={tr('targets.password', 'Password')}\n value={formPassword}\n onChange={setFormPassword}\n type=\"password\"\n autoComplete=\"new-password\"\n />\n ) : (\n <TextField\n label={tr('targets.privateKeyPath', 'Private Key Path')}\n value={formKeyPath}\n onChange={setFormKeyPath}\n placeholder={tr('targets.privateKeyPathPlaceholder', '~/.ssh/id_rsa')}\n />\n )}\n </>\n )}\n </>\n )}\n\n {/* Label + Tags (all types) */}\n <TextField\n label={tr('targets.labelOptional', 'Label (optional)')}\n value={formLabel}\n onChange={setFormLabel}\n placeholder={tr('targets.labelPlaceholder', 'web-server-01')}\n />\n <TextField\n label={tr('targets.tagsCommaSeparated', 'Tags (comma-separated)')}\n value={formTags}\n onChange={setFormTags}\n placeholder={tr('targets.tagsPlaceholder', 'production, web')}\n />\n\n <div className=\"border-t border-border-default pt-3\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('targets.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_TARGET\" onDraftChange={setDraftTagIds} />\n </div>\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setShowAddForm(false)}\n className=\"max-md:min-h-11\"\n >\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleCreate()}\n className=\"max-md:min-h-11 active:scale-[0.98]\"\n >\n {tr('targets.addTarget', 'Add Target')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit Target dialog */}\n <ResponsiveDialog\n open={editTarget !== null}\n onOpenChange={(open) => {\n if (!open && !editLoading) setEditTarget(null);\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n {editTarget && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('targets.editTarget', 'Edit Target')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>{editTarget.name}</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"max-h-[min(60vh,34rem)] space-y-4 overflow-y-auto px-0.5 py-1\">\n {editError && (\n <div\n role=\"alert\"\n className=\"rounded-card border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {editError}\n </div>\n )}\n\n <TextField\n label={tr('webhooks.name', 'Name')}\n value={editName}\n onChange={setEditName}\n requiredField\n placeholder={tr('targets.namePlaceholderServer', 'Production Server')}\n />\n\n <TextField\n label={tr('targets.labelOptional', 'Label (optional)')}\n value={editLabel}\n onChange={setEditLabel}\n placeholder={tr('targets.labelPlaceholder', 'web-server-01')}\n />\n\n {/* Connection fields — only for non-DIRECT targets */}\n {editTarget.type !== 'DIRECT' && (\n <>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <TextField\n label={tr('targets.host', 'Host')}\n value={editHost}\n onChange={setEditHost}\n placeholder={tr('targets.hostPlaceholder', '192.168.1.100')}\n />\n <TextField\n label={tr('tunnels.port', 'Port')}\n value={editPort}\n onChange={(v) => setEditPort(Number(v))}\n type=\"number\"\n />\n </div>\n\n <TextField\n label={tr('targets.username', 'Username')}\n value={editUsername}\n onChange={setEditUsername}\n placeholder={tr('targets.usernamePlaceholder', 'root')}\n />\n\n {editTarget.type === 'SSH' && (\n <>\n <AuthMethodPicker\n legend={tr(\n 'targets.updateCredentialsOptional',\n 'Update credentials (optional)'\n )}\n name={`${baseId}-edit-auth`}\n value={editAuthMethod}\n onChange={setEditAuthMethod}\n passwordLabel={tr('targets.password', 'Password')}\n keyLabel={tr('targets.privateKey', 'Private Key')}\n hint={tr(\n 'targets.keepCredentialsHint',\n 'Leave blank to keep existing credentials.'\n )}\n />\n {editAuthMethod === 'password' ? (\n <TextField\n label={tr('targets.newPassword', 'New Password')}\n value={editPassword}\n onChange={setEditPassword}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={tr(\n 'targets.keepCurrentPlaceholder',\n 'Leave blank to keep current'\n )}\n />\n ) : (\n <TextField\n label={tr('targets.newPrivateKeyPath', 'New Private Key Path')}\n value={editKeyPath}\n onChange={setEditKeyPath}\n placeholder={tr(\n 'targets.keepCurrentKeyPlaceholder',\n 'Leave blank to keep current (~/.ssh/id_rsa)'\n )}\n />\n )}\n </>\n )}\n </>\n )}\n\n <TextField\n label={tr('targets.tagsCommaSeparated', 'Tags (comma-separated)')}\n value={editTags}\n onChange={setEditTags}\n placeholder={tr('targets.tagsPlaceholder', 'production, web')}\n />\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setEditTarget(null)}\n disabled={editLoading}\n className=\"max-md:min-h-11\"\n >\n {tr('common.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSaveEdit()}\n disabled={editLoading}\n aria-busy={editLoading}\n className=\"max-md:min-h-11\"\n >\n {editLoading\n ? tr('common.saving', 'Saving...')\n : tr('targets.saveChanges', 'Save Changes')}\n </Button>\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Single delete confirmation */}\n <ConfirmDialog\n isOpen={!!deleteTargetId}\n onClose={() => setDeleteTargetId(null)}\n onConfirm={() => void handleDeleteSingle()}\n title={tr('targets.deleteTarget', 'Delete Target')}\n message={tr(\n 'targets.deleteTargetMessage',\n 'This will remove the target and its connection from the agent. Active terminals will be disconnected.'\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n />\n\n {/* Bulk delete confirmation */}\n <ConfirmDialog\n isOpen={bulkDeleteOpen}\n onClose={() => setBulkDeleteOpen(false)}\n onConfirm={() => void handleBulkDelete()}\n title={tr('targets.deleteSelectedTargets', 'Delete Selected Targets')}\n message={`${tr('targets.confirmDeleteSelectedPrefix', 'Are you sure you want to delete')} ${selectedIds.size} ${tr('nav.targets', 'target')}${selectedIds.size === 1 ? '' : 's'}? ${tr('targets.confirmDeleteSelectedSuffix', 'This action cannot be undone.')}`}\n confirmLabel={tr('targets.deleteAll', 'Delete All')}\n variant=\"danger\"\n />\n\n {/* Install progress dialog */}\n <ResponsiveDialog\n open={installJobTarget !== null}\n onOpenChange={(open) => {\n if (!open && installJob?.status !== 'running') closeInstallDialog();\n }}\n >\n <ResponsiveDialogContent\n className=\"sm:max-w-md\"\n showCloseButton={installJob?.status !== 'running'}\n >\n {installJobTarget && (\n <>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle className=\"break-words\">\n {tr('targets.installingAgentOn', 'Installing Agent on')} {installJobTarget.name}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr('targets.installProgressBody', 'Installation progress for this target.')}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"space-y-2\" role=\"status\">\n {installJob && installJob.steps ? (\n installJob.steps.map((step: InstallJob['steps'][number], idx: number) => (\n <div key={idx} className=\"flex items-center gap-3\">\n {step.status === 'running' && (\n <Loader2\n className=\"size-4 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n )}\n {step.status === 'completed' && (\n <CheckCircle\n className=\"size-4 text-status-success-text\"\n aria-hidden=\"true\"\n />\n )}\n {step.status === 'failed' && (\n <XCircle className=\"size-4 text-status-error-text\" aria-hidden=\"true\" />\n )}\n {step.status === 'skipped' && (\n <Check className=\"size-4 text-text-muted\" aria-hidden=\"true\" />\n )}\n {step.status === 'pending' && (\n <div\n className=\"size-4 rounded-full border-2 border-border-default\"\n aria-hidden=\"true\"\n />\n )}\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {tr(\n `targets.installStep.${step.name}`,\n stepLabels[step.name] ?? step.name\n )}\n </div>\n {step.message && (\n <div className=\"text-xs text-text-muted break-words\">{step.message}</div>\n )}\n </div>\n </div>\n ))\n ) : (\n <div className=\"flex items-center gap-3\">\n <Loader2\n className=\"size-4 animate-spin text-text-secondary\"\n aria-hidden=\"true\"\n />\n <span className=\"text-sm text-text-primary\">\n {tr('targets.startingInstallJob', 'Starting install job...')}\n </span>\n </div>\n )}\n </div>\n\n {installJob?.status === 'completed' && (\n <div\n role=\"status\"\n className=\"rounded-card border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\"\n >\n {tr('targets.agentInstalledSuccess', 'Agent installed successfully!')}\n </div>\n )}\n {installJob?.status === 'failed' && (\n <div\n role=\"alert\"\n className=\"rounded-card border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\"\n >\n {installJob.error ?? tr('targets.installationFailed', 'Installation failed.')}\n </div>\n )}\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={closeInstallDialog}\n disabled={installJob?.status === 'running'}\n aria-busy={installJob?.status === 'running'}\n className=\"w-full max-md:min-h-11\"\n >\n {!installJob || installJob.status === 'running'\n ? tr('targets.installing', 'Installing...')\n : tr('common.close', 'Close')}\n </Button>\n </ResponsiveDialogFooter>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n <ConfirmDialog\n isOpen={quotaExhaustedKind !== null}\n onClose={() => setQuotaExhaustedKind(null)}\n onConfirm={() => {\n setQuotaExhaustedKind(null);\n navigate('/billing/plans');\n }}\n title={getQuotaDialogContent(quotaExhaustedKind ?? 'generic').title}\n message={getQuotaDialogContent(quotaExhaustedKind ?? 'generic').message}\n confirmLabel={tr('targets.viewBilling', 'View Billing')}\n cancelLabel={tr('common.dismiss', 'Dismiss')}\n variant=\"warning\"\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
@@ -82,9 +82,9 @@ async function _e(e, t, n, r) {
82
82
  }
83
83
  var ve = {
84
84
  STARTER: "bg-bg-sunken text-text-secondary",
85
- MICROSERVICE: "bg-[var(--color-accent-soft)] text-action-primary-bg",
85
+ MICROSERVICE: "bg-[var(--color-accent-soft)] text-text-primary",
86
86
  FRONTEND: "bg-status-info-bg text-status-info-text",
87
- BACKEND: "bg-[var(--color-accent-soft)] text-action-primary-bg",
87
+ BACKEND: "bg-[var(--color-accent-soft)] text-text-primary",
88
88
  FULLSTACK: "bg-status-success-bg text-status-success-text",
89
89
  INFRASTRUCTURE: "bg-status-warning-bg text-status-warning-text",
90
90
  LIBRARY: "bg-status-warning-bg text-status-warning-text",