fix(team): resolve TypeScript errors in team collaboration module
- Remove unused imports and variables in Team components - Fix CollaborationEvent type import in useTeamEvents - Add proper type guards for Hand status in gatewayStore - Fix Session status type compatibility in gateway-client - Remove unused getGatewayClient import from teamStore - Handle unknown payload types in TeamCollaborationView Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,11 +7,11 @@
|
|||||||
* @module components/DevQALoop
|
* @module components/DevQALoop
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTeamStore } from '../store/teamStore';
|
import { useTeamStore } from '../store/teamStore';
|
||||||
import type { DevQALoop as DevQALoopType, ReviewFeedback, ReviewIssue } from '../types/team';
|
import type { DevQALoop as DevQALoopType, ReviewFeedback, ReviewIssue } from '../types/team';
|
||||||
import {
|
import {
|
||||||
RefreshCw, CheckCircle, XCircle, AlertTriangle, ArrowRight,
|
RefreshCw, CheckCircle, XCircle, AlertTriangle,
|
||||||
Clock, MessageSquare, FileCode, Bug, Lightbulb, ChevronDown, ChevronUp,
|
Clock, MessageSquare, FileCode, Bug, Lightbulb, ChevronDown, ChevronUp,
|
||||||
Send, ThumbsUp, ThumbsDown, AlertOctagon,
|
Send, ThumbsUp, ThumbsDown, AlertOctagon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
@@ -126,7 +126,7 @@ interface ReviewFormProps {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReviewForm({ loopId, teamId, onSubmit, onCancel }: ReviewFormProps) {
|
function ReviewForm({ loopId: _loopId, teamId: _teamId, onSubmit, onCancel }: ReviewFormProps) {
|
||||||
const [verdict, setVerdict] = useState<ReviewFeedback['verdict']>('needs_work');
|
const [verdict, setVerdict] = useState<ReviewFeedback['verdict']>('needs_work');
|
||||||
const [comment, setComment] = useState('');
|
const [comment, setComment] = useState('');
|
||||||
const [issues, setIssues] = useState<ReviewIssue[]>([]);
|
const [issues, setIssues] = useState<ReviewIssue[]>([]);
|
||||||
@@ -316,10 +316,6 @@ export function DevQALoopPanel({ loop, teamId, developerName, reviewerName, task
|
|||||||
setShowReviewForm(false);
|
setShowReviewForm(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStartRevising = async () => {
|
|
||||||
await updateLoopState(teamId, loop.id, 'revising');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCompleteRevision = async () => {
|
const handleCompleteRevision = async () => {
|
||||||
await updateLoopState(teamId, loop.id, 'reviewing');
|
await updateLoopState(teamId, loop.id, 'reviewing');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useState, useEffect, useRef } from 'react';
|
|||||||
import { useTeamStore } from '../store/teamStore';
|
import { useTeamStore } from '../store/teamStore';
|
||||||
import type { Team, TeamMember, TeamTask, CollaborationEvent } from '../types/team';
|
import type { Team, TeamMember, TeamTask, CollaborationEvent } from '../types/team';
|
||||||
import {
|
import {
|
||||||
Activity, Users, CheckCircle, Clock, AlertTriangle, Play, Pause,
|
Activity, Users, CheckCircle, AlertTriangle, Play,
|
||||||
ArrowRight, GitBranch, MessageSquare, FileCode, Bot, Zap,
|
ArrowRight, GitBranch, MessageSquare, FileCode, Bot, Zap,
|
||||||
TrendingUp, TrendingDown, Minus, Circle,
|
TrendingUp, TrendingDown, Minus, Circle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
@@ -56,7 +56,9 @@ function EventFeedItem({ event, team }: EventFeedItemProps) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-0.5 line-clamp-2">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mt-0.5 line-clamp-2">
|
||||||
{event.payload.description || JSON.stringify(event.payload).slice(0, 100)}
|
{typeof event.payload.description === 'string'
|
||||||
|
? event.payload.description
|
||||||
|
: JSON.stringify(event.payload).slice(0, 100)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-400 whitespace-nowrap">
|
<span className="text-xs text-gray-400 whitespace-nowrap">
|
||||||
|
|||||||
@@ -7,11 +7,10 @@
|
|||||||
* @module components/TeamOrchestrator
|
* @module components/TeamOrchestrator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useTeamStore } from '../store/teamStore';
|
import { useTeamStore } from '../store/teamStore';
|
||||||
import { useGatewayStore } from '../store/gatewayStore';
|
import { useGatewayStore } from '../store/gatewayStore';
|
||||||
import type {
|
import type {
|
||||||
Team,
|
|
||||||
TeamMember,
|
TeamMember,
|
||||||
TeamTask,
|
TeamTask,
|
||||||
TeamMemberRole,
|
TeamMemberRole,
|
||||||
@@ -19,9 +18,9 @@ import type {
|
|||||||
CollaborationPattern,
|
CollaborationPattern,
|
||||||
} from '../types/team';
|
} from '../types/team';
|
||||||
import {
|
import {
|
||||||
Users, Plus, Trash2, Edit2, Check, X, ChevronDown, ChevronUp,
|
Users, Plus, Trash2, X,
|
||||||
Bot, GitBranch, ArrowRight, Clock, AlertTriangle, CheckCircle,
|
Bot, Clock, AlertTriangle, CheckCircle,
|
||||||
Play, Pause, Settings, UserPlus, FileText, Activity,
|
Play, UserPlus, FileText,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
// === Sub-Components ===
|
// === Sub-Components ===
|
||||||
@@ -115,7 +114,7 @@ interface TaskCardProps {
|
|||||||
onStatusChange: (status: TeamTask['status']) => void;
|
onStatusChange: (status: TeamTask['status']) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TaskCard({ task, members, isSelected, onSelect, onAssign, onStatusChange }: TaskCardProps) {
|
function TaskCard({ task, members, isSelected, onSelect, onAssign, onStatusChange: _onStatusChange }: TaskCardProps) {
|
||||||
const [showAssignMenu, setShowAssignMenu] = useState(false);
|
const [showAssignMenu, setShowAssignMenu] = useState(false);
|
||||||
|
|
||||||
const priorityColors: Record<TaskPriority, string> = {
|
const priorityColors: Record<TaskPriority, string> = {
|
||||||
@@ -216,7 +215,6 @@ export function TeamOrchestrator({ isOpen, onClose }: TeamOrchestratorProps) {
|
|||||||
teams,
|
teams,
|
||||||
activeTeam,
|
activeTeam,
|
||||||
metrics,
|
metrics,
|
||||||
isLoading,
|
|
||||||
error,
|
error,
|
||||||
selectedTaskId,
|
selectedTaskId,
|
||||||
selectedMemberId,
|
selectedMemberId,
|
||||||
|
|||||||
@@ -967,12 +967,42 @@ export class GatewayClient {
|
|||||||
// === OpenFang Hands API ===
|
// === OpenFang Hands API ===
|
||||||
|
|
||||||
/** List available Hands */
|
/** List available Hands */
|
||||||
async listHands(): Promise<{ hands: { id: string; name: string; description: string; status: string; requirements_met?: boolean; category?: string }[] }> {
|
async listHands(): Promise<{
|
||||||
|
hands: {
|
||||||
|
id?: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
status?: string;
|
||||||
|
requirements_met?: boolean;
|
||||||
|
category?: string;
|
||||||
|
icon?: string;
|
||||||
|
tool_count?: number;
|
||||||
|
tools?: string[];
|
||||||
|
metric_count?: number;
|
||||||
|
metrics?: string[];
|
||||||
|
}[]
|
||||||
|
}> {
|
||||||
return this.restGet('/api/hands');
|
return this.restGet('/api/hands');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get Hand details */
|
/** Get Hand details */
|
||||||
async getHand(name: string): Promise<{ name: string; description: string; config: Record<string, unknown> }> {
|
async getHand(name: string): Promise<{
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
status?: string;
|
||||||
|
requirements_met?: boolean;
|
||||||
|
category?: string;
|
||||||
|
icon?: string;
|
||||||
|
provider?: string;
|
||||||
|
model?: string;
|
||||||
|
requirements?: { description?: string; name?: string; met?: boolean; satisfied?: boolean; details?: string; hint?: string }[];
|
||||||
|
tools?: string[];
|
||||||
|
metrics?: string[];
|
||||||
|
config?: Record<string, unknown>;
|
||||||
|
tool_count?: number;
|
||||||
|
metric_count?: number;
|
||||||
|
}> {
|
||||||
return this.restGet(`/api/hands/${name}`);
|
return this.restGet(`/api/hands/${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1097,7 +1127,7 @@ export class GatewayClient {
|
|||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
message_count?: number;
|
message_count?: number;
|
||||||
status?: string;
|
status?: 'active' | 'archived' | 'expired';
|
||||||
}>;
|
}>;
|
||||||
}> {
|
}> {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -1113,7 +1143,7 @@ export class GatewayClient {
|
|||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
message_count?: number;
|
message_count?: number;
|
||||||
status?: string;
|
status?: 'active' | 'archived' | 'expired';
|
||||||
metadata?: Record<string, unknown>;
|
metadata?: Record<string, unknown>;
|
||||||
}> {
|
}> {
|
||||||
return this.restGet(`/api/sessions/${sessionId}`);
|
return this.restGet(`/api/sessions/${sessionId}`);
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import type {
|
|||||||
TeamMetrics,
|
TeamMetrics,
|
||||||
} from '../types/team';
|
} from '../types/team';
|
||||||
|
|
||||||
|
// Re-export types for consumers
|
||||||
|
export type { CollaborationEvent } from '../types/team';
|
||||||
|
|
||||||
// === Configuration ===
|
// === Configuration ===
|
||||||
|
|
||||||
const API_BASE = '/api'; // Uses Vite proxy
|
const API_BASE = '/api'; // Uses Vite proxy
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import { useEffect, useRef, useCallback } from 'react';
|
import { useEffect, useRef, useCallback } from 'react';
|
||||||
import { useTeamStore } from '../store/teamStore';
|
import { useTeamStore } from '../store/teamStore';
|
||||||
import { useGatewayStore } from '../store/gatewayStore';
|
import { useGatewayStore } from '../store/gatewayStore';
|
||||||
import type { TeamEventMessage, TeamEventType } from '../lib/team-client';
|
import type { TeamEventMessage, TeamEventType, CollaborationEvent } from '../lib/team-client';
|
||||||
|
|
||||||
interface UseTeamEventsOptions {
|
interface UseTeamEventsOptions {
|
||||||
/** Subscribe to specific team only, or null for all teams */
|
/** Subscribe to specific team only, or null for all teams */
|
||||||
@@ -25,12 +25,11 @@ interface UseTeamEventsOptions {
|
|||||||
* Hook for subscribing to real-time team collaboration events
|
* Hook for subscribing to real-time team collaboration events
|
||||||
*/
|
*/
|
||||||
export function useTeamEvents(options: UseTeamEventsOptions = {}) {
|
export function useTeamEvents(options: UseTeamEventsOptions = {}) {
|
||||||
const { teamId = null, eventTypes, maxEvents = 100 } = options;
|
const { teamId = null, eventTypes } = options;
|
||||||
const unsubscribeRef = useRef<(() => void) | null>(null);
|
const unsubscribeRef = useRef<(() => void) | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
addEvent,
|
addEvent,
|
||||||
setActiveTeam,
|
|
||||||
updateTaskStatus,
|
updateTaskStatus,
|
||||||
updateLoopState,
|
updateLoopState,
|
||||||
loadTeams,
|
loadTeams,
|
||||||
|
|||||||
@@ -1039,17 +1039,23 @@ export const useGatewayStore = create<GatewayStore>((set, get) => {
|
|||||||
try {
|
try {
|
||||||
const result = await get().client.listHands();
|
const result = await get().client.listHands();
|
||||||
// Map API response to Hand interface
|
// Map API response to Hand interface
|
||||||
const hands: Hand[] = (result?.hands || []).map(h => ({
|
const validStatuses = ['idle', 'running', 'needs_approval', 'error', 'unavailable', 'setup_needed'] as const;
|
||||||
|
const hands: Hand[] = (result?.hands || []).map(h => {
|
||||||
|
const status = validStatuses.includes(h.status as any)
|
||||||
|
? h.status as Hand['status']
|
||||||
|
: (h.requirements_met ? 'idle' : 'setup_needed');
|
||||||
|
return {
|
||||||
id: h.id || h.name,
|
id: h.id || h.name,
|
||||||
name: h.name,
|
name: h.name,
|
||||||
description: h.description || '',
|
description: h.description || '',
|
||||||
status: h.status || (h.requirements_met ? 'idle' : 'setup_needed'),
|
status,
|
||||||
requirements_met: h.requirements_met,
|
requirements_met: h.requirements_met,
|
||||||
category: h.category,
|
category: h.category,
|
||||||
icon: h.icon,
|
icon: h.icon,
|
||||||
toolCount: h.tool_count || h.tools?.length,
|
toolCount: h.tool_count || h.tools?.length,
|
||||||
metricCount: h.metric_count || h.metrics?.length,
|
metricCount: h.metric_count || h.metrics?.length,
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
set({ hands, isLoading: false });
|
set({ hands, isLoading: false });
|
||||||
} catch {
|
} catch {
|
||||||
set({ isLoading: false });
|
set({ isLoading: false });
|
||||||
@@ -1062,24 +1068,39 @@ export const useGatewayStore = create<GatewayStore>((set, get) => {
|
|||||||
const result = await get().client.getHand(name);
|
const result = await get().client.getHand(name);
|
||||||
if (!result) return undefined;
|
if (!result) return undefined;
|
||||||
|
|
||||||
|
// Helper to extract string from unknown config
|
||||||
|
const getStringFromConfig = (key: string): string | undefined => {
|
||||||
|
const val = result.config?.[key];
|
||||||
|
return typeof val === 'string' ? val : undefined;
|
||||||
|
};
|
||||||
|
const getArrayFromConfig = (key: string): string[] | undefined => {
|
||||||
|
const val = result.config?.[key];
|
||||||
|
return Array.isArray(val) ? val : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validStatuses = ['idle', 'running', 'needs_approval', 'error', 'unavailable', 'setup_needed'] as const;
|
||||||
|
const status = validStatuses.includes(result.status as any)
|
||||||
|
? result.status as Hand['status']
|
||||||
|
: (result.requirements_met ? 'idle' : 'setup_needed');
|
||||||
|
|
||||||
// Map API response to extended Hand interface
|
// Map API response to extended Hand interface
|
||||||
const hand: Hand = {
|
const hand: Hand = {
|
||||||
id: result.id || result.name || name,
|
id: result.id || result.name || name,
|
||||||
name: result.name || name,
|
name: result.name || name,
|
||||||
description: result.description || '',
|
description: result.description || '',
|
||||||
status: result.status || (result.requirements_met ? 'idle' : 'setup_needed'),
|
status,
|
||||||
requirements_met: result.requirements_met,
|
requirements_met: result.requirements_met,
|
||||||
category: result.category,
|
category: result.category,
|
||||||
icon: result.icon,
|
icon: result.icon,
|
||||||
provider: result.provider || result.config?.provider,
|
provider: result.provider || getStringFromConfig('provider'),
|
||||||
model: result.model || result.config?.model,
|
model: result.model || getStringFromConfig('model'),
|
||||||
requirements: result.requirements?.map((r: any) => ({
|
requirements: result.requirements?.map((r: any) => ({
|
||||||
description: r.description || r.name || String(r),
|
description: r.description || r.name || String(r),
|
||||||
met: r.met ?? r.satisfied ?? true,
|
met: r.met ?? r.satisfied ?? true,
|
||||||
details: r.details || r.hint,
|
details: r.details || r.hint,
|
||||||
})),
|
})),
|
||||||
tools: result.tools || result.config?.tools,
|
tools: result.tools || getArrayFromConfig('tools'),
|
||||||
metrics: result.metrics || result.config?.metrics,
|
metrics: result.metrics || getArrayFromConfig('metrics'),
|
||||||
toolCount: result.tool_count || result.tools?.length || 0,
|
toolCount: result.tool_count || result.tools?.length || 0,
|
||||||
metricCount: result.metric_count || result.metrics?.length || 0,
|
metricCount: result.metric_count || result.metrics?.length || 0,
|
||||||
};
|
};
|
||||||
@@ -1119,7 +1140,7 @@ export const useGatewayStore = create<GatewayStore>((set, get) => {
|
|||||||
triggerHand: async (name: string, params?: Record<string, unknown>) => {
|
triggerHand: async (name: string, params?: Record<string, unknown>) => {
|
||||||
try {
|
try {
|
||||||
const result = await get().client.triggerHand(name, params);
|
const result = await get().client.triggerHand(name, params);
|
||||||
return result ? { runId: result.runId, status: result.status } : undefined;
|
return result ? { runId: result.runId, status: result.status, startedAt: new Date().toISOString() } : undefined;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
set({ error: err.message });
|
set({ error: err.message });
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -1446,7 +1467,7 @@ export const useGatewayStore = create<GatewayStore>((set, get) => {
|
|||||||
agentId: result.agent_id,
|
agentId: result.agent_id,
|
||||||
createdAt: result.created_at,
|
createdAt: result.created_at,
|
||||||
status: 'active',
|
status: 'active',
|
||||||
metadata: result.metadata,
|
metadata: metadata,
|
||||||
};
|
};
|
||||||
set(state => ({ sessions: [...state.sessions, session] }));
|
set(state => ({ sessions: [...state.sessions, session] }));
|
||||||
return session;
|
return session;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import type {
|
|||||||
TeamMemberRole,
|
TeamMemberRole,
|
||||||
DevQALoop,
|
DevQALoop,
|
||||||
DevQALoopState,
|
DevQALoopState,
|
||||||
CollaborationPattern,
|
|
||||||
CreateTeamRequest,
|
CreateTeamRequest,
|
||||||
AddTeamTaskRequest,
|
AddTeamTaskRequest,
|
||||||
TeamMetrics,
|
TeamMetrics,
|
||||||
@@ -24,7 +23,6 @@ import type {
|
|||||||
ReviewFeedback,
|
ReviewFeedback,
|
||||||
TaskDeliverable,
|
TaskDeliverable,
|
||||||
} from '../types/team';
|
} from '../types/team';
|
||||||
import { getGatewayClient } from '../lib/gateway-client';
|
|
||||||
|
|
||||||
// === Store State ===
|
// === Store State ===
|
||||||
|
|
||||||
@@ -136,7 +134,6 @@ export const useTeamStore = create<TeamStoreState>((set, get) => ({
|
|||||||
loadTeams: async () => {
|
loadTeams: async () => {
|
||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
try {
|
try {
|
||||||
const client = getGatewayClient();
|
|
||||||
// For now, load from localStorage until API is available
|
// For now, load from localStorage until API is available
|
||||||
const stored = localStorage.getItem('zclaw-teams');
|
const stored = localStorage.getItem('zclaw-teams');
|
||||||
const teams: Team[] = stored ? JSON.parse(stored) : [];
|
const teams: Team[] = stored ? JSON.parse(stored) : [];
|
||||||
@@ -150,7 +147,7 @@ export const useTeamStore = create<TeamStoreState>((set, get) => ({
|
|||||||
set({ isLoading: true, error: null });
|
set({ isLoading: true, error: null });
|
||||||
try {
|
try {
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
const members: TeamMember[] = request.memberAgents.map((m, index) => ({
|
const members: TeamMember[] = request.memberAgents.map((m) => ({
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
agentId: m.agentId,
|
agentId: m.agentId,
|
||||||
name: `Agent-${m.agentId.slice(0, 4)}`,
|
name: `Agent-${m.agentId.slice(0, 4)}`,
|
||||||
@@ -208,7 +205,7 @@ export const useTeamStore = create<TeamStoreState>((set, get) => ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setActiveTeam: (team: Team | null) => {
|
setActiveTeam: (team: Team | null) => {
|
||||||
set(state => ({
|
set(() => ({
|
||||||
activeTeam: team,
|
activeTeam: team,
|
||||||
metrics: team ? calculateMetrics(team) : null,
|
metrics: team ? calculateMetrics(team) : null,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @module types/team
|
* @module types/team
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Agent, AgentStatus } from './agent';
|
import type { AgentStatus } from './agent';
|
||||||
|
|
||||||
// === Team Definition ===
|
// === Team Definition ===
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user