Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
635 changes: 635 additions & 0 deletions frontend/src/components/AdminChatsTable.tsx

Large diffs are not rendered by default.

123 changes: 107 additions & 16 deletions frontend/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react"
import { useNavigate } from "@tanstack/react-router"
import {
Users,
Activity,
Expand Down Expand Up @@ -37,6 +38,7 @@ import {
Area,
AreaChart,
} from "recharts"
import type { AdminChat } from "@/components/AdminChatsTable"

interface Chat {
externalId: string
Expand Down Expand Up @@ -1507,10 +1509,14 @@ const AdminUsersLeaderboard = ({
users,
loading = false,
onUserClick,
onAllChatsClick,
onUserChatsClick,
}: {
users: AdminUserUsage[]
loading?: boolean
onUserClick?: (user: AdminUserUsage) => void
onAllChatsClick?: () => void
onUserChatsClick?: (userId: number, userName: string) => void
}) => {
const [searchQuery, setSearchQuery] = useState<string>("")
const [feedbackModal, setFeedbackModal] = useState<{
Expand All @@ -1535,6 +1541,11 @@ const AdminUsersLeaderboard = ({
})
}

const handleViewChatsClick = (e: React.MouseEvent, user: AdminUserUsage) => {
e.stopPropagation()
onUserChatsClick?.(user.userId, user.userName)
}

const closeFeedbackModal = () => {
setFeedbackModal({
isOpen: false,
Expand Down Expand Up @@ -1579,11 +1590,24 @@ const AdminUsersLeaderboard = ({
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Users className="h-5 w-5" />
Users Leaderboard
</CardTitle>
<CardDescription>All active users by message count</CardDescription>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<Users className="h-5 w-5" />
Users Leaderboard
</CardTitle>
<CardDescription>All active users by message count</CardDescription>
</div>
{onAllChatsClick && (
<button
onClick={onAllChatsClick}
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors flex items-center gap-2"
>
<MessageSquare className="h-4 w-4" />
All Chats
</button>
)}
</div>
<div className="mt-4">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4" />
Expand Down Expand Up @@ -1687,15 +1711,26 @@ const AdminUsersLeaderboard = ({
<span>{user.dislikes}</span>
</div>
</div>
{(user.likes > 0 || user.dislikes > 0) && (
<button
onClick={(e) => handleFeedbackClick(e, user)}
className="ml-3 px-3 py-1.5 text-xs text-blue-600 border border-blue-200 rounded-lg hover:bg-blue-50 hover:border-blue-300 transition-colors font-medium flex items-center gap-1"
>
<MessageSquare className="h-3 w-3" />
Feedbacks
</button>
)}
<div className="flex items-center gap-2">
{onUserChatsClick && (
<button
onClick={(e) => handleViewChatsClick(e, user)}
className="px-3 py-1.5 text-xs text-green-600 border border-green-200 rounded-lg hover:bg-green-50 hover:border-green-300 transition-colors font-medium flex items-center gap-1"
>
<MessageSquare className="h-3 w-3" />
View Chats
</button>
)}
{(user.likes > 0 || user.dislikes > 0) && (
<button
onClick={(e) => handleFeedbackClick(e, user)}
className="px-3 py-1.5 text-xs text-blue-600 border border-blue-200 rounded-lg hover:bg-blue-50 hover:border-blue-300 transition-colors font-medium flex items-center gap-1"
>
<MessageSquare className="h-3 w-3" />
Feedbacks
</button>
)}
</div>
</div>
</div>
)
Expand Down Expand Up @@ -2604,6 +2639,7 @@ export const Dashboard = ({
role?: string
isAgentMode?: boolean
} = {}) => {
const navigate = useNavigate()
const [stats, setStats] = useState<DashboardStats>({
totalChats: 0,
totalMessages: 0,
Expand Down Expand Up @@ -2667,6 +2703,7 @@ export const Dashboard = ({
feedbackMessages: [],
},
})
const [_, setAdminChats] = useState<AdminChat[]>([])
const [adminLoading, setAdminLoading] = useState(false)
const [adminError, setAdminError] = useState<string | null>(null)
const [selectedUser, setSelectedUser] = useState<AdminUserUsage | null>(null)
Expand Down Expand Up @@ -2853,16 +2890,59 @@ export const Dashboard = ({
throw new Error("Failed to fetch admin data")
}

const adminChats = await adminChatsResponse.json()
const adminChatsData = await adminChatsResponse.json()
const adminAgents = await adminAgentsResponse.json()

// Handle both new pagination format and old format for backward compatibility
let chatsArray: any[]
if (
adminChatsData &&
typeof adminChatsData === "object" &&
"data" in adminChatsData &&
"pagination" in adminChatsData
) {
// New format with pagination metadata
chatsArray = adminChatsData.data
} else if (Array.isArray(adminChatsData)) {
// Old format - direct array
chatsArray = adminChatsData
} else {
throw new Error("Invalid response format from admin chats API")
}

// Process admin stats
const processedAdminStats = processAdminChatsData(
adminChats,
chatsArray,
adminAgents,
timeRange,
)
setAdminStats(processedAdminStats)

// Set the raw admin chats data for the table
setAdminChats(
chatsArray.map((chat: any) => ({
externalId: chat.externalId,
title: chat.title || "Untitled Chat",
createdAt: chat.createdAt,
userId: chat.userId || chat.user?.id,
userName: chat.userName || chat.user?.name || "Unknown User",
userEmail: chat.userEmail || chat.user?.email || "",
agentId: chat.agentId,
agentName:
chat.agentName ||
(chat.agentId
? adminAgents.find((a: any) => a.externalId === chat.agentId)
?.name
: null),
messageCount: chat.messageCount || chat.messages?.length || 0,
totalCost: chat.totalCost || 0,
totalTokens: chat.totalTokens || 0,
likes: chat.likes || 0,
dislikes: chat.dislikes || 0,
isBookmarked: chat.isBookmarked || false,
})),
)

setAdminError(null)
} catch (err) {
console.error("Error fetching admin data:", err)
Expand Down Expand Up @@ -3974,6 +4054,17 @@ export const Dashboard = ({
users={adminStats.topUsers}
loading={adminLoading}
onUserClick={handleAdminUserSelect}
onAllChatsClick={() => {
// Navigate to all chats view
navigate({ to: "/admin/chat-overview" as const })
}}
onUserChatsClick={(userId: number, userName: string) => {
// Navigate to user-specific chats view
navigate({
to: "/admin/chat-overview" as const,
search: { userName: userName },
})
}}
/>

{/* Top Agents System-wide */}
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { Route as AuthenticatedIntegrationsApiKeyImport } from './routes/_authen
import { Route as AuthenticatedDataSourceDocIdImport } from './routes/_authenticated/dataSource.$docId'
import { Route as AuthenticatedChatChatIdImport } from './routes/_authenticated/chat.$chatId'
import { Route as AuthenticatedAdminUserManagementImport } from './routes/_authenticated/admin/userManagement'
import { Route as AuthenticatedAdminChatOverviewImport } from './routes/_authenticated/admin/chat-overview'
import { Route as AuthenticatedAdminIntegrationsIndexImport } from './routes/_authenticated/admin/integrations/index'
import { Route as AuthenticatedTraceChatIdMsgIdImport } from './routes/_authenticated/trace.$chatId.$msgId'
import { Route as AuthenticatedAdminIntegrationsSlackImport } from './routes/_authenticated/admin/integrations/slack'
Expand Down Expand Up @@ -175,6 +176,13 @@ const AuthenticatedAdminUserManagementRoute =
getParentRoute: () => AuthenticatedRoute,
} as any)

const AuthenticatedAdminChatOverviewRoute =
AuthenticatedAdminChatOverviewImport.update({
id: '/admin/chat-overview',
path: '/admin/chat-overview',
getParentRoute: () => AuthenticatedRoute,
} as any)

const AuthenticatedAdminIntegrationsIndexRoute =
AuthenticatedAdminIntegrationsIndexImport.update({
id: '/admin/integrations/',
Expand Down Expand Up @@ -305,6 +313,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedIndexImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/admin/chat-overview': {
id: '/_authenticated/admin/chat-overview'
path: '/admin/chat-overview'
fullPath: '/admin/chat-overview'
preLoaderRoute: typeof AuthenticatedAdminChatOverviewImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/admin/userManagement': {
id: '/_authenticated/admin/userManagement'
path: '/admin/userManagement'
Expand Down Expand Up @@ -435,6 +450,7 @@ interface AuthenticatedRouteChildren {
AuthenticatedTuningRoute: typeof AuthenticatedTuningRoute
AuthenticatedWorkflowRoute: typeof AuthenticatedWorkflowRoute
AuthenticatedIndexRoute: typeof AuthenticatedIndexRoute
AuthenticatedAdminChatOverviewRoute: typeof AuthenticatedAdminChatOverviewRoute
AuthenticatedAdminUserManagementRoute: typeof AuthenticatedAdminUserManagementRoute
AuthenticatedDataSourceDocIdRoute: typeof AuthenticatedDataSourceDocIdRoute
AuthenticatedIntegrationsApiKeyRoute: typeof AuthenticatedIntegrationsApiKeyRoute
Expand All @@ -460,6 +476,7 @@ const AuthenticatedRouteChildren: AuthenticatedRouteChildren = {
AuthenticatedTuningRoute: AuthenticatedTuningRoute,
AuthenticatedWorkflowRoute: AuthenticatedWorkflowRoute,
AuthenticatedIndexRoute: AuthenticatedIndexRoute,
AuthenticatedAdminChatOverviewRoute: AuthenticatedAdminChatOverviewRoute,
AuthenticatedAdminUserManagementRoute: AuthenticatedAdminUserManagementRoute,
AuthenticatedDataSourceDocIdRoute: AuthenticatedDataSourceDocIdRoute,
AuthenticatedIntegrationsApiKeyRoute: AuthenticatedIntegrationsApiKeyRoute,
Expand Down Expand Up @@ -499,6 +516,7 @@ export interface FileRoutesByFullPath {
'/workflow': typeof AuthenticatedWorkflowRoute
'/oauth/success': typeof OauthSuccessRoute
'/': typeof AuthenticatedIndexRoute
'/admin/chat-overview': typeof AuthenticatedAdminChatOverviewRoute
'/admin/userManagement': typeof AuthenticatedAdminUserManagementRoute
'/chat/$chatId': typeof AuthenticatedChatChatIdRoute
'/dataSource/$docId': typeof AuthenticatedDataSourceDocIdRoute
Expand Down Expand Up @@ -528,6 +546,7 @@ export interface FileRoutesByTo {
'/workflow': typeof AuthenticatedWorkflowRoute
'/oauth/success': typeof OauthSuccessRoute
'/': typeof AuthenticatedIndexRoute
'/admin/chat-overview': typeof AuthenticatedAdminChatOverviewRoute
'/admin/userManagement': typeof AuthenticatedAdminUserManagementRoute
'/chat/$chatId': typeof AuthenticatedChatChatIdRoute
'/dataSource/$docId': typeof AuthenticatedDataSourceDocIdRoute
Expand Down Expand Up @@ -559,6 +578,7 @@ export interface FileRoutesById {
'/_authenticated/workflow': typeof AuthenticatedWorkflowRoute
'/oauth/success': typeof OauthSuccessRoute
'/_authenticated/': typeof AuthenticatedIndexRoute
'/_authenticated/admin/chat-overview': typeof AuthenticatedAdminChatOverviewRoute
'/_authenticated/admin/userManagement': typeof AuthenticatedAdminUserManagementRoute
'/_authenticated/chat/$chatId': typeof AuthenticatedChatChatIdRoute
'/_authenticated/dataSource/$docId': typeof AuthenticatedDataSourceDocIdRoute
Expand Down Expand Up @@ -591,6 +611,7 @@ export interface FileRouteTypes {
| '/workflow'
| '/oauth/success'
| '/'
| '/admin/chat-overview'
| '/admin/userManagement'
| '/chat/$chatId'
| '/dataSource/$docId'
Expand Down Expand Up @@ -619,6 +640,7 @@ export interface FileRouteTypes {
| '/workflow'
| '/oauth/success'
| '/'
| '/admin/chat-overview'
| '/admin/userManagement'
| '/chat/$chatId'
| '/dataSource/$docId'
Expand Down Expand Up @@ -648,6 +670,7 @@ export interface FileRouteTypes {
| '/_authenticated/workflow'
| '/oauth/success'
| '/_authenticated/'
| '/_authenticated/admin/chat-overview'
| '/_authenticated/admin/userManagement'
| '/_authenticated/chat/$chatId'
| '/_authenticated/dataSource/$docId'
Expand Down Expand Up @@ -707,6 +730,7 @@ export const routeTree = rootRoute
"/_authenticated/tuning",
"/_authenticated/workflow",
"/_authenticated/",
"/_authenticated/admin/chat-overview",
"/_authenticated/admin/userManagement",
"/_authenticated/dataSource/$docId",
"/_authenticated/integrations/apiKey",
Expand Down Expand Up @@ -767,6 +791,10 @@ export const routeTree = rootRoute
"filePath": "_authenticated/index.tsx",
"parent": "/_authenticated"
},
"/_authenticated/admin/chat-overview": {
"filePath": "_authenticated/admin/chat-overview.tsx",
"parent": "/_authenticated"
},
"/_authenticated/admin/userManagement": {
"filePath": "_authenticated/admin/userManagement.tsx",
"parent": "/_authenticated"
Expand Down
Loading