Merge pull request #14003 from BerriAI/teams-on-users-page

Team name badge added on the User Details
This commit is contained in:
Krish Dholakia
2025-09-04 21:03:02 -07:00
committed by GitHub
@@ -14,9 +14,9 @@ import { rolesWithWriteAccess } from "../../utils/roles"
import { UserEditView } from "../user_edit_view"
import OnboardingModal, { InvitationLink } from "../onboarding_link"
import { formatNumberWithCommas, copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils"
import { CopyIcon, CheckIcon } from "lucide-react";
import NotificationsManager from "../molecules/notifications_manager";
import { getBudgetDurationLabel } from "../common_components/budget_duration_dropdown";
import { CopyIcon, CheckIcon } from "lucide-react"
import NotificationsManager from "../molecules/notifications_manager"
import { getBudgetDurationLabel } from "../common_components/budget_duration_dropdown"
interface UserInfoViewProps {
userId: string
@@ -57,16 +57,17 @@ export default function UserInfoView({
initialTab = 0,
startInEditMode = false,
}: UserInfoViewProps) {
const [userData, setUserData] = useState<UserInfo | null>(null);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [isEditing, setIsEditing] = useState(startInEditMode);
const [userModels, setUserModels] = useState<string[]>([]);
const [isInvitationLinkModalVisible, setIsInvitationLinkModalVisible] = useState(false);
const [invitationLinkData, setInvitationLinkData] = useState<InvitationLink | null>(null);
const [baseUrl, setBaseUrl] = useState<string | null>(null);
const [activeTab, setActiveTab] = useState(initialTab);
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
const [userData, setUserData] = useState<UserInfo | null>(null)
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [isEditing, setIsEditing] = useState(startInEditMode)
const [userModels, setUserModels] = useState<string[]>([])
const [isInvitationLinkModalVisible, setIsInvitationLinkModalVisible] = useState(false)
const [invitationLinkData, setInvitationLinkData] = useState<InvitationLink | null>(null)
const [baseUrl, setBaseUrl] = useState<string | null>(null)
const [activeTab, setActiveTab] = useState(initialTab)
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({})
const [isTeamsExpanded, setIsTeamsExpanded] = useState(false)
React.useEffect(() => {
setBaseUrl(getProxyBaseUrl())
@@ -175,14 +176,14 @@ export default function UserInfoView({
}
const copyToClipboard = async (text: string, key: string) => {
const success = await utilCopyToClipboard(text);
const success = await utilCopyToClipboard(text)
if (success) {
setCopiedStates((prev) => ({ ...prev, [key]: true }));
setCopiedStates((prev) => ({ ...prev, [key]: true }))
setTimeout(() => {
setCopiedStates((prev) => ({ ...prev, [key]: false }));
}, 2000);
setCopiedStates((prev) => ({ ...prev, [key]: false }))
}, 2000)
}
};
}
return (
<div className="p-4">
@@ -200,9 +201,9 @@ export default function UserInfoView({
icon={copiedStates["user-id"] ? <CheckIcon size={12} /> : <CopyIcon size={12} />}
onClick={() => copyToClipboard(userData.user_id, "user-id")}
className={`left-2 z-10 transition-all duration-200 ${
copiedStates["user-id"]
? 'text-green-600 bg-green-50 border-green-200'
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
copiedStates["user-id"]
? "text-green-600 bg-green-50 border-green-200"
: "text-gray-500 hover:text-gray-700 hover:bg-gray-100"
}`}
/>
</div>
@@ -284,7 +285,35 @@ export default function UserInfoView({
<Card>
<Text>Teams</Text>
<div className="mt-2">
<Text>{userData.teams?.length || 0} teams</Text>
{userData.teams?.length && userData.teams?.length > 0 ? (
<div className="flex flex-wrap gap-2">
{userData.teams?.slice(0, isTeamsExpanded ? userData.teams.length : 20).map((team, index) => (
<Badge key={index} color="blue" title={team.team_alias}>
{team.team_alias}
</Badge>
))}
{!isTeamsExpanded && userData.teams?.length > 20 && (
<Badge
color="gray"
className="cursor-pointer hover:bg-gray-200 transition-colors"
onClick={() => setIsTeamsExpanded(true)}
>
+{userData.teams.length - 20} more
</Badge>
)}
{isTeamsExpanded && userData.teams?.length > 20 && (
<Badge
color="gray"
className="cursor-pointer hover:bg-gray-200 transition-colors"
onClick={() => setIsTeamsExpanded(false)}
>
Show Less
</Badge>
)}
</div>
) : (
<Text>No teams</Text>
)}
</div>
</Card>
@@ -344,9 +373,9 @@ export default function UserInfoView({
icon={copiedStates["user-id"] ? <CheckIcon size={12} /> : <CopyIcon size={12} />}
onClick={() => copyToClipboard(userData.user_id, "user-id")}
className={`left-2 z-10 transition-all duration-200 ${
copiedStates["user-id"]
? 'text-green-600 bg-green-50 border-green-200'
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
copiedStates["user-id"]
? "text-green-600 bg-green-50 border-green-200"
: "text-gray-500 hover:text-gray-700 hover:bg-gray-100"
}`}
/>
</div>
@@ -384,11 +413,33 @@ export default function UserInfoView({
<Text className="font-medium">Teams</Text>
<div className="flex flex-wrap gap-2 mt-1">
{userData.teams?.length && userData.teams?.length > 0 ? (
userData.teams?.map((team, index) => (
<span key={index} className="px-2 py-1 bg-blue-100 rounded text-xs">
{team.team_alias || team.team_id}
</span>
))
<>
{userData.teams?.slice(0, isTeamsExpanded ? userData.teams.length : 20).map((team, index) => (
<span
key={index}
className="px-2 py-1 bg-blue-100 rounded text-xs"
title={team.team_alias || team.team_id}
>
{team.team_alias || team.team_id}
</span>
))}
{!isTeamsExpanded && userData.teams?.length > 20 && (
<span
className="px-2 py-1 bg-gray-100 rounded text-xs cursor-pointer hover:bg-gray-200 transition-colors"
onClick={() => setIsTeamsExpanded(true)}
>
+{userData.teams.length - 20} more
</span>
)}
{isTeamsExpanded && userData.teams?.length > 20 && (
<span
className="px-2 py-1 bg-gray-100 rounded text-xs cursor-pointer hover:bg-gray-200 transition-colors"
onClick={() => setIsTeamsExpanded(false)}
>
Show Less
</span>
)}
</>
) : (
<Text>No teams</Text>
)}