@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/dist/components/actions/ActionCard.js +2 -2
  2. package/dist/components/actions/ActionCard.js.map +1 -1
  3. package/dist/components/actions/ExecutionHistory.js +1 -1
  4. package/dist/components/actions/ExecutionHistory.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
  6. package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
  7. package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
  8. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
  10. package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
  12. package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
  13. package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
  14. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  15. package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
  16. package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
  17. package/dist/components/agent-manager/AgentManagerTab.js +1 -1
  18. package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
  19. package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
  20. package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
  21. package/dist/components/agent-manager/BridleConfigPanel.js +23 -23
  22. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  23. package/dist/components/agent-manager/PluginInstallModal.js +1 -1
  24. package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
  25. package/dist/components/agent-manager/ProfilesPanel.js +5 -5
  26. package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
  27. package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
  28. package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
  29. package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
  30. package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
  31. package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
  32. package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
  33. package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
  34. package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
  35. package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
  36. package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
  37. package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
  38. package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
  39. package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
  40. package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
  41. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
  42. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
  43. package/dist/components/agents/AgentCard.js +14 -11
  44. package/dist/components/agents/AgentCard.js.map +1 -1
  45. package/dist/components/ai/AIContextsTab.js +1 -1
  46. package/dist/components/ai/AIContextsTab.js.map +1 -1
  47. package/dist/components/ai/AIOverviewTab.js +4 -4
  48. package/dist/components/ai/AIOverviewTab.js.map +1 -1
  49. package/dist/components/ai/AIPlanPanel.js +4 -4
  50. package/dist/components/ai/AIPlanPanel.js.map +1 -1
  51. package/dist/components/ai/AIPlaygroundTab.js +1 -1
  52. package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
  53. package/dist/components/ai/AIPromptsTab.js +2 -2
  54. package/dist/components/ai/AIPromptsTab.js.map +1 -1
  55. package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
  56. package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
  57. package/dist/components/ai/AIQueueTab.js +4 -4
  58. package/dist/components/ai/AIQueueTab.js.map +1 -1
  59. package/dist/components/ai/AISessionsTab.js +4 -4
  60. package/dist/components/ai/AISessionsTab.js.map +1 -1
  61. package/dist/components/ai/AITasksTab.js +1 -1
  62. package/dist/components/ai/AITasksTab.js.map +1 -1
  63. package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
  64. package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
  65. package/dist/components/ai/PluginHarnessPicker.js +3 -3
  66. package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
  67. package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
  68. package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
  69. package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
  70. package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
  71. package/dist/components/audit/AuditDeleteDialog.js +2 -2
  72. package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
  73. package/dist/components/audit/AuditExportButton.js +5 -5
  74. package/dist/components/audit/AuditExportButton.js.map +1 -1
  75. package/dist/components/audit/AuditLogDetail.js +9 -9
  76. package/dist/components/audit/AuditLogDetail.js.map +1 -1
  77. package/dist/components/audit/AuditLogFilters.js +1 -1
  78. package/dist/components/audit/AuditLogFilters.js.map +1 -1
  79. package/dist/components/audit/AuditLogStats.js +1 -1
  80. package/dist/components/audit/AuditLogStats.js.map +1 -1
  81. package/dist/components/audit/AuditLogTable.js +28 -28
  82. package/dist/components/audit/AuditLogTable.js.map +1 -1
  83. package/dist/components/catalog/CatalogCard.js +3 -3
  84. package/dist/components/catalog/CatalogCard.js.map +1 -1
  85. package/dist/components/catalog/CatalogForm.js +1 -1
  86. package/dist/components/catalog/CatalogForm.js.map +1 -1
  87. package/dist/components/common/ActionAgentTargetNotice.js +6 -6
  88. package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
  89. package/dist/components/common/CommandEditor.js +1 -1
  90. package/dist/components/common/CommandEditor.js.map +1 -1
  91. package/dist/components/docs/DocsPageTree.js +1 -1
  92. package/dist/components/docs/DocsPageTree.js.map +1 -1
  93. package/dist/components/docs/DocsPreviewPanel.js +1 -1
  94. package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
  95. package/dist/components/docs/DocsRevisionHistory.js +4 -4
  96. package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
  97. package/dist/components/docs/DocsSettingsPanel.js +7 -7
  98. package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
  99. package/dist/components/docs/DocsTabPanel.js +1 -1
  100. package/dist/components/docs/DocsTabPanel.js.map +1 -1
  101. package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
  102. package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
  103. package/dist/components/graph/MeshDeployWizard.js +3 -3
  104. package/dist/components/graph/MeshDeployWizard.js.map +1 -1
  105. package/dist/components/graph/MeshRequestPanel.js +2 -2
  106. package/dist/components/graph/MeshRequestPanel.js.map +1 -1
  107. package/dist/components/idp-components/ComponentCard.js +5 -5
  108. package/dist/components/idp-components/ComponentCard.js.map +1 -1
  109. package/dist/components/idp-components/ComponentForm.js +1 -1
  110. package/dist/components/idp-components/ComponentForm.js.map +1 -1
  111. package/dist/components/idp-components/DependencyGraphTab.js +7 -7
  112. package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
  113. package/dist/components/logs/LogViewer.js +14 -14
  114. package/dist/components/logs/LogViewer.js.map +1 -1
  115. package/dist/components/notes/NoteCard.js +1 -1
  116. package/dist/components/notes/NoteCard.js.map +1 -1
  117. package/dist/components/notes/NoteEditor.js +2 -2
  118. package/dist/components/notes/NoteEditor.js.map +1 -1
  119. package/dist/components/notes/NoteList.js +1 -1
  120. package/dist/components/notes/NoteList.js.map +1 -1
  121. package/dist/components/notes/NotesTabPanel.js +1 -1
  122. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  123. package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
  124. package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
  125. package/dist/components/scorecard/ScorecardPanel.js +3 -3
  126. package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
  127. package/dist/components/security/SecurityScanList.js +11 -11
  128. package/dist/components/security/SecurityScanList.js.map +1 -1
  129. package/dist/components/sessions/SessionCard.js +26 -19
  130. package/dist/components/sessions/SessionCard.js.map +1 -1
  131. package/dist/components/sessions/SessionStatusBadge.js +7 -7
  132. package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
  133. package/dist/components/shared/AgentSelector.js +4 -4
  134. package/dist/components/shared/AgentSelector.js.map +1 -1
  135. package/dist/components/shared/AgentSetupBanner.js +3 -3
  136. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  137. package/dist/components/shared/Card.js +4 -4
  138. package/dist/components/shared/Card.js.map +1 -1
  139. package/dist/components/shared/ErrorState.js +15 -9
  140. package/dist/components/shared/ErrorState.js.map +1 -1
  141. package/dist/components/shared/FilterDropdown.js +2 -2
  142. package/dist/components/shared/FilterDropdown.js.map +1 -1
  143. package/dist/components/shared/PageSkeleton.js +6 -6
  144. package/dist/components/shared/PageSkeleton.js.map +1 -1
  145. package/dist/components/shared/SearchInput.js +1 -1
  146. package/dist/components/shared/SearchInput.js.map +1 -1
  147. package/dist/components/shared/StatCard.js +7 -7
  148. package/dist/components/shared/StatCard.js.map +1 -1
  149. package/dist/components/templates/PublishTemplateForm.js +2 -2
  150. package/dist/components/templates/PublishTemplateForm.js.map +1 -1
  151. package/dist/components/templates/TemplateCard.js +5 -5
  152. package/dist/components/templates/TemplateCard.js.map +1 -1
  153. package/dist/components/templates/TemplateWizard.js +2 -2
  154. package/dist/components/templates/TemplateWizard.js.map +1 -1
  155. package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
  156. package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
  157. package/dist/components/tunnels/ProviderBadge.js +1 -1
  158. package/dist/components/tunnels/ProviderBadge.js.map +1 -1
  159. package/dist/components/tunnels/TunnelCard.js +16 -16
  160. package/dist/components/tunnels/TunnelCard.js.map +1 -1
  161. package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
  162. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  163. package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
  164. package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
  165. package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
  166. package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
  167. package/dist/components/vibecalendar/LogsDrawer.js +2 -2
  168. package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
  169. package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
  170. package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
  171. package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
  172. package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
  173. package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
  174. package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
  175. package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
  176. package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
  177. package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
  178. package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
  179. package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
  180. package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
  181. package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
  182. package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
  183. package/dist/components/vibecalendar/types.js +6 -6
  184. package/dist/components/vibecalendar/types.js.map +1 -1
  185. package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
  186. package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
  187. package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
  188. package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
  189. package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
  190. package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
  191. package/dist/components/vibedeck/VibeDeckForm.js +8 -8
  192. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  193. package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
  194. package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
  195. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
  196. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
  197. package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
  198. package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
  199. package/dist/components/vibes/VibeCard.js +22 -18
  200. package/dist/components/vibes/VibeCard.js.map +1 -1
  201. package/dist/components/vibes/VibeList.js +16 -12
  202. package/dist/components/vibes/VibeList.js.map +1 -1
  203. package/dist/pages/ActionsPage.js +4 -3
  204. package/dist/pages/ActionsPage.js.map +1 -1
  205. package/dist/pages/AgentDetailsPage.js +9 -9
  206. package/dist/pages/AgentDetailsPage.js.map +1 -1
  207. package/dist/pages/AgentsLandingPage.js +4 -4
  208. package/dist/pages/AgentsLandingPage.js.map +1 -1
  209. package/dist/pages/AgentsPage.js +14 -14
  210. package/dist/pages/AgentsPage.js.map +1 -1
  211. package/dist/pages/AnalyticsPage.js +8 -8
  212. package/dist/pages/AnalyticsPage.js.map +1 -1
  213. package/dist/pages/CatalogDetailPage.js +11 -11
  214. package/dist/pages/CatalogDetailPage.js.map +1 -1
  215. package/dist/pages/CatalogPage.js +12 -9
  216. package/dist/pages/CatalogPage.js.map +1 -1
  217. package/dist/pages/ComponentDetailPage.js +7 -7
  218. package/dist/pages/ComponentDetailPage.js.map +1 -1
  219. package/dist/pages/ComponentsPage.js +12 -9
  220. package/dist/pages/ComponentsPage.js.map +1 -1
  221. package/dist/pages/EnvTopologyPage.js +2 -2
  222. package/dist/pages/EnvTopologyPage.js.map +1 -1
  223. package/dist/pages/GettingStartedPage.js +12 -12
  224. package/dist/pages/GettingStartedPage.js.map +1 -1
  225. package/dist/pages/HealthPage.js +12 -12
  226. package/dist/pages/HealthPage.js.map +1 -1
  227. package/dist/pages/LogsPage.js +4 -4
  228. package/dist/pages/LogsPage.js.map +1 -1
  229. package/dist/pages/NoteDetailsPage.js +5 -5
  230. package/dist/pages/NoteDetailsPage.js.map +1 -1
  231. package/dist/pages/OverviewPage.js +74 -41
  232. package/dist/pages/OverviewPage.js.map +1 -1
  233. package/dist/pages/PluginsCatalogPage.js +6 -3
  234. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  235. package/dist/pages/ScheduledJobDetailPage.js +7 -7
  236. package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
  237. package/dist/pages/SessionDetailsPage.js +21 -21
  238. package/dist/pages/SessionDetailsPage.js.map +1 -1
  239. package/dist/pages/SessionsPage.js +20 -20
  240. package/dist/pages/SessionsPage.js.map +1 -1
  241. package/dist/pages/SettingsPage.js +64 -52
  242. package/dist/pages/SettingsPage.js.map +1 -1
  243. package/dist/pages/SharedSessionPage.js +21 -21
  244. package/dist/pages/SharedSessionPage.js.map +1 -1
  245. package/dist/pages/SharedVibeDeckPage.js +5 -5
  246. package/dist/pages/SharedVibeDeckPage.js.map +1 -1
  247. package/dist/pages/TargetDetailsPage.js +56 -65
  248. package/dist/pages/TargetDetailsPage.js.map +1 -1
  249. package/dist/pages/TargetsPage.js +4 -4
  250. package/dist/pages/TargetsPage.js.map +1 -1
  251. package/dist/pages/TemplateDetailPage.js +10 -10
  252. package/dist/pages/TemplateDetailPage.js.map +1 -1
  253. package/dist/pages/TemplatesPage.js +6 -5
  254. package/dist/pages/TemplatesPage.js.map +1 -1
  255. package/dist/pages/VibeAuditPage.js +3 -3
  256. package/dist/pages/VibeAuditPage.js.map +1 -1
  257. package/dist/pages/VibeCalendarPage.js +3 -3
  258. package/dist/pages/VibeCalendarPage.js.map +1 -1
  259. package/dist/pages/VibeDeckPage.js +3 -3
  260. package/dist/pages/VibeDeckPage.js.map +1 -1
  261. package/dist/pages/VibeDetailsPage.js +15 -15
  262. package/dist/pages/VibeDetailsPage.js.map +1 -1
  263. package/dist/pages/VibeTaskDetailPage.js +5 -5
  264. package/dist/pages/VibeTaskDetailPage.js.map +1 -1
  265. package/dist/pages/VibesPage.js +18 -18
  266. package/dist/pages/VibesPage.js.map +1 -1
  267. package/dist/pages/WebhooksPage.js +11 -11
  268. package/dist/pages/WebhooksPage.js.map +1 -1
  269. package/dist/pages/ai/AIBookmarksPage.js +1 -1
  270. package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
  271. package/dist/pages/ai/AISessionDetailPage.js +6 -6
  272. package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
  273. package/dist/pages/tunnels/AuditTab.js +1 -1
  274. package/dist/pages/tunnels/AuditTab.js.map +1 -1
  275. package/dist/pages/tunnels/DomainsTab.js +1 -1
  276. package/dist/pages/tunnels/DomainsTab.js.map +1 -1
  277. package/dist/pages/tunnels/ProvidersTab.js +1 -1
  278. package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
  279. package/dist/pages/tunnels/SessionsTab.js +1 -1
  280. package/dist/pages/tunnels/SessionsTab.js.map +1 -1
  281. package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
  282. package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
  283. package/dist/pages/tunnels/UsageTab.js +1 -1
  284. package/dist/pages/tunnels/UsageTab.js.map +1 -1
  285. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
  286. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
  287. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
  288. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
  289. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
  290. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
  291. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
  292. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
  293. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AITasksTab.js","names":[],"sources":["../../../src/components/ai/AITasksTab.tsx"],"sourcesContent":["/**\n * AI Tasks Tab — Kanban board backed by PlanMagnet's WorkItem model\n */\n\nimport { useState, useCallback, useEffect, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useApolloClient } from '@apollo/client/react';\nimport { Plus, X, GripVertical, RefreshCw, AlertCircle } from 'lucide-react';\nimport {\n Card,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n SelectionCheckbox,\n} from '@/components/shared';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { useTr } from '../../shared/hooks/useTr';\n\nconst AI_TASK_LABEL = 'ai-task';\nconst AI_PROJECT_KEY = 'AI';\nconst AI_PROJECT_STORAGE_KEY = 'vibecontrols:ai-project-id';\n\ntype TaskStatus = 'BACKLOG' | 'TODO' | 'IN_PROGRESS' | 'IN_REVIEW' | 'DONE';\ntype TaskPriority = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';\n\ninterface WorkItem {\n id: string;\n key: string;\n title: string;\n description?: string | null;\n type: string;\n status: TaskStatus;\n priority: TaskPriority;\n assigneeId?: string | null;\n storyPoints?: number | null;\n dueDate?: string | null;\n labels: string[];\n tags: string[];\n metadata?: Record<string, unknown> | null;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n}\n\nconst columns: { id: TaskStatus; label: string; color: string }[] = [\n { id: 'BACKLOG', label: 'Backlog', color: 'border-t-text-secondary' },\n { id: 'TODO', label: 'To Do', color: 'border-t-action-primary-bg' },\n { id: 'IN_PROGRESS', label: 'In Progress', color: 'border-t-status-warning-text' },\n { id: 'IN_REVIEW', label: 'In Review', color: 'border-t-action-primary-bg' },\n { id: 'DONE', label: 'Done', color: 'border-t-status-success-text' },\n];\n\nconst priorityLabels: Record<string, { label: string; color: string }> = {\n CRITICAL: { label: 'Critical', color: 'text-status-error-text' },\n HIGH: { label: 'High', color: 'text-status-warning-text' },\n MEDIUM: { label: 'Medium', color: 'text-action-primary-bg' },\n LOW: { label: 'Low', color: 'text-text-secondary' },\n};\n\nconst WORKITEM_FIELDS = `id key title description type status priority assigneeId storyPoints dueDate labels tags metadata createdBy createdAt updatedAt`;\n\nexport function AITasksTab() {\n const tr = useTr();\n const apolloClient = useApolloClient();\n const { workspaceId: ctxWorkspaceId } = useVibeControls();\n // Fall back to URL param if context doesn't have workspaceId\n const workspaceId =\n ctxWorkspaceId || new URLSearchParams(window.location.search).get('workspace') || '';\n\n const [projectId, setProjectId] = useState<string | null>(() => {\n try {\n return localStorage.getItem(getProfileScopedKey(AI_PROJECT_STORAGE_KEY));\n } catch {\n return null;\n }\n });\n const [items, setItems] = useState<WorkItem[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedTaskIds, setSelectedTaskIds] = useState<Set<string>>(new Set());\n const [bulkMoveStatus, setBulkMoveStatus] = useState<TaskStatus>('TODO');\n const [bulkDeleting, setBulkDeleting] = useState(false);\n const [bulkMoving, setBulkMoving] = useState(false);\n const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);\n\n const gqlFetch = useCallback(\n async <T,>(query: string, variables?: Record<string, unknown>): Promise<T> => {\n const document = gql(query);\n const trimmed = query.trimStart();\n\n if (trimmed.startsWith('mutation')) {\n const result = await apolloClient.mutate<T>({ mutation: document, variables });\n if (result.error) throw new Error(result.error.message || 'Mutation failed');\n if (!result.data) throw new Error('Mutation returned no data');\n return result.data;\n }\n\n const result = await apolloClient.query<T>({\n query: document,\n variables,\n fetchPolicy: 'network-only',\n });\n if (result.error) throw new Error(result.error.message || 'Query failed');\n if (!result.data) throw new Error('Query returned no data');\n return result.data;\n },\n [apolloClient]\n );\n\n // ── Project auto-discovery ─────────────────────────────────────────\n useEffect(() => {\n if (projectId || !workspaceId) return;\n let cancelled = false;\n (async () => {\n try {\n const data = await gqlFetch<{\n planmagnetProjects: { items: { id: string; key: string }[] };\n }>(`query($wsId: ID!) { planmagnetProjects(workspaceId: $wsId) { items { id key } } }`, {\n wsId: workspaceId,\n });\n const existing = data.planmagnetProjects.items.find((p) => p.key === AI_PROJECT_KEY);\n if (existing && !cancelled) {\n setProjectId(existing.id);\n try {\n localStorage.setItem(getProfileScopedKey(AI_PROJECT_STORAGE_KEY), existing.id);\n } catch {\n // localStorage unavailable\n }\n return;\n }\n const createData = await gqlFetch<{ planmagnetCreateProject: { id: string } }>(\n `mutation($wsId: ID!, $input: PlanmagnetCreateProjectInput!) { planmagnetCreateProject(workspaceId: $wsId, input: $input) { id } }`,\n {\n wsId: workspaceId,\n input: {\n name: 'AI Tasks',\n key: AI_PROJECT_KEY,\n ownerId: 'system',\n methodology: 'KANBAN',\n tags: [AI_TASK_LABEL],\n },\n }\n );\n if (createData.planmagnetCreateProject.id && !cancelled) {\n setProjectId(createData.planmagnetCreateProject.id);\n try {\n localStorage.setItem(\n getProfileScopedKey(AI_PROJECT_STORAGE_KEY),\n createData.planmagnetCreateProject.id\n );\n } catch {\n // localStorage unavailable\n }\n }\n } catch (err) {\n console.error('Failed to find/create AI project:', err);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [workspaceId, projectId, apolloClient]);\n\n // ── Fetch tasks ────────────────────────────────────────────────────\n const fetchTasks = useCallback(async () => {\n if (!workspaceId) return;\n setLoading(true);\n setError(null);\n try {\n const data = await gqlFetch<{\n planmagnetWorkItems: { items: WorkItem[]; totalCount: number };\n }>(\n `query($wsId: ID!, $filter: PlanmagnetWorkItemFilter, $pagination: PlanmagnetPaginationInput) {\n planmagnetWorkItems(workspaceId: $wsId, filter: $filter, pagination: $pagination) {\n items { ${WORKITEM_FIELDS} }\n totalCount\n }\n }`,\n { wsId: workspaceId, filter: { type: 'TASK' }, pagination: { first: 200 } }\n );\n const all = data.planmagnetWorkItems.items ?? [];\n setItems(\n all.filter((i) => i.labels?.includes(AI_TASK_LABEL) || i.tags?.includes(AI_TASK_LABEL))\n );\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load tasks');\n } finally {\n setLoading(false);\n }\n }, [workspaceId, apolloClient]);\n\n useEffect(() => {\n fetchTasks();\n }, [fetchTasks]);\n\n const selectedTasks = useMemo(\n () => items.filter((item) => selectedTaskIds.has(item.id)),\n [items, selectedTaskIds]\n );\n const allVisibleSelected =\n items.length > 0 && items.every((item) => selectedTaskIds.has(item.id));\n const partiallySelected =\n items.some((item) => selectedTaskIds.has(item.id)) && !allVisibleSelected;\n\n // ── Create ─────────────────────────────────────────────────────────\n const [showCreate, setShowCreate] = useState(false);\n const [newTitle, setNewTitle] = useState('');\n const [newDescription, setNewDescription] = useState('');\n const [newStatus, setNewStatus] = useState<TaskStatus>('BACKLOG');\n const [newPriority, setNewPriority] = useState<TaskPriority>('MEDIUM');\n const [creating, setCreating] = useState(false);\n\n const handleCreate = async () => {\n if (!newTitle.trim() || !workspaceId || !projectId) return;\n setCreating(true);\n try {\n await gqlFetch(\n `mutation($wsId: ID!, $input: PlanmagnetCreateWorkItemInput!) { planmagnetCreateWorkItem(workspaceId: $wsId, input: $input) { id } }`,\n {\n wsId: workspaceId,\n input: {\n title: newTitle.trim(),\n description: newDescription.trim() || undefined,\n type: 'TASK',\n status: newStatus,\n priority: newPriority,\n labels: [AI_TASK_LABEL],\n projectId,\n },\n }\n );\n setNewTitle('');\n setNewDescription('');\n setNewStatus('BACKLOG');\n setNewPriority('MEDIUM');\n setShowCreate(false);\n fetchTasks();\n } catch (err) {\n console.error('Failed to create task:', err);\n } finally {\n setCreating(false);\n }\n };\n\n // ── Move (drag-drop status change) ─────────────────────────────────\n const [draggedId, setDraggedId] = useState<string | null>(null);\n\n const handleStatusChange = useCallback(\n async (taskId: string, targetStatus: TaskStatus) => {\n // Optimistic update\n setItems((prev) => prev.map((i) => (i.id === taskId ? { ...i, status: targetStatus } : i)));\n try {\n await gqlFetch(\n 'mutation($id: ID!, $input: PlanmagnetUpdateWorkItemInput!) { planmagnetUpdateWorkItem(id: $id, input: $input) { id } }',\n { id: taskId, input: { status: targetStatus } }\n );\n } catch (err) {\n console.error('Failed to move task:', err);\n fetchTasks(); // revert on failure\n }\n },\n [fetchTasks, gqlFetch]\n );\n\n const handleDrop = useCallback(\n (targetStatus: TaskStatus) => {\n if (!draggedId) return;\n const item = items.find((i) => i.id === draggedId);\n setDraggedId(null);\n if (!item || item.status === targetStatus) return;\n handleStatusChange(draggedId, targetStatus);\n },\n [draggedId, items, handleStatusChange]\n );\n\n // ── Delete ─────────────────────────────────────────────────────────\n const handleDelete = useCallback(\n async (id: string) => {\n setItems((prev) => prev.filter((i) => i.id !== id)); // optimistic\n try {\n await gqlFetch('mutation($id: ID!) { planmagnetDeleteWorkItem(id: $id) }', { id });\n } catch (err) {\n console.error('Failed to delete task:', err);\n fetchTasks(); // revert\n }\n },\n [fetchTasks, gqlFetch]\n );\n\n const handleBulkDelete = useCallback(async () => {\n if (selectedTasks.length === 0) return;\n setBulkDeleting(true);\n try {\n await Promise.allSettled(\n selectedTasks.map((task) =>\n gqlFetch('mutation($id: ID!) { planmagnetDeleteWorkItem(id: $id) }', { id: task.id })\n )\n );\n setSelectedTaskIds(new Set());\n setShowBulkDeleteDialog(false);\n await fetchTasks();\n } catch (err) {\n console.error('Failed to bulk delete tasks:', err);\n } finally {\n setBulkDeleting(false);\n }\n }, [fetchTasks, gqlFetch, selectedTasks]);\n\n const handleBulkMove = useCallback(async () => {\n if (selectedTasks.length === 0) return;\n setBulkMoving(true);\n try {\n setItems((prev) =>\n prev.map((item) =>\n selectedTaskIds.has(item.id) ? { ...item, status: bulkMoveStatus } : item\n )\n );\n await Promise.allSettled(\n selectedTasks.map((task) =>\n gqlFetch(\n 'mutation($id: ID!, $input: PlanmagnetUpdateWorkItemInput!) { planmagnetUpdateWorkItem(id: $id, input: $input) { id } }',\n { id: task.id, input: { status: bulkMoveStatus } }\n )\n )\n );\n setSelectedTaskIds(new Set());\n await fetchTasks();\n } catch (err) {\n console.error('Failed to bulk move tasks:', err);\n fetchTasks();\n } finally {\n setBulkMoving(false);\n }\n }, [bulkMoveStatus, fetchTasks, gqlFetch, selectedTaskIds, selectedTasks]);\n\n const toggleTaskSelection = useCallback((taskId: string, checked: boolean) => {\n setSelectedTaskIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(taskId);\n } else {\n next.delete(taskId);\n }\n return next;\n });\n }, []);\n\n const toggleAllVisibleTasks = useCallback(\n (checked: boolean) => {\n setSelectedTaskIds((prev) => {\n const next = new Set(prev);\n items.forEach((item) => {\n if (checked) {\n next.add(item.id);\n } else {\n next.delete(item.id);\n }\n });\n return next;\n });\n },\n [items]\n );\n\n const clearSelection = () => setSelectedTaskIds(new Set());\n\n // ── Render ─────────────────────────────────────────────────────────\n if (!workspaceId) {\n return (\n <EmptyState\n icon={<AlertCircle className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.tasks.noWorkspace', 'No workspace selected')}\n description={tr(\n 'vibecontrols.ai.tasks.noWorkspaceDesc',\n 'Select a workspace to manage AI tasks'\n )}\n />\n );\n }\n\n if (loading && items.length === 0) {\n return (\n <LoadingState\n message={tr('vibecontrols.ai.tasks.loading', 'Loading AI tasks from PlanMagnet...')}\n />\n );\n }\n\n if (error && items.length === 0) {\n return (\n <ErrorState\n title={tr('vibecontrols.ai.tasks.loadFailed', 'Failed to load tasks')}\n message={error}\n action={\n <button\n type=\"button\"\n onClick={fetchTasks}\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 text-sm\"\n >\n <RefreshCw className=\"size-4\" />\n {tr('vibecontrols.ai.tasks.retry', 'Retry')}\n </button>\n }\n />\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Toolbar */}\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm text-text-secondary\">\n {items.length} task{items.length !== 1 ? 's' : ''}\n </p>\n <button\n type=\"button\"\n onClick={fetchTasks}\n className=\"p-1 hover:bg-bg-sunken rounded-md text-text-secondary hover:text-text-primary transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw className=\"size-3.5\" />\n </button>\n </div>\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm text-action-primary-text transition-colors hover:bg-action-primary-bg-hover sm:w-auto\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.ai.tasks.createTask', 'Create Task')}\n </button>\n </div>\n\n {items.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleTasks}\n ariaLabel=\"Select all visible AI tasks\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.ai.tasks.selectAllVisible', 'Select all visible')}\n </span>\n </div>\n {selectedTaskIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.ai.tasks.clearSelection', 'Clear selection')}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedTaskIds.size} {tr('vibecontrols.ai.tasks.selected', 'selected')}\n </span>\n <select\n value={bulkMoveStatus}\n onChange={(event) => setBulkMoveStatus(event.target.value as TaskStatus)}\n className=\"rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {columns.map((column) => (\n <option key={column.id} value={column.id}>\n Move to {column.label}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={handleBulkMove}\n disabled={selectedTasks.length === 0 || bulkMoving}\n className=\"rounded-md border border-action-primary-bg/40 px-3 py-2 text-sm font-medium text-action-primary-bg hover:bg-action-primary-bg/10 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr('vibecontrols.ai.tasks.moveSelected', 'Move Selected')}\n {selectedTasks.length > 0 ? ` (${selectedTasks.length})` : ''}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowBulkDeleteDialog(true)}\n disabled={selectedTasks.length === 0 || bulkDeleting}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr('vibecontrols.ai.tasks.deleteSelected', 'Delete Selected')}\n {selectedTasks.length > 0 ? ` (${selectedTasks.length})` : ''}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Kanban Board */}\n {items.length === 0 ? (\n <EmptyState\n icon={<GripVertical className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.tasks.emptyTitle', 'No AI tasks')}\n description={tr(\n 'vibecontrols.ai.tasks.emptyDescription',\n 'Create tasks to track and organize your AI vibecoding work. Tasks are stored in PlanMagnet.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(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('vibecontrols.ai.tasks.createTask', 'Create Task')}\n </button>\n }\n />\n ) : (\n <div className=\"flex gap-3 overflow-x-auto pb-2\">\n {columns.map((col) => {\n const colItems = items.filter((i) => i.status === col.id);\n return (\n <div\n key={col.id}\n className={`flex-shrink-0 w-56 bg-bg-sunken rounded-lg border-t-2 ${col.color}`}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n e.preventDefault();\n handleDrop(col.id);\n }}\n >\n <div className=\"px-3 py-2 flex items-center justify-between\">\n <h4 className=\"text-xs font-semibold text-text-primary uppercase tracking-wide\">\n {col.label}\n </h4>\n <span className=\"text-xs text-text-secondary bg-bg-surface rounded-full px-1.5 py-0.5\">\n {colItems.length}\n </span>\n </div>\n <div className=\"px-2 pb-2 space-y-2 min-h-[100px]\">\n {colItems.map((item) => (\n <Card\n key={item.id}\n className=\"p-0 cursor-grab active:cursor-grabbing\"\n padding=\"none\"\n >\n <div draggable onDragStart={() => setDraggedId(item.id)} className=\"p-2\">\n <div className=\"flex items-start justify-between gap-1\">\n <div className=\"flex items-start gap-2 min-w-0 flex-1\">\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedTaskIds.has(item.id)}\n onChange={(checked) => toggleTaskSelection(item.id, checked)}\n ariaLabel={`Select task ${item.title}`}\n />\n </div>\n <div className=\"min-w-0 flex-1\">\n {item.key && (\n <span className=\"text-[10px] font-mono text-text-secondary\">\n {item.key}\n </span>\n )}\n <span className=\"block text-sm font-medium text-text-primary line-clamp-2\">\n {item.title}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(item.id);\n }}\n className=\"p-0.5 hover:bg-status-error-bg rounded flex-shrink-0\"\n >\n <X className=\"size-3 text-text-secondary hover:text-status-error-text\" />\n </button>\n </div>\n <div className=\"flex items-center gap-1.5 mt-1.5\">\n {item.priority && priorityLabels[item.priority] && (\n <span\n className={`text-[10px] font-medium ${priorityLabels[item.priority].color}`}\n >\n {priorityLabels[item.priority].label}\n </span>\n )}\n {item.storyPoints != null && (\n <span className=\"text-[10px] text-text-secondary bg-bg-surface rounded px-1\">\n {item.storyPoints}pt\n </span>\n )}\n </div>\n </div>\n </Card>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Create Task Dialog */}\n {showCreate && (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShowCreate(false))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\"\n onClick={() => setShowCreate(false)}\n >\n <div\n className=\"bg-bg-surface border border-border-default rounded-lg shadow-xl w-full max-w-sm p-6\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.ai.tasks.newAITask', 'New AI Task')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"p-1 hover:bg-bg-sunken rounded-md\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.tasks.title', 'Title')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={newTitle}\n onChange={(e) => setNewTitle(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:border-border-strong\"\n placeholder={tr('vibecontrols.ai.tasks.titlePlaceholder', 'Task title')}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) handleCreate();\n }}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.tasks.description', 'Description')}\n </label>\n <textarea\n aria-label=\"control\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:border-border-strong resize-none\"\n placeholder={tr(\n 'vibecontrols.ai.tasks.descriptionPlaceholder',\n 'Optional description'\n )}\n rows={3}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"aitaskstab-7\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Status\n </label>\n <select\n id=\"aitaskstab-7\"\n value={newStatus}\n onChange={(e) => setNewStatus(e.target.value as TaskStatus)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {columns.map((c) => (\n <option key={c.id} value={c.id}>\n {c.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label\n htmlFor=\"aitaskstab-6\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Priority\n </label>\n <select\n id=\"aitaskstab-6\"\n value={newPriority}\n onChange={(e) => setNewPriority(e.target.value as TaskPriority)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n <option value=\"CRITICAL\">Critical</option>\n <option value=\"HIGH\">High</option>\n <option value=\"MEDIUM\">Medium</option>\n <option value=\"LOW\">Low</option>\n </select>\n </div>\n </div>\n </div>\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={!newTitle.trim() || creating || !projectId}\n className=\"px-4 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {creating ? 'Creating...' : 'Create'}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showBulkDeleteDialog}\n onClose={() => setShowBulkDeleteDialog(false)}\n onConfirm={handleBulkDelete}\n title=\"Delete AI Tasks\"\n message={`Delete ${selectedTasks.length} selected AI task${selectedTasks.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel={bulkDeleting ? 'Deleting...' : 'Delete'}\n variant=\"danger\"\n isLoading={bulkDeleting}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AITasksTab.js","names":[],"sources":["../../../src/components/ai/AITasksTab.tsx"],"sourcesContent":["/**\n * AI Tasks Tab — Kanban board backed by PlanMagnet's WorkItem model\n */\n\nimport { useState, useCallback, useEffect, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useApolloClient } from '@apollo/client/react';\nimport { Plus, X, GripVertical, RefreshCw, AlertCircle } from 'lucide-react';\nimport {\n Card,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n SelectionCheckbox,\n} from '@/components/shared';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { useTr } from '../../shared/hooks/useTr';\n\nconst AI_TASK_LABEL = 'ai-task';\nconst AI_PROJECT_KEY = 'AI';\nconst AI_PROJECT_STORAGE_KEY = 'vibecontrols:ai-project-id';\n\ntype TaskStatus = 'BACKLOG' | 'TODO' | 'IN_PROGRESS' | 'IN_REVIEW' | 'DONE';\ntype TaskPriority = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';\n\ninterface WorkItem {\n id: string;\n key: string;\n title: string;\n description?: string | null;\n type: string;\n status: TaskStatus;\n priority: TaskPriority;\n assigneeId?: string | null;\n storyPoints?: number | null;\n dueDate?: string | null;\n labels: string[];\n tags: string[];\n metadata?: Record<string, unknown> | null;\n createdBy: string;\n createdAt: string;\n updatedAt: string;\n}\n\nconst columns: { id: TaskStatus; label: string; color: string }[] = [\n { id: 'BACKLOG', label: 'Backlog', color: 'border-t-text-secondary' },\n { id: 'TODO', label: 'To Do', color: 'border-t-action-primary-bg' },\n { id: 'IN_PROGRESS', label: 'In Progress', color: 'border-t-status-warning-text' },\n { id: 'IN_REVIEW', label: 'In Review', color: 'border-t-action-primary-bg' },\n { id: 'DONE', label: 'Done', color: 'border-t-status-success-text' },\n];\n\nconst priorityLabels: Record<string, { label: string; color: string }> = {\n CRITICAL: { label: 'Critical', color: 'text-status-error-text' },\n HIGH: { label: 'High', color: 'text-status-warning-text' },\n MEDIUM: { label: 'Medium', color: 'text-action-primary-bg' },\n LOW: { label: 'Low', color: 'text-text-secondary' },\n};\n\nconst WORKITEM_FIELDS = `id key title description type status priority assigneeId storyPoints dueDate labels tags metadata createdBy createdAt updatedAt`;\n\nexport function AITasksTab() {\n const tr = useTr();\n const apolloClient = useApolloClient();\n const { workspaceId: ctxWorkspaceId } = useVibeControls();\n // Fall back to URL param if context doesn't have workspaceId\n const workspaceId =\n ctxWorkspaceId || new URLSearchParams(window.location.search).get('workspace') || '';\n\n const [projectId, setProjectId] = useState<string | null>(() => {\n try {\n return localStorage.getItem(getProfileScopedKey(AI_PROJECT_STORAGE_KEY));\n } catch {\n return null;\n }\n });\n const [items, setItems] = useState<WorkItem[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedTaskIds, setSelectedTaskIds] = useState<Set<string>>(new Set());\n const [bulkMoveStatus, setBulkMoveStatus] = useState<TaskStatus>('TODO');\n const [bulkDeleting, setBulkDeleting] = useState(false);\n const [bulkMoving, setBulkMoving] = useState(false);\n const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);\n\n const gqlFetch = useCallback(\n async <T,>(query: string, variables?: Record<string, unknown>): Promise<T> => {\n const document = gql(query);\n const trimmed = query.trimStart();\n\n if (trimmed.startsWith('mutation')) {\n const result = await apolloClient.mutate<T>({ mutation: document, variables });\n if (result.error) throw new Error(result.error.message || 'Mutation failed');\n if (!result.data) throw new Error('Mutation returned no data');\n return result.data;\n }\n\n const result = await apolloClient.query<T>({\n query: document,\n variables,\n fetchPolicy: 'network-only',\n });\n if (result.error) throw new Error(result.error.message || 'Query failed');\n if (!result.data) throw new Error('Query returned no data');\n return result.data;\n },\n [apolloClient]\n );\n\n // ── Project auto-discovery ─────────────────────────────────────────\n useEffect(() => {\n if (projectId || !workspaceId) return;\n let cancelled = false;\n (async () => {\n try {\n const data = await gqlFetch<{\n planmagnetProjects: { items: { id: string; key: string }[] };\n }>(`query($wsId: ID!) { planmagnetProjects(workspaceId: $wsId) { items { id key } } }`, {\n wsId: workspaceId,\n });\n const existing = data.planmagnetProjects.items.find((p) => p.key === AI_PROJECT_KEY);\n if (existing && !cancelled) {\n setProjectId(existing.id);\n try {\n localStorage.setItem(getProfileScopedKey(AI_PROJECT_STORAGE_KEY), existing.id);\n } catch {\n // localStorage unavailable\n }\n return;\n }\n const createData = await gqlFetch<{ planmagnetCreateProject: { id: string } }>(\n `mutation($wsId: ID!, $input: PlanmagnetCreateProjectInput!) { planmagnetCreateProject(workspaceId: $wsId, input: $input) { id } }`,\n {\n wsId: workspaceId,\n input: {\n name: 'AI Tasks',\n key: AI_PROJECT_KEY,\n ownerId: 'system',\n methodology: 'KANBAN',\n tags: [AI_TASK_LABEL],\n },\n }\n );\n if (createData.planmagnetCreateProject.id && !cancelled) {\n setProjectId(createData.planmagnetCreateProject.id);\n try {\n localStorage.setItem(\n getProfileScopedKey(AI_PROJECT_STORAGE_KEY),\n createData.planmagnetCreateProject.id\n );\n } catch {\n // localStorage unavailable\n }\n }\n } catch (err) {\n console.error('Failed to find/create AI project:', err);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [workspaceId, projectId, apolloClient]);\n\n // ── Fetch tasks ────────────────────────────────────────────────────\n const fetchTasks = useCallback(async () => {\n if (!workspaceId) return;\n setLoading(true);\n setError(null);\n try {\n const data = await gqlFetch<{\n planmagnetWorkItems: { items: WorkItem[]; totalCount: number };\n }>(\n `query($wsId: ID!, $filter: PlanmagnetWorkItemFilter, $pagination: PlanmagnetPaginationInput) {\n planmagnetWorkItems(workspaceId: $wsId, filter: $filter, pagination: $pagination) {\n items { ${WORKITEM_FIELDS} }\n totalCount\n }\n }`,\n { wsId: workspaceId, filter: { type: 'TASK' }, pagination: { first: 200 } }\n );\n const all = data.planmagnetWorkItems.items ?? [];\n setItems(\n all.filter((i) => i.labels?.includes(AI_TASK_LABEL) || i.tags?.includes(AI_TASK_LABEL))\n );\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load tasks');\n } finally {\n setLoading(false);\n }\n }, [workspaceId, apolloClient]);\n\n useEffect(() => {\n fetchTasks();\n }, [fetchTasks]);\n\n const selectedTasks = useMemo(\n () => items.filter((item) => selectedTaskIds.has(item.id)),\n [items, selectedTaskIds]\n );\n const allVisibleSelected =\n items.length > 0 && items.every((item) => selectedTaskIds.has(item.id));\n const partiallySelected =\n items.some((item) => selectedTaskIds.has(item.id)) && !allVisibleSelected;\n\n // ── Create ─────────────────────────────────────────────────────────\n const [showCreate, setShowCreate] = useState(false);\n const [newTitle, setNewTitle] = useState('');\n const [newDescription, setNewDescription] = useState('');\n const [newStatus, setNewStatus] = useState<TaskStatus>('BACKLOG');\n const [newPriority, setNewPriority] = useState<TaskPriority>('MEDIUM');\n const [creating, setCreating] = useState(false);\n\n const handleCreate = async () => {\n if (!newTitle.trim() || !workspaceId || !projectId) return;\n setCreating(true);\n try {\n await gqlFetch(\n `mutation($wsId: ID!, $input: PlanmagnetCreateWorkItemInput!) { planmagnetCreateWorkItem(workspaceId: $wsId, input: $input) { id } }`,\n {\n wsId: workspaceId,\n input: {\n title: newTitle.trim(),\n description: newDescription.trim() || undefined,\n type: 'TASK',\n status: newStatus,\n priority: newPriority,\n labels: [AI_TASK_LABEL],\n projectId,\n },\n }\n );\n setNewTitle('');\n setNewDescription('');\n setNewStatus('BACKLOG');\n setNewPriority('MEDIUM');\n setShowCreate(false);\n fetchTasks();\n } catch (err) {\n console.error('Failed to create task:', err);\n } finally {\n setCreating(false);\n }\n };\n\n // ── Move (drag-drop status change) ─────────────────────────────────\n const [draggedId, setDraggedId] = useState<string | null>(null);\n\n const handleStatusChange = useCallback(\n async (taskId: string, targetStatus: TaskStatus) => {\n // Optimistic update\n setItems((prev) => prev.map((i) => (i.id === taskId ? { ...i, status: targetStatus } : i)));\n try {\n await gqlFetch(\n 'mutation($id: ID!, $input: PlanmagnetUpdateWorkItemInput!) { planmagnetUpdateWorkItem(id: $id, input: $input) { id } }',\n { id: taskId, input: { status: targetStatus } }\n );\n } catch (err) {\n console.error('Failed to move task:', err);\n fetchTasks(); // revert on failure\n }\n },\n [fetchTasks, gqlFetch]\n );\n\n const handleDrop = useCallback(\n (targetStatus: TaskStatus) => {\n if (!draggedId) return;\n const item = items.find((i) => i.id === draggedId);\n setDraggedId(null);\n if (!item || item.status === targetStatus) return;\n handleStatusChange(draggedId, targetStatus);\n },\n [draggedId, items, handleStatusChange]\n );\n\n // ── Delete ─────────────────────────────────────────────────────────\n const handleDelete = useCallback(\n async (id: string) => {\n setItems((prev) => prev.filter((i) => i.id !== id)); // optimistic\n try {\n await gqlFetch('mutation($id: ID!) { planmagnetDeleteWorkItem(id: $id) }', { id });\n } catch (err) {\n console.error('Failed to delete task:', err);\n fetchTasks(); // revert\n }\n },\n [fetchTasks, gqlFetch]\n );\n\n const handleBulkDelete = useCallback(async () => {\n if (selectedTasks.length === 0) return;\n setBulkDeleting(true);\n try {\n await Promise.allSettled(\n selectedTasks.map((task) =>\n gqlFetch('mutation($id: ID!) { planmagnetDeleteWorkItem(id: $id) }', { id: task.id })\n )\n );\n setSelectedTaskIds(new Set());\n setShowBulkDeleteDialog(false);\n await fetchTasks();\n } catch (err) {\n console.error('Failed to bulk delete tasks:', err);\n } finally {\n setBulkDeleting(false);\n }\n }, [fetchTasks, gqlFetch, selectedTasks]);\n\n const handleBulkMove = useCallback(async () => {\n if (selectedTasks.length === 0) return;\n setBulkMoving(true);\n try {\n setItems((prev) =>\n prev.map((item) =>\n selectedTaskIds.has(item.id) ? { ...item, status: bulkMoveStatus } : item\n )\n );\n await Promise.allSettled(\n selectedTasks.map((task) =>\n gqlFetch(\n 'mutation($id: ID!, $input: PlanmagnetUpdateWorkItemInput!) { planmagnetUpdateWorkItem(id: $id, input: $input) { id } }',\n { id: task.id, input: { status: bulkMoveStatus } }\n )\n )\n );\n setSelectedTaskIds(new Set());\n await fetchTasks();\n } catch (err) {\n console.error('Failed to bulk move tasks:', err);\n fetchTasks();\n } finally {\n setBulkMoving(false);\n }\n }, [bulkMoveStatus, fetchTasks, gqlFetch, selectedTaskIds, selectedTasks]);\n\n const toggleTaskSelection = useCallback((taskId: string, checked: boolean) => {\n setSelectedTaskIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(taskId);\n } else {\n next.delete(taskId);\n }\n return next;\n });\n }, []);\n\n const toggleAllVisibleTasks = useCallback(\n (checked: boolean) => {\n setSelectedTaskIds((prev) => {\n const next = new Set(prev);\n items.forEach((item) => {\n if (checked) {\n next.add(item.id);\n } else {\n next.delete(item.id);\n }\n });\n return next;\n });\n },\n [items]\n );\n\n const clearSelection = () => setSelectedTaskIds(new Set());\n\n // ── Render ─────────────────────────────────────────────────────────\n if (!workspaceId) {\n return (\n <EmptyState\n icon={<AlertCircle className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.tasks.noWorkspace', 'No workspace selected')}\n description={tr(\n 'vibecontrols.ai.tasks.noWorkspaceDesc',\n 'Select a workspace to manage AI tasks'\n )}\n />\n );\n }\n\n if (loading && items.length === 0) {\n return (\n <LoadingState\n message={tr('vibecontrols.ai.tasks.loading', 'Loading AI tasks from PlanMagnet...')}\n />\n );\n }\n\n if (error && items.length === 0) {\n return (\n <ErrorState\n title={tr('vibecontrols.ai.tasks.loadFailed', 'Failed to load tasks')}\n message={error}\n action={\n <button\n type=\"button\"\n onClick={fetchTasks}\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 text-sm\"\n >\n <RefreshCw className=\"size-4\" />\n {tr('vibecontrols.ai.tasks.retry', 'Retry')}\n </button>\n }\n />\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Toolbar */}\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm text-text-secondary\">\n {items.length} task{items.length !== 1 ? 's' : ''}\n </p>\n <button\n type=\"button\"\n onClick={fetchTasks}\n className=\"p-1 hover:bg-bg-sunken rounded-md text-text-secondary hover:text-text-primary transition-colors\"\n title=\"Refresh\"\n >\n <RefreshCw className=\"size-3.5\" />\n </button>\n </div>\n <button\n type=\"button\"\n onClick={() => setShowCreate(true)}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm text-action-primary-text transition-colors hover:bg-action-primary-bg-hover sm:w-auto\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.ai.tasks.createTask', 'Create Task')}\n </button>\n </div>\n\n {items.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleTasks}\n ariaLabel=\"Select all visible AI tasks\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.ai.tasks.selectAllVisible', 'Select all visible')}\n </span>\n </div>\n {selectedTaskIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.ai.tasks.clearSelection', 'Clear selection')}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedTaskIds.size} {tr('vibecontrols.ai.tasks.selected', 'selected')}\n </span>\n <select\n value={bulkMoveStatus}\n onChange={(event) => setBulkMoveStatus(event.target.value as TaskStatus)}\n className=\"rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {columns.map((column) => (\n <option key={column.id} value={column.id}>\n Move to {column.label}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={handleBulkMove}\n disabled={selectedTasks.length === 0 || bulkMoving}\n className=\"rounded-md border border-[var(--color-accent-soft)] px-3 py-2 text-sm font-medium text-action-primary-bg hover:bg-[var(--color-accent-soft)] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr('vibecontrols.ai.tasks.moveSelected', 'Move Selected')}\n {selectedTasks.length > 0 ? ` (${selectedTasks.length})` : ''}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowBulkDeleteDialog(true)}\n disabled={selectedTasks.length === 0 || bulkDeleting}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr('vibecontrols.ai.tasks.deleteSelected', 'Delete Selected')}\n {selectedTasks.length > 0 ? ` (${selectedTasks.length})` : ''}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Kanban Board */}\n {items.length === 0 ? (\n <EmptyState\n icon={<GripVertical className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.tasks.emptyTitle', 'No AI tasks')}\n description={tr(\n 'vibecontrols.ai.tasks.emptyDescription',\n 'Create tasks to track and organize your AI vibecoding work. Tasks are stored in PlanMagnet.'\n )}\n action={\n <button\n type=\"button\"\n onClick={() => setShowCreate(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('vibecontrols.ai.tasks.createTask', 'Create Task')}\n </button>\n }\n />\n ) : (\n <div className=\"flex gap-3 overflow-x-auto pb-2\">\n {columns.map((col) => {\n const colItems = items.filter((i) => i.status === col.id);\n return (\n <div\n key={col.id}\n className={`flex-shrink-0 w-56 bg-bg-sunken rounded-lg border-t-2 ${col.color}`}\n onDragOver={(e) => e.preventDefault()}\n onDrop={(e) => {\n e.preventDefault();\n handleDrop(col.id);\n }}\n >\n <div className=\"px-3 py-2 flex items-center justify-between\">\n <h4 className=\"text-xs font-semibold text-text-primary uppercase tracking-wide\">\n {col.label}\n </h4>\n <span className=\"text-xs text-text-secondary bg-bg-surface rounded-full px-1.5 py-0.5\">\n {colItems.length}\n </span>\n </div>\n <div className=\"px-2 pb-2 space-y-2 min-h-[100px]\">\n {colItems.map((item) => (\n <Card\n key={item.id}\n className=\"p-0 cursor-grab active:cursor-grabbing\"\n padding=\"none\"\n >\n <div draggable onDragStart={() => setDraggedId(item.id)} className=\"p-2\">\n <div className=\"flex items-start justify-between gap-1\">\n <div className=\"flex items-start gap-2 min-w-0 flex-1\">\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedTaskIds.has(item.id)}\n onChange={(checked) => toggleTaskSelection(item.id, checked)}\n ariaLabel={`Select task ${item.title}`}\n />\n </div>\n <div className=\"min-w-0 flex-1\">\n {item.key && (\n <span className=\"text-[10px] font-mono text-text-secondary\">\n {item.key}\n </span>\n )}\n <span className=\"block text-sm font-medium text-text-primary line-clamp-2\">\n {item.title}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(item.id);\n }}\n className=\"p-0.5 hover:bg-status-error-bg rounded flex-shrink-0\"\n >\n <X className=\"size-3 text-text-secondary hover:text-status-error-text\" />\n </button>\n </div>\n <div className=\"flex items-center gap-1.5 mt-1.5\">\n {item.priority && priorityLabels[item.priority] && (\n <span\n className={`text-[10px] font-medium ${priorityLabels[item.priority].color}`}\n >\n {priorityLabels[item.priority].label}\n </span>\n )}\n {item.storyPoints != null && (\n <span className=\"text-[10px] text-text-secondary bg-bg-surface rounded px-1\">\n {item.storyPoints}pt\n </span>\n )}\n </div>\n </div>\n </Card>\n ))}\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Create Task Dialog */}\n {showCreate && (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShowCreate(false))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\"\n onClick={() => setShowCreate(false)}\n >\n <div\n className=\"bg-bg-surface border border-border-default rounded-lg shadow-xl w-full max-w-sm p-6\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-4 flex items-start justify-between gap-3\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.ai.tasks.newAITask', 'New AI Task')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"p-1 hover:bg-bg-sunken rounded-md\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.tasks.title', 'Title')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={newTitle}\n onChange={(e) => setNewTitle(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:border-border-strong\"\n placeholder={tr('vibecontrols.ai.tasks.titlePlaceholder', 'Task title')}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) handleCreate();\n }}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.tasks.description', 'Description')}\n </label>\n <textarea\n aria-label=\"control\"\n value={newDescription}\n onChange={(e) => setNewDescription(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:border-border-strong resize-none\"\n placeholder={tr(\n 'vibecontrols.ai.tasks.descriptionPlaceholder',\n 'Optional description'\n )}\n rows={3}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"aitaskstab-7\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Status\n </label>\n <select\n id=\"aitaskstab-7\"\n value={newStatus}\n onChange={(e) => setNewStatus(e.target.value as TaskStatus)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {columns.map((c) => (\n <option key={c.id} value={c.id}>\n {c.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label\n htmlFor=\"aitaskstab-6\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Priority\n </label>\n <select\n id=\"aitaskstab-6\"\n value={newPriority}\n onChange={(e) => setNewPriority(e.target.value as TaskPriority)}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n <option value=\"CRITICAL\">Critical</option>\n <option value=\"HIGH\">High</option>\n <option value=\"MEDIUM\">Medium</option>\n <option value=\"LOW\">Low</option>\n </select>\n </div>\n </div>\n </div>\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowCreate(false)}\n className=\"px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={!newTitle.trim() || creating || !projectId}\n className=\"px-4 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {creating ? 'Creating...' : 'Create'}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <ConfirmDialog\n isOpen={showBulkDeleteDialog}\n onClose={() => setShowBulkDeleteDialog(false)}\n onConfirm={handleBulkDelete}\n title=\"Delete AI Tasks\"\n message={`Delete ${selectedTasks.length} selected AI task${selectedTasks.length === 1 ? '' : 's'}? This cannot be undone.`}\n confirmLabel={bulkDeleting ? 'Deleting...' : 'Delete'}\n variant=\"danger\"\n isLoading={bulkDeleting}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -49,11 +49,11 @@ function h({ agent: s }) {
49
49
  })]
50
50
  }),
51
51
  S ? /* @__PURE__ */ p("div", {
52
- className: "rounded-lg border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text",
52
+ className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
53
53
  children: S
54
54
  }) : null,
55
55
  w ? /* @__PURE__ */ p("div", {
56
- className: "rounded-lg border border-status-success-border bg-status-success-bg px-3 py-2 text-sm text-status-success-text",
56
+ className: "rounded-lg border border-status-success-border bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text",
57
57
  children: w
58
58
  }) : null,
59
59
  v ? /* @__PURE__ */ m("div", {
@@ -86,7 +86,7 @@ function h({ agent: s }) {
86
86
  }
87
87
  function g({ label: e, value: t, onChange: n, saving: r, visible: i, onToggleVisible: a, onSave: o, onClear: l }) {
88
88
  return /* @__PURE__ */ m("div", {
89
- className: "space-y-2 rounded-lg border border-border-default bg-bg-primary p-3",
89
+ className: "space-y-2 rounded-lg border border-border-default bg-bg-surface p-3",
90
90
  children: [/* @__PURE__ */ p("label", {
91
91
  className: "block text-xs font-medium text-text-secondary",
92
92
  children: e
@@ -1 +1 @@
1
- {"version":3,"file":"ClaudeCredentialsCard.js","names":[],"sources":["../../../src/components/ai/ClaudeCredentialsCard.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { Eye, EyeOff, KeyRound, Loader2, Save, Trash2 } from 'lucide-react';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n deleteAgentConfigValue,\n getAgentConfigValue,\n setAgentConfigValue,\n type AgentRef,\n} from '@/services/aiApi';\n\ninterface ClaudeCredentialsCardProps {\n agent: AgentRef;\n}\n\nexport function ClaudeCredentialsCard({ agent }: ClaudeCredentialsCardProps) {\n const tr = useTr();\n const [apiKey, setApiKey] = useState('');\n const [authToken, setAuthToken] = useState('');\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState<'apiKey' | 'authToken' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState<string | null>(null);\n const [showApiKey, setShowApiKey] = useState(false);\n const [showAuthToken, setShowAuthToken] = useState(false);\n\n const loadValues = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const [nextApiKey, nextAuthToken] = await Promise.all([\n getAgentConfigValue(agent, 'ANTHROPIC_API_KEY'),\n getAgentConfigValue(agent, 'ANTHROPIC_AUTH_TOKEN'),\n ]);\n setApiKey(nextApiKey ?? '');\n setAuthToken(nextAuthToken ?? '');\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('ai.claudeCredentials.loadFailed', 'Failed to load Claude credentials')\n );\n } finally {\n setLoading(false);\n }\n }, [agent, tr]);\n\n useEffect(() => {\n void loadValues();\n }, [loadValues]);\n\n const persistValue = useCallback(\n async (key: 'ANTHROPIC_API_KEY' | 'ANTHROPIC_AUTH_TOKEN', value: string) => {\n setSaving(key === 'ANTHROPIC_API_KEY' ? 'apiKey' : 'authToken');\n setError(null);\n setSuccess(null);\n try {\n if (value.trim()) {\n await setAgentConfigValue(agent, key, value.trim());\n } else {\n await deleteAgentConfigValue(agent, key);\n }\n setSuccess(\n value.trim()\n ? tr('ai.claudeCredentials.saved', 'Credential saved')\n : tr('ai.claudeCredentials.cleared', 'Credential cleared')\n );\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('ai.claudeCredentials.saveFailed', 'Failed to save Claude credential')\n );\n } finally {\n setSaving(null);\n }\n },\n [agent, tr]\n );\n\n return (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-4 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-bg-sunken p-2 text-text-secondary\">\n <KeyRound className=\"size-4\" />\n </div>\n <div className=\"space-y-1\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('ai.claudeCredentials.title', 'Claude SDK credentials')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'ai.claudeCredentials.description',\n 'Claude SDK sessions require either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN on the agent. Save one of them here to make SDK sessions work.'\n )}\n </p>\n </div>\n </div>\n\n {error ? (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n\n {success ? (\n <div className=\"rounded-lg border border-status-success-border bg-status-success-bg px-3 py-2 text-sm text-status-success-text\">\n {success}\n </div>\n ) : null}\n\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('ai.claudeCredentials.loading', 'Loading Claude credential settings...')}\n </div>\n ) : (\n <div className=\"space-y-4\">\n <CredentialField\n label=\"ANTHROPIC_API_KEY\"\n value={apiKey}\n onChange={setApiKey}\n saving={saving === 'apiKey'}\n visible={showApiKey}\n onToggleVisible={() => setShowApiKey((current) => !current)}\n onSave={() => void persistValue('ANTHROPIC_API_KEY', apiKey)}\n onClear={() => void persistValue('ANTHROPIC_API_KEY', '')}\n />\n <CredentialField\n label=\"ANTHROPIC_AUTH_TOKEN\"\n value={authToken}\n onChange={setAuthToken}\n saving={saving === 'authToken'}\n visible={showAuthToken}\n onToggleVisible={() => setShowAuthToken((current) => !current)}\n onSave={() => void persistValue('ANTHROPIC_AUTH_TOKEN', authToken)}\n onClear={() => void persistValue('ANTHROPIC_AUTH_TOKEN', '')}\n />\n </div>\n )}\n </div>\n );\n}\n\ninterface CredentialFieldProps {\n label: string;\n value: string;\n onChange: (value: string) => void;\n saving: boolean;\n visible: boolean;\n onToggleVisible: () => void;\n onSave: () => void;\n onClear: () => void;\n}\n\nfunction CredentialField({\n label,\n value,\n onChange,\n saving,\n visible,\n onToggleVisible,\n onSave,\n onClear,\n}: CredentialFieldProps) {\n return (\n <div className=\"space-y-2 rounded-lg border border-border-default bg-bg-primary p-3\">\n <label className=\"block text-xs font-medium text-text-secondary\">{label}</label>\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <input\n aria-label=\"control\"\n type={visible ? 'text' : 'password'}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n autoComplete=\"off\"\n spellCheck={false}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 pr-10 text-sm text-text-primary\"\n />\n <button\n type=\"button\"\n onClick={onToggleVisible}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 text-text-secondary hover:text-text-primary\"\n >\n {visible ? <EyeOff className=\"size-4\" /> : <Eye className=\"size-4\" />}\n </button>\n </div>\n <button\n type=\"button\"\n onClick={onSave}\n disabled={saving}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {saving ? <Loader2 className=\"size-4 animate-spin\" /> : <Save className=\"size-4\" />}\n Save\n </button>\n <button\n type=\"button\"\n onClick={onClear}\n disabled={saving || !value}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default px-3 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50\"\n >\n <Trash2 className=\"size-4\" />\n Clear\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ClaudeCredentialsCard.js","names":[],"sources":["../../../src/components/ai/ClaudeCredentialsCard.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { Eye, EyeOff, KeyRound, Loader2, Save, Trash2 } from 'lucide-react';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n deleteAgentConfigValue,\n getAgentConfigValue,\n setAgentConfigValue,\n type AgentRef,\n} from '@/services/aiApi';\n\ninterface ClaudeCredentialsCardProps {\n agent: AgentRef;\n}\n\nexport function ClaudeCredentialsCard({ agent }: ClaudeCredentialsCardProps) {\n const tr = useTr();\n const [apiKey, setApiKey] = useState('');\n const [authToken, setAuthToken] = useState('');\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState<'apiKey' | 'authToken' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState<string | null>(null);\n const [showApiKey, setShowApiKey] = useState(false);\n const [showAuthToken, setShowAuthToken] = useState(false);\n\n const loadValues = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const [nextApiKey, nextAuthToken] = await Promise.all([\n getAgentConfigValue(agent, 'ANTHROPIC_API_KEY'),\n getAgentConfigValue(agent, 'ANTHROPIC_AUTH_TOKEN'),\n ]);\n setApiKey(nextApiKey ?? '');\n setAuthToken(nextAuthToken ?? '');\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('ai.claudeCredentials.loadFailed', 'Failed to load Claude credentials')\n );\n } finally {\n setLoading(false);\n }\n }, [agent, tr]);\n\n useEffect(() => {\n void loadValues();\n }, [loadValues]);\n\n const persistValue = useCallback(\n async (key: 'ANTHROPIC_API_KEY' | 'ANTHROPIC_AUTH_TOKEN', value: string) => {\n setSaving(key === 'ANTHROPIC_API_KEY' ? 'apiKey' : 'authToken');\n setError(null);\n setSuccess(null);\n try {\n if (value.trim()) {\n await setAgentConfigValue(agent, key, value.trim());\n } else {\n await deleteAgentConfigValue(agent, key);\n }\n setSuccess(\n value.trim()\n ? tr('ai.claudeCredentials.saved', 'Credential saved')\n : tr('ai.claudeCredentials.cleared', 'Credential cleared')\n );\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('ai.claudeCredentials.saveFailed', 'Failed to save Claude credential')\n );\n } finally {\n setSaving(null);\n }\n },\n [agent, tr]\n );\n\n return (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-4 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"rounded-lg bg-bg-sunken p-2 text-text-secondary\">\n <KeyRound className=\"size-4\" />\n </div>\n <div className=\"space-y-1\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('ai.claudeCredentials.title', 'Claude SDK credentials')}\n </h3>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'ai.claudeCredentials.description',\n 'Claude SDK sessions require either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN on the agent. Save one of them here to make SDK sessions work.'\n )}\n </p>\n </div>\n </div>\n\n {error ? (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n\n {success ? (\n <div className=\"rounded-lg border border-status-success-border bg-status-success-bg-subtle px-3 py-2 text-sm text-status-success-text\">\n {success}\n </div>\n ) : null}\n\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('ai.claudeCredentials.loading', 'Loading Claude credential settings...')}\n </div>\n ) : (\n <div className=\"space-y-4\">\n <CredentialField\n label=\"ANTHROPIC_API_KEY\"\n value={apiKey}\n onChange={setApiKey}\n saving={saving === 'apiKey'}\n visible={showApiKey}\n onToggleVisible={() => setShowApiKey((current) => !current)}\n onSave={() => void persistValue('ANTHROPIC_API_KEY', apiKey)}\n onClear={() => void persistValue('ANTHROPIC_API_KEY', '')}\n />\n <CredentialField\n label=\"ANTHROPIC_AUTH_TOKEN\"\n value={authToken}\n onChange={setAuthToken}\n saving={saving === 'authToken'}\n visible={showAuthToken}\n onToggleVisible={() => setShowAuthToken((current) => !current)}\n onSave={() => void persistValue('ANTHROPIC_AUTH_TOKEN', authToken)}\n onClear={() => void persistValue('ANTHROPIC_AUTH_TOKEN', '')}\n />\n </div>\n )}\n </div>\n );\n}\n\ninterface CredentialFieldProps {\n label: string;\n value: string;\n onChange: (value: string) => void;\n saving: boolean;\n visible: boolean;\n onToggleVisible: () => void;\n onSave: () => void;\n onClear: () => void;\n}\n\nfunction CredentialField({\n label,\n value,\n onChange,\n saving,\n visible,\n onToggleVisible,\n onSave,\n onClear,\n}: CredentialFieldProps) {\n return (\n <div className=\"space-y-2 rounded-lg border border-border-default bg-bg-surface p-3\">\n <label className=\"block text-xs font-medium text-text-secondary\">{label}</label>\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <input\n aria-label=\"control\"\n type={visible ? 'text' : 'password'}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n autoComplete=\"off\"\n spellCheck={false}\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 pr-10 text-sm text-text-primary\"\n />\n <button\n type=\"button\"\n onClick={onToggleVisible}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 text-text-secondary hover:text-text-primary\"\n >\n {visible ? <EyeOff className=\"size-4\" /> : <Eye className=\"size-4\" />}\n </button>\n </div>\n <button\n type=\"button\"\n onClick={onSave}\n disabled={saving}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {saving ? <Loader2 className=\"size-4 animate-spin\" /> : <Save className=\"size-4\" />}\n Save\n </button>\n <button\n type=\"button\"\n onClick={onClear}\n disabled={saving || !value}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default px-3 py-2 text-sm font-medium text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50\"\n >\n <Trash2 className=\"size-4\" />\n Clear\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -79,7 +79,7 @@ function _({ packages: _, agentId: v, title: y, description: b, onChange: x, ren
79
79
  })] }),
80
80
  O && /* @__PURE__ */ h("div", {
81
81
  role: "alert",
82
- className: "rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text",
82
+ className: "rounded-md border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
83
83
  children: O
84
84
  }),
85
85
  /* @__PURE__ */ h("div", {
@@ -104,7 +104,7 @@ function _({ packages: _, agentId: v, title: y, description: b, onChange: x, ren
104
104
  className: "font-medium text-text-primary break-words max-w-[100ch]",
105
105
  children: n.displayName
106
106
  }), i && /* @__PURE__ */ g("span", {
107
- className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg text-status-success-text shrink-0",
107
+ className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg-subtle text-status-success-text shrink-0",
108
108
  children: [/* @__PURE__ */ h(c, { className: "size-3" }), " installed"]
109
109
  })]
110
110
  }),
@@ -126,7 +126,7 @@ function _({ packages: _, agentId: v, title: y, description: b, onChange: x, ren
126
126
  i && _ ? /* @__PURE__ */ g("button", {
127
127
  type: "button",
128
128
  disabled: !0,
129
- className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg text-status-success-text border border-status-success-border cursor-default",
129
+ className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg-subtle text-status-success-text border border-status-success-border cursor-default",
130
130
  "data-testid": `plugin-installed-${e}`,
131
131
  "aria-live": "polite",
132
132
  children: [/* @__PURE__ */ h(l, { className: "size-3.5" }), "Installed"]
@@ -1 +1 @@
1
- {"version":3,"file":"PluginHarnessPicker.js","names":[],"sources":["../../../src/components/ai/PluginHarnessPicker.tsx"],"sourcesContent":["/**\n * <PluginHarnessPicker />\n *\n * Grid of installable VibeControls agent plugins with per-card\n * Install / Installed / Remove state. Driven by the existing\n * `usePluginAvailability` hook (same code path as `<PluginGate>`), so\n * the Plugins tab on the Agent details page and this picker stay in\n * sync automatically.\n *\n * The default use-case is the AI workbench: once the base\n * `@vibecontrols/vibe-plugin-ai` plugin is installed, this picker lists\n * every AI provider harness (claude, codex, copilot, gemini, …) so the\n * user can add / remove harnesses without leaving the AI page.\n *\n * The component is feature-neutral — pass any `packages` array (e.g.\n * session providers, tunnel providers) to reuse the same UI.\n */\n\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';\nimport { Check, CheckCircle2, Download, Loader2, Puzzle, Trash2 } from 'lucide-react';\nimport { useRemoveAgentPluginMutation } from '@/generated/wspace-operations';\nimport { usePluginAvailability } from '@/hooks/usePluginAvailability';\nimport { getPluginMeta } from '@/constants/pluginCatalog';\nimport { Card } from '../shared/Card';\n\nexport interface PluginHarnessPickerProps {\n /** Package names to offer. Metadata is resolved via `getPluginMeta`. */\n packages: readonly string[];\n /** Active agent id. When null, the picker renders in a disabled state. */\n agentId: string | null;\n /** Optional section heading (rendered above the grid). */\n title?: string;\n /** Optional section description. */\n description?: string;\n /** Fired after a successful install or remove so parents can refetch. */\n onChange?: () => void;\n /**\n * Optional per-card action slot, rendered alongside the Remove button when\n * a package is installed. Lets a feature (e.g. the Security tab) attach a\n * contextual action — like \"Run scan\" — without coupling this generic\n * picker to that feature. Receives the installed package name.\n */\n renderInstalledAction?: (pkg: string) => ReactNode;\n}\n\nexport function PluginHarnessPicker({\n packages,\n agentId,\n title,\n description,\n onChange,\n renderInstalledAction,\n}: PluginHarnessPickerProps) {\n // Plugins are agent-wide (BOFF-2612); no profile threading needed.\n const { installed, installing, install, refetch, loading, error } = usePluginAvailability(\n packages,\n agentId\n );\n const [removePlugin] = useRemoveAgentPluginMutation();\n const [removingPkg, setRemovingPkg] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<Record<string, string>>({});\n // Per-package \"just installed\" pulse — flips on for ~1.5s after a\n // successful install so the button shows a clear success affordance\n // (checkmark + \"Installed\") before the card transitions to its\n // installed/Remove state. Without this the button silently flips from\n // spinner to Remove and users miss the success signal.\n const [justInstalled, setJustInstalled] = useState<Record<string, boolean>>({});\n const justInstalledTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = justInstalledTimers.current;\n return () => {\n for (const t of timers.values()) clearTimeout(t);\n timers.clear();\n };\n }, []);\n\n const handleInstall = useCallback(\n async (pkg: string) => {\n const ok = await install(pkg);\n if (ok) {\n setJustInstalled((prev) => ({ ...prev, [pkg]: true }));\n const existing = justInstalledTimers.current.get(pkg);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n setJustInstalled((prev) => {\n if (!prev[pkg]) return prev;\n const next = { ...prev };\n delete next[pkg];\n return next;\n });\n justInstalledTimers.current.delete(pkg);\n }, 1500);\n justInstalledTimers.current.set(pkg, timer);\n onChange?.();\n }\n },\n [install, onChange]\n );\n\n const handleRemove = useCallback(\n async (pkg: string) => {\n if (!agentId) return;\n setRemovingPkg(pkg);\n setRemoveError((e) => ({ ...e, [pkg]: '' }));\n try {\n const { data } = await removePlugin({\n variables: { agentId, packageName: pkg },\n });\n if (!data?.removeAgentPlugin.success) {\n setRemoveError((e) => ({\n ...e,\n [pkg]: data?.removeAgentPlugin.error || 'Remove failed',\n }));\n return;\n }\n await refetch();\n onChange?.();\n } catch (e) {\n setRemoveError((err) => ({\n ...err,\n [pkg]: e instanceof Error ? e.message : String(e),\n }));\n } finally {\n setRemovingPkg(null);\n }\n },\n [agentId, removePlugin, refetch, onChange]\n );\n\n if (packages.length === 0) return null;\n\n return (\n <section className=\"flex flex-col gap-3\" data-testid=\"plugin-harness-picker\">\n {(title || description) && (\n <div>\n {title && <h3 className=\"text-base font-semibold text-text-primary\">{title}</h3>}\n {description && <p className=\"text-sm text-text-secondary mt-1\">{description}</p>}\n </div>\n )}\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n {error}\n </div>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {packages.map((pkg) => {\n const meta = getPluginMeta(pkg);\n const isInstalled = !!installed[pkg];\n const isInstalling = installing === pkg;\n const isRemoving = removingPkg === pkg;\n const isBusy = isInstalling || isRemoving || loading;\n const showJustInstalled = !!justInstalled[pkg];\n const err = removeError[pkg];\n return (\n <Card key={pkg} padding=\"md\" className=\"flex flex-col gap-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0 size-9 rounded-md bg-bg-sunken flex items-center justify-center\">\n <Puzzle className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start gap-2 flex-wrap\">\n {/*\n Plugin display name must wrap freely up to a\n reasonable hard cap so the column never blows up\n the modal layout. break-words allows wrapping on\n arbitrary characters when a name has no spaces;\n max-w bound corresponds to ~100 char-cells (a\n generous upper bound on real-world display names).\n */}\n <h4 className=\"font-medium text-text-primary break-words max-w-[100ch]\">\n {meta.displayName}\n </h4>\n {isInstalled && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg text-status-success-text shrink-0\">\n <Check className=\"size-3\" /> installed\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-0.5\">{meta.description}</p>\n <p\n className=\"text-[10px] font-mono text-text-placeholder mt-1 break-all\"\n title={pkg}\n >\n {pkg}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {isInstalled && showJustInstalled ? (\n <button\n type=\"button\"\n disabled\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg text-status-success-text border border-status-success-border cursor-default\"\n data-testid={`plugin-installed-${pkg}`}\n aria-live=\"polite\"\n >\n <CheckCircle2 className=\"size-3.5\" />\n Installed\n </button>\n ) : isInstalled ? (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleRemove(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md border border-border-default text-text-secondary hover:border-status-error-border hover:text-status-error-text transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-remove-${pkg}`}\n >\n {isRemoving ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"size-3.5\" />\n )}\n Remove\n </button>\n ) : null}\n {isInstalled && !showJustInstalled && renderInstalledAction?.(pkg)}\n {!isInstalled && (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleInstall(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-install-${pkg}`}\n aria-busy={isInstalling}\n >\n {isInstalling ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n Installing…\n </>\n ) : (\n <>\n <Download className=\"size-3.5\" />\n Install\n </>\n )}\n </button>\n )}\n </div>\n {err && <p className=\"text-xs text-status-error-text\">{err}</p>}\n </Card>\n );\n })}\n </div>\n {!agentId && <p className=\"text-xs text-text-muted\">Select an agent to manage plugins.</p>}\n </section>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PluginHarnessPicker.js","names":[],"sources":["../../../src/components/ai/PluginHarnessPicker.tsx"],"sourcesContent":["/**\n * <PluginHarnessPicker />\n *\n * Grid of installable VibeControls agent plugins with per-card\n * Install / Installed / Remove state. Driven by the existing\n * `usePluginAvailability` hook (same code path as `<PluginGate>`), so\n * the Plugins tab on the Agent details page and this picker stay in\n * sync automatically.\n *\n * The default use-case is the AI workbench: once the base\n * `@vibecontrols/vibe-plugin-ai` plugin is installed, this picker lists\n * every AI provider harness (claude, codex, copilot, gemini, …) so the\n * user can add / remove harnesses without leaving the AI page.\n *\n * The component is feature-neutral — pass any `packages` array (e.g.\n * session providers, tunnel providers) to reuse the same UI.\n */\n\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';\nimport { Check, CheckCircle2, Download, Loader2, Puzzle, Trash2 } from 'lucide-react';\nimport { useRemoveAgentPluginMutation } from '@/generated/wspace-operations';\nimport { usePluginAvailability } from '@/hooks/usePluginAvailability';\nimport { getPluginMeta } from '@/constants/pluginCatalog';\nimport { Card } from '../shared/Card';\n\nexport interface PluginHarnessPickerProps {\n /** Package names to offer. Metadata is resolved via `getPluginMeta`. */\n packages: readonly string[];\n /** Active agent id. When null, the picker renders in a disabled state. */\n agentId: string | null;\n /** Optional section heading (rendered above the grid). */\n title?: string;\n /** Optional section description. */\n description?: string;\n /** Fired after a successful install or remove so parents can refetch. */\n onChange?: () => void;\n /**\n * Optional per-card action slot, rendered alongside the Remove button when\n * a package is installed. Lets a feature (e.g. the Security tab) attach a\n * contextual action — like \"Run scan\" — without coupling this generic\n * picker to that feature. Receives the installed package name.\n */\n renderInstalledAction?: (pkg: string) => ReactNode;\n}\n\nexport function PluginHarnessPicker({\n packages,\n agentId,\n title,\n description,\n onChange,\n renderInstalledAction,\n}: PluginHarnessPickerProps) {\n // Plugins are agent-wide (BOFF-2612); no profile threading needed.\n const { installed, installing, install, refetch, loading, error } = usePluginAvailability(\n packages,\n agentId\n );\n const [removePlugin] = useRemoveAgentPluginMutation();\n const [removingPkg, setRemovingPkg] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<Record<string, string>>({});\n // Per-package \"just installed\" pulse — flips on for ~1.5s after a\n // successful install so the button shows a clear success affordance\n // (checkmark + \"Installed\") before the card transitions to its\n // installed/Remove state. Without this the button silently flips from\n // spinner to Remove and users miss the success signal.\n const [justInstalled, setJustInstalled] = useState<Record<string, boolean>>({});\n const justInstalledTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\n\n useEffect(() => {\n const timers = justInstalledTimers.current;\n return () => {\n for (const t of timers.values()) clearTimeout(t);\n timers.clear();\n };\n }, []);\n\n const handleInstall = useCallback(\n async (pkg: string) => {\n const ok = await install(pkg);\n if (ok) {\n setJustInstalled((prev) => ({ ...prev, [pkg]: true }));\n const existing = justInstalledTimers.current.get(pkg);\n if (existing) clearTimeout(existing);\n const timer = setTimeout(() => {\n setJustInstalled((prev) => {\n if (!prev[pkg]) return prev;\n const next = { ...prev };\n delete next[pkg];\n return next;\n });\n justInstalledTimers.current.delete(pkg);\n }, 1500);\n justInstalledTimers.current.set(pkg, timer);\n onChange?.();\n }\n },\n [install, onChange]\n );\n\n const handleRemove = useCallback(\n async (pkg: string) => {\n if (!agentId) return;\n setRemovingPkg(pkg);\n setRemoveError((e) => ({ ...e, [pkg]: '' }));\n try {\n const { data } = await removePlugin({\n variables: { agentId, packageName: pkg },\n });\n if (!data?.removeAgentPlugin.success) {\n setRemoveError((e) => ({\n ...e,\n [pkg]: data?.removeAgentPlugin.error || 'Remove failed',\n }));\n return;\n }\n await refetch();\n onChange?.();\n } catch (e) {\n setRemoveError((err) => ({\n ...err,\n [pkg]: e instanceof Error ? e.message : String(e),\n }));\n } finally {\n setRemovingPkg(null);\n }\n },\n [agentId, removePlugin, refetch, onChange]\n );\n\n if (packages.length === 0) return null;\n\n return (\n <section className=\"flex flex-col gap-3\" data-testid=\"plugin-harness-picker\">\n {(title || description) && (\n <div>\n {title && <h3 className=\"text-base font-semibold text-text-primary\">{title}</h3>}\n {description && <p className=\"text-sm text-text-secondary mt-1\">{description}</p>}\n </div>\n )}\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\"\n >\n {error}\n </div>\n )}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {packages.map((pkg) => {\n const meta = getPluginMeta(pkg);\n const isInstalled = !!installed[pkg];\n const isInstalling = installing === pkg;\n const isRemoving = removingPkg === pkg;\n const isBusy = isInstalling || isRemoving || loading;\n const showJustInstalled = !!justInstalled[pkg];\n const err = removeError[pkg];\n return (\n <Card key={pkg} padding=\"md\" className=\"flex flex-col gap-3\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0 size-9 rounded-md bg-bg-sunken flex items-center justify-center\">\n <Puzzle className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start gap-2 flex-wrap\">\n {/*\n Plugin display name must wrap freely up to a\n reasonable hard cap so the column never blows up\n the modal layout. break-words allows wrapping on\n arbitrary characters when a name has no spaces;\n max-w bound corresponds to ~100 char-cells (a\n generous upper bound on real-world display names).\n */}\n <h4 className=\"font-medium text-text-primary break-words max-w-[100ch]\">\n {meta.displayName}\n </h4>\n {isInstalled && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-status-success-bg-subtle text-status-success-text shrink-0\">\n <Check className=\"size-3\" /> installed\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-0.5\">{meta.description}</p>\n <p\n className=\"text-[10px] font-mono text-text-placeholder mt-1 break-all\"\n title={pkg}\n >\n {pkg}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {isInstalled && showJustInstalled ? (\n <button\n type=\"button\"\n disabled\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-status-success-bg-subtle text-status-success-text border border-status-success-border cursor-default\"\n data-testid={`plugin-installed-${pkg}`}\n aria-live=\"polite\"\n >\n <CheckCircle2 className=\"size-3.5\" />\n Installed\n </button>\n ) : isInstalled ? (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleRemove(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md border border-border-default text-text-secondary hover:border-status-error-border hover:text-status-error-text transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-remove-${pkg}`}\n >\n {isRemoving ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"size-3.5\" />\n )}\n Remove\n </button>\n ) : null}\n {isInstalled && !showJustInstalled && renderInstalledAction?.(pkg)}\n {!isInstalled && (\n <button\n type=\"button\"\n disabled={isBusy || !agentId}\n onClick={() => handleInstall(pkg)}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition disabled:opacity-60 disabled:cursor-not-allowed\"\n data-testid={`plugin-install-${pkg}`}\n aria-busy={isInstalling}\n >\n {isInstalling ? (\n <>\n <Loader2 className=\"size-3.5 animate-spin\" />\n Installing…\n </>\n ) : (\n <>\n <Download className=\"size-3.5\" />\n Install\n </>\n )}\n </button>\n )}\n </div>\n {err && <p className=\"text-xs text-status-error-text\">{err}</p>}\n </Card>\n );\n })}\n </div>\n {!agentId && <p className=\"text-xs text-text-muted\">Select an agent to manage plugins.</p>}\n </section>\n );\n}\n"],"mappings":"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"}
@@ -9,14 +9,14 @@ import { useMemo as s } from "react";
9
9
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
10
10
  //#region src/components/analytics/AnalyticsBreakdownChart.tsx
11
11
  var u = [
12
- "var(--color-text-link)",
13
- "var(--color-status-success-text)",
14
- "var(--color-accent-purple)",
15
- "var(--color-status-warning-text)",
16
- "var(--color-status-error-text)",
17
- "var(--color-accent-teal)",
18
- "var(--color-text-tertiary)",
19
- "var(--color-border-strong)"
12
+ "var(--color-chart-1)",
13
+ "var(--color-chart-2)",
14
+ "var(--color-chart-3)",
15
+ "var(--color-chart-4)",
16
+ "var(--color-chart-5)",
17
+ "var(--color-chart-6)",
18
+ "var(--color-chart-7)",
19
+ "var(--color-chart-8)"
20
20
  ];
21
21
  function d({ title: d, data: f, height: p = 250 }) {
22
22
  let m = s(() => !f || f.length === 0 ? [] : f.map((e) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsBreakdownChart.js","names":[],"sources":["../../../src/components/analytics/AnalyticsBreakdownChart.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend } from 'recharts';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface BreakdownBucket {\n key: string;\n count: number;\n}\n\ninterface AnalyticsBreakdownChartProps {\n title: string;\n data: BreakdownBucket[] | null | undefined;\n height?: number;\n}\n\nconst PIE_COLORS = [\n 'var(--color-text-link)',\n 'var(--color-status-success-text)',\n 'var(--color-accent-purple)',\n 'var(--color-status-warning-text)',\n 'var(--color-status-error-text)',\n 'var(--color-accent-teal)',\n 'var(--color-text-tertiary)',\n 'var(--color-border-strong)',\n];\n\nexport function AnalyticsBreakdownChart({\n title,\n data,\n height = 250,\n}: AnalyticsBreakdownChartProps) {\n const chartData = useMemo(() => {\n if (!data || data.length === 0) return [];\n return data.map((d) => ({\n name: d.key,\n value: d.count,\n }));\n }, [data]);\n\n const tr = useTr();\n\n if (chartData.length === 0) {\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>\n <div className=\"flex items-center justify-center h-[150px] text-text-secondary text-sm\">\n {tr('vibecontrols.analytics.noData', 'No data')}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>\n <ResponsiveContainer width=\"100%\" height={height}>\n <PieChart>\n <Pie\n data={chartData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={55}\n outerRadius={80}\n paddingAngle={2}\n dataKey=\"value\"\n >\n {chartData.map((_, i) => (\n <Cell key={i} fill={PIE_COLORS[i % PIE_COLORS.length]} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-default)',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n />\n <Legend\n wrapperStyle={{ fontSize: '11px' }}\n layout=\"vertical\"\n align=\"right\"\n verticalAlign=\"middle\"\n />\n </PieChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAeA,IAAM,IAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAwB,EACtC,UACA,SACA,YAAS,OACsB;CAC/B,IAAM,IAAY,QACZ,CAAC,KAAQ,EAAK,WAAW,IAAU,EAAE,GAClC,EAAK,KAAK,OAAO;EACtB,MAAM,EAAE;EACR,OAAO,EAAE;EACV,EAAE,EACF,CAAC,EAAK,CAAC,EAEJ,IAAK,GAAO;AAalB,QAXI,EAAU,WAAW,IAErB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACvE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,iCAAiC,UAAU;GAC3C,CAAA,CACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACvE,kBAAC,GAAD;GAAqB,OAAM;GAAe;aACxC,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,GAAD;KACE,MAAM;KACN,IAAG;KACH,IAAG;KACH,aAAa;KACb,aAAa;KACb,cAAc;KACd,SAAQ;eAEP,EAAU,KAAK,GAAG,MACjB,kBAAC,GAAD,EAAc,MAAM,EAAW,IAAI,EAAW,SAAW,EAA9C,EAA8C,CACzD;KACE,CAAA;IACN,kBAAC,GAAD,EACE,cAAc;KACZ,YAAY;KACZ,QAAQ;KACR,cAAc;KACd,UAAU;KACX,EACD,CAAA;IACF,kBAAC,GAAD;KACE,cAAc,EAAE,UAAU,QAAQ;KAClC,QAAO;KACP,OAAM;KACN,eAAc;KACd,CAAA;IACO,EAAA,CAAA;GACS,CAAA,CAClB"}
1
+ {"version":3,"file":"AnalyticsBreakdownChart.js","names":[],"sources":["../../../src/components/analytics/AnalyticsBreakdownChart.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend } from 'recharts';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface BreakdownBucket {\n key: string;\n count: number;\n}\n\ninterface AnalyticsBreakdownChartProps {\n title: string;\n data: BreakdownBucket[] | null | undefined;\n height?: number;\n}\n\nconst PIE_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n 'var(--color-chart-6)',\n 'var(--color-chart-7)',\n 'var(--color-chart-8)',\n];\n\nexport function AnalyticsBreakdownChart({\n title,\n data,\n height = 250,\n}: AnalyticsBreakdownChartProps) {\n const chartData = useMemo(() => {\n if (!data || data.length === 0) return [];\n return data.map((d) => ({\n name: d.key,\n value: d.count,\n }));\n }, [data]);\n\n const tr = useTr();\n\n if (chartData.length === 0) {\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>\n <div className=\"flex items-center justify-center h-[150px] text-text-secondary text-sm\">\n {tr('vibecontrols.analytics.noData', 'No data')}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>\n <ResponsiveContainer width=\"100%\" height={height}>\n <PieChart>\n <Pie\n data={chartData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={55}\n outerRadius={80}\n paddingAngle={2}\n dataKey=\"value\"\n >\n {chartData.map((_, i) => (\n <Cell key={i} fill={PIE_COLORS[i % PIE_COLORS.length]} />\n ))}\n </Pie>\n <Tooltip\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-default)',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n />\n <Legend\n wrapperStyle={{ fontSize: '11px' }}\n layout=\"vertical\"\n align=\"right\"\n verticalAlign=\"middle\"\n />\n </PieChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAeA,IAAM,IAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAwB,EACtC,UACA,SACA,YAAS,OACsB;CAC/B,IAAM,IAAY,QACZ,CAAC,KAAQ,EAAK,WAAW,IAAU,EAAE,GAClC,EAAK,KAAK,OAAO;EACtB,MAAM,EAAE;EACR,OAAO,EAAE;EACV,EAAE,EACF,CAAC,EAAK,CAAC,EAEJ,IAAK,GAAO;AAalB,QAXI,EAAU,WAAW,IAErB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACvE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,iCAAiC,UAAU;GAC3C,CAAA,CACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACvE,kBAAC,GAAD;GAAqB,OAAM;GAAe;aACxC,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,GAAD;KACE,MAAM;KACN,IAAG;KACH,IAAG;KACH,aAAa;KACb,aAAa;KACb,cAAc;KACd,SAAQ;eAEP,EAAU,KAAK,GAAG,MACjB,kBAAC,GAAD,EAAc,MAAM,EAAW,IAAI,EAAW,SAAW,EAA9C,EAA8C,CACzD;KACE,CAAA;IACN,kBAAC,GAAD,EACE,cAAc;KACZ,YAAY;KACZ,QAAQ;KACR,cAAc;KACd,UAAU;KACX,EACD,CAAA;IACF,kBAAC,GAAD;KACE,cAAc,EAAE,UAAU,QAAQ;KAClC,QAAO;KACP,OAAM;KACN,eAAc;KACd,CAAA;IACO,EAAA,CAAA;GACS,CAAA,CAClB"}
@@ -11,11 +11,11 @@ import { useMemo as l } from "react";
11
11
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
12
12
  //#region src/components/analytics/AnalyticsTimeSeriesChart.tsx
13
13
  var f = [
14
- "var(--color-text-link)",
15
- "var(--color-status-success-text)",
16
- "var(--color-accent-purple)",
17
- "var(--color-status-warning-text)",
18
- "var(--color-status-error-text)"
14
+ "var(--color-chart-1)",
15
+ "var(--color-chart-2)",
16
+ "var(--color-chart-3)",
17
+ "var(--color-chart-4)",
18
+ "var(--color-chart-5)"
19
19
  ];
20
20
  function p({ datasets: p, title: m, height: h = 300 }) {
21
21
  let g = l(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsTimeSeriesChart.js","names":[],"sources":["../../../src/components/analytics/AnalyticsTimeSeriesChart.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n Tooltip,\n Legend,\n CartesianGrid,\n} from 'recharts';\n\ninterface TimeSeriesDataset {\n label: string;\n data: Array<{ timestamp: string; value: number }> | null | undefined;\n color: string;\n}\n\ninterface AnalyticsTimeSeriesChartProps {\n datasets: TimeSeriesDataset[];\n title?: string;\n height?: number;\n}\n\nconst COLORS = [\n 'var(--color-text-link)',\n 'var(--color-status-success-text)',\n 'var(--color-accent-purple)',\n 'var(--color-status-warning-text)',\n 'var(--color-status-error-text)',\n];\n\nexport function AnalyticsTimeSeriesChart({\n datasets,\n title,\n height = 300,\n}: AnalyticsTimeSeriesChartProps) {\n const chartData = useMemo(() => {\n // Merge all datasets into a single array keyed by timestamp\n const map = new Map<string, Record<string, number>>();\n\n for (const ds of datasets) {\n if (!ds.data) continue;\n for (const point of ds.data) {\n const ts = new Date(point.timestamp).toISOString();\n if (!map.has(ts)) map.set(ts, {});\n map.get(ts)![ds.label] = point.value;\n }\n }\n\n return Array.from(map.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([ts, values]) => ({\n timestamp: ts,\n ...values,\n }));\n }, [datasets]);\n\n const formatDate = (ts: string) => {\n const d = new Date(ts);\n return `${d.getMonth() + 1}/${d.getDate()}`;\n };\n\n const formatTooltipDate = (ts: string) => {\n return new Date(ts).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n };\n\n const tr = useTr();\n\n if (chartData.length === 0) {\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n {title && <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>}\n <div className=\"flex items-center justify-center h-[200px] text-text-secondary text-sm\">\n {tr(\n 'vibecontrols.analytics.noDataForRange',\n 'No data available for the selected time range'\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n {title && <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>}\n <ResponsiveContainer width=\"100%\" height={height}>\n <AreaChart data={chartData} margin={{ top: 5, right: 10, left: 0, bottom: 5 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.3} />\n <XAxis\n dataKey=\"timestamp\"\n tickFormatter={formatDate}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 11 }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n <YAxis\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 11 }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n allowDecimals={false}\n />\n <Tooltip\n labelFormatter={formatTooltipDate}\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-default)',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n />\n <Legend wrapperStyle={{ fontSize: '12px' }} />\n {datasets.map((ds, i) => (\n <Area\n key={ds.label}\n type=\"monotone\"\n dataKey={ds.label}\n stroke={ds.color || COLORS[i % COLORS.length]}\n fill={ds.color || COLORS[i % COLORS.length]}\n fillOpacity={0.1}\n strokeWidth={2}\n dot={false}\n />\n ))}\n </AreaChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAyBA,IAAM,IAAS;CACb;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAyB,EACvC,aACA,UACA,YAAS,OACuB;CAChC,IAAM,IAAY,QAAc;EAE9B,IAAM,oBAAM,IAAI,KAAqC;AAErD,OAAK,IAAM,KAAM,EACV,OAAG,KACR,MAAK,IAAM,KAAS,EAAG,MAAM;GAC3B,IAAM,IAAK,IAAI,KAAK,EAAM,UAAU,CAAC,aAAa;AAElD,GADK,EAAI,IAAI,EAAG,IAAE,EAAI,IAAI,GAAI,EAAE,CAAC,EACjC,EAAI,IAAI,EAAG,CAAE,EAAG,SAAS,EAAM;;AAInC,SAAO,MAAM,KAAK,EAAI,SAAS,CAAC,CAC7B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CACtC,KAAK,CAAC,GAAI,QAAa;GACtB,WAAW;GACX,GAAG;GACJ,EAAE;IACJ,CAAC,EAAS,CAAC,EAER,KAAc,MAAe;EACjC,IAAM,IAAI,IAAI,KAAK,EAAG;AACtB,SAAO,GAAG,EAAE,UAAU,GAAG,EAAE,GAAG,EAAE,SAAS;IAGrC,KAAqB,MAClB,IAAI,KAAK,EAAG,CAAC,eAAe,KAAA,GAAW;EAC5C,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CAAC,EAGE,IAAK,GAAO;AAgBlB,QAdI,EAAU,WAAW,IAErB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,KAAS,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACjF,kBAAC,OAAD;GAAK,WAAU;aACZ,EACC,yCACA,gDACD;GACG,CAAA,CACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,KAAS,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACjF,kBAAC,GAAD;GAAqB,OAAM;GAAe;aACxC,kBAAC,GAAD;IAAW,MAAM;IAAW,QAAQ;KAAE,KAAK;KAAG,OAAO;KAAI,MAAM;KAAG,QAAQ;KAAG;cAA7E;KACE,kBAAC,GAAD;MAAe,iBAAgB;MAAM,QAAO;MAA8B,SAAS;MAAO,CAAA;KAC1F,kBAAC,GAAD;MACE,SAAQ;MACR,eAAe;MACf,MAAM;OAAE,MAAM;OAA+B,UAAU;OAAI;MAC3D,UAAU,EAAE,QAAQ,+BAA+B;MACnD,CAAA;KACF,kBAAC,GAAD;MACE,MAAM;OAAE,MAAM;OAA+B,UAAU;OAAI;MAC3D,UAAU,EAAE,QAAQ,+BAA+B;MACnD,eAAe;MACf,CAAA;KACF,kBAAC,GAAD;MACE,gBAAgB;MAChB,cAAc;OACZ,YAAY;OACZ,QAAQ;OACR,cAAc;OACd,UAAU;OACX;MACD,CAAA;KACF,kBAAC,GAAD,EAAQ,cAAc,EAAE,UAAU,QAAQ,EAAI,CAAA;KAC7C,EAAS,KAAK,GAAI,MACjB,kBAAC,GAAD;MAEE,MAAK;MACL,SAAS,EAAG;MACZ,QAAQ,EAAG,SAAS,EAAO,IAAI,EAAO;MACtC,MAAM,EAAG,SAAS,EAAO,IAAI,EAAO;MACpC,aAAa;MACb,aAAa;MACb,KAAK;MACL,EARK,EAAG,MAQR,CACF;KACQ;;GACQ,CAAA,CAClB"}
1
+ {"version":3,"file":"AnalyticsTimeSeriesChart.js","names":[],"sources":["../../../src/components/analytics/AnalyticsTimeSeriesChart.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n Tooltip,\n Legend,\n CartesianGrid,\n} from 'recharts';\n\ninterface TimeSeriesDataset {\n label: string;\n data: Array<{ timestamp: string; value: number }> | null | undefined;\n color: string;\n}\n\ninterface AnalyticsTimeSeriesChartProps {\n datasets: TimeSeriesDataset[];\n title?: string;\n height?: number;\n}\n\nconst COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\nexport function AnalyticsTimeSeriesChart({\n datasets,\n title,\n height = 300,\n}: AnalyticsTimeSeriesChartProps) {\n const chartData = useMemo(() => {\n // Merge all datasets into a single array keyed by timestamp\n const map = new Map<string, Record<string, number>>();\n\n for (const ds of datasets) {\n if (!ds.data) continue;\n for (const point of ds.data) {\n const ts = new Date(point.timestamp).toISOString();\n if (!map.has(ts)) map.set(ts, {});\n map.get(ts)![ds.label] = point.value;\n }\n }\n\n return Array.from(map.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([ts, values]) => ({\n timestamp: ts,\n ...values,\n }));\n }, [datasets]);\n\n const formatDate = (ts: string) => {\n const d = new Date(ts);\n return `${d.getMonth() + 1}/${d.getDate()}`;\n };\n\n const formatTooltipDate = (ts: string) => {\n return new Date(ts).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n };\n\n const tr = useTr();\n\n if (chartData.length === 0) {\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n {title && <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>}\n <div className=\"flex items-center justify-center h-[200px] text-text-secondary text-sm\">\n {tr(\n 'vibecontrols.analytics.noDataForRange',\n 'No data available for the selected time range'\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n {title && <h3 className=\"text-sm font-medium text-text-primary mb-3\">{title}</h3>}\n <ResponsiveContainer width=\"100%\" height={height}>\n <AreaChart data={chartData} margin={{ top: 5, right: 10, left: 0, bottom: 5 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.3} />\n <XAxis\n dataKey=\"timestamp\"\n tickFormatter={formatDate}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 11 }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n <YAxis\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 11 }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n allowDecimals={false}\n />\n <Tooltip\n labelFormatter={formatTooltipDate}\n contentStyle={{\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-default)',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n />\n <Legend wrapperStyle={{ fontSize: '12px' }} />\n {datasets.map((ds, i) => (\n <Area\n key={ds.label}\n type=\"monotone\"\n dataKey={ds.label}\n stroke={ds.color || COLORS[i % COLORS.length]}\n fill={ds.color || COLORS[i % COLORS.length]}\n fillOpacity={0.1}\n strokeWidth={2}\n dot={false}\n />\n ))}\n </AreaChart>\n </ResponsiveContainer>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAyBA,IAAM,IAAS;CACb;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAyB,EACvC,aACA,UACA,YAAS,OACuB;CAChC,IAAM,IAAY,QAAc;EAE9B,IAAM,oBAAM,IAAI,KAAqC;AAErD,OAAK,IAAM,KAAM,EACV,OAAG,KACR,MAAK,IAAM,KAAS,EAAG,MAAM;GAC3B,IAAM,IAAK,IAAI,KAAK,EAAM,UAAU,CAAC,aAAa;AAElD,GADK,EAAI,IAAI,EAAG,IAAE,EAAI,IAAI,GAAI,EAAE,CAAC,EACjC,EAAI,IAAI,EAAG,CAAE,EAAG,SAAS,EAAM;;AAInC,SAAO,MAAM,KAAK,EAAI,SAAS,CAAC,CAC7B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CACtC,KAAK,CAAC,GAAI,QAAa;GACtB,WAAW;GACX,GAAG;GACJ,EAAE;IACJ,CAAC,EAAS,CAAC,EAER,KAAc,MAAe;EACjC,IAAM,IAAI,IAAI,KAAK,EAAG;AACtB,SAAO,GAAG,EAAE,UAAU,GAAG,EAAE,GAAG,EAAE,SAAS;IAGrC,KAAqB,MAClB,IAAI,KAAK,EAAG,CAAC,eAAe,KAAA,GAAW;EAC5C,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CAAC,EAGE,IAAK,GAAO;AAgBlB,QAdI,EAAU,WAAW,IAErB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,KAAS,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACjF,kBAAC,OAAD;GAAK,WAAU;aACZ,EACC,yCACA,gDACD;GACG,CAAA,CACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,KAAS,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,EACjF,kBAAC,GAAD;GAAqB,OAAM;GAAe;aACxC,kBAAC,GAAD;IAAW,MAAM;IAAW,QAAQ;KAAE,KAAK;KAAG,OAAO;KAAI,MAAM;KAAG,QAAQ;KAAG;cAA7E;KACE,kBAAC,GAAD;MAAe,iBAAgB;MAAM,QAAO;MAA8B,SAAS;MAAO,CAAA;KAC1F,kBAAC,GAAD;MACE,SAAQ;MACR,eAAe;MACf,MAAM;OAAE,MAAM;OAA+B,UAAU;OAAI;MAC3D,UAAU,EAAE,QAAQ,+BAA+B;MACnD,CAAA;KACF,kBAAC,GAAD;MACE,MAAM;OAAE,MAAM;OAA+B,UAAU;OAAI;MAC3D,UAAU,EAAE,QAAQ,+BAA+B;MACnD,eAAe;MACf,CAAA;KACF,kBAAC,GAAD;MACE,gBAAgB;MAChB,cAAc;OACZ,YAAY;OACZ,QAAQ;OACR,cAAc;OACd,UAAU;OACX;MACD,CAAA;KACF,kBAAC,GAAD,EAAQ,cAAc,EAAE,UAAU,QAAQ,EAAI,CAAA;KAC7C,EAAS,KAAK,GAAI,MACjB,kBAAC,GAAD;MAEE,MAAK;MACL,SAAS,EAAG;MACZ,QAAQ,EAAG,SAAS,EAAO,IAAI,EAAO;MACtC,MAAM,EAAG,SAAS,EAAO,IAAI,EAAO;MACpC,aAAa;MACb,aAAa;MACb,KAAK;MACL,EARK,EAAG,MAQR,CACF;KACQ;;GACQ,CAAA,CAClB"}
@@ -53,7 +53,7 @@ function a({ isOpen: a, onClose: o, onConfirm: s, loading: c, title: l = "Delete
53
53
  onClick: () => {
54
54
  m(""), o();
55
55
  },
56
- className: "px-4 py-2 text-sm rounded-lg border border-border-primary hover:bg-bg-secondary",
56
+ className: "px-4 py-2 text-sm rounded-lg border border-border-default hover:bg-bg-surface",
57
57
  disabled: c,
58
58
  children: f("vibecontrols.audit.cancel", "Cancel")
59
59
  }), /* @__PURE__ */ r("button", {
@@ -62,7 +62,7 @@ function a({ isOpen: a, onClose: o, onConfirm: s, loading: c, title: l = "Delete
62
62
  p.trim() && (s(p.trim()), m(""));
63
63
  },
64
64
  disabled: !p.trim() || c,
65
- className: "rounded-lg bg-status-error-bg px-4 py-2 text-sm text-status-error-text transition-colors hover:bg-status-error-bg/80 disabled:opacity-50",
65
+ className: "rounded-button bg-action-danger-bg px-4 py-2 text-sm text-action-danger-text transition-colors hover:bg-action-danger-bg-hover disabled:opacity-50",
66
66
  children: c ? f("vibecontrols.audit.deleting", "Deleting...") : d
67
67
  })]
68
68
  })
@@ -1 +1 @@
1
- {"version":3,"file":"AuditDeleteDialog.js","names":[],"sources":["../../../src/components/audit/AuditDeleteDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { AlertTriangle } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AuditDeleteDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onConfirm: (reason: string) => void;\n loading: boolean;\n title?: string;\n description?: string;\n confirmLabel?: string;\n}\n\nexport function AuditDeleteDialog({\n isOpen,\n onClose,\n onConfirm,\n loading,\n title = 'Delete Audit Log',\n description = 'This action will be recorded in the audit trail.',\n confirmLabel = 'Delete',\n}: AuditDeleteDialogProps) {\n const tr = useTr();\n const [reason, setReason] = useState('');\n\n const handleConfirm = () => {\n if (reason.trim()) {\n onConfirm(reason.trim());\n setReason('');\n }\n };\n\n const handleClose = () => {\n setReason('');\n onClose();\n };\n\n if (!isOpen) return null;\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-md w-full p-6\">\n <div className=\"mb-4 flex items-center gap-3\">\n <div className=\"rounded-full bg-status-error-bg-subtle p-2\">\n <AlertTriangle size={20} className=\"text-status-error-text\" />\n </div>\n <div>\n <h3 className=\"text-lg font-semibold\">{title}</h3>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n </div>\n </div>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium mb-1\">\n {tr('vibecontrols.audit.reasonLabel', 'Reason for deletion')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n aria-label=\"control\"\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n placeholder={tr(\n 'vibecontrols.audit.reasonPlaceholder',\n 'Enter the reason for deleting this audit log entry...'\n )}\n className=\"h-24 w-full resize-none rounded-lg border border-border-default bg-bg-sunken px-3 py-2 text-sm focus:border-action-primary-bg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/30\"\n />\n </div>\n\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"px-4 py-2 text-sm rounded-lg border border-border-primary hover:bg-bg-secondary\"\n disabled={loading}\n >\n {tr('vibecontrols.audit.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleConfirm}\n disabled={!reason.trim() || loading}\n className=\"rounded-lg bg-status-error-bg px-4 py-2 text-sm text-status-error-text transition-colors hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {loading ? tr('vibecontrols.audit.deleting', 'Deleting...') : confirmLabel}\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAcA,SAAgB,EAAkB,EAChC,WACA,YACA,cACA,YACA,WAAQ,oBACR,iBAAc,oDACd,kBAAe,YACU;CACzB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAG;AAgBxC,QAFK,IAGH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAe,MAAM;OAAI,WAAU;OAA2B,CAAA;MAC1D,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAyB;MAAW,CAAA,EAClD,kBAAC,KAAD;MAAG,WAAU;gBAA+B;MAAgB,CAAA,CACxD,EAAA,CAAA,CACF;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB;OACG,EAAG,kCAAkC,sBAAsB;OAAE;OAC9D,kBAAC,QAAD;QAAM,WAAU;kBAAyB;QAAQ,CAAA;OAC3C;SACR,kBAAC,YAAD;MACE,cAAW;MACX,OAAO;MACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,aAAa,EACX,wCACA,wDACD;MACD,WAAU;MACV,CAAA,CACE;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAxCgB;AAExB,OADA,EAAU,GAAG,EACb,GAAS;;MAuCD,WAAU;MACV,UAAU;gBAET,EAAG,6BAA6B,SAAS;MACnC,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,eAvDkB;AAC1B,OAAI,EAAO,MAAM,KACf,EAAU,EAAO,MAAM,CAAC,EACxB,EAAU,GAAG;;MAqDP,UAAU,CAAC,EAAO,MAAM,IAAI;MAC5B,WAAU;gBAET,IAAU,EAAG,+BAA+B,cAAc,GAAG;MACvD,CAAA,CACL;;IACF;;EACF,CAAA,GAnDY"}
1
+ {"version":3,"file":"AuditDeleteDialog.js","names":[],"sources":["../../../src/components/audit/AuditDeleteDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { AlertTriangle } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AuditDeleteDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onConfirm: (reason: string) => void;\n loading: boolean;\n title?: string;\n description?: string;\n confirmLabel?: string;\n}\n\nexport function AuditDeleteDialog({\n isOpen,\n onClose,\n onConfirm,\n loading,\n title = 'Delete Audit Log',\n description = 'This action will be recorded in the audit trail.',\n confirmLabel = 'Delete',\n}: AuditDeleteDialogProps) {\n const tr = useTr();\n const [reason, setReason] = useState('');\n\n const handleConfirm = () => {\n if (reason.trim()) {\n onConfirm(reason.trim());\n setReason('');\n }\n };\n\n const handleClose = () => {\n setReason('');\n onClose();\n };\n\n if (!isOpen) return null;\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay z-50 flex items-center justify-center p-4\">\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-md w-full p-6\">\n <div className=\"mb-4 flex items-center gap-3\">\n <div className=\"rounded-full bg-status-error-bg-subtle p-2\">\n <AlertTriangle size={20} className=\"text-status-error-text\" />\n </div>\n <div>\n <h3 className=\"text-lg font-semibold\">{title}</h3>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n </div>\n </div>\n\n <div className=\"mb-4\">\n <label className=\"block text-sm font-medium mb-1\">\n {tr('vibecontrols.audit.reasonLabel', 'Reason for deletion')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n aria-label=\"control\"\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n placeholder={tr(\n 'vibecontrols.audit.reasonPlaceholder',\n 'Enter the reason for deleting this audit log entry...'\n )}\n className=\"h-24 w-full resize-none rounded-lg border border-border-default bg-bg-sunken px-3 py-2 text-sm focus:border-action-primary-bg focus:outline-none focus:ring-2 focus:ring-action-primary-bg/30\"\n />\n </div>\n\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"px-4 py-2 text-sm rounded-lg border border-border-default hover:bg-bg-surface\"\n disabled={loading}\n >\n {tr('vibecontrols.audit.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleConfirm}\n disabled={!reason.trim() || loading}\n className=\"rounded-button bg-action-danger-bg px-4 py-2 text-sm text-action-danger-text transition-colors hover:bg-action-danger-bg-hover disabled:opacity-50\"\n >\n {loading ? tr('vibecontrols.audit.deleting', 'Deleting...') : confirmLabel}\n </button>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAcA,SAAgB,EAAkB,EAChC,WACA,YACA,cACA,YACA,WAAQ,oBACR,iBAAc,oDACd,kBAAe,YACU;CACzB,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAG;AAgBxC,QAFK,IAGH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OAAe,MAAM;OAAI,WAAU;OAA2B,CAAA;MAC1D,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAyB;MAAW,CAAA,EAClD,kBAAC,KAAD;MAAG,WAAU;gBAA+B;MAAgB,CAAA,CACxD,EAAA,CAAA,CACF;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB;OACG,EAAG,kCAAkC,sBAAsB;OAAE;OAC9D,kBAAC,QAAD;QAAM,WAAU;kBAAyB;QAAQ,CAAA;OAC3C;SACR,kBAAC,YAAD;MACE,cAAW;MACX,OAAO;MACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,aAAa,EACX,wCACA,wDACD;MACD,WAAU;MACV,CAAA,CACE;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAxCgB;AAExB,OADA,EAAU,GAAG,EACb,GAAS;;MAuCD,WAAU;MACV,UAAU;gBAET,EAAG,6BAA6B,SAAS;MACnC,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,eAvDkB;AAC1B,OAAI,EAAO,MAAM,KACf,EAAU,EAAO,MAAM,CAAC,EACxB,EAAU,GAAG;;MAqDP,UAAU,CAAC,EAAO,MAAM,IAAI;MAC5B,WAAU;gBAET,IAAU,EAAG,+BAA+B,cAAc,GAAG;MACvD,CAAA,CACL;;IACF;;EACF,CAAA,GAnDY"}
@@ -44,7 +44,7 @@ function d({ filter: d }) {
44
44
  "aria-expanded": m,
45
45
  onClick: () => h((e) => !e),
46
46
  disabled: y,
47
- className: "inline-flex items-center gap-2 rounded-lg border border-border-primary px-3 py-2 text-sm transition-colors hover:bg-bg-secondary disabled:opacity-50",
47
+ className: "inline-flex items-center gap-2 rounded-lg border border-border-default px-3 py-2 text-sm transition-colors hover:bg-bg-surface disabled:opacity-50",
48
48
  children: [/* @__PURE__ */ c(o, { size: 14 }), y ? b("audit.exporting", "Exporting…") : b("audit.export", "Export")]
49
49
  }),
50
50
  m ? /* @__PURE__ */ l("div", {
@@ -56,14 +56,14 @@ function d({ filter: d }) {
56
56
  type: "button",
57
57
  role: "menuitem",
58
58
  onClick: () => x(t.Csv),
59
- className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-secondary",
59
+ className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-surface",
60
60
  children: [/* @__PURE__ */ c(o, { size: 12 }), b("audit.exportCsv", "Download CSV")]
61
61
  }),
62
62
  /* @__PURE__ */ l("button", {
63
63
  type: "button",
64
64
  role: "menuitem",
65
65
  onClick: () => x(t.Json),
66
- className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-secondary",
66
+ className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-surface",
67
67
  children: [/* @__PURE__ */ c(o, { size: 12 }), b("audit.exportJson", "Download JSON")]
68
68
  }),
69
69
  /* @__PURE__ */ c("div", { className: "my-1 border-t border-border-subtle" }),
@@ -73,7 +73,7 @@ function d({ filter: d }) {
73
73
  onClick: () => {
74
74
  h(!1), _(t.Csv);
75
75
  },
76
- className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-secondary",
76
+ className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-surface",
77
77
  children: [/* @__PURE__ */ c(s, { size: 12 }), b("audit.saveCsvToFiles", "Save CSV to Files…")]
78
78
  }),
79
79
  /* @__PURE__ */ l("button", {
@@ -82,7 +82,7 @@ function d({ filter: d }) {
82
82
  onClick: () => {
83
83
  h(!1), _(t.Json);
84
84
  },
85
- className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-secondary",
85
+ className: "flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors hover:bg-bg-surface",
86
86
  children: [/* @__PURE__ */ c(s, { size: 12 }), b("audit.saveJsonToFiles", "Save JSON to Files…")]
87
87
  })
88
88
  ]