feat(graph): add Assign button and archetype dropdown to GraphNodeCard
## The Collaboration Story User requested ability to assign agent archetypes to tasks directly from graph nodes. This was the core UI feature request. ## Design Decisions Made Together 1. **Placement**: We decided to put the assign UI at the bottom of the card to not interfere with existing status/badges display 2. **Pattern**: Used Radix dropdown-menu (already in project) for consistency 3. **Visual feedback**: Added loading spinner during API calls, success/error messages that auto-dismiss 4. **Closed tasks**: Excluded closed tasks from assignment (logical constraint) ## Issues We Encountered - Initially only added POST handler for assignment - User pointed out we needed DELETE for unassign - added that - The unassign button (X) needed to call DELETE not POST ## API Changes - Added DELETE handler to /api/swarm/prep for removing agent assignments - Uses 'bd label remove' under the hood ## What the UI Shows - Dropdown with available archetypes - Badge showing assigned archetype name with color - X button to unassign (on each badge) - 'Assigned' label on already-assigned archetypes in dropdown ## Test Coverage - Added graph-node-assign.test.tsx with 6 TDD tests ## Beads: beadboard-brq (closed)
This commit is contained in:
parent
164b26e570
commit
512a836db4
3 changed files with 297 additions and 19 deletions
66
tests/components/graph/graph-node-assign.test.tsx
Normal file
66
tests/components/graph/graph-node-assign.test.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
// Test that GraphNodeCard has Assign button for assignable tasks
|
||||
test('GraphNodeCard checks for assignable status (open, blocked, ready)', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/graph/graph-node-card.tsx'), 'utf-8');
|
||||
assert.ok(
|
||||
fileContent.includes("'open'") &&
|
||||
(fileContent.includes("'blocked'") || fileContent.includes("status === 'blocked'")),
|
||||
'GraphNodeCard should check for open/blocked status for assign button'
|
||||
);
|
||||
});
|
||||
|
||||
// Test that Assign button is NOT shown for closed tasks
|
||||
test('GraphNodeCard excludes closed tasks from assign button', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/graph/graph-node-card.tsx'), 'utf-8');
|
||||
assert.ok(
|
||||
!fileContent.includes("status === 'closed' &&") ||
|
||||
fileContent.includes("status !== 'closed'"),
|
||||
'Assign button should not show for closed tasks'
|
||||
);
|
||||
});
|
||||
|
||||
// Test that GraphNodeCard shows assigned archetype from labels
|
||||
test('GraphNodeCard parses agent: label to show assigned archetype', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/graph/graph-node-card.tsx'), 'utf-8');
|
||||
assert.ok(
|
||||
fileContent.includes('agent:') ||
|
||||
fileContent.includes('getArchetypeFromLabels') ||
|
||||
fileContent.includes('data.labels'),
|
||||
'GraphNodeCard should check for agent: labels'
|
||||
);
|
||||
});
|
||||
|
||||
// Test that Radix dropdown-menu is imported
|
||||
test('GraphNodeCard imports Radix dropdown-menu for archetype selection', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/graph/graph-node-card.tsx'), 'utf-8');
|
||||
assert.ok(
|
||||
fileContent.includes('@radix-ui/react-dropdown-menu') ||
|
||||
fileContent.includes('DropdownMenu'),
|
||||
'GraphNodeCard should import Radix dropdown-menu'
|
||||
);
|
||||
});
|
||||
|
||||
// Test that archetypes are passed to node and used
|
||||
test('GraphNodeCard receives and uses archetypes for dropdown', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/graph/graph-node-card.tsx'), 'utf-8');
|
||||
assert.ok(
|
||||
fileContent.includes('archetypes') ||
|
||||
fileContent.includes('AgentArchetype'),
|
||||
'GraphNodeCard should reference archetypes'
|
||||
);
|
||||
});
|
||||
|
||||
// Test that onAssign callback or similar is supported
|
||||
test('GraphNodeCard supports assignment callback', async () => {
|
||||
const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/graph/graph-node-card.tsx'), 'utf-8');
|
||||
assert.ok(
|
||||
fileContent.includes('onAssign') ||
|
||||
fileContent.includes('Assign') ||
|
||||
fileContent.includes('/api/swarm/prep'),
|
||||
'GraphNodeCard should support assignment action'
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue