mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-24 02:39:17 -05:00
* refactor: complete Phase 2 Query Keys Standardization Standardize query keys across all features following vertical slice architecture, ensuring they mirror backend API structure exactly with no backward compatibility. Key Changes: - Refactor all query key factories to follow consistent patterns - Move progress feature from knowledge/progress to top-level /features/progress - Create shared query patterns for consistency (DISABLED_QUERY_KEY, STALE_TIMES) - Remove all hardcoded stale times and disabled keys - Update all imports after progress feature relocation Query Key Factories Standardized: - projectKeys: removed task-related keys (tasks, taskCounts) - taskKeys: added dual nature support (global via lists(), project-scoped via byProject()) - knowledgeKeys: removed redundant methods (details, summary) - progressKeys: new top-level feature with consistent factory - documentKeys: full factory pattern with versions support - mcpKeys: complete with health endpoint Shared Patterns Implementation: - STALE_TIMES: instant (0), realtime (3s), frequent (5s), normal (30s), rare (5m), static (∞) - DISABLED_QUERY_KEY: consistent disabled query pattern across all features - Removed unused createQueryOptions helper Testing: - Added comprehensive tests for progress hooks - Updated all test mocks to include new STALE_TIMES values - All 81 feature tests passing Documentation: - Created QUERY_PATTERNS.md guide for future implementations - Clear patterns, examples, and migration checklist Breaking Changes: - Progress imports moved from knowledge/progress to progress - Query key structure changes (cache will reset) - No backward compatibility maintained Co-Authored-By: Claude <noreply@anthropic.com> * fix: establish single source of truth for tags in metadata - Remove ambiguous top-level tags field from KnowledgeItem interface - Update all UI components to use metadata.tags exclusively - Fix mutations to correctly update tags in metadata object - Remove duplicate tags field from backend KnowledgeSummaryService - Fix test setup issue with QueryClient instance in knowledge tests - Add TODO comments for filter-blind optimistic updates (Phase 3) This eliminates the ambiguity identified in Phase 2 where both item.tags and metadata.tags existed, establishing metadata.tags as the single source of truth across the entire stack. * fix: comprehensive progress hooks improvements - Integrate useSmartPolling for all polling queries - Fix memory leaks from uncleaned timeouts - Replace string-based error checking with status codes - Remove TypeScript any usage with proper types - Fix unstable dependencies with sorted JSON serialization - Add staleTime to document queries for consistency * feat: implement flexible assignee system for dynamic agents - Changed assignee from restricted enum to flexible string type - Renamed "AI IDE Agent" to "Coding Agent" for clarity - Enhanced ComboBox with Radix UI best practices: - Full ARIA compliance (roles, labels, keyboard nav) - Performance optimizations (memoization, useCallback) - Improved UX (auto-scroll, keyboard shortcuts) - Fixed event bubbling preventing unintended modal opens - Updated MCP server docs to reflect flexible assignee capability - Removed unnecessary UI elements (arrows, helper text) - Styled ComboBox to match priority selector aesthetic This allows external MCP clients to create and assign custom sub-agents dynamically, supporting advanced agent orchestration workflows. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * fix: complete Phase 2 summariesPrefix usage for cache consistency - Fix all knowledgeKeys.summaries() calls to use summariesPrefix() for operations targeting multiple summary caches - Update cancelQueries, getQueriesData, setQueriesData, invalidateQueries, and refetchQueries calls - Fix critical cache invalidation bug where filtered summaries weren't being cleared - Update test expectations to match new factory patterns - Address CodeRabbit review feedback on cache stability issues This completes the Phase 2 Query Keys Standardization work documented in PRPs/local/frontend-state-management-refactor.md 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: update MCP task tools documentation for Coding Agent rename Update task assignee documentation from "AI IDE Agent" to "Coding Agent" to match frontend changes for consistency across the system. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: implement assignee filtering in MCP find_tasks function Add missing implementation for filter_by="assignee" that was documented but not coded. The filter now properly passes the assignee parameter to the backend API, matching the existing pattern used for status filtering. Fixes documentation/implementation mismatch identified by CodeRabbit. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: Phase 2 cleanup - address review comments and improve code quality Changes made: - Reduced smart polling interval from 60s to 5s for background tabs (better responsiveness) - Fixed cache coherence bug in knowledge queries (missing limit parameter) - Standardized "Coding Agent" naming (was inconsistently "AI IDE Agent") - Improved task queries with 2s polling, type safety, and proper invalidation - Enhanced combobox accessibility with proper ARIA attributes and IDs - Delegated useCrawlProgressPolling to useActiveOperations (removed duplication) - Added exact: true to progress query removals (prevents sibling removal) - Fixed invalid Tailwind class ml-4.5 to ml-4 All changes align with Phase 2 query key standardization goals and improve overall code quality, accessibility, and performance. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
210 lines
6.9 KiB
TypeScript
210 lines
6.9 KiB
TypeScript
import { memo, useCallback, useEffect, useState } from "react";
|
|
import {
|
|
Button,
|
|
ComboBox,
|
|
type ComboBoxOption,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
FormField,
|
|
FormGrid,
|
|
Input,
|
|
Label,
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
TextArea,
|
|
} from "../../../ui/primitives";
|
|
import { useTaskEditor } from "../hooks";
|
|
import { type Assignee, COMMON_ASSIGNEES, type Task, type TaskPriority } from "../types";
|
|
import { FeatureSelect } from "./FeatureSelect";
|
|
|
|
interface TaskEditModalProps {
|
|
isModalOpen: boolean;
|
|
editingTask: Task | null;
|
|
projectId: string;
|
|
onClose: () => void;
|
|
onSaved?: () => void;
|
|
onOpenChange?: (open: boolean) => void;
|
|
}
|
|
|
|
// Convert common assignees to ComboBox options
|
|
const ASSIGNEE_OPTIONS: ComboBoxOption[] = COMMON_ASSIGNEES.map((name) => ({
|
|
value: name,
|
|
label: name,
|
|
description:
|
|
name === "User" ? "Assign to human user" : name === "Archon" ? "Assign to Archon system" : "Assign to Coding Agent",
|
|
}));
|
|
|
|
export const TaskEditModal = memo(
|
|
({ isModalOpen, editingTask, projectId, onClose, onSaved, onOpenChange }: TaskEditModalProps) => {
|
|
const [localTask, setLocalTask] = useState<Partial<Task> | null>(null);
|
|
|
|
// Use business logic hook
|
|
const { projectFeatures, saveTask, isLoadingFeatures, isSaving: isSavingTask } = useTaskEditor(projectId);
|
|
|
|
// Sync local state with editingTask when it changes
|
|
useEffect(() => {
|
|
if (editingTask) {
|
|
setLocalTask(editingTask);
|
|
} else {
|
|
// Reset for new task
|
|
setLocalTask({
|
|
title: "",
|
|
description: "",
|
|
status: "todo",
|
|
assignee: "User" as Assignee,
|
|
feature: "",
|
|
priority: "medium" as TaskPriority, // Direct priority field
|
|
});
|
|
}
|
|
}, [editingTask]);
|
|
|
|
// Memoized handlers for input changes
|
|
const handleTitleChange = useCallback((value: string) => {
|
|
setLocalTask((prev) => (prev ? { ...prev, title: value } : null));
|
|
}, []);
|
|
|
|
const handleDescriptionChange = useCallback((value: string) => {
|
|
setLocalTask((prev) => (prev ? { ...prev, description: value } : null));
|
|
}, []);
|
|
|
|
const handleFeatureChange = useCallback((value: string) => {
|
|
setLocalTask((prev) => (prev ? { ...prev, feature: value } : null));
|
|
}, []);
|
|
|
|
const handleSave = useCallback(() => {
|
|
// All validation is now in the hook
|
|
saveTask(localTask, editingTask, () => {
|
|
onSaved?.();
|
|
onClose();
|
|
});
|
|
}, [localTask, editingTask, saveTask, onSaved, onClose]);
|
|
|
|
const handleClose = useCallback(() => {
|
|
onClose();
|
|
}, [onClose]);
|
|
|
|
return (
|
|
<Dialog open={isModalOpen} onOpenChange={onOpenChange || ((open) => !open && onClose())}>
|
|
<DialogContent className="max-w-2xl">
|
|
<DialogHeader>
|
|
<DialogTitle>{editingTask?.id ? "Edit Task" : "New Task"}</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4">
|
|
<FormField>
|
|
<Label required>Title</Label>
|
|
<Input
|
|
value={localTask?.title || ""}
|
|
onChange={(e) => handleTitleChange(e.target.value)}
|
|
placeholder="Enter task title"
|
|
/>
|
|
</FormField>
|
|
|
|
<FormField>
|
|
<Label>Description</Label>
|
|
<TextArea
|
|
value={localTask?.description || ""}
|
|
onChange={(e) => handleDescriptionChange(e.target.value)}
|
|
rows={5}
|
|
placeholder="Enter task description"
|
|
/>
|
|
</FormField>
|
|
|
|
<FormGrid columns={2}>
|
|
<FormField>
|
|
<Label>Status</Label>
|
|
<Select
|
|
value={localTask?.status || "todo"}
|
|
onValueChange={(value) =>
|
|
setLocalTask((prev) => (prev ? { ...prev, status: value as Task["status"] } : null))
|
|
}
|
|
>
|
|
<SelectTrigger className="w-full">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="todo">Todo</SelectItem>
|
|
<SelectItem value="doing">Doing</SelectItem>
|
|
<SelectItem value="review">Review</SelectItem>
|
|
<SelectItem value="done">Done</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</FormField>
|
|
|
|
<FormField>
|
|
<Label>Priority</Label>
|
|
<Select
|
|
value={localTask?.priority || "medium"}
|
|
onValueChange={(value) =>
|
|
setLocalTask((prev) => (prev ? { ...prev, priority: value as TaskPriority } : null))
|
|
}
|
|
>
|
|
<SelectTrigger className="w-full">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="critical">Critical</SelectItem>
|
|
<SelectItem value="high">High</SelectItem>
|
|
<SelectItem value="medium">Medium</SelectItem>
|
|
<SelectItem value="low">Low</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</FormField>
|
|
</FormGrid>
|
|
|
|
<FormGrid columns={2}>
|
|
<FormField>
|
|
<Label>Assignee</Label>
|
|
<ComboBox
|
|
options={ASSIGNEE_OPTIONS}
|
|
value={localTask?.assignee || "User"}
|
|
onValueChange={(value) => setLocalTask((prev) => (prev ? { ...prev, assignee: value } : null))}
|
|
placeholder="Select or type assignee..."
|
|
searchPlaceholder="Search or enter custom..."
|
|
emptyMessage="Type a custom assignee name"
|
|
className="w-full"
|
|
allowCustomValue={true}
|
|
/>
|
|
</FormField>
|
|
|
|
<FormField>
|
|
<Label>Feature</Label>
|
|
<FeatureSelect
|
|
value={localTask?.feature || ""}
|
|
onChange={handleFeatureChange}
|
|
projectFeatures={projectFeatures}
|
|
isLoadingFeatures={isLoadingFeatures}
|
|
placeholder="Select or create feature..."
|
|
className="w-full"
|
|
/>
|
|
</FormField>
|
|
</FormGrid>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button onClick={handleClose} variant="outline" disabled={isSavingTask}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={handleSave}
|
|
variant="cyan"
|
|
loading={isSavingTask}
|
|
disabled={isSavingTask || !localTask?.title}
|
|
>
|
|
{editingTask?.id ? "Update Task" : "Create Task"}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
},
|
|
);
|
|
|
|
TaskEditModal.displayName = "TaskEditModal";
|