@polderlabs/bizar 6.3.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/sessionend-recall.mjs +3 -3
- package/.claude/hooks/worker-suggest.mjs +110 -0
- package/.claude/settings.json +9 -0
- package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js → EnvVarsSection-CW6fWKds.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
- package/bizar-dash/dist/assets/{MobileChat-BJrqwVDd.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
- package/bizar-dash/dist/assets/{MobileSettings-CEQNJNLJ.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
- package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
- package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
- package/bizar-dash/dist/assets/{markdown-tOLaD6nm.js.map → markdown-w2bDDkAH.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
- package/bizar-dash/dist/assets/{mobile-DYCHcUpq.js.map → mobile-DXYCA0hC.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
- package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
- package/bizar-dash/dist/index.html +12 -10
- package/bizar-dash/dist/mobile.html +7 -4
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +11 -0
- package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
- package/bizar-dash/src/server/memory-distillation.mjs +296 -0
- package/bizar-dash/src/server/routes/distill.mjs +129 -0
- package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
- package/bizar-dash/src/web/App.tsx +4 -0
- package/bizar-dash/src/web/components/Tag.tsx +18 -5
- package/bizar-dash/src/web/components/Topbar.tsx +3 -0
- package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
- package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
- package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
- package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
- package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
- package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
- package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
- package/bizar-dash/src/web/lib/types.ts +25 -0
- package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
- package/bizar-dash/src/web/styles/main.css +251 -0
- package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
- package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
- package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
- package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
- package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
- package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
- package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
- package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
- package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
- package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
- package/bizar-dash/src/web/ui/controls/controls.css +454 -0
- package/bizar-dash/src/web/ui/controls/index.ts +33 -0
- package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
- package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
- package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
- package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
- package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
- package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
- package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
- package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
- package/bizar-dash/src/web/ui/data/data.css +535 -0
- package/bizar-dash/src/web/ui/data/index.ts +31 -0
- package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
- package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
- package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
- package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
- package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
- package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
- package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
- package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
- package/bizar-dash/src/web/ui/index.ts +56 -0
- package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
- package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
- package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
- package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
- package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
- package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
- package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
- package/bizar-dash/src/web/ui/layout/index.ts +33 -0
- package/bizar-dash/src/web/ui/layout/layout.css +413 -0
- package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
- package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
- package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
- package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
- package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
- package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
- package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
- package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
- package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
- package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
- package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
- package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
- package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
- package/bizar-dash/src/web/ui/styles/globals.css +86 -0
- package/bizar-dash/src/web/ui/styles/reset.css +125 -0
- package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
- package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
- package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
- package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
- package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
- package/bizar-dash/src/web/views/Agents.tsx +506 -266
- package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
- package/bizar-dash/src/web/views/Overview.tsx +996 -448
- package/bizar-dash/src/web/views/Tasks.tsx +277 -273
- package/bizar-dash/tests/communication-log.test.tsx +67 -0
- package/bizar-dash/tests/dep-graph.test.tsx +40 -0
- package/bizar-dash/tests/event-log.test.tsx +61 -0
- package/bizar-dash/tests/goal-input.test.tsx +72 -0
- package/bizar-dash/tests/goap-planner.test.ts +68 -0
- package/bizar-dash/tests/quality-gates.test.tsx +59 -0
- package/bizar-dash/tests/setup.ts +31 -1
- package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
- package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
- package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
- package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
- package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
- package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
- package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
- package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
- package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
- package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
- package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
- package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
- package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
- package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
- package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
- package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
- package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
- package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
- package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
- package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
- package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
- package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
- package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
- package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
- package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
- package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
- package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
- package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
- package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
- package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
- package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
- package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
- package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
- package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
- package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
- package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
- package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
- package/bizar-dash/tests/views/Agents.test.tsx +359 -0
- package/bizar-dash/tests/views/Overview.test.tsx +413 -0
- package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
- package/bizar-dash/vitest.config.ts +3 -1
- package/cli/__tests__/cost-gate.test.mjs +301 -0
- package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
- package/cli/bin.mjs +38 -0
- package/cli/commands/claim.mjs +282 -0
- package/cli/commands/cost.mjs +279 -0
- package/cli/cost-gate.mjs +501 -0
- package/cli/feature-list-bridge.mjs +400 -0
- package/cli/memory-constants.mjs +17 -0
- package/cli/worker-dispatcher.mjs +259 -0
- package/cli/worker-dispatcher.test.mjs +204 -0
- package/config/trigger-patterns.json +186 -0
- package/package.json +2 -2
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/agent-registry.ts +304 -0
- package/packages/sdk/src/consensus/byzantine.ts +587 -0
- package/packages/sdk/src/consensus/index.ts +147 -0
- package/packages/sdk/src/consensus/queen.ts +173 -0
- package/packages/sdk/src/consensus/types.ts +199 -0
- package/packages/sdk/src/federation/audit.ts +156 -0
- package/packages/sdk/src/federation/budget.ts +269 -0
- package/packages/sdk/src/federation/envelope.ts +144 -0
- package/packages/sdk/src/federation/hmac.ts +160 -0
- package/packages/sdk/src/federation/index.ts +443 -0
- package/packages/sdk/src/federation/pii.ts +241 -0
- package/packages/sdk/src/federation/policy.ts +112 -0
- package/packages/sdk/src/federation/trust.ts +146 -0
- package/packages/sdk/src/index.ts +67 -0
- package/packages/sdk/src/mcp/server.ts +351 -1
- package/packages/sdk/src/router/codemod-intent.ts +125 -0
- package/packages/sdk/src/router/index.ts +247 -0
- package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
- package/packages/sdk/src/router/memory-distillation.ts +249 -0
- package/packages/sdk/src/router/model-router.ts +235 -0
- package/packages/sdk/src/router/q-learning-router.ts +304 -0
- package/packages/sdk/src/swarm-topology.ts +279 -0
- package/packages/sdk/tests/agent-registry.test.ts +285 -0
- package/packages/sdk/tests/consensus.test.ts +376 -0
- package/packages/sdk/tests/federation/audit.test.ts +164 -0
- package/packages/sdk/tests/federation/budget.test.ts +189 -0
- package/packages/sdk/tests/federation/envelope.test.ts +120 -0
- package/packages/sdk/tests/federation/hmac.test.ts +194 -0
- package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
- package/packages/sdk/tests/federation/pii.test.ts +106 -0
- package/packages/sdk/tests/federation/policy.test.ts +140 -0
- package/packages/sdk/tests/federation/trust.test.ts +124 -0
- package/packages/sdk/tests/mcp-tools.test.ts +204 -0
- package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
- package/packages/sdk/tests/model-router.test.mjs +108 -0
- package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
- package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
- package/packages/sdk/tests/router.test.mjs +91 -0
- package/packages/sdk/tests/sdk.test.mjs +26 -2
- package/packages/sdk/tests/swarm-topology.test.ts +258 -0
- package/scripts/bh-full-e2e.mjs +11 -15
- package/bizar-dash/dist/assets/MobileChat-BJrqwVDd.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-CEQNJNLJ.js +0 -1
- package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
- package/bizar-dash/dist/assets/main-IvfQAOfy.js +0 -18
- package/bizar-dash/dist/assets/main-IvfQAOfy.js.map +0 -1
- package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
- package/bizar-dash/dist/assets/markdown-tOLaD6nm.js +0 -1
- package/bizar-dash/dist/assets/mobile-DYCHcUpq.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js.map +0 -1
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
- package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js +0 -2
- package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js.map +0 -1
- package/bizar-dash/dist/assets/vendor-C843201K.js +0 -29
- package/bizar-dash/dist/assets/vendor-C843201K.js.map +0 -1
- package/packages/sdk/package-lock.json +0 -32
|
@@ -1,55 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Tasks.tsx — Wave 3 redesigned kanban board (Supabase-style simplicity).
|
|
3
|
+
*
|
|
4
|
+
* The view groups tasks into 5 columns by API status (queued | doing | done
|
|
5
|
+
* | blocked | backlog) and renders each column as a `Panel` from the new
|
|
6
|
+
* design system. Task rows are compact (~76px) cards with a left-edge
|
|
7
|
+
* priority accent (3px) derived from the priority token. The redesign
|
|
8
|
+
* intentionally uses only design-system primitives (Panel, Grid, Stack,
|
|
9
|
+
* Inline, Badge, StatusDot, Button, IconButton, SearchInput, Select,
|
|
10
|
+
* EmptyState, LoadingState) — no legacy `.task-card` / `.kanban-column`
|
|
11
|
+
* classes from main.css.
|
|
12
|
+
*
|
|
13
|
+
* Hard constraints preserved from the previous implementation:
|
|
14
|
+
* - Same export name (`Tasks`) and same `Props` shape so App.tsx keeps
|
|
15
|
+
* importing without changes.
|
|
16
|
+
* - Same API contracts (`api.get('/tasks')`, `api.patch(.../status)`, etc).
|
|
17
|
+
* - Same WS subscriptions (`tasks:change`, `tasks:delete`).
|
|
18
|
+
* - Same COLUMNS array — the API status enum is the public contract.
|
|
19
|
+
* - BacklogPanel is still imported from `components/tasks/BacklogPanel`
|
|
20
|
+
* and receives the same props (out-of-scope for this redesign).
|
|
21
|
+
* - Legacy `Modal` is reused for the create/edit dialogs (out-of-scope).
|
|
22
|
+
* - 30s tick for relative-time labels.
|
|
23
|
+
*
|
|
24
|
+
* What changed:
|
|
25
|
+
* - Columns are now `Panel`s with a StatusDot + count in the header.
|
|
26
|
+
* - Task rows render with `Badge` for status/priority/agent and
|
|
27
|
+
* `IconButton` for actions (edit, submit-to-odin, retry, delete).
|
|
28
|
+
* - The whole task row is clickable (delegates to the edit modal) —
|
|
29
|
+
* action buttons stop propagation so they keep their dedicated path.
|
|
30
|
+
* - Toolbar uses `SearchInput` + `Select` (priority) instead of raw
|
|
31
|
+
* inputs.
|
|
32
|
+
*/
|
|
23
33
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
24
34
|
import {
|
|
25
35
|
CheckSquare,
|
|
26
36
|
Plus,
|
|
27
37
|
Trash2,
|
|
28
|
-
|
|
29
|
-
ChevronRight,
|
|
30
|
-
Search,
|
|
31
|
-
X as XIcon,
|
|
32
|
-
Clock,
|
|
38
|
+
RefreshCw,
|
|
33
39
|
Tag as TagIcon,
|
|
34
40
|
ArchiveRestore,
|
|
35
|
-
|
|
41
|
+
RotateCw,
|
|
36
42
|
Send,
|
|
37
43
|
Inbox,
|
|
38
|
-
Sparkles,
|
|
39
44
|
Edit2,
|
|
40
|
-
RotateCw,
|
|
41
45
|
Bot,
|
|
42
46
|
} from 'lucide-react';
|
|
43
47
|
import { Button } from '../components/Button';
|
|
44
48
|
import { Card, CardTitle, CardMeta } from '../components/Card';
|
|
45
|
-
import { Spinner } from '../components/Spinner';
|
|
46
|
-
import { Tag } from '../components/Tag';
|
|
47
49
|
import { useModal } from '../components/Modal';
|
|
48
50
|
import { useToast } from '../components/Toast';
|
|
49
51
|
import { api, enhancePrompt } from '../lib/api';
|
|
50
|
-
import {
|
|
52
|
+
import { formatRelative } from '../lib/utils';
|
|
51
53
|
import type { Settings, Snapshot, Task } from '../lib/types';
|
|
52
54
|
import { BacklogPanel } from '../components/tasks/BacklogPanel';
|
|
55
|
+
import {
|
|
56
|
+
Badge,
|
|
57
|
+
cx,
|
|
58
|
+
Grid,
|
|
59
|
+
IconButton,
|
|
60
|
+
Inline,
|
|
61
|
+
LoadingState,
|
|
62
|
+
Panel,
|
|
63
|
+
SearchInput,
|
|
64
|
+
Select,
|
|
65
|
+
Stack,
|
|
66
|
+
StatusDot,
|
|
67
|
+
type BadgeVariant,
|
|
68
|
+
} from '../ui';
|
|
69
|
+
|
|
70
|
+
import '../styles/tasks-redesign.css';
|
|
53
71
|
|
|
54
72
|
type Props = {
|
|
55
73
|
snapshot: Snapshot;
|
|
@@ -60,6 +78,7 @@ type Props = {
|
|
|
60
78
|
};
|
|
61
79
|
|
|
62
80
|
type StatusKind = 'info' | 'accent' | 'success' | 'warning' | 'error';
|
|
81
|
+
type PriorityKind = 'low' | 'normal' | 'high';
|
|
63
82
|
|
|
64
83
|
/**
|
|
65
84
|
* UI <-> API status mapping. We keep the existing API enum
|
|
@@ -67,17 +86,49 @@ type StatusKind = 'info' | 'accent' | 'success' | 'warning' | 'error';
|
|
|
67
86
|
* rename the labels here so the dashboard, server, and tests stay in
|
|
68
87
|
* sync. Switch back if you need to expose the raw API value.
|
|
69
88
|
*/
|
|
70
|
-
const COLUMNS: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
const COLUMNS: {
|
|
90
|
+
id: Task['status'];
|
|
91
|
+
label: string;
|
|
92
|
+
kind: StatusKind;
|
|
93
|
+
dotVariant: 'info' | 'success' | 'warning' | 'danger';
|
|
94
|
+
}[] = [
|
|
95
|
+
{ id: 'queued', label: 'Todo', kind: 'info', dotVariant: 'info' },
|
|
96
|
+
{ id: 'doing', label: 'In progress', kind: 'accent', dotVariant: 'info' },
|
|
97
|
+
{ id: 'done', label: 'Done', kind: 'success', dotVariant: 'success' },
|
|
98
|
+
{ id: 'blocked', label: 'Failed', kind: 'error', dotVariant: 'danger' },
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const PRIORITIES: readonly PriorityKind[] = ['low', 'normal', 'high'];
|
|
102
|
+
|
|
103
|
+
const PRIORITY_OPTIONS = [
|
|
104
|
+
{ value: '', label: 'All' },
|
|
105
|
+
...PRIORITIES.map((p) => ({ value: p, label: p })),
|
|
75
106
|
];
|
|
76
107
|
|
|
77
|
-
const
|
|
78
|
-
|
|
108
|
+
const STATUS_BADGE: Record<StatusKind, BadgeVariant> = {
|
|
109
|
+
info: 'info',
|
|
110
|
+
accent: 'accent',
|
|
111
|
+
success: 'success',
|
|
112
|
+
warning: 'warning',
|
|
113
|
+
error: 'danger',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const PRIORITY_BADGE: Record<PriorityKind, BadgeVariant> = {
|
|
117
|
+
high: 'danger',
|
|
118
|
+
normal: 'info',
|
|
119
|
+
low: 'neutral',
|
|
120
|
+
};
|
|
79
121
|
|
|
80
|
-
|
|
122
|
+
const PRIORITY_ROW_CLASS: Record<PriorityKind, string> = {
|
|
123
|
+
high: 'tasks-wave3__row--priority-high',
|
|
124
|
+
normal: 'tasks-wave3__row--priority-normal',
|
|
125
|
+
low: 'tasks-wave3__row--priority-low',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
function TasksInner({
|
|
129
|
+
snapshot,
|
|
130
|
+
refreshSnapshot,
|
|
131
|
+
}: Props) {
|
|
81
132
|
const toast = useToast();
|
|
82
133
|
const modal = useModal();
|
|
83
134
|
const [tasks, setTasks] = useState<Task[]>(snapshot.tasks || []);
|
|
@@ -219,72 +270,63 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
219
270
|
}
|
|
220
271
|
};
|
|
221
272
|
|
|
273
|
+
const handleTaskClick = (task: Task) => {
|
|
274
|
+
openEditTaskModal(modal, toast, task, setTasks, reload, refreshSnapshot);
|
|
275
|
+
};
|
|
276
|
+
|
|
222
277
|
return (
|
|
223
|
-
<div className="view view-tasks">
|
|
278
|
+
<div className="view view-tasks tasks-wave3">
|
|
224
279
|
{/* Hidden live region for screen-reader announcements of status changes. */}
|
|
225
280
|
<div className="sr-only" role="status" aria-live="polite">
|
|
226
281
|
{statusAnnouncement}
|
|
227
282
|
</div>
|
|
228
|
-
<header className="
|
|
229
|
-
<div
|
|
230
|
-
<h2 className="
|
|
231
|
-
<CheckSquare size={18} /> Tasks ({sorted.length})
|
|
283
|
+
<header className="tasks-wave3__header">
|
|
284
|
+
<div>
|
|
285
|
+
<h2 className="tasks-wave3__title-row">
|
|
286
|
+
<CheckSquare size={18} aria-hidden /> Tasks ({sorted.length})
|
|
232
287
|
</h2>
|
|
233
|
-
<p className="
|
|
288
|
+
<p className="tasks-wave3__subtitle">
|
|
234
289
|
Add a task — title and description is all you need. Odin picks the agent and priority.
|
|
235
290
|
</p>
|
|
236
291
|
</div>
|
|
237
292
|
</header>
|
|
238
293
|
|
|
239
|
-
{/* Toolbar: search, filter,
|
|
240
|
-
<div className="tasks-toolbar">
|
|
241
|
-
<div className="tasks-
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
onChange={(e) => setFilter(e.target.value)}
|
|
250
|
-
aria-label="Search tasks"
|
|
251
|
-
/>
|
|
252
|
-
{filter && (
|
|
253
|
-
<button
|
|
254
|
-
type="button"
|
|
255
|
-
className="icon-btn"
|
|
256
|
-
aria-label="Clear search"
|
|
257
|
-
onClick={() => setFilter('')}
|
|
258
|
-
>
|
|
259
|
-
<XIcon size={12} />
|
|
260
|
-
</button>
|
|
261
|
-
)}
|
|
262
|
-
</div>
|
|
294
|
+
{/* Toolbar: search, priority filter, refresh, backlog toggle, new task */}
|
|
295
|
+
<div className="tasks-wave3__toolbar" role="toolbar" aria-label="Task filters">
|
|
296
|
+
<div className="tasks-wave3__toolbar-group">
|
|
297
|
+
<SearchInput
|
|
298
|
+
inputSize="sm"
|
|
299
|
+
value={filter}
|
|
300
|
+
onChange={((v: unknown) => setFilter(String(v))) as unknown as React.ComponentProps<typeof SearchInput>['onChange']}
|
|
301
|
+
placeholder="Search…"
|
|
302
|
+
aria-label="Search tasks"
|
|
303
|
+
/>
|
|
263
304
|
</div>
|
|
264
305
|
|
|
265
|
-
<div className="tasks-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
306
|
+
<div className="tasks-wave3__toolbar-group">
|
|
307
|
+
<span className="tasks-wave3__toolbar-label" id="tasks-priority-filter-label">
|
|
308
|
+
Priority
|
|
309
|
+
</span>
|
|
310
|
+
<Select
|
|
311
|
+
inputSize="sm"
|
|
312
|
+
aria-labelledby="tasks-priority-filter-label"
|
|
313
|
+
options={PRIORITY_OPTIONS}
|
|
272
314
|
value={priorityFilter}
|
|
273
|
-
onChange={(e) => setPriorityFilter(e.target.value)}
|
|
274
|
-
|
|
275
|
-
>
|
|
276
|
-
<option value="">All</option>
|
|
277
|
-
{PRIORITIES.map((p) => (
|
|
278
|
-
<option key={p} value={p}>{p}</option>
|
|
279
|
-
))}
|
|
280
|
-
</select>
|
|
315
|
+
onChange={(e) => setPriorityFilter((e.target as HTMLSelectElement).value)}
|
|
316
|
+
/>
|
|
281
317
|
</div>
|
|
282
318
|
|
|
283
|
-
<div className="tasks-
|
|
319
|
+
<div className="tasks-wave3__toolbar-spacer" />
|
|
284
320
|
|
|
285
|
-
<div className="tasks-
|
|
286
|
-
<Button
|
|
287
|
-
|
|
321
|
+
<div className="tasks-wave3__toolbar-group">
|
|
322
|
+
<Button
|
|
323
|
+
variant="ghost"
|
|
324
|
+
size="sm"
|
|
325
|
+
onClick={reload}
|
|
326
|
+
title="Refresh"
|
|
327
|
+
aria-label="Refresh tasks"
|
|
328
|
+
>
|
|
329
|
+
<RefreshCw size={14} aria-hidden />
|
|
288
330
|
</Button>
|
|
289
331
|
{backlogCount > 0 && (
|
|
290
332
|
<Button
|
|
@@ -293,9 +335,11 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
293
335
|
onClick={() => setShowBacklog((v) => !v)}
|
|
294
336
|
title={showBacklog ? 'Hide backlog' : 'Show backlog'}
|
|
295
337
|
>
|
|
296
|
-
<Inbox size={14} />
|
|
338
|
+
<Inbox size={14} aria-hidden />
|
|
297
339
|
Backlog
|
|
298
|
-
<span className="
|
|
340
|
+
<span className="tasks-wave3__column-count" aria-label={`${backlogCount} items`}>
|
|
341
|
+
{backlogCount}
|
|
342
|
+
</span>
|
|
299
343
|
</Button>
|
|
300
344
|
)}
|
|
301
345
|
<Button
|
|
@@ -303,7 +347,7 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
303
347
|
size="sm"
|
|
304
348
|
onClick={() => openCreateTaskModal(modal, toast, setTasks, reload, refreshSnapshot)}
|
|
305
349
|
>
|
|
306
|
-
<Plus size={14} /> New task
|
|
350
|
+
<Plus size={14} aria-hidden /> New task
|
|
307
351
|
</Button>
|
|
308
352
|
</div>
|
|
309
353
|
</div>
|
|
@@ -316,218 +360,167 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
316
360
|
)}
|
|
317
361
|
|
|
318
362
|
{loading ? (
|
|
319
|
-
<
|
|
363
|
+
<LoadingState label="Loading tasks…" />
|
|
320
364
|
) : (
|
|
321
|
-
<
|
|
322
|
-
{COLUMNS.map((col) =>
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
365
|
+
<Grid cols={5} gap={3} className="tasks-wave3__kanban">
|
|
366
|
+
{COLUMNS.map((col) => {
|
|
367
|
+
const columnTasks = sorted.filter((t) => t.status === col.id);
|
|
368
|
+
return (
|
|
369
|
+
<Panel
|
|
370
|
+
key={col.id}
|
|
371
|
+
padding={3}
|
|
372
|
+
className="tasks-wave3__column"
|
|
373
|
+
title={
|
|
374
|
+
<span className="tasks-wave3__column-title">
|
|
375
|
+
<StatusDot variant={col.dotVariant} size="sm" label={col.label} />
|
|
376
|
+
{col.label}
|
|
377
|
+
</span>
|
|
378
|
+
}
|
|
379
|
+
actions={
|
|
380
|
+
<span
|
|
381
|
+
className="tasks-wave3__column-count"
|
|
382
|
+
aria-label={`${columnTasks.length} tasks`}
|
|
383
|
+
>
|
|
384
|
+
{columnTasks.length}
|
|
385
|
+
</span>
|
|
386
|
+
}
|
|
387
|
+
>
|
|
388
|
+
<Stack gap={2} className="tasks-wave3__column-body">
|
|
389
|
+
{columnTasks.length === 0 ? (
|
|
390
|
+
<div className="tasks-wave3__column-empty">No tasks</div>
|
|
391
|
+
) : (
|
|
392
|
+
columnTasks.map((t) => (
|
|
393
|
+
<TaskRow
|
|
394
|
+
key={t.id}
|
|
395
|
+
task={t}
|
|
396
|
+
column={col}
|
|
397
|
+
onTaskClick={handleTaskClick}
|
|
398
|
+
onEdit={() => openEditTaskModal(modal, toast, t, setTasks, reload, refreshSnapshot)}
|
|
399
|
+
onDelete={() => deleteTask(t.id)}
|
|
400
|
+
onRetry={() => retryTask(t.id)}
|
|
401
|
+
onSubmitToOdin={() => submitToOdin(t.id)}
|
|
402
|
+
tick={tick}
|
|
403
|
+
/>
|
|
404
|
+
))
|
|
405
|
+
)}
|
|
406
|
+
</Stack>
|
|
407
|
+
</Panel>
|
|
408
|
+
);
|
|
409
|
+
})}
|
|
410
|
+
</Grid>
|
|
336
411
|
)}
|
|
337
412
|
</div>
|
|
338
413
|
);
|
|
339
414
|
}
|
|
340
415
|
|
|
341
|
-
// ───
|
|
416
|
+
// ─── Row ─────────────────────────────────────────────────────────────────
|
|
342
417
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
onSubmitToOdin
|
|
351
|
-
tick,
|
|
352
|
-
}: {
|
|
353
|
-
column: { id: string; label: string; kind: StatusKind };
|
|
354
|
-
tasks: Task[];
|
|
355
|
-
onMove: (id: string, status: string) => void;
|
|
356
|
-
onDelete: (id: string) => void;
|
|
357
|
-
onRetry: (id: string) => void;
|
|
358
|
-
onEdit: (t: Task) => void;
|
|
359
|
-
onSubmitToOdin: (id: string) => void;
|
|
418
|
+
type RowProps = {
|
|
419
|
+
task: Task;
|
|
420
|
+
column: (typeof COLUMNS)[number];
|
|
421
|
+
onTaskClick: (task: Task) => void;
|
|
422
|
+
onEdit: () => void;
|
|
423
|
+
onDelete: () => void;
|
|
424
|
+
onRetry: () => void;
|
|
425
|
+
onSubmitToOdin: () => void;
|
|
360
426
|
tick: number;
|
|
361
|
-
}
|
|
362
|
-
const [dragOver, setDragOver] = useState(false);
|
|
363
|
-
return (
|
|
364
|
-
<div
|
|
365
|
-
className={cn('kanban-column', dragOver && 'kanban-column-drop')}
|
|
366
|
-
data-column={column.id}
|
|
367
|
-
onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
|
|
368
|
-
onDragLeave={() => setDragOver(false)}
|
|
369
|
-
onDrop={(e) => {
|
|
370
|
-
e.preventDefault();
|
|
371
|
-
setDragOver(false);
|
|
372
|
-
const id = e.dataTransfer.getData('text/task-id');
|
|
373
|
-
if (id) onMove(id, column.id);
|
|
374
|
-
}}
|
|
375
|
-
>
|
|
376
|
-
<div className="kanban-col-header">
|
|
377
|
-
<CardTitle>
|
|
378
|
-
<span className={cn('status-dot', `status-${column.kind}`)} />
|
|
379
|
-
{column.label}
|
|
380
|
-
</CardTitle>
|
|
381
|
-
<span className="kanban-col-count tabular-nums">{tasks.length}</span>
|
|
382
|
-
</div>
|
|
383
|
-
<div className="kanban-col-body">
|
|
384
|
-
{tasks.length === 0 ? (
|
|
385
|
-
<div className="kanban-empty">No tasks</div>
|
|
386
|
-
) : (
|
|
387
|
-
tasks.map((t) => (
|
|
388
|
-
<TaskCard
|
|
389
|
-
key={t.id}
|
|
390
|
-
task={t}
|
|
391
|
-
onMove={(dir) => {
|
|
392
|
-
const idx = COLUMNS.findIndex((c) => c.id === t.status);
|
|
393
|
-
const next = idx + dir;
|
|
394
|
-
if (next >= 0 && next < COLUMNS.length) onMove(t.id, COLUMNS[next].id);
|
|
395
|
-
}}
|
|
396
|
-
onEdit={() => onEdit(t)}
|
|
397
|
-
onDelete={() => onDelete(t.id)}
|
|
398
|
-
onRetry={() => onRetry(t.id)}
|
|
399
|
-
onSubmitToOdin={() => onSubmitToOdin(t.id)}
|
|
400
|
-
tick={tick}
|
|
401
|
-
/>
|
|
402
|
-
))
|
|
403
|
-
)}
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// ─── Card ──────────────────────────────────────────────────────────────────
|
|
427
|
+
};
|
|
410
428
|
|
|
411
|
-
function
|
|
429
|
+
function TaskRow({
|
|
412
430
|
task,
|
|
413
|
-
|
|
431
|
+
column,
|
|
432
|
+
onTaskClick,
|
|
414
433
|
onEdit,
|
|
415
434
|
onDelete,
|
|
416
435
|
onRetry,
|
|
417
436
|
onSubmitToOdin,
|
|
418
437
|
tick,
|
|
419
|
-
}: {
|
|
420
|
-
task: Task;
|
|
421
|
-
onMove: (dir: -1 | 1) => void;
|
|
422
|
-
onEdit: () => void;
|
|
423
|
-
onDelete: () => void;
|
|
424
|
-
onRetry: () => void;
|
|
425
|
-
onSubmitToOdin: () => void;
|
|
426
|
-
tick: number;
|
|
427
|
-
}) {
|
|
438
|
+
}: RowProps) {
|
|
428
439
|
const assignedAgent = task.workedBy || task.assignee || null;
|
|
440
|
+
const priority: PriorityKind = (PRIORITIES as readonly string[]).includes(task.priority as string)
|
|
441
|
+
? (task.priority as PriorityKind)
|
|
442
|
+
: 'normal';
|
|
429
443
|
return (
|
|
430
444
|
<div
|
|
431
|
-
className={
|
|
445
|
+
className={cx('tasks-wave3__row', PRIORITY_ROW_CLASS[priority])}
|
|
432
446
|
data-task-id={task.id}
|
|
433
|
-
draggable
|
|
434
|
-
onDragStart={(e) => {
|
|
435
|
-
e.dataTransfer.setData('text/task-id', task.id);
|
|
436
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
437
|
-
}}
|
|
438
447
|
data-tick={tick}
|
|
448
|
+
role="button"
|
|
449
|
+
tabIndex={0}
|
|
450
|
+
onClick={() => onTaskClick(task)}
|
|
451
|
+
onKeyDown={(e) => {
|
|
452
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
453
|
+
e.preventDefault();
|
|
454
|
+
onTaskClick(task);
|
|
455
|
+
}
|
|
456
|
+
}}
|
|
439
457
|
>
|
|
440
|
-
<div className="
|
|
441
|
-
<
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
458
|
+
<div className="tasks-wave3__row-title-row">
|
|
459
|
+
<div className="tasks-wave3__row-title" title={task.title}>
|
|
460
|
+
{task.title}
|
|
461
|
+
</div>
|
|
462
|
+
<Badge variant={STATUS_BADGE[column.kind]} size="sm" aria-label={`Status ${column.label}`}>
|
|
463
|
+
{column.label}
|
|
464
|
+
</Badge>
|
|
446
465
|
</div>
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
466
|
+
|
|
467
|
+
<Inline gap={1} align="center" className="tasks-wave3__row-badges">
|
|
468
|
+
<Badge variant={PRIORITY_BADGE[priority]} size="sm">
|
|
469
|
+
{priority}
|
|
470
|
+
</Badge>
|
|
451
471
|
{assignedAgent && (
|
|
452
|
-
<
|
|
453
|
-
<Bot size={10} /> @{assignedAgent}
|
|
454
|
-
</
|
|
472
|
+
<Badge variant="neutral" size="sm">
|
|
473
|
+
<Bot size={10} aria-hidden /> @{assignedAgent}
|
|
474
|
+
</Badge>
|
|
455
475
|
)}
|
|
456
|
-
{task.
|
|
457
|
-
<
|
|
458
|
-
|
|
459
|
-
</
|
|
460
|
-
) : null}
|
|
461
|
-
{task.tags && task.tags.length > 0 && task.tags.slice(0, 3).map((tag) => (
|
|
462
|
-
<Tag key={tag}>{tag}</Tag>
|
|
476
|
+
{(task.tags || []).slice(0, 2).map((tag) => (
|
|
477
|
+
<Badge key={tag} variant="neutral" size="sm">
|
|
478
|
+
#{tag}
|
|
479
|
+
</Badge>
|
|
463
480
|
))}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
aria-label="Move left"
|
|
480
|
-
title="Move left"
|
|
481
|
-
onClick={() => onMove(-1)}
|
|
482
|
-
>
|
|
483
|
-
<ChevronLeft size={14} />
|
|
484
|
-
</button>
|
|
485
|
-
<button
|
|
486
|
-
type="button"
|
|
487
|
-
className="icon-btn"
|
|
488
|
-
aria-label="Edit"
|
|
481
|
+
</Inline>
|
|
482
|
+
|
|
483
|
+
<div className="tasks-wave3__row-footer">
|
|
484
|
+
<span className="tasks-wave3__row-time">{formatRelative(task.createdAt)}</span>
|
|
485
|
+
<Inline
|
|
486
|
+
gap={1}
|
|
487
|
+
align="center"
|
|
488
|
+
className="tasks-wave3__row-actions"
|
|
489
|
+
onClick={(e) => e.stopPropagation()}
|
|
490
|
+
>
|
|
491
|
+
<IconButton
|
|
492
|
+
variant="ghost"
|
|
493
|
+
size="sm"
|
|
494
|
+
icon={<Edit2 size={12} aria-hidden />}
|
|
495
|
+
aria-label="Edit task"
|
|
489
496
|
title="Edit"
|
|
490
497
|
onClick={onEdit}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
className="icon-btn"
|
|
498
|
+
/>
|
|
499
|
+
<IconButton
|
|
500
|
+
variant="ghost"
|
|
501
|
+
size="sm"
|
|
502
|
+
icon={<Send size={12} aria-hidden />}
|
|
497
503
|
aria-label="Submit to Odin"
|
|
498
504
|
title="Re-delegate to Odin"
|
|
499
505
|
onClick={onSubmitToOdin}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
aria-label="Retry"
|
|
506
|
+
/>
|
|
507
|
+
<IconButton
|
|
508
|
+
variant="ghost"
|
|
509
|
+
size="sm"
|
|
510
|
+
icon={<RotateCw size={12} aria-hidden />}
|
|
511
|
+
aria-label="Retry task"
|
|
507
512
|
title="Retry"
|
|
508
513
|
onClick={onRetry}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
aria-label="Delete"
|
|
514
|
+
/>
|
|
515
|
+
<IconButton
|
|
516
|
+
variant="danger"
|
|
517
|
+
size="sm"
|
|
518
|
+
icon={<Trash2 size={12} aria-hidden />}
|
|
519
|
+
aria-label="Delete task"
|
|
516
520
|
title="Delete"
|
|
517
521
|
onClick={onDelete}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
</button>
|
|
521
|
-
<button
|
|
522
|
-
type="button"
|
|
523
|
-
className="icon-btn"
|
|
524
|
-
aria-label="Move right"
|
|
525
|
-
title="Move right"
|
|
526
|
-
onClick={() => onMove(1)}
|
|
527
|
-
>
|
|
528
|
-
<ChevronRight size={14} />
|
|
529
|
-
</button>
|
|
530
|
-
</div>
|
|
522
|
+
/>
|
|
523
|
+
</Inline>
|
|
531
524
|
</div>
|
|
532
525
|
</div>
|
|
533
526
|
);
|
|
@@ -560,7 +553,7 @@ function openCreateTaskModal(
|
|
|
560
553
|
titleEl?.focus();
|
|
561
554
|
return;
|
|
562
555
|
}
|
|
563
|
-
const priority = (priorityEl?.value || 'normal') as
|
|
556
|
+
const priority = (priorityEl?.value || 'normal') as PriorityKind;
|
|
564
557
|
const tags = (tagsEl?.value || '')
|
|
565
558
|
.split(',')
|
|
566
559
|
.map((s) => s.trim())
|
|
@@ -575,6 +568,11 @@ function openCreateTaskModal(
|
|
|
575
568
|
// No assignee / agent — Odin decides.
|
|
576
569
|
});
|
|
577
570
|
modal.close();
|
|
571
|
+
// Synthetic mousedown is intentional: it signals any open outside-
|
|
572
|
+
// click listeners (e.g. WorkspaceSelector, Notifications popover)
|
|
573
|
+
// that another surface has just stolen focus, so they collapse
|
|
574
|
+
// before the toast overlays the layout. The event carries no
|
|
575
|
+
// coordinates by design — those listeners only check the dispatch.
|
|
578
576
|
if (typeof window !== 'undefined') {
|
|
579
577
|
window.dispatchEvent(new MouseEvent('mousedown'));
|
|
580
578
|
}
|
|
@@ -694,7 +692,7 @@ function openEditTaskModal(
|
|
|
694
692
|
titleEl?.focus();
|
|
695
693
|
return;
|
|
696
694
|
}
|
|
697
|
-
const priority = (priorityEl?.value || 'normal') as
|
|
695
|
+
const priority = (priorityEl?.value || 'normal') as PriorityKind;
|
|
698
696
|
const tags = (tagsEl?.value || '')
|
|
699
697
|
.split(',')
|
|
700
698
|
.map((s) => s.trim())
|
|
@@ -708,6 +706,11 @@ function openEditTaskModal(
|
|
|
708
706
|
tags,
|
|
709
707
|
});
|
|
710
708
|
modal.close();
|
|
709
|
+
// Synthetic mousedown is intentional: it signals any open outside-
|
|
710
|
+
// click listeners (e.g. WorkspaceSelector, Notifications popover)
|
|
711
|
+
// that another surface has just stolen focus, so they collapse
|
|
712
|
+
// before the toast overlays the layout. The event carries no
|
|
713
|
+
// coordinates by design — those listeners only check the dispatch.
|
|
711
714
|
if (typeof window !== 'undefined') {
|
|
712
715
|
window.dispatchEvent(new MouseEvent('mousedown'));
|
|
713
716
|
}
|
|
@@ -788,4 +791,5 @@ function openEditTaskModal(
|
|
|
788
791
|
),
|
|
789
792
|
});
|
|
790
793
|
}
|
|
794
|
+
|
|
791
795
|
export const Tasks = React.memo(TasksInner);
|