From 0a672619ea8d28df2b90c58d49487ae2c1252277 Mon Sep 17 00:00:00 2001 From: sibarchannayak Date: Fri, 29 May 2026 18:14:08 +0530 Subject: [PATCH] feat: add AI service client and implement tenant AI management dashboards --- src/pages/superadmin/TenantDetails.tsx | 8 +- src/pages/tenant/TenantAIDashboard.tsx | 267 +++++++++++++------------ src/services/ai-service.ts | 16 +- 3 files changed, 160 insertions(+), 131 deletions(-) diff --git a/src/pages/superadmin/TenantDetails.tsx b/src/pages/superadmin/TenantDetails.tsx index 627796b..ba43e23 100644 --- a/src/pages/superadmin/TenantDetails.tsx +++ b/src/pages/superadmin/TenantDetails.tsx @@ -42,6 +42,7 @@ import { formatDate } from "@/utils/format-date"; import AuditLogs from "@/pages/tenant/AuditLogs"; import TenantSettings from "@/pages/tenant/Settings"; import TenantAIProviders from "@/pages/tenant/TenantAIProviders"; +import { TenantAIDashboard } from "@/pages/tenant/TenantAIDashboard"; // import DepartmentsTable from "@/components/superadmin/DepartmentsTable"; import DesignationsTable from "@/components/superadmin/DesignationsTable"; @@ -59,7 +60,8 @@ type TabType = | "license" | "audit-logs" | "billing" - | "ai-providers"; + | "ai-providers" + | "ai-dashboard"; const tabs: Array<{ id: TabType; label: string; icon: ReactElement }> = [ { id: "overview", label: "Overview", icon: }, @@ -89,6 +91,7 @@ const tabs: Array<{ id: TabType; label: string; icon: ReactElement }> = [ { id: "modules", label: "Modules", icon: }, { id: "settings", label: "Settings", icon: }, { id: "ai-providers", label: "AI Providers", icon: }, + { id: "ai-dashboard", label: "AI Dashboard", icon: }, { id: "license", label: "License", icon: }, { id: "audit-logs", @@ -381,6 +384,9 @@ const TenantDetails = (): ReactElement => { {activeTab === "ai-providers" && id && ( )} + {activeTab === "ai-dashboard" && id && ( + + )} {activeTab === "license" && } {activeTab === "audit-logs" && id && ( diff --git a/src/pages/tenant/TenantAIDashboard.tsx b/src/pages/tenant/TenantAIDashboard.tsx index 556a006..b87377c 100644 --- a/src/pages/tenant/TenantAIDashboard.tsx +++ b/src/pages/tenant/TenantAIDashboard.tsx @@ -21,7 +21,15 @@ import { formatDate } from "@/utils/format-date"; type GroupBy = "day" | "week" | "month"; -export const TenantAIDashboard = (): ReactElement => { +interface TenantAIDashboardProps { + customTenantId?: string; + hideLayout?: boolean; +} + +export const TenantAIDashboard = ({ + customTenantId, + hideLayout = false, +}: TenantAIDashboardProps): ReactElement => { const [groupBy, setGroupBy] = useState(null); const [startDate, setStartDate] = useState(""); const [endDate, setEndDate] = useState(""); @@ -54,11 +62,14 @@ export const TenantAIDashboard = (): ReactElement => { ) => { setIsLoading(true); try { - const data = await aiService.getCostSummary({ - group_by: group || undefined, - start_date: group && start ? `${start}T00:00:00.000Z` : undefined, - end_date: group && end ? `${end}T23:59:59.999Z` : undefined, - }); + const data = await aiService.getCostSummary( + { + group_by: group || undefined, + start_date: group && start ? `${start}T00:00:00.000Z` : undefined, + end_date: group && end ? `${end}T23:59:59.999Z` : undefined, + }, + customTenantId + ); setCosts(data); } catch (err: any) { showToast.error( @@ -73,7 +84,7 @@ export const TenantAIDashboard = (): ReactElement => { if (!groupBy || (startDate && endDate)) { void fetchCostSummary(groupBy, startDate, endDate); } - }, [groupBy, startDate, endDate]); + }, [groupBy, startDate, endDate, customTenantId]); const providerColumns: Column[] = [ { @@ -170,6 +181,130 @@ export const TenantAIDashboard = (): ReactElement => { return `${Math.round(ms)}ms`; }; + const content = ( +
+ {/* Top Controls Row */} + + {/* Filters */} +
+ {/* Date Range */} +
+ + +
+ + {/* Group By */} +
+ { + setGroupBy(val as GroupBy | null); + }} + placeholder="All" + /> +
+
+ + {/* Stats Row */} +
+ + + + + + + +
+ + {/* Charts and Tables */} +
+ {/* Usage by Provider Section */} +
+
+

+ Usage by Provider +

+

+ Breakdown of requests, tokens and usage costs for each distinct + provider. +

+
+ +
+ + item.provider || Math.random().toString() + } + isLoading={isLoading} + emptyMessage="No provider metrics available for this period." + /> +
+
+ + {/* Cost by Model Section */} +
+
+

+ Cost by Model +

+

+ Breakdown of requests and costs grouped at the AI model level. +

+
+ +
+ + `${item.model}-${item.provider}` || Math.random().toString() + } + isLoading={isLoading} + emptyMessage="No model metrics available for this period." + /> +
+
+
+
+ ); + + if (hideLayout) { + return content; + } + return ( { "Monitor AI completions, tokens, and billing analytics across all providers.", }} > -
- {/* Top Controls Row */} - - {/* Filters */} -
- {/* Date Range */} -
- - -
- - {/* Group By */} -
- { - setGroupBy(val as GroupBy | null); - }} - placeholder="All" - /> -
-
- - {/* Stats Row */} -
- - - - - - - -
- - {/* Charts and Tables */} -
- {/* Usage by Provider Section */} -
-
-

- Usage by Provider -

-

- Breakdown of requests, tokens and usage costs for each distinct - provider. -

-
- -
- - item.provider || Math.random().toString() - } - isLoading={isLoading} - emptyMessage="No provider metrics available for this period." - /> -
-
- - {/* Cost by Model Section */} -
-
-

- Cost by Model -

-

- Breakdown of requests and costs grouped at the AI model level. -

-
- -
- - `${item.model}-${item.provider}` || Math.random().toString() - } - isLoading={isLoading} - emptyMessage="No model metrics available for this period." - /> -
-
-
-
+ {content}
); }; diff --git a/src/services/ai-service.ts b/src/services/ai-service.ts index a652a5b..56b8c86 100644 --- a/src/services/ai-service.ts +++ b/src/services/ai-service.ts @@ -84,12 +84,16 @@ class AIService { return unwrap(response); } - async getCostSummary(params: { - group_by?: "day" | "week" | "month"; - start_date?: string; - end_date?: string; - } = {}): Promise { - const response = await apiClient.get("/ai/costs", { params }); + async getCostSummary( + params: { + group_by?: "day" | "week" | "month"; + start_date?: string; + end_date?: string; + } = {}, + tenantId?: string + ): Promise { + const headers = tenantId ? { "x-tenant-id": tenantId } : undefined; + const response = await apiClient.get("/ai/costs", { params, headers }); return unwrap(response); }