@burdenoff/microfe-vibecontrols 2026.528.2 → 2026.528.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.
- package/README.md +1 -0
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/settings/WorkspaceDefaultsSection.js.map +1 -1
- package/dist/components/shared/ConfirmDialog.js.map +1 -1
- package/dist/components/shared/EmptyState.js.map +1 -1
- package/dist/components/targets/TargetCard.js +82 -74
- package/dist/components/targets/TargetCard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/generated/wspace-operations.js +1 -0
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +10 -1
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +274 -259
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
- 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 } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { useLazyQuery } from '@apollo/client/react';\nimport {\n Plus,\n RefreshCw,\n Trash2,\n Server,\n Grid,\n List,\n CheckCircle,\n XCircle,\n Loader2,\n X,\n Check,\n Pencil,\n CheckSquare,\n XSquare,\n} from 'lucide-react';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ConfirmDialog,\n AgentSetupBanner,\n SearchableSelect,\n GetHelpButton,\n} 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 { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useSearchParams } from 'react-router-dom';\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} 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-action-primary-bg/10 text-action-primary-bg',\n SSH: 'bg-status-success-bg-subtle text-status-success-text',\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// Targets Page\n// ---------------------------------------------------------------------------\n\nexport function TargetsPage() {\n const { t } = useI18n();\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\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 const [testResult, setTestResult] = useState<{\n id: string;\n success: boolean;\n message: string;\n } | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n // 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 }\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, 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 setShowQuotaExhausted(true);\n return;\n }\n setTestResult({\n id: 'create',\n success: false,\n message: res.error.message || 'Failed to create target',\n });\n setTimeout(() => setTestResult(null), 5000);\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 setShowQuotaExhausted(true);\n return;\n }\n // Unexpected throw (network error, etc.) — surface inline rather\n // than letting it bubble silently into the React error boundary.\n setTestResult({\n id: 'create',\n success: false,\n message: err instanceof Error ? err.message : 'Failed to create target',\n });\n setTimeout(() => setTestResult(null), 5000);\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 refetchTargets,\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 setTestResult({ id, success: data.success, message: data.message || '' });\n setTimeout(() => setTestResult(null), 5000);\n void refetchTargets();\n }\n },\n [testTarget, refetchTargets]\n );\n\n const handleStartTerminal = useCallback(\n async (id: string) => {\n try {\n const result = await startTerminal({ variables: { targetId: id } });\n if (result.error && isQuotaExhaustedError(result.error)) {\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\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 setShowQuotaExhausted(true);\n return;\n }\n\n throw err;\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 }, [deleteTargetId, deleteTarget, 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 }, [selectedIds, deleteTarget, 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 },\n [uninstallAgent, 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 } 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 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 if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.TARGET_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Toast — reused for both Test Connection results and create-target\n errors (id: 'create'). Update-target errors surface inline in the\n edit modal's `editError` banner instead, so there's no 'update'\n branch here. BOFF-2626. */}\n {testResult && (\n <div\n className={`fixed top-4 right-4 z-[10000] rounded-md px-4 py-3 shadow-lg text-sm max-w-md ${\n testResult.success\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}\n >\n {testResult.success\n ? tr('targets.connectionSuccessful', 'Connection successful')\n : testResult.id === 'create'\n ? tr('targets.createFailed', 'Failed to create target')\n : tr('targets.connectionFailed', 'Connection failed')}\n : {testResult.message}\n </div>\n )}\n\n <PageHeader\n title={tr('nav.targets', 'Targets')}\n description={`${stats.connected} of ${stats.total} targets online`}\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void refetchTargets()}\n disabled={loading}\n className=\"flex items-center justify-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('targets.refreshTitle', 'Refresh targets')}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\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 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('targets.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('targets.listViewTitle', 'List view')}\n >\n <List className=\"size-4 text-text-secondary\" />\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 items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <CheckSquare className=\"size-3.5\" />\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 bg-action-primary-bg/10 border border-action-primary-bg/20 rounded-lg\">\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 items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-status-error-text bg-status-error-bg border border-status-error-text/20 rounded-md hover:bg-status-error-bg/80 transition-colors\"\n >\n <Trash2 className=\"size-3\" /> {tr('common.delete', 'Delete')}\n </button>\n <button\n type=\"button\"\n onClick={() => setSelectedIds(new Set())}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <XSquare className=\"size-3\" /> {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\">\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 />\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 return (\n <MotionItem key={target.id} variant=\"fadeRight\">\n <div\n id={`target-row-${target.id}`}\n className={`p-4 rounded-lg border transition-colors ${\n isSelectedTarget\n ? 'border-action-primary-bg bg-action-primary-bg/5 shadow-sm'\n : 'border-border-default hover:bg-bg-sunken/30'\n }`}\n >\n {/* Row 1: Info */}\n <div className=\"flex items-start gap-3\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={selectedIds.has(target.id)}\n onChange={() => toggleSelect(target.id)}\n className=\"rounded border-border-default 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\">{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 size={10} />\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\">\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-tertiary\">\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 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors\"\n >\n {tr('targets.test', 'Test')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleStartTerminal(target.id)}\n title={tr('targets.openTerminal', 'Open terminal')}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors\"\n >\n {tr('targets.terminal', 'Terminal')}\n </button>\n <button\n type=\"button\"\n onClick={() => handleOpenEdit(target)}\n title=\"Edit target configuration\"\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors\"\n >\n <Pencil size={14} />\n Edit\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 items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('targets.installAgent', 'Install Agent')}\n </button>\n )}\n {target.type !== 'DIRECT' && installed && (\n <button\n type=\"button\"\n onClick={() => void handleUninstallAgent(target)}\n title={tr('targets.uninstallAgent', 'Uninstall agent')}\n className=\"flex items-center gap-1.5 rounded-md border border-status-error-text/20 px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg/80 transition-colors\"\n >\n {tr('targets.uninstall', 'Uninstall')}\n </button>\n )}\n <div className=\"hidden flex-1 sm:block\" />\n <button\n type=\"button\"\n onClick={() => setDeleteTargetId(target.id)}\n title={tr('targets.deleteTarget', 'Delete target')}\n className=\"flex items-center gap-1.5 rounded-md border border-status-error-border px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg/80 transition-colors\"\n >\n <X size={14} />\n {tr('common.delete', 'Delete')}\n </button>\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\" />}\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 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\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 modal */}\n {showAddForm && (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center pt-16 overflow-y-auto\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShowAddForm(false))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm\"\n onClick={() => setShowAddForm(false)}\n />\n <div\n className=\"relative bg-bg-elevated rounded-xl shadow-2xl border border-border-default max-w-lg w-full mx-4 p-6 space-y-4 mb-8\"\n onClick={(e: React.MouseEvent) => e.stopPropagation()}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <h2 className=\"text-lg font-semibold\">{tr('targets.addTarget', 'Add Target')}</h2>\n <button\n type=\"button\"\n onClick={() => setShowAddForm(false)}\n className=\"p-1 rounded-lg hover:bg-bg-sunken transition-colors\"\n >\n <X size={18} />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n {/* Target Type */}\n <div>\n <label className=\"text-sm font-medium block mb-1.5\">\n {tr('targets.targetType', 'Target Type')}\n </label>\n <div 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 className={`px-3 py-2 text-sm font-medium rounded-lg border transition-colors ${\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=\"text-xs text-text-tertiary mt-1\">\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 {/* Name */}\n <div>\n <label className=\"text-sm font-medium\">{tr('webhooks.name', 'Name')}</label>\n <input\n aria-label=\"control\"\n value={formName}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFormName(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={\n formType === 'DIRECT'\n ? tr('targets.namePlaceholderDirect', 'My Laptop')\n : tr('targets.namePlaceholderServer', 'Production Server')\n }\n />\n </div>\n\n {/* Description (all types) */}\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.descriptionOptional', 'Description (optional)')}\n </label>\n <input\n aria-label=\"control\"\n value={formDescription}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormDescription(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr(\n 'targets.descriptionPlaceholder',\n 'Brief description of this target'\n )}\n />\n </div>\n\n {/* Connection fields — only for SSH */}\n {formType !== 'DIRECT' && (\n <>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.sourceAgent', 'Source Agent')}\n </label>\n <select\n value={formAgent}\n onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>\n setFormAgent(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated 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 className=\"text-xs text-text-tertiary mt-0.5\">\n {tr('targets.agentConnectionHintPrefix', 'The agent that will establish the')}{' '}\n {formType}{' '}\n {tr('targets.agentConnectionHintSuffix', 'connection to this target.')}\n </p>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div>\n <label className=\"text-sm font-medium\">{tr('targets.host', 'Host')}</label>\n <input\n aria-label=\"control\"\n value={formHost}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormHost(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.hostPlaceholder', '192.168.1.100')}\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">{tr('tunnels.port', 'Port')}</label>\n <input\n aria-label=\"control\"\n type=\"number\"\n value={formPort}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormPort(Number(e.target.value))\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.username', 'Username')}\n </label>\n <input\n aria-label=\"control\"\n value={formUsername}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormUsername(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.usernamePlaceholder', 'root')}\n />\n </div>\n {formType === 'SSH' && (\n <>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.authentication', 'Authentication')}\n </label>\n <div className=\"flex gap-4 mt-1\">\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n aria-label=\"control\"\n type=\"radio\"\n checked={formAuthMethod === 'password'}\n onChange={() => setFormAuthMethod('password')}\n />\n {tr('targets.password', 'Password')}\n </label>\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n aria-label=\"control\"\n type=\"radio\"\n checked={formAuthMethod === 'key'}\n onChange={() => setFormAuthMethod('key')}\n />\n {tr('targets.privateKey', 'Private Key')}\n </label>\n </div>\n </div>\n {formAuthMethod === 'password' ? (\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.password', 'Password')}\n </label>\n <input\n aria-label=\"control\"\n type=\"password\"\n value={formPassword}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormPassword(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n />\n </div>\n ) : (\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.privateKeyPath', 'Private Key Path')}\n </label>\n <input\n aria-label=\"control\"\n value={formKeyPath}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormKeyPath(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.privateKeyPathPlaceholder', '~/.ssh/id_rsa')}\n />\n </div>\n )}\n </>\n )}\n </>\n )}\n\n {/* Label + Tags (all types) */}\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.labelOptional', 'Label (optional)')}\n </label>\n <input\n aria-label=\"control\"\n value={formLabel}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormLabel(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.labelPlaceholder', 'web-server-01')}\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.tagsCommaSeparated', 'Tags (comma-separated)')}\n </label>\n <input\n aria-label=\"control\"\n value={formTags}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFormTags(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.tagsPlaceholder', 'production, web')}\n />\n </div>\n </div>\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\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => setShowAddForm(false)}\n className=\"px-4 py-2 text-sm rounded-md border border-border-default hover:bg-bg-sunken transition-colors\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleCreate()}\n className=\"px-4 py-2 text-sm rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('targets.addTarget', 'Add Target')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Edit Target modal */}\n {editTarget && (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center pt-16 overflow-y-auto\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => !editLoading && setEditTarget(null))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm\"\n onClick={() => !editLoading && setEditTarget(null)}\n />\n <div\n className=\"relative bg-bg-elevated rounded-xl shadow-2xl border border-border-default max-w-lg w-full mx-4 p-6 space-y-4 mb-8\"\n onClick={(e: React.MouseEvent) => e.stopPropagation()}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold\">Edit Target</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">{editTarget.name}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => setEditTarget(null)}\n disabled={editLoading}\n className=\"p-1 rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X size={18} />\n </button>\n </div>\n\n {editError && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n {editError}\n </div>\n )}\n\n <div className=\"space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"targetspage-67\" className=\"text-sm font-medium\">\n Name\n </label>\n <input\n id=\"targetspage-67\"\n aria-label=\"Production Server\"\n value={editName}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEditName(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"Production Server\"\n />\n </div>\n\n {/* Label */}\n <div>\n <label htmlFor=\"targetspage-66\" className=\"text-sm font-medium\">\n Label (optional)\n </label>\n <input\n id=\"targetspage-66\"\n aria-label=\"web-server-01\"\n value={editLabel}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditLabel(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"web-server-01\"\n />\n </div>\n\n {/* Connection fields — only for non-DIRECT targets */}\n {editTarget.type !== 'DIRECT' && (\n <>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"targetspage-65\" className=\"text-sm font-medium\">\n Host\n </label>\n <input\n id=\"targetspage-65\"\n aria-label=\"192.168.1.100\"\n value={editHost}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditHost(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"192.168.1.100\"\n />\n </div>\n <div>\n <label htmlFor=\"targetspage-64\" className=\"text-sm font-medium\">\n Port\n </label>\n <input\n id=\"targetspage-64\"\n aria-label=\"control\"\n type=\"number\"\n value={editPort}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditPort(Number(e.target.value))\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"targetspage-63\" className=\"text-sm font-medium\">\n Username\n </label>\n <input\n id=\"targetspage-63\"\n aria-label=\"root\"\n value={editUsername}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditUsername(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"root\"\n />\n </div>\n\n {editTarget.type === 'SSH' && (\n <>\n <div>\n <label htmlFor=\"targetspage-62\" className=\"text-sm font-medium\">\n Update credentials (optional)\n </label>\n <div className=\"flex gap-4 mt-1\">\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n id=\"targetspage-62\"\n aria-label=\"control\"\n type=\"radio\"\n checked={editAuthMethod === 'password'}\n onChange={() => setEditAuthMethod('password')}\n />\n Password\n </label>\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n aria-label=\"control\"\n type=\"radio\"\n checked={editAuthMethod === 'key'}\n onChange={() => setEditAuthMethod('key')}\n />\n Private Key\n </label>\n </div>\n <p className=\"text-xs text-text-tertiary mt-0.5\">\n Leave blank to keep existing credentials.\n </p>\n </div>\n {editAuthMethod === 'password' ? (\n <div>\n <label htmlFor=\"targetspage-61\" className=\"text-sm font-medium\">\n New Password\n </label>\n <input\n id=\"targetspage-61\"\n aria-label=\"Leave blank to keep current\"\n type=\"password\"\n value={editPassword}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditPassword(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"Leave blank to keep current\"\n />\n </div>\n ) : (\n <div>\n <label htmlFor=\"targetspage-60\" className=\"text-sm font-medium\">\n New Private Key Path\n </label>\n <input\n id=\"targetspage-60\"\n aria-label=\"Leave blank to keep current (~/.ssh/id_rsa)\"\n value={editKeyPath}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditKeyPath(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"Leave blank to keep current (~/.ssh/id_rsa)\"\n />\n </div>\n )}\n </>\n )}\n </>\n )}\n\n {/* Tags */}\n <div>\n <label htmlFor=\"targetspage-59\" className=\"text-sm font-medium\">\n Tags (comma-separated)\n </label>\n <input\n id=\"targetspage-59\"\n aria-label=\"production, web\"\n value={editTags}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEditTags(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"production, web\"\n />\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => setEditTarget(null)}\n disabled={editLoading}\n className=\"px-4 py-2 text-sm rounded-lg border border-border-default hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSaveEdit()}\n disabled={editLoading}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm text-action-primary-text transition-colors hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {editLoading ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </div>\n </div>\n )}\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 {installJobTarget && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50\">\n <div className=\"bg-bg-elevated rounded-xl shadow-xl max-w-md w-full mx-4 p-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <h2 className=\"text-lg font-semibold break-words\">\n {tr('targets.installingAgentOn', 'Installing Agent on')} {installJobTarget.name}\n </h2>\n {installJob?.status !== 'running' && (\n <button\n type=\"button\"\n onClick={closeInstallDialog}\n className=\"p-1 rounded hover:bg-bg-sunken\"\n >\n <X size={16} />\n </button>\n )}\n </div>\n\n <div className=\"space-y-2 mt-4\">\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 className=\"animate-spin text-action-primary-bg\" size={16} />\n )}\n {step.status === 'completed' && (\n <CheckCircle className=\"text-status-success-text\" size={16} />\n )}\n {step.status === 'failed' && (\n <XCircle className=\"text-status-error-text\" size={16} />\n )}\n {step.status === 'skipped' && (\n <Check className=\"text-text-tertiary\" size={16} />\n )}\n {step.status === 'pending' && (\n <div className=\"size-4 rounded-full border-2 border-border-default\" />\n )}\n <div>\n <div className=\"text-sm font-medium\">\n {tr(`targets.installStep.${step.name}`, stepLabels[step.name] ?? step.name)}\n </div>\n {step.message && (\n <div className=\"text-xs text-text-tertiary\">{step.message}</div>\n )}\n </div>\n </div>\n ))\n ) : (\n <div className=\"flex items-center gap-3\">\n <Loader2 className=\"animate-spin text-action-primary-bg\" size={16} />\n <span className=\"text-sm\">\n {tr('targets.startingInstallJob', 'Starting install job...')}\n </span>\n </div>\n )}\n </div>\n\n {installJob?.status === 'completed' && (\n <div className=\"mt-4 rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\">\n {tr('targets.agentInstalledSuccess', 'Agent installed successfully!')}\n </div>\n )}\n {installJob?.status === 'failed' && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n {installJob.error ?? tr('targets.installationFailed', 'Installation failed.')}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={closeInstallDialog}\n disabled={installJob?.status === 'running'}\n className=\"mt-4 w-full px-4 py-2 rounded-md border border-border-default hover:bg-bg-sunken transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium\"\n >\n {!installJob || installJob.status === 'running'\n ? tr('targets.installing', 'Installing...')\n : tr('common.close', 'Close')}\n </button>\n </div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title={getQuotaDialogContent('environment').title}\n message={getQuotaDialogContent('environment').message}\n confirmLabel=\"View Billing\"\n cancelLabel=\"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 } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { isNative, nativeNotify } from '../utils/nativeBridge';\nimport { useLazyQuery } from '@apollo/client/react';\nimport {\n Plus,\n RefreshCw,\n Trash2,\n Server,\n Grid,\n List,\n CheckCircle,\n XCircle,\n Loader2,\n X,\n Check,\n Pencil,\n CheckSquare,\n XSquare,\n} from 'lucide-react';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ConfirmDialog,\n AgentSetupBanner,\n SearchableSelect,\n GetHelpButton,\n} 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 { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useSearchParams } from 'react-router-dom';\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} 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-action-primary-bg/10 text-action-primary-bg',\n SSH: 'bg-status-success-bg-subtle text-status-success-text',\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// Targets Page\n// ---------------------------------------------------------------------------\n\nexport function TargetsPage() {\n const { t } = useI18n();\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\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 const [testResult, setTestResult] = useState<{\n id: string;\n success: boolean;\n message: string;\n } | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n // 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 }\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, 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 setShowQuotaExhausted(true);\n return;\n }\n setTestResult({\n id: 'create',\n success: false,\n message: res.error.message || 'Failed to create target',\n });\n setTimeout(() => setTestResult(null), 5000);\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 setShowQuotaExhausted(true);\n return;\n }\n // Unexpected throw (network error, etc.) — surface inline rather\n // than letting it bubble silently into the React error boundary.\n setTestResult({\n id: 'create',\n success: false,\n message: err instanceof Error ? err.message : 'Failed to create target',\n });\n setTimeout(() => setTestResult(null), 5000);\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 refetchTargets,\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 setTestResult({ id, success: data.success, message: data.message || '' });\n setTimeout(() => setTestResult(null), 5000);\n void refetchTargets();\n }\n },\n [testTarget, refetchTargets]\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 setShowQuotaExhausted(true);\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 setShowQuotaExhausted(true);\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 }, [deleteTargetId, deleteTarget, 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 }, [selectedIds, deleteTarget, 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 },\n [uninstallAgent, 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 } 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 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 if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.TARGET_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Toast — reused for both Test Connection results and create-target\n errors (id: 'create'). Update-target errors surface inline in the\n edit modal's `editError` banner instead, so there's no 'update'\n branch here. BOFF-2626. */}\n {testResult && (\n <div\n className={`fixed top-4 right-4 z-[10000] rounded-md px-4 py-3 shadow-lg text-sm max-w-md ${\n testResult.success\n ? 'bg-status-success-bg text-status-success-text'\n : 'bg-status-error-bg text-status-error-text'\n }`}\n >\n {testResult.success\n ? tr('targets.connectionSuccessful', 'Connection successful')\n : testResult.id === 'create'\n ? tr('targets.createFailed', 'Failed to create target')\n : tr('targets.connectionFailed', 'Connection failed')}\n : {testResult.message}\n </div>\n )}\n\n <PageHeader\n title={tr('nav.targets', 'Targets')}\n description={`${stats.connected} of ${stats.total} targets online`}\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void refetchTargets()}\n disabled={loading}\n className=\"flex items-center justify-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('targets.refreshTitle', 'Refresh targets')}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\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 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('targets.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('targets.listViewTitle', 'List view')}\n >\n <List className=\"size-4 text-text-secondary\" />\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 items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <CheckSquare className=\"size-3.5\" />\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 bg-action-primary-bg/10 border border-action-primary-bg/20 rounded-lg\">\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 items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-status-error-text bg-status-error-bg border border-status-error-text/20 rounded-md hover:bg-status-error-bg/80 transition-colors\"\n >\n <Trash2 className=\"size-3\" /> {tr('common.delete', 'Delete')}\n </button>\n <button\n type=\"button\"\n onClick={() => setSelectedIds(new Set())}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs text-text-secondary hover:text-text-primary transition-colors\"\n >\n <XSquare className=\"size-3\" /> {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\">\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 return (\n <MotionItem key={target.id} variant=\"fadeRight\">\n <div\n id={`target-row-${target.id}`}\n className={`p-4 rounded-lg border transition-colors ${\n isSelectedTarget\n ? 'border-action-primary-bg bg-action-primary-bg/5 shadow-sm'\n : 'border-border-default hover:bg-bg-sunken/30'\n }`}\n >\n {/* Row 1: Info */}\n <div className=\"flex items-start gap-3\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={selectedIds.has(target.id)}\n onChange={() => toggleSelect(target.id)}\n className=\"rounded border-border-default 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\">{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 size={10} />\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\">\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-tertiary\">\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 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors\"\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 title={tr('targets.openTerminal', 'Open terminal')}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors disabled:opacity-60 disabled:cursor-not-allowed\"\n >\n {startingTerminalIds.has(target.id) ? (\n <>\n <Loader2 size={12} className=\"animate-spin\" />\n {tr('targets.starting', 'Starting…')}\n </>\n ) : (\n tr('targets.terminal', 'Terminal')\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => handleOpenEdit(target)}\n title=\"Edit target configuration\"\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-border-default bg-bg-surface hover:bg-bg-sunken transition-colors\"\n >\n <Pencil size={14} />\n Edit\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 items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('targets.installAgent', 'Install Agent')}\n </button>\n )}\n {target.type !== 'DIRECT' && installed && (\n <button\n type=\"button\"\n onClick={() => void handleUninstallAgent(target)}\n title={tr('targets.uninstallAgent', 'Uninstall agent')}\n className=\"flex items-center gap-1.5 rounded-md border border-status-error-text/20 px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg/80 transition-colors\"\n >\n {tr('targets.uninstall', 'Uninstall')}\n </button>\n )}\n <div className=\"hidden flex-1 sm:block\" />\n <button\n type=\"button\"\n onClick={() => setDeleteTargetId(target.id)}\n title={tr('targets.deleteTarget', 'Delete target')}\n className=\"flex items-center gap-1.5 rounded-md border border-status-error-border px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg/80 transition-colors\"\n >\n <X size={14} />\n {tr('common.delete', 'Delete')}\n </button>\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\" />}\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 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowAddForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\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 modal */}\n {showAddForm && (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center pt-16 overflow-y-auto\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShowAddForm(false))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm\"\n onClick={() => setShowAddForm(false)}\n />\n <div\n className=\"relative bg-bg-elevated rounded-xl shadow-2xl border border-border-default max-w-lg w-full mx-4 p-6 space-y-4 mb-8\"\n onClick={(e: React.MouseEvent) => e.stopPropagation()}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <h2 className=\"text-lg font-semibold\">{tr('targets.addTarget', 'Add Target')}</h2>\n <button\n type=\"button\"\n onClick={() => setShowAddForm(false)}\n className=\"p-1 rounded-lg hover:bg-bg-sunken transition-colors\"\n >\n <X size={18} />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n {/* Target Type */}\n <div>\n <label className=\"text-sm font-medium block mb-1.5\">\n {tr('targets.targetType', 'Target Type')}\n </label>\n <div 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 className={`px-3 py-2 text-sm font-medium rounded-lg border transition-colors ${\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=\"text-xs text-text-tertiary mt-1\">\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 {/* Name */}\n <div>\n <label className=\"text-sm font-medium\">{tr('webhooks.name', 'Name')}</label>\n <input\n aria-label=\"control\"\n value={formName}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFormName(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={\n formType === 'DIRECT'\n ? tr('targets.namePlaceholderDirect', 'My Laptop')\n : tr('targets.namePlaceholderServer', 'Production Server')\n }\n />\n </div>\n\n {/* Description (all types) */}\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.descriptionOptional', 'Description (optional)')}\n </label>\n <input\n aria-label=\"control\"\n value={formDescription}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormDescription(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr(\n 'targets.descriptionPlaceholder',\n 'Brief description of this target'\n )}\n />\n </div>\n\n {/* Connection fields — only for SSH */}\n {formType !== 'DIRECT' && (\n <>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.sourceAgent', 'Source Agent')}\n </label>\n <select\n value={formAgent}\n onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>\n setFormAgent(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated 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 className=\"text-xs text-text-tertiary mt-0.5\">\n {tr('targets.agentConnectionHintPrefix', 'The agent that will establish the')}{' '}\n {formType}{' '}\n {tr('targets.agentConnectionHintSuffix', 'connection to this target.')}\n </p>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div>\n <label className=\"text-sm font-medium\">{tr('targets.host', 'Host')}</label>\n <input\n aria-label=\"control\"\n value={formHost}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormHost(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.hostPlaceholder', '192.168.1.100')}\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">{tr('tunnels.port', 'Port')}</label>\n <input\n aria-label=\"control\"\n type=\"number\"\n value={formPort}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormPort(Number(e.target.value))\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.username', 'Username')}\n </label>\n <input\n aria-label=\"control\"\n value={formUsername}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormUsername(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.usernamePlaceholder', 'root')}\n />\n </div>\n {formType === 'SSH' && (\n <>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.authentication', 'Authentication')}\n </label>\n <div className=\"flex gap-4 mt-1\">\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n aria-label=\"control\"\n type=\"radio\"\n checked={formAuthMethod === 'password'}\n onChange={() => setFormAuthMethod('password')}\n />\n {tr('targets.password', 'Password')}\n </label>\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n aria-label=\"control\"\n type=\"radio\"\n checked={formAuthMethod === 'key'}\n onChange={() => setFormAuthMethod('key')}\n />\n {tr('targets.privateKey', 'Private Key')}\n </label>\n </div>\n </div>\n {formAuthMethod === 'password' ? (\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.password', 'Password')}\n </label>\n <input\n aria-label=\"control\"\n type=\"password\"\n value={formPassword}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormPassword(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n />\n </div>\n ) : (\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.privateKeyPath', 'Private Key Path')}\n </label>\n <input\n aria-label=\"control\"\n value={formKeyPath}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormKeyPath(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.privateKeyPathPlaceholder', '~/.ssh/id_rsa')}\n />\n </div>\n )}\n </>\n )}\n </>\n )}\n\n {/* Label + Tags (all types) */}\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.labelOptional', 'Label (optional)')}\n </label>\n <input\n aria-label=\"control\"\n value={formLabel}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setFormLabel(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.labelPlaceholder', 'web-server-01')}\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">\n {tr('targets.tagsCommaSeparated', 'Tags (comma-separated)')}\n </label>\n <input\n aria-label=\"control\"\n value={formTags}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFormTags(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder={tr('targets.tagsPlaceholder', 'production, web')}\n />\n </div>\n </div>\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\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => setShowAddForm(false)}\n className=\"px-4 py-2 text-sm rounded-md border border-border-default hover:bg-bg-sunken transition-colors\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleCreate()}\n className=\"px-4 py-2 text-sm rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('targets.addTarget', 'Add Target')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Edit Target modal */}\n {editTarget && (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center pt-16 overflow-y-auto\">\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => !editLoading && setEditTarget(null))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay backdrop-blur-sm\"\n onClick={() => !editLoading && setEditTarget(null)}\n />\n <div\n className=\"relative bg-bg-elevated rounded-xl shadow-2xl border border-border-default max-w-lg w-full mx-4 p-6 space-y-4 mb-8\"\n onClick={(e: React.MouseEvent) => e.stopPropagation()}\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold\">Edit Target</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">{editTarget.name}</p>\n </div>\n <button\n type=\"button\"\n onClick={() => setEditTarget(null)}\n disabled={editLoading}\n className=\"p-1 rounded-lg hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X size={18} />\n </button>\n </div>\n\n {editError && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n {editError}\n </div>\n )}\n\n <div className=\"space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"targetspage-67\" className=\"text-sm font-medium\">\n Name\n </label>\n <input\n id=\"targetspage-67\"\n aria-label=\"Production Server\"\n value={editName}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEditName(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"Production Server\"\n />\n </div>\n\n {/* Label */}\n <div>\n <label htmlFor=\"targetspage-66\" className=\"text-sm font-medium\">\n Label (optional)\n </label>\n <input\n id=\"targetspage-66\"\n aria-label=\"web-server-01\"\n value={editLabel}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditLabel(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"web-server-01\"\n />\n </div>\n\n {/* Connection fields — only for non-DIRECT targets */}\n {editTarget.type !== 'DIRECT' && (\n <>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"targetspage-65\" className=\"text-sm font-medium\">\n Host\n </label>\n <input\n id=\"targetspage-65\"\n aria-label=\"192.168.1.100\"\n value={editHost}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditHost(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"192.168.1.100\"\n />\n </div>\n <div>\n <label htmlFor=\"targetspage-64\" className=\"text-sm font-medium\">\n Port\n </label>\n <input\n id=\"targetspage-64\"\n aria-label=\"control\"\n type=\"number\"\n value={editPort}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditPort(Number(e.target.value))\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n />\n </div>\n </div>\n\n <div>\n <label htmlFor=\"targetspage-63\" className=\"text-sm font-medium\">\n Username\n </label>\n <input\n id=\"targetspage-63\"\n aria-label=\"root\"\n value={editUsername}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditUsername(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"root\"\n />\n </div>\n\n {editTarget.type === 'SSH' && (\n <>\n <div>\n <label htmlFor=\"targetspage-62\" className=\"text-sm font-medium\">\n Update credentials (optional)\n </label>\n <div className=\"flex gap-4 mt-1\">\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n id=\"targetspage-62\"\n aria-label=\"control\"\n type=\"radio\"\n checked={editAuthMethod === 'password'}\n onChange={() => setEditAuthMethod('password')}\n />\n Password\n </label>\n <label className=\"flex items-center gap-2 cursor-pointer text-sm\">\n <input\n aria-label=\"control\"\n type=\"radio\"\n checked={editAuthMethod === 'key'}\n onChange={() => setEditAuthMethod('key')}\n />\n Private Key\n </label>\n </div>\n <p className=\"text-xs text-text-tertiary mt-0.5\">\n Leave blank to keep existing credentials.\n </p>\n </div>\n {editAuthMethod === 'password' ? (\n <div>\n <label htmlFor=\"targetspage-61\" className=\"text-sm font-medium\">\n New Password\n </label>\n <input\n id=\"targetspage-61\"\n aria-label=\"Leave blank to keep current\"\n type=\"password\"\n value={editPassword}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditPassword(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"Leave blank to keep current\"\n />\n </div>\n ) : (\n <div>\n <label htmlFor=\"targetspage-60\" className=\"text-sm font-medium\">\n New Private Key Path\n </label>\n <input\n id=\"targetspage-60\"\n aria-label=\"Leave blank to keep current (~/.ssh/id_rsa)\"\n value={editKeyPath}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setEditKeyPath(e.target.value)\n }\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"Leave blank to keep current (~/.ssh/id_rsa)\"\n />\n </div>\n )}\n </>\n )}\n </>\n )}\n\n {/* Tags */}\n <div>\n <label htmlFor=\"targetspage-59\" className=\"text-sm font-medium\">\n Tags (comma-separated)\n </label>\n <input\n id=\"targetspage-59\"\n aria-label=\"production, web\"\n value={editTags}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEditTags(e.target.value)}\n className=\"w-full mt-1 px-3 py-2 rounded-lg border border-border-default bg-bg-elevated text-sm\"\n placeholder=\"production, web\"\n />\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => setEditTarget(null)}\n disabled={editLoading}\n className=\"px-4 py-2 text-sm rounded-lg border border-border-default hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSaveEdit()}\n disabled={editLoading}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm text-action-primary-text transition-colors hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {editLoading ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </div>\n </div>\n )}\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 {installJobTarget && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50\">\n <div className=\"bg-bg-elevated rounded-xl shadow-xl max-w-md w-full mx-4 p-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <h2 className=\"text-lg font-semibold break-words\">\n {tr('targets.installingAgentOn', 'Installing Agent on')} {installJobTarget.name}\n </h2>\n {installJob?.status !== 'running' && (\n <button\n type=\"button\"\n onClick={closeInstallDialog}\n className=\"p-1 rounded hover:bg-bg-sunken\"\n >\n <X size={16} />\n </button>\n )}\n </div>\n\n <div className=\"space-y-2 mt-4\">\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 className=\"animate-spin text-action-primary-bg\" size={16} />\n )}\n {step.status === 'completed' && (\n <CheckCircle className=\"text-status-success-text\" size={16} />\n )}\n {step.status === 'failed' && (\n <XCircle className=\"text-status-error-text\" size={16} />\n )}\n {step.status === 'skipped' && (\n <Check className=\"text-text-tertiary\" size={16} />\n )}\n {step.status === 'pending' && (\n <div className=\"size-4 rounded-full border-2 border-border-default\" />\n )}\n <div>\n <div className=\"text-sm font-medium\">\n {tr(`targets.installStep.${step.name}`, stepLabels[step.name] ?? step.name)}\n </div>\n {step.message && (\n <div className=\"text-xs text-text-tertiary\">{step.message}</div>\n )}\n </div>\n </div>\n ))\n ) : (\n <div className=\"flex items-center gap-3\">\n <Loader2 className=\"animate-spin text-action-primary-bg\" size={16} />\n <span className=\"text-sm\">\n {tr('targets.startingInstallJob', 'Starting install job...')}\n </span>\n </div>\n )}\n </div>\n\n {installJob?.status === 'completed' && (\n <div className=\"mt-4 rounded-lg border border-status-success-border bg-status-success-bg-subtle p-3 text-sm text-status-success-text\">\n {tr('targets.agentInstalledSuccess', 'Agent installed successfully!')}\n </div>\n )}\n {installJob?.status === 'failed' && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text\">\n {installJob.error ?? tr('targets.installationFailed', 'Installation failed.')}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={closeInstallDialog}\n disabled={installJob?.status === 'running'}\n className=\"mt-4 w-full px-4 py-2 rounded-md border border-border-default hover:bg-bg-sunken transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium\"\n >\n {!installJob || installJob.status === 'running'\n ? tr('targets.installing', 'Installing...')\n : tr('common.close', 'Close')}\n </button>\n </div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title={getQuotaDialogContent('environment').title}\n message={getQuotaDialogContent('environment').message}\n confirmLabel=\"View Billing\"\n cancelLabel=\"Dismiss\"\n variant=\"warning\"\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|