From fc49d51567f92f668189db8c5fd76e5c9009018c Mon Sep 17 00:00:00 2001 From: youngbeom Date: Fri, 10 Jul 2026 18:42:25 +0800 Subject: [PATCH 1/2] feat(team): enhance team avatar handling - Change RoomAvatar size prop to number - Add teamMemberIDs function for member IDs - Implement resolveTeamAvatarMembers function - Update TeamDetailPane to use RoomAvatar - Refactor CSS for team avatars - Adjust WorkspaceTabPanels to display avatars --- .../business/RoomAvatar/RoomAvatar.tsx | 2 +- web/app/src/models/tasks.ts | 24 ++++++++ .../TeamDetailPane/TeamDetailPane.module.css | 13 +++-- .../TeamDetailPane/TeamDetailPane.tsx | 31 +++++------ .../WorkspaceRows/WorkspaceRows.module.css | 55 +++++++++++++++++++ .../WorkspaceRows/WorkspaceRows.tsx | 12 ++-- .../WorkspaceTabPanels.module.css | 14 +++++ .../WorkspaceSidebar/WorkspaceTabPanels.tsx | 11 ++-- 8 files changed, 130 insertions(+), 32 deletions(-) diff --git a/web/app/src/components/business/RoomAvatar/RoomAvatar.tsx b/web/app/src/components/business/RoomAvatar/RoomAvatar.tsx index f2aad93e..1a514f19 100644 --- a/web/app/src/components/business/RoomAvatar/RoomAvatar.tsx +++ b/web/app/src/components/business/RoomAvatar/RoomAvatar.tsx @@ -11,7 +11,7 @@ type RoomAvatarProps = { ariaLabel?: string; count?: number; members: RoomAvatarMember[]; - size?: 32 | 48; + size?: number; showCountBadge?: boolean; }; diff --git a/web/app/src/models/tasks.ts b/web/app/src/models/tasks.ts index b9be4034..82cf7d1a 100644 --- a/web/app/src/models/tasks.ts +++ b/web/app/src/models/tasks.ts @@ -1,3 +1,5 @@ +import type { AgentLike } from "@/models/agents"; + export type WorkspaceTask = { id: string; assignment_type: string; @@ -303,6 +305,28 @@ export function displayTeam(team: WorkspaceTeam): string { return team.title || team.id; } +export function teamMemberIDs(team: WorkspaceTeam): string[] { + const ids = new Set(team.member_agent_ids ?? []); + if (team.lead_agent_id) { + const altID = team.lead_agent_id.startsWith("u-") ? team.lead_agent_id.slice(2) : "u-" + team.lead_agent_id; + ids.delete(altID); + ids.add(team.lead_agent_id); + } + return Array.from(ids); +} + +export function resolveTeamAvatarMembers(team: WorkspaceTeam, agents: readonly AgentLike[]) { + return teamMemberIDs(team).map((memberID) => { + const agent = agents.find((item) => item.id === memberID); + return { + id: memberID, + name: agent?.name || memberID, + avatar: agent?.avatar || "", + accent_hex: undefined, + }; + }); +} + type TranslateFn = (key: string) => string; export function taskStatusLabel(status: unknown, t: TranslateFn): string { diff --git a/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.module.css b/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.module.css index e882d096..fb22a259 100644 --- a/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.module.css +++ b/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.module.css @@ -88,9 +88,10 @@ flex-shrink: 0; width: 48px; height: 48px; - border-radius: var(--radius-xl); - background: color-mix(in srgb, var(--blue-600) 10%, transparent); - color: var(--blue-600); + border: 0; + border-radius: 0; + background: transparent; + color: inherit; } .teamDetailAvatar svg { @@ -132,9 +133,9 @@ justify-content: center; width: 76px; height: 76px; - border-radius: 14px; - background: var(--panel-soft); - color: var(--muted); + border-radius: 0; + background: transparent; + color: inherit; } .teamProfileBlock h2, diff --git a/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.tsx b/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.tsx index 682eb479..30ec2b93 100644 --- a/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.tsx +++ b/web/app/src/pages/TeamPage/components/TeamDetailPane/TeamDetailPane.tsx @@ -1,21 +1,24 @@ import { useState } from "react"; import type { ReactNode } from "react"; +import { AgentAvatarContent, RoomAvatar } from "@/components/business"; import { ListChecks, Trash2, UserPlus, Users } from "lucide-react"; import { TaskSubtaskIndicator } from "@/components/business"; import { Button } from "@/components/ui"; -import { AgentIcon, UsersIcon } from "@/components/ui/Icons"; import { isAgentRunning } from "@/models/agents"; import type { AgentLike } from "@/models/agents"; import type { IMUser, TranslateFn, UsersById } from "@/models/conversations"; import { displayTeam, formatTaskUpdatedAt, + resolveTeamAvatarMembers, resolveTaskSidebarPhase, rootTasks, taskChildren, + teamMemberIDs, teamStatusLabel, } from "@/models/tasks"; import type { WorkspaceTask, WorkspaceTeam } from "@/models/tasks"; +import { avatarFallbackText } from "@/shared/avatar"; import { classNames } from "@/shared/lib/classNames"; import styles from "./TeamDetailPane.module.css"; @@ -67,6 +70,7 @@ export function TeamDetailPane({ const memberIDs = teamMemberIDs(team); const members = memberIDs.map((memberID) => memberDisplay(memberID, agents, usersById, team.lead_agent_id)); + const avatarMembers = resolveTeamAvatarMembers(team, agents); const parentTasks = rootTasks(tasks); const locale = document.documentElement.lang || "en"; @@ -74,7 +78,7 @@ export function TeamDetailPane({
- +
@@ -121,7 +125,7 @@ export function TeamDetailPane({