@swarmclawai/swarmclaw 0.7.7 → 0.8.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/README.md +12 -14
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/layout/app-layout.tsx +40 -23
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +84 -47
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +247 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +20 -11
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-definitions.ts +2 -1
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +249 -14
|
@@ -3,22 +3,36 @@
|
|
|
3
3
|
import { useEffect, useCallback, useState, useRef, useMemo } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { useWs } from '@/hooks/use-ws'
|
|
6
|
-
import { updateTask, bulkUpdateTasks } from '@/lib/tasks'
|
|
6
|
+
import { updateTask, bulkUpdateTasks, importGitHubIssues, type GitHubIssueImportResult } from '@/lib/tasks'
|
|
7
7
|
import { TaskColumn } from './task-column'
|
|
8
8
|
import { TaskCard } from './task-card'
|
|
9
9
|
import { Skeleton } from '@/components/shared/skeleton'
|
|
10
10
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
11
|
+
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
12
|
+
import { inputClass } from '@/components/shared/form-styles'
|
|
11
13
|
import type { BoardTask, BoardTaskStatus } from '@/types'
|
|
12
14
|
import { toast } from 'sonner'
|
|
13
15
|
|
|
14
16
|
const ACTIVE_COLUMNS: BoardTaskStatus[] = ['backlog', 'queued', 'running', 'completed', 'failed']
|
|
15
17
|
type BoardViewMode = 'board' | 'list'
|
|
16
18
|
type AttentionFilter = 'all' | 'needs-attention' | 'approval' | 'blocked' | 'overdue' | 'failed'
|
|
19
|
+
type TaskScopeFilter = 'user-facing' | 'all' | 'agent'
|
|
17
20
|
|
|
18
21
|
function isTaskOverdue(task: BoardTask): boolean {
|
|
19
22
|
return !!task.dueAt && task.dueAt < Date.now() && task.status !== 'completed' && task.status !== 'archived'
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
function isInternalAgentTask(task: BoardTask): boolean {
|
|
26
|
+
if (task.sourceType === 'schedule' || task.sourceType === 'delegation') return true
|
|
27
|
+
return Boolean(task.createdByAgentId || task.delegatedByAgentId)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isTaskRelevantToAgent(task: BoardTask, agentId: string): boolean {
|
|
31
|
+
return task.agentId === agentId
|
|
32
|
+
|| task.createdByAgentId === agentId
|
|
33
|
+
|| task.delegatedByAgentId === agentId
|
|
34
|
+
}
|
|
35
|
+
|
|
22
36
|
function matchesAttentionFilter(task: BoardTask, filter: AttentionFilter): boolean {
|
|
23
37
|
const blocked = !!task.blockedBy?.length
|
|
24
38
|
const pendingApproval = !!task.pendingApproval
|
|
@@ -139,8 +153,24 @@ export function TaskBoard() {
|
|
|
139
153
|
if (typeof window === 'undefined') return ''
|
|
140
154
|
return new URLSearchParams(window.location.search).get('tag') || ''
|
|
141
155
|
})
|
|
156
|
+
const [taskScopeFilter, setTaskScopeFilter] = useState<TaskScopeFilter>(() => {
|
|
157
|
+
if (typeof window === 'undefined') return 'user-facing'
|
|
158
|
+
const params = new URLSearchParams(window.location.search)
|
|
159
|
+
if (params.get('agent')) return 'agent'
|
|
160
|
+
const raw = params.get('taskView')
|
|
161
|
+
return raw === 'all' ? 'all' : 'user-facing'
|
|
162
|
+
})
|
|
142
163
|
const [viewMode, setViewMode] = useState<BoardViewMode>('board')
|
|
143
164
|
const [attentionFilter, setAttentionFilter] = useState<AttentionFilter>('all')
|
|
165
|
+
const [githubImportOpen, setGitHubImportOpen] = useState(false)
|
|
166
|
+
const [githubRepo, setGitHubRepo] = useState('')
|
|
167
|
+
const [githubToken, setGitHubToken] = useState('')
|
|
168
|
+
const [githubState, setGitHubState] = useState<'open' | 'closed' | 'all'>('open')
|
|
169
|
+
const [githubLimit, setGitHubLimit] = useState('25')
|
|
170
|
+
const [githubLabels, setGitHubLabels] = useState('')
|
|
171
|
+
const [githubImporting, setGitHubImporting] = useState(false)
|
|
172
|
+
const [githubImportError, setGitHubImportError] = useState<string | null>(null)
|
|
173
|
+
const [githubImportResult, setGitHubImportResult] = useState<GitHubIssueImportResult | null>(null)
|
|
144
174
|
|
|
145
175
|
// Seed activeProjectFilter from URL on mount
|
|
146
176
|
useEffect(() => {
|
|
@@ -156,16 +186,19 @@ export function TaskBoard() {
|
|
|
156
186
|
useEffect(() => {
|
|
157
187
|
if (typeof window === 'undefined') return
|
|
158
188
|
const params = new URLSearchParams()
|
|
159
|
-
if (filterAgentId) params.set('agent', filterAgentId)
|
|
189
|
+
if (taskScopeFilter === 'agent' && filterAgentId) params.set('agent', filterAgentId)
|
|
190
|
+
else if (taskScopeFilter === 'all') params.set('taskView', 'all')
|
|
160
191
|
if (filterTag) params.set('tag', filterTag)
|
|
161
192
|
if (activeProjectFilter) params.set('project', activeProjectFilter)
|
|
162
193
|
const qs = params.toString()
|
|
163
194
|
const newUrl = `${window.location.pathname}${qs ? `?${qs}` : ''}`
|
|
164
195
|
window.history.replaceState(null, '', newUrl)
|
|
165
|
-
}, [filterAgentId, filterTag, activeProjectFilter])
|
|
196
|
+
}, [filterAgentId, filterTag, activeProjectFilter, taskScopeFilter])
|
|
166
197
|
|
|
167
198
|
const [loaded, setLoaded] = useState(Object.keys(tasks).length > 0)
|
|
168
|
-
useEffect(() => {
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
Promise.all([loadTasks(), loadAgents(), loadProjects()]).then(() => setLoaded(true))
|
|
201
|
+
}, [loadAgents, loadProjects, loadTasks])
|
|
169
202
|
useWs('tasks', loadTasks, 5000)
|
|
170
203
|
|
|
171
204
|
// Collect all unique tags across tasks
|
|
@@ -173,17 +206,28 @@ export function TaskBoard() {
|
|
|
173
206
|
|
|
174
207
|
const columns: BoardTaskStatus[] = showArchived ? [...ACTIVE_COLUMNS, 'archived'] : ACTIVE_COLUMNS
|
|
175
208
|
|
|
176
|
-
const
|
|
209
|
+
const matchesScopeFilters = useCallback((task: BoardTask) => {
|
|
177
210
|
if (!showArchived && task.status === 'archived') return false
|
|
178
|
-
if (
|
|
211
|
+
if (taskScopeFilter === 'user-facing' && isInternalAgentTask(task)) return false
|
|
212
|
+
if (taskScopeFilter === 'agent' && (!filterAgentId || !isTaskRelevantToAgent(task, filterAgentId))) return false
|
|
179
213
|
if (filterTag && !(task.tags && task.tags.includes(filterTag))) return false
|
|
180
214
|
if (activeProjectFilter && task.projectId !== activeProjectFilter) return false
|
|
215
|
+
return true
|
|
216
|
+
}, [activeProjectFilter, filterAgentId, filterTag, showArchived, taskScopeFilter])
|
|
217
|
+
|
|
218
|
+
const matchesBaseFilters = useCallback((task: BoardTask) => {
|
|
219
|
+
if (!matchesScopeFilters(task)) return false
|
|
181
220
|
if (!matchesAttentionFilter(task, attentionFilter)) return false
|
|
182
221
|
return true
|
|
183
|
-
}, [
|
|
222
|
+
}, [attentionFilter, matchesScopeFilters])
|
|
223
|
+
|
|
224
|
+
const scopedTasks = useMemo(
|
|
225
|
+
() => Object.values(tasks).filter(matchesScopeFilters),
|
|
226
|
+
[tasks, matchesScopeFilters],
|
|
227
|
+
)
|
|
184
228
|
|
|
185
229
|
const filteredTasks = useMemo(() => (
|
|
186
|
-
|
|
230
|
+
scopedTasks
|
|
187
231
|
.filter(matchesBaseFilters)
|
|
188
232
|
.sort((a, b) => {
|
|
189
233
|
const rankDiff = attentionRank(a) - attentionRank(b)
|
|
@@ -192,7 +236,7 @@ export function TaskBoard() {
|
|
|
192
236
|
if (dueDiff !== 0) return dueDiff
|
|
193
237
|
return b.updatedAt - a.updatedAt
|
|
194
238
|
})
|
|
195
|
-
), [
|
|
239
|
+
), [scopedTasks, matchesBaseFilters])
|
|
196
240
|
|
|
197
241
|
const tasksByStatus = useCallback((status: BoardTaskStatus) =>
|
|
198
242
|
filteredTasks
|
|
@@ -221,9 +265,62 @@ export function TaskBoard() {
|
|
|
221
265
|
|
|
222
266
|
const archivedCount = Object.values(tasks).filter((t) => t.status === 'archived').length
|
|
223
267
|
|
|
268
|
+
const resetGitHubImportState = useCallback(() => {
|
|
269
|
+
setGitHubImportError(null)
|
|
270
|
+
setGitHubImportResult(null)
|
|
271
|
+
}, [])
|
|
272
|
+
|
|
273
|
+
const handleGitHubImport = useCallback(async () => {
|
|
274
|
+
if (!githubRepo.trim()) {
|
|
275
|
+
setGitHubImportError('Repository is required.')
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
setGitHubImporting(true)
|
|
280
|
+
setGitHubImportError(null)
|
|
281
|
+
setGitHubImportResult(null)
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const rawLimit = Number.parseInt(githubLimit, 10)
|
|
285
|
+
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 200)) : 25
|
|
286
|
+
const result = await importGitHubIssues({
|
|
287
|
+
repo: githubRepo.trim(),
|
|
288
|
+
token: githubToken.trim() || undefined,
|
|
289
|
+
state: githubState,
|
|
290
|
+
limit,
|
|
291
|
+
labels: githubLabels
|
|
292
|
+
.split(',')
|
|
293
|
+
.map((value) => value.trim())
|
|
294
|
+
.filter(Boolean),
|
|
295
|
+
projectId: activeProjectFilter,
|
|
296
|
+
})
|
|
297
|
+
setGitHubImportResult(result)
|
|
298
|
+
await loadTasks()
|
|
299
|
+
const summary = result.created.length > 0
|
|
300
|
+
? `Imported ${result.created.length} issue(s) from ${result.repo}`
|
|
301
|
+
: `No new issues imported from ${result.repo}`
|
|
302
|
+
const suffix = result.skipped.length > 0 ? `, skipped ${result.skipped.length} existing` : ''
|
|
303
|
+
toast.success(summary + suffix)
|
|
304
|
+
} catch (err) {
|
|
305
|
+
const message = err instanceof Error ? err.message : 'GitHub import failed'
|
|
306
|
+
setGitHubImportError(message)
|
|
307
|
+
toast.error(message)
|
|
308
|
+
} finally {
|
|
309
|
+
setGitHubImporting(false)
|
|
310
|
+
}
|
|
311
|
+
}, [
|
|
312
|
+
activeProjectFilter,
|
|
313
|
+
githubLabels,
|
|
314
|
+
githubLimit,
|
|
315
|
+
githubRepo,
|
|
316
|
+
githubState,
|
|
317
|
+
githubToken,
|
|
318
|
+
loadTasks,
|
|
319
|
+
])
|
|
320
|
+
|
|
224
321
|
// Task counts per project (non-archived)
|
|
225
322
|
const projectTaskCounts: Record<string, number> = {}
|
|
226
|
-
for (const t of
|
|
323
|
+
for (const t of scopedTasks) {
|
|
227
324
|
if (t.projectId && t.status !== 'archived') {
|
|
228
325
|
projectTaskCounts[t.projectId] = (projectTaskCounts[t.projectId] || 0) + 1
|
|
229
326
|
}
|
|
@@ -231,7 +328,7 @@ export function TaskBoard() {
|
|
|
231
328
|
|
|
232
329
|
// Summary stats
|
|
233
330
|
const stats = useMemo(() => {
|
|
234
|
-
const all =
|
|
331
|
+
const all = scopedTasks.filter((t) => t.status !== 'archived')
|
|
235
332
|
return {
|
|
236
333
|
total: all.length,
|
|
237
334
|
running: all.filter((t) => t.status === 'running').length,
|
|
@@ -242,7 +339,13 @@ export function TaskBoard() {
|
|
|
242
339
|
approvals: all.filter((t) => !!t.pendingApproval).length,
|
|
243
340
|
attention: all.filter((t) => matchesAttentionFilter(t, 'needs-attention')).length,
|
|
244
341
|
}
|
|
245
|
-
}, [
|
|
342
|
+
}, [scopedTasks])
|
|
343
|
+
|
|
344
|
+
const activeScopeLabel = useMemo(() => {
|
|
345
|
+
if (taskScopeFilter === 'all') return 'All tasks'
|
|
346
|
+
if (taskScopeFilter === 'agent' && filterAgentId && agents[filterAgentId]) return `${agents[filterAgentId].name} activity`
|
|
347
|
+
return 'User-facing tasks'
|
|
348
|
+
}, [agents, filterAgentId, taskScopeFilter])
|
|
246
349
|
|
|
247
350
|
const activeAttentionLabel = useMemo(() => {
|
|
248
351
|
if (attentionFilter === 'all') return null
|
|
@@ -301,6 +404,16 @@ export function TaskBoard() {
|
|
|
301
404
|
<p className="text-[13px] text-text-3">
|
|
302
405
|
{stats.total} task{stats.total !== 1 ? 's' : ''}
|
|
303
406
|
</p>
|
|
407
|
+
<span className="inline-flex items-center gap-1 rounded-full bg-white/[0.04] px-2 py-1 text-[11px] font-600 text-text-2">
|
|
408
|
+
{taskScopeFilter === 'agent' && filterAgentId && agents[filterAgentId] ? (
|
|
409
|
+
<>
|
|
410
|
+
<AgentAvatar seed={agents[filterAgentId].avatarSeed || null} avatarUrl={agents[filterAgentId].avatarUrl} name={agents[filterAgentId].name} size={14} />
|
|
411
|
+
{activeScopeLabel}
|
|
412
|
+
</>
|
|
413
|
+
) : (
|
|
414
|
+
activeScopeLabel
|
|
415
|
+
)}
|
|
416
|
+
</span>
|
|
304
417
|
{stats.running > 0 && (
|
|
305
418
|
<span className="inline-flex items-center gap-1 text-[11px] font-600 text-blue-400">
|
|
306
419
|
<span className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse" />
|
|
@@ -338,41 +451,83 @@ export function TaskBoard() {
|
|
|
338
451
|
<button
|
|
339
452
|
onClick={() => setAgentDropdownOpen(!agentDropdownOpen)}
|
|
340
453
|
className={`flex items-center gap-2 px-3 py-2 rounded-[10px] text-[13px] font-600 cursor-pointer transition-all border
|
|
341
|
-
${
|
|
454
|
+
${taskScopeFilter !== 'user-facing'
|
|
342
455
|
? 'bg-white/[0.06] border-white/[0.1] text-text-2'
|
|
343
456
|
: 'bg-transparent border-white/[0.06] text-text-3 hover:bg-white/[0.03]'}`}
|
|
344
457
|
style={{ fontFamily: 'inherit', minWidth: 130 }}
|
|
345
458
|
>
|
|
346
|
-
{filterAgentId && agents[filterAgentId] ? (
|
|
459
|
+
{taskScopeFilter === 'agent' && filterAgentId && agents[filterAgentId] ? (
|
|
347
460
|
<>
|
|
348
461
|
<AgentAvatar seed={agents[filterAgentId].avatarSeed || null} avatarUrl={agents[filterAgentId].avatarUrl} name={agents[filterAgentId].name} size={18} />
|
|
349
462
|
{agents[filterAgentId].name}
|
|
350
463
|
</>
|
|
351
|
-
) : 'All
|
|
464
|
+
) : taskScopeFilter === 'all' ? 'All Tasks' : 'User View'}
|
|
352
465
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" className="ml-auto opacity-50">
|
|
353
466
|
<path d="M2.5 4L5 6.5L7.5 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
354
467
|
</svg>
|
|
355
468
|
</button>
|
|
356
469
|
{agentDropdownOpen && (
|
|
357
|
-
<div className="absolute top-full right-0 mt-1 min-w-[
|
|
470
|
+
<div className="absolute top-full right-0 mt-1 min-w-[240px] py-1 rounded-[12px] border border-white/[0.08] bg-surface-2 shadow-lg z-50">
|
|
471
|
+
<button
|
|
472
|
+
onClick={() => {
|
|
473
|
+
setTaskScopeFilter('user-facing')
|
|
474
|
+
setFilterAgentId('')
|
|
475
|
+
setAgentDropdownOpen(false)
|
|
476
|
+
}}
|
|
477
|
+
className={`w-full flex items-start gap-2.5 px-3 py-2.5 text-[13px] font-600 cursor-pointer border-none text-left transition-colors
|
|
478
|
+
${taskScopeFilter === 'user-facing' ? 'bg-white/[0.06] text-text' : 'bg-transparent text-text-3 hover:bg-white/[0.04]'}`}
|
|
479
|
+
style={{ fontFamily: 'inherit' }}
|
|
480
|
+
>
|
|
481
|
+
<span className="mt-0.5 inline-flex h-5 items-center rounded-full bg-emerald-500/12 px-1.5 text-[10px] font-700 uppercase tracking-[0.08em] text-emerald-400">
|
|
482
|
+
Default
|
|
483
|
+
</span>
|
|
484
|
+
<span className="min-w-0">
|
|
485
|
+
<span className="block">User-facing tasks</span>
|
|
486
|
+
<span className="mt-0.5 block text-[11px] font-500 text-text-3/60">
|
|
487
|
+
Hide scheduled, delegated, and agent-created internal work.
|
|
488
|
+
</span>
|
|
489
|
+
</span>
|
|
490
|
+
</button>
|
|
358
491
|
<button
|
|
359
|
-
onClick={() => {
|
|
360
|
-
|
|
361
|
-
|
|
492
|
+
onClick={() => {
|
|
493
|
+
setTaskScopeFilter('all')
|
|
494
|
+
setFilterAgentId('')
|
|
495
|
+
setAgentDropdownOpen(false)
|
|
496
|
+
}}
|
|
497
|
+
className={`w-full flex items-start gap-2.5 px-3 py-2.5 text-[13px] font-600 cursor-pointer border-none text-left transition-colors
|
|
498
|
+
${taskScopeFilter === 'all' ? 'bg-white/[0.06] text-text' : 'bg-transparent text-text-3 hover:bg-white/[0.04]'}`}
|
|
362
499
|
style={{ fontFamily: 'inherit' }}
|
|
363
500
|
>
|
|
364
|
-
|
|
501
|
+
<span className="mt-0.5 inline-flex h-5 items-center rounded-full bg-white/[0.06] px-1.5 text-[10px] font-700 uppercase tracking-[0.08em] text-text-3">
|
|
502
|
+
All
|
|
503
|
+
</span>
|
|
504
|
+
<span className="min-w-0">
|
|
505
|
+
<span className="block">All tasks</span>
|
|
506
|
+
<span className="mt-0.5 block text-[11px] font-500 text-text-3/60">
|
|
507
|
+
Include internal agent execution, schedules, and delegations.
|
|
508
|
+
</span>
|
|
509
|
+
</span>
|
|
365
510
|
</button>
|
|
511
|
+
<div className="my-1 border-t border-white/[0.06]" />
|
|
366
512
|
{Object.values(agents).sort((a, b) => a.name.localeCompare(b.name)).map((a) => (
|
|
367
513
|
<button
|
|
368
514
|
key={a.id}
|
|
369
|
-
onClick={() => {
|
|
515
|
+
onClick={() => {
|
|
516
|
+
setTaskScopeFilter('agent')
|
|
517
|
+
setFilterAgentId(a.id)
|
|
518
|
+
setAgentDropdownOpen(false)
|
|
519
|
+
}}
|
|
370
520
|
className={`w-full flex items-center gap-2.5 px-3 py-2 text-[13px] font-600 cursor-pointer border-none text-left transition-colors
|
|
371
|
-
${filterAgentId === a.id ? 'bg-white/[0.06] text-text' : 'bg-transparent text-text-3 hover:bg-white/[0.04]'}`}
|
|
521
|
+
${taskScopeFilter === 'agent' && filterAgentId === a.id ? 'bg-white/[0.06] text-text' : 'bg-transparent text-text-3 hover:bg-white/[0.04]'}`}
|
|
372
522
|
style={{ fontFamily: 'inherit' }}
|
|
373
523
|
>
|
|
374
524
|
<AgentAvatar seed={a.avatarSeed || null} avatarUrl={a.avatarUrl} name={a.name} size={20} />
|
|
375
|
-
|
|
525
|
+
<span className="min-w-0 flex-1">
|
|
526
|
+
<span className="block truncate">{a.name}</span>
|
|
527
|
+
<span className="mt-0.5 block text-[11px] font-500 text-text-3/60">
|
|
528
|
+
Assigned, created, or delegated by this agent
|
|
529
|
+
</span>
|
|
530
|
+
</span>
|
|
376
531
|
</button>
|
|
377
532
|
))}
|
|
378
533
|
</div>
|
|
@@ -451,6 +606,16 @@ export function TaskBoard() {
|
|
|
451
606
|
>
|
|
452
607
|
{showArchived ? 'Hide' : 'Show'} Archived{!showArchived && archivedCount > 0 ? ` (${archivedCount})` : ''}
|
|
453
608
|
</button>
|
|
609
|
+
<button
|
|
610
|
+
onClick={() => {
|
|
611
|
+
resetGitHubImportState()
|
|
612
|
+
setGitHubImportOpen(true)
|
|
613
|
+
}}
|
|
614
|
+
className="px-4 py-2 rounded-[10px] text-[13px] font-600 cursor-pointer transition-all border border-white/[0.08] bg-white/[0.04] text-text-2 hover:bg-white/[0.08]"
|
|
615
|
+
style={{ fontFamily: 'inherit' }}
|
|
616
|
+
>
|
|
617
|
+
Import GitHub
|
|
618
|
+
</button>
|
|
454
619
|
<button
|
|
455
620
|
onClick={() => {
|
|
456
621
|
setEditingTaskId(null)
|
|
@@ -520,8 +685,33 @@ export function TaskBoard() {
|
|
|
520
685
|
))}
|
|
521
686
|
</div>
|
|
522
687
|
|
|
523
|
-
{(activeProjectFilter && projects[activeProjectFilter]) || activeAttentionLabel ? (
|
|
688
|
+
{(activeProjectFilter && projects[activeProjectFilter]) || activeAttentionLabel || taskScopeFilter !== 'all' ? (
|
|
524
689
|
<div className="flex flex-wrap items-center gap-2 px-8 pb-3">
|
|
690
|
+
{taskScopeFilter !== 'all' && (
|
|
691
|
+
<span className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-[8px] border text-[12px] font-600 ${
|
|
692
|
+
taskScopeFilter === 'agent'
|
|
693
|
+
? 'bg-accent-soft border-accent-bright/20 text-accent-bright'
|
|
694
|
+
: 'bg-emerald-500/10 border-emerald-500/20 text-emerald-400'
|
|
695
|
+
}`}>
|
|
696
|
+
{taskScopeFilter === 'agent' && filterAgentId && agents[filterAgentId] ? (
|
|
697
|
+
<>
|
|
698
|
+
<AgentAvatar seed={agents[filterAgentId].avatarSeed || null} avatarUrl={agents[filterAgentId].avatarUrl} name={agents[filterAgentId].name} size={14} />
|
|
699
|
+
{agents[filterAgentId].name} activity
|
|
700
|
+
</>
|
|
701
|
+
) : (
|
|
702
|
+
'User-facing tasks'
|
|
703
|
+
)}
|
|
704
|
+
<button
|
|
705
|
+
onClick={() => {
|
|
706
|
+
setTaskScopeFilter('all')
|
|
707
|
+
setFilterAgentId('')
|
|
708
|
+
}}
|
|
709
|
+
className="ml-1 cursor-pointer border-none bg-transparent p-0 text-[14px] leading-none text-current opacity-80 hover:opacity-100"
|
|
710
|
+
>
|
|
711
|
+
×
|
|
712
|
+
</button>
|
|
713
|
+
</span>
|
|
714
|
+
)}
|
|
525
715
|
{activeProjectFilter && projects[activeProjectFilter] && (
|
|
526
716
|
<span className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-[8px] bg-white/[0.04] border border-white/[0.06] text-[12px] font-600 text-text-2">
|
|
527
717
|
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: projects[activeProjectFilter].color || '#6366F1' }} />
|
|
@@ -593,7 +783,7 @@ export function TaskBoard() {
|
|
|
593
783
|
) : filteredTasks.length === 0 ? (
|
|
594
784
|
<div className="max-w-3xl mx-auto rounded-[16px] border border-dashed border-white/[0.08] px-6 py-14 text-center">
|
|
595
785
|
<p className="text-[14px] font-600 text-text-2 mb-1">No tasks match this view</p>
|
|
596
|
-
<p className="text-[12px] text-text-3/60">Try clearing one of the active filters or switching back to
|
|
786
|
+
<p className="text-[12px] text-text-3/60">Try clearing one of the active filters or switching back to all tasks.</p>
|
|
597
787
|
</div>
|
|
598
788
|
) : (
|
|
599
789
|
<div className="max-w-4xl mx-auto">
|
|
@@ -629,6 +819,201 @@ export function TaskBoard() {
|
|
|
629
819
|
</div>
|
|
630
820
|
)}
|
|
631
821
|
|
|
822
|
+
<BottomSheet
|
|
823
|
+
open={githubImportOpen}
|
|
824
|
+
onClose={() => {
|
|
825
|
+
setGitHubImportOpen(false)
|
|
826
|
+
setGitHubImportError(null)
|
|
827
|
+
}}
|
|
828
|
+
wide
|
|
829
|
+
title="Import GitHub Issues"
|
|
830
|
+
description="Pull issues from a GitHub repository into the task board as backlog items."
|
|
831
|
+
>
|
|
832
|
+
<div className="mx-auto w-full max-w-2xl">
|
|
833
|
+
<div className="mb-6">
|
|
834
|
+
<h2 className="font-display text-[24px] font-800 tracking-[-0.03em] text-text">Import GitHub Issues</h2>
|
|
835
|
+
<p className="mt-2 text-[14px] text-text-3">
|
|
836
|
+
Pull issues from a GitHub repository into the task board as backlog items.
|
|
837
|
+
</p>
|
|
838
|
+
</div>
|
|
839
|
+
|
|
840
|
+
<div className="grid gap-4 sm:grid-cols-[minmax(0,1fr)_180px]">
|
|
841
|
+
<label className="block">
|
|
842
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">Repository</span>
|
|
843
|
+
<input
|
|
844
|
+
value={githubRepo}
|
|
845
|
+
onChange={(e) => setGitHubRepo(e.target.value)}
|
|
846
|
+
placeholder="owner/repo or https://github.com/owner/repo"
|
|
847
|
+
className={inputClass}
|
|
848
|
+
style={{ fontFamily: 'inherit' }}
|
|
849
|
+
/>
|
|
850
|
+
</label>
|
|
851
|
+
<label className="block">
|
|
852
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">State</span>
|
|
853
|
+
<select
|
|
854
|
+
value={githubState}
|
|
855
|
+
onChange={(e) => setGitHubState(e.target.value as 'open' | 'closed' | 'all')}
|
|
856
|
+
className={inputClass}
|
|
857
|
+
style={{ fontFamily: 'inherit' }}
|
|
858
|
+
>
|
|
859
|
+
<option value="open">Open issues</option>
|
|
860
|
+
<option value="closed">Closed issues</option>
|
|
861
|
+
<option value="all">All issues</option>
|
|
862
|
+
</select>
|
|
863
|
+
</label>
|
|
864
|
+
</div>
|
|
865
|
+
|
|
866
|
+
<div className="mt-4 grid gap-4 sm:grid-cols-2">
|
|
867
|
+
<label className="block">
|
|
868
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">Limit</span>
|
|
869
|
+
<input
|
|
870
|
+
type="number"
|
|
871
|
+
min={1}
|
|
872
|
+
max={200}
|
|
873
|
+
value={githubLimit}
|
|
874
|
+
onChange={(e) => setGitHubLimit(e.target.value)}
|
|
875
|
+
className={inputClass}
|
|
876
|
+
style={{ fontFamily: 'inherit' }}
|
|
877
|
+
/>
|
|
878
|
+
</label>
|
|
879
|
+
<label className="block">
|
|
880
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">Labels</span>
|
|
881
|
+
<input
|
|
882
|
+
value={githubLabels}
|
|
883
|
+
onChange={(e) => setGitHubLabels(e.target.value)}
|
|
884
|
+
placeholder="bug, api, high priority"
|
|
885
|
+
className={inputClass}
|
|
886
|
+
style={{ fontFamily: 'inherit' }}
|
|
887
|
+
/>
|
|
888
|
+
</label>
|
|
889
|
+
</div>
|
|
890
|
+
|
|
891
|
+
<label className="mt-4 block">
|
|
892
|
+
<span className="mb-2 block text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/70">GitHub token</span>
|
|
893
|
+
<input
|
|
894
|
+
type="password"
|
|
895
|
+
value={githubToken}
|
|
896
|
+
onChange={(e) => setGitHubToken(e.target.value)}
|
|
897
|
+
placeholder="Optional. Needed for private repos or higher rate limits."
|
|
898
|
+
className={inputClass}
|
|
899
|
+
style={{ fontFamily: 'inherit' }}
|
|
900
|
+
/>
|
|
901
|
+
</label>
|
|
902
|
+
|
|
903
|
+
<div className="mt-3 flex flex-wrap items-center gap-2">
|
|
904
|
+
<span className="inline-flex items-center rounded-full bg-white/[0.05] px-3 py-1 text-[12px] font-600 text-text-2">
|
|
905
|
+
Imported tasks land in backlog
|
|
906
|
+
</span>
|
|
907
|
+
{activeProjectFilter && projects[activeProjectFilter] && (
|
|
908
|
+
<span className="inline-flex items-center gap-1.5 rounded-full bg-accent-soft px-3 py-1 text-[12px] font-600 text-accent-bright">
|
|
909
|
+
<span className="h-2 w-2 rounded-full" style={{ backgroundColor: projects[activeProjectFilter].color || '#6366F1' }} />
|
|
910
|
+
Project: {projects[activeProjectFilter].name}
|
|
911
|
+
</span>
|
|
912
|
+
)}
|
|
913
|
+
</div>
|
|
914
|
+
|
|
915
|
+
{githubImportError && (
|
|
916
|
+
<div className="mt-4 rounded-[14px] border border-red-500/20 bg-red-500/10 px-4 py-3 text-[13px] text-red-200">
|
|
917
|
+
{githubImportError}
|
|
918
|
+
</div>
|
|
919
|
+
)}
|
|
920
|
+
|
|
921
|
+
{githubImportResult && (
|
|
922
|
+
<div className="mt-5 rounded-[18px] border border-white/[0.08] bg-white/[0.03] p-4">
|
|
923
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
924
|
+
<span className="text-[14px] font-700 text-text">{githubImportResult.repo}</span>
|
|
925
|
+
<span className="rounded-full bg-white/[0.05] px-2 py-1 text-[11px] font-600 text-text-3">
|
|
926
|
+
{githubImportResult.fetched} fetched
|
|
927
|
+
</span>
|
|
928
|
+
<span className="rounded-full bg-emerald-500/10 px-2 py-1 text-[11px] font-600 text-emerald-300">
|
|
929
|
+
{githubImportResult.created.length} created
|
|
930
|
+
</span>
|
|
931
|
+
<span className="rounded-full bg-amber-500/10 px-2 py-1 text-[11px] font-600 text-amber-300">
|
|
932
|
+
{githubImportResult.skipped.length} skipped
|
|
933
|
+
</span>
|
|
934
|
+
</div>
|
|
935
|
+
|
|
936
|
+
{githubImportResult.created.length > 0 && (
|
|
937
|
+
<div className="mt-4">
|
|
938
|
+
<p className="text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/60">Created</p>
|
|
939
|
+
<div className="mt-2 flex flex-col gap-2">
|
|
940
|
+
{githubImportResult.created.slice(0, 8).map((item) => (
|
|
941
|
+
item.url ? (
|
|
942
|
+
<a
|
|
943
|
+
key={`created-${item.taskId || item.number}`}
|
|
944
|
+
href={item.url}
|
|
945
|
+
target="_blank"
|
|
946
|
+
rel="noreferrer"
|
|
947
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-2 no-underline transition-colors hover:bg-white/[0.05]"
|
|
948
|
+
>
|
|
949
|
+
#{item.number} {item.title}
|
|
950
|
+
</a>
|
|
951
|
+
) : (
|
|
952
|
+
<div
|
|
953
|
+
key={`created-${item.taskId || item.number}`}
|
|
954
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-2"
|
|
955
|
+
>
|
|
956
|
+
#{item.number} {item.title}
|
|
957
|
+
</div>
|
|
958
|
+
)
|
|
959
|
+
))}
|
|
960
|
+
</div>
|
|
961
|
+
</div>
|
|
962
|
+
)}
|
|
963
|
+
|
|
964
|
+
{githubImportResult.skipped.length > 0 && (
|
|
965
|
+
<div className="mt-4">
|
|
966
|
+
<p className="text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/60">Skipped existing</p>
|
|
967
|
+
<div className="mt-2 flex flex-col gap-2">
|
|
968
|
+
{githubImportResult.skipped.slice(0, 8).map((item) => (
|
|
969
|
+
item.url ? (
|
|
970
|
+
<a
|
|
971
|
+
key={`skipped-${item.taskId || item.number}`}
|
|
972
|
+
href={item.url}
|
|
973
|
+
target="_blank"
|
|
974
|
+
rel="noreferrer"
|
|
975
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-3 no-underline transition-colors hover:bg-white/[0.05] hover:text-text-2"
|
|
976
|
+
>
|
|
977
|
+
#{item.number} {item.title}
|
|
978
|
+
</a>
|
|
979
|
+
) : (
|
|
980
|
+
<div
|
|
981
|
+
key={`skipped-${item.taskId || item.number}`}
|
|
982
|
+
className="rounded-[12px] border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[13px] text-text-3"
|
|
983
|
+
>
|
|
984
|
+
#{item.number} {item.title}
|
|
985
|
+
</div>
|
|
986
|
+
)
|
|
987
|
+
))}
|
|
988
|
+
</div>
|
|
989
|
+
</div>
|
|
990
|
+
)}
|
|
991
|
+
</div>
|
|
992
|
+
)}
|
|
993
|
+
|
|
994
|
+
<div className="mt-6 flex items-center justify-end gap-3">
|
|
995
|
+
<button
|
|
996
|
+
onClick={() => {
|
|
997
|
+
setGitHubImportOpen(false)
|
|
998
|
+
setGitHubImportError(null)
|
|
999
|
+
}}
|
|
1000
|
+
className="px-4 py-2 rounded-[10px] border border-white/[0.08] bg-transparent text-[13px] font-600 text-text-3 transition-colors hover:bg-white/[0.04] hover:text-text-2"
|
|
1001
|
+
style={{ fontFamily: 'inherit' }}
|
|
1002
|
+
>
|
|
1003
|
+
Close
|
|
1004
|
+
</button>
|
|
1005
|
+
<button
|
|
1006
|
+
onClick={handleGitHubImport}
|
|
1007
|
+
disabled={githubImporting}
|
|
1008
|
+
className="px-5 py-2.5 rounded-[12px] border-none bg-accent-bright text-white text-[14px] font-700 transition-all disabled:cursor-not-allowed disabled:opacity-60"
|
|
1009
|
+
style={{ fontFamily: 'inherit' }}
|
|
1010
|
+
>
|
|
1011
|
+
{githubImporting ? 'Importing...' : 'Import issues'}
|
|
1012
|
+
</button>
|
|
1013
|
+
</div>
|
|
1014
|
+
</div>
|
|
1015
|
+
</BottomSheet>
|
|
1016
|
+
|
|
632
1017
|
{/* Bulk action bar */}
|
|
633
1018
|
{selectionMode && (
|
|
634
1019
|
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 flex items-center gap-2 px-4 py-3 rounded-[16px] bg-surface-2/95 backdrop-blur-xl border border-white/[0.1] shadow-[0_8px_40px_rgba(0,0,0,0.5)] z-50">
|