fix(c8): shared TeamPanel component (arch-062) + curated icon map in SortableMenuItem (arch-063 OOM fix)
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* SharedTeamPanel — single source of truth for the team list panel used by
|
||||
* AISidebar and MessageSidebar (ARCH-062).
|
||||
*
|
||||
* Pass `onStartDirectChat` to make user rows clickable (MessageSidebar);
|
||||
* omit it for a read-only list (AISidebar).
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Users } from 'lucide-react';
|
||||
import { useUsers, useGroups } from '@/api/hooks';
|
||||
import { Avatar } from '@/components/ui/Avatar';
|
||||
|
||||
interface SharedTeamPanelProps {
|
||||
onStartDirectChat?: (userId: string, userName: string) => void;
|
||||
}
|
||||
|
||||
export function SharedTeamPanel({ onStartDirectChat }: SharedTeamPanelProps) {
|
||||
const { data: usersData, isLoading: usersLoading } = useUsers();
|
||||
const { data: groupsData, isLoading: groupsLoading } = useGroups();
|
||||
const users: any[] = usersData?.items || [];
|
||||
const groups: any[] = groupsData?.items || [];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full overflow-y-auto p-3 gap-3" data-testid="team-panel">
|
||||
<div>
|
||||
<h3 className="text-xs font-semibold text-secondary-500 uppercase tracking-wide mb-2">Mitarbeiter</h3>
|
||||
{usersLoading ? (
|
||||
<p className="text-sm text-secondary-400">Laden...</p>
|
||||
) : users.length === 0 ? (
|
||||
<p className="text-sm text-secondary-400">Keine Mitarbeiter</p>
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
{users.map((u) =>
|
||||
onStartDirectChat ? (
|
||||
<button
|
||||
key={u.id}
|
||||
onClick={() => onStartDirectChat(u.id, u.name)}
|
||||
className="flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-secondary-50 transition-colors w-full text-left"
|
||||
>
|
||||
<div className="relative flex-shrink-0">
|
||||
<Avatar name={u.name} size="sm" />
|
||||
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full border-2 border-white bg-secondary-300" aria-label="offline" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-secondary-700 truncate">{u.name}</p>
|
||||
<p className="text-xs text-secondary-400 truncate">{u.email}</p>
|
||||
</div>
|
||||
<span className="text-xs text-secondary-400">{u.role}</span>
|
||||
</button>
|
||||
) : (
|
||||
<div key={u.id} className="flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-secondary-50 transition-colors">
|
||||
<div className="relative flex-shrink-0">
|
||||
<Avatar name={u.name} size="sm" />
|
||||
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full border-2 border-white bg-secondary-300" aria-label="offline" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-secondary-700 truncate">{u.name}</p>
|
||||
<p className="text-xs text-secondary-400 truncate">{u.email}</p>
|
||||
</div>
|
||||
<span className="text-xs text-secondary-400">{u.role}</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xs font-semibold text-secondary-500 uppercase tracking-wide mb-2">Gruppen</h3>
|
||||
{groupsLoading ? (
|
||||
<p className="text-sm text-secondary-400">Laden...</p>
|
||||
) : groups.length === 0 ? (
|
||||
<p className="text-sm text-secondary-400">Keine Gruppen</p>
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
{groups.map((g) => (
|
||||
<div key={g.id} className="flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-secondary-50 transition-colors">
|
||||
<div className="w-8 h-8 rounded-full bg-secondary-200 flex items-center justify-center flex-shrink-0">
|
||||
<Users className="w-4 h-4 text-secondary-500" aria-hidden="true" strokeWidth={2} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-secondary-700 truncate">{g.name}</p>
|
||||
{g.description && <p className="text-xs text-secondary-400 truncate">{g.description}</p>}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user