- Add Team tab to Sidebar with TeamList component - Update MainViewType to include 'team' view - Add TeamCollaborationView rendering in App.tsx - Add selectedTeamId and onSelectTeam props to Sidebar - Create TeamList component for sidebar team navigation UI Integration: - Team tab shows list of teams with status indicators - Selecting a team displays TeamCollaborationView - Empty state shows guidance for creating teams Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
/**
|
|
* TeamList - Sidebar Team List Component
|
|
*
|
|
* Displays a compact list of teams for the sidebar navigation.
|
|
*
|
|
* @module components/TeamList
|
|
*/
|
|
|
|
import { useEffect } from 'react';
|
|
import { useTeamStore } from '../store/teamStore';
|
|
import { Users, Plus, Activity, CheckCircle, AlertTriangle } from 'lucide-react';
|
|
|
|
interface TeamListProps {
|
|
onSelectTeam?: (teamId: string) => void;
|
|
selectedTeamId?: string;
|
|
}
|
|
|
|
export function TeamList({ onSelectTeam, selectedTeamId }: TeamListProps) {
|
|
const { teams, loadTeams, setActiveTeam, isLoading } = useTeamStore();
|
|
|
|
useEffect(() => {
|
|
loadTeams();
|
|
}, [loadTeams]);
|
|
|
|
const handleSelectTeam = (teamId: string) => {
|
|
const team = teams.find(t => t.id === teamId);
|
|
if (team) {
|
|
setActiveTeam(team);
|
|
onSelectTeam?.(teamId);
|
|
}
|
|
};
|
|
|
|
const getStatusIcon = (status: string) => {
|
|
switch (status) {
|
|
case 'active':
|
|
return <Activity className="w-3 h-3 text-green-500" />;
|
|
case 'paused':
|
|
return <AlertTriangle className="w-3 h-3 text-yellow-500" />;
|
|
case 'completed':
|
|
return <CheckCircle className="w-3 h-3 text-blue-500" />;
|
|
default:
|
|
return <Activity className="w-3 h-3 text-gray-400" />;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="h-full flex flex-col">
|
|
{/* Header */}
|
|
<div className="p-3 border-b border-gray-200 dark:border-gray-700">
|
|
<div className="flex items-center justify-between">
|
|
<h3 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
Teams
|
|
</h3>
|
|
<button
|
|
className="p-1 hover:bg-gray-100 dark:hover:bg-gray-800 rounded"
|
|
title="Create Team"
|
|
>
|
|
<Plus className="w-4 h-4 text-gray-400" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Team List */}
|
|
<div className="flex-1 overflow-y-auto">
|
|
{isLoading ? (
|
|
<div className="p-4 text-center text-gray-400 text-sm">Loading...</div>
|
|
) : teams.length === 0 ? (
|
|
<div className="p-4 text-center">
|
|
<Users className="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-600" />
|
|
<p className="text-xs text-gray-400 dark:text-gray-500">
|
|
No teams yet
|
|
</p>
|
|
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
Click + to create one
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-1 p-2">
|
|
{teams.map((team) => (
|
|
<button
|
|
key={team.id}
|
|
onClick={() => handleSelectTeam(team.id)}
|
|
className={`w-full p-2 rounded-lg text-left transition-colors ${
|
|
selectedTeamId === team.id
|
|
? 'bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800'
|
|
: 'hover:bg-gray-100 dark:hover:bg-gray-800'
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
{getStatusIcon(team.status)}
|
|
<span className="text-sm font-medium text-gray-900 dark:text-white truncate">
|
|
{team.name}
|
|
</span>
|
|
</div>
|
|
<div className="mt-1 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
|
|
<span className="flex items-center gap-1">
|
|
<Users className="w-3 h-3" />
|
|
{team.members.length}
|
|
</span>
|
|
<span>·</span>
|
|
<span>{team.tasks.length} tasks</span>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default TeamList;
|