From 3bbabdcc1d9351365de66f8e5a890e47f92d70fc Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Mon, 16 Dec 2024 10:47:56 +0900 Subject: [PATCH 1/8] Add agent usage component with mock data --- app/(main)/settings/team/agent-usage.tsx | 48 ++++++++++++++++++++++++ app/(main)/settings/team/page.tsx | 2 + 2 files changed, 50 insertions(+) create mode 100644 app/(main)/settings/team/agent-usage.tsx diff --git a/app/(main)/settings/team/agent-usage.tsx b/app/(main)/settings/team/agent-usage.tsx new file mode 100644 index 00000000..be2ecc97 --- /dev/null +++ b/app/(main)/settings/team/agent-usage.tsx @@ -0,0 +1,48 @@ +import { Card } from "@/app/(main)/settings/components/card"; + +const agentLogs = [ + { + agentId: "agent_1", + agentName: "Data Analysis Agent", + startTime: "2024-03-18 10:00:00", + endTime: "2024-03-18 10:05:00", + errorMessage: "", + usedCharge: 5, + }, + { + agentId: "agent_2", + agentName: "Code Review Agent", + startTime: "2024-03-18 11:00:00", + endTime: null, + errorMessage: "", + usedCharge: 3, + }, +]; + +export function AgentUsage() { + return ( + +
+
+
Agent
+
Start Time
+
End Time
+
Charge
+
+
+ {agentLogs.slice(0, 5).map((log) => ( +
+
{log.agentName}
+
{log.startTime}
+
{log.endTime || "-"}
+
{log.usedCharge} minutes
+
+ ))} +
+
+
+ ); +} diff --git a/app/(main)/settings/team/page.tsx b/app/(main)/settings/team/page.tsx index 50acfd70..31943b2f 100644 --- a/app/(main)/settings/team/page.tsx +++ b/app/(main)/settings/team/page.tsx @@ -1,6 +1,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Suspense } from "react"; import { AgentTimeCharge } from "./agent-time-charge"; +import { AgentUsage } from "./agent-usage"; import BillingSection from "./billing-section"; import { TeamMembers } from "./team-members"; import { TeamName } from "./team-name"; @@ -34,6 +35,7 @@ export default function TeamPage() { + ); From 4f6fcb66751ff2f3c6d8d038bc9fcbd11ec13f1f Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Mon, 16 Dec 2024 11:14:49 +0900 Subject: [PATCH 2/8] feat: Implement agent activity view with real data - Add getAgentActivities action to fetch agent activities from DB - Replace mock data with real agent activity data from database - Format timestamps using formatTimestamp utility - Add proper error handling and empty state --- app/(main)/settings/team/actions.ts | 50 ++++++++++++++++- app/(main)/settings/team/agent-usage.tsx | 69 +++++++++++++----------- 2 files changed, 88 insertions(+), 31 deletions(-) diff --git a/app/(main)/settings/team/actions.ts b/app/(main)/settings/team/actions.ts index 0ae46545..e0d20890 100644 --- a/app/(main)/settings/team/actions.ts +++ b/app/(main)/settings/team/actions.ts @@ -3,6 +3,8 @@ import { type TeamRole, type UserId, + agentActivities, + agents, db, supabaseUserMappings, teamMemberships, @@ -11,7 +13,7 @@ import { } from "@/drizzle"; import { getUser } from "@/lib/supabase"; import { fetchCurrentTeam, isProPlan } from "@/services/teams"; -import { and, asc, count, eq, ne } from "drizzle-orm"; +import { and, asc, count, desc, eq, ne } from "drizzle-orm"; import { revalidatePath } from "next/cache"; function isUserId(value: string): value is UserId { @@ -422,3 +424,49 @@ export async function getCurrentUserRole() { }; } } + +export async function getAgentActivities() { + try { + const currentTeam = await fetchCurrentTeam(); + + const activities = await db + .select({ + agentId: agents.id, + agentName: agents.name, + startTime: agentActivities.startedAt, + endTime: agentActivities.endedAt, + usedCharge: agentActivities.totalDurationMs, + }) + .from(agentActivities) + .innerJoin( + agents, + and( + eq(agentActivities.agentDbId, agents.dbId), + eq(agents.teamDbId, currentTeam.dbId), + ), + ) + .orderBy(desc(agentActivities.startedAt)) + .limit(5); + + const formattedActivities = activities.map((activity) => ({ + ...activity, + // Convert milliseconds to seconds and round to 2 decimal places + usedCharge: Math.ceil((activity.usedCharge / 1000) * 100) / 100, + })); + + return { + success: true, + data: formattedActivities, + }; + } catch (error) { + console.error("Failed to get agent activities:", error); + + return { + success: false, + error: + error instanceof Error + ? error.message + : "Failed to get agent activities", + }; + } +} diff --git a/app/(main)/settings/team/agent-usage.tsx b/app/(main)/settings/team/agent-usage.tsx index be2ecc97..ae1ec888 100644 --- a/app/(main)/settings/team/agent-usage.tsx +++ b/app/(main)/settings/team/agent-usage.tsx @@ -1,25 +1,20 @@ import { Card } from "@/app/(main)/settings/components/card"; +import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; +import { getAgentActivities } from "./actions"; -const agentLogs = [ - { - agentId: "agent_1", - agentName: "Data Analysis Agent", - startTime: "2024-03-18 10:00:00", - endTime: "2024-03-18 10:05:00", - errorMessage: "", - usedCharge: 5, - }, - { - agentId: "agent_2", - agentName: "Code Review Agent", - startTime: "2024-03-18 11:00:00", - endTime: null, - errorMessage: "", - usedCharge: 3, - }, -]; +export async function AgentUsage() { + const result = await getAgentActivities(); + + if (!result.success || !result.data) { + return ( + +
Failed to load agent activities
+
+ ); + } + + const activities = result.data; -export function AgentUsage() { return (
@@ -30,17 +25,31 @@ export function AgentUsage() {
Charge
- {agentLogs.slice(0, 5).map((log) => ( -
-
{log.agentName}
-
{log.startTime}
-
{log.endTime || "-"}
-
{log.usedCharge} minutes
-
- ))} + {activities.length > 0 ? ( + activities.map((activity) => ( +
+
{activity.agentName ?? activity.agentId}
+
+ {formatTimestamp.toShortDateTime( + new Date(activity.startTime).getTime(), + )} +
+
+ {activity.endTime + ? formatTimestamp.toShortDateTime( + new Date(activity.endTime).getTime(), + ) + : "-"} +
+
{activity.usedCharge} seconds
+
+ )) + ) : ( +
No recent agent activities
+ )}
From dac807e8a2014b0a18b7d9012dba3fa01aa6e307 Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Mon, 16 Dec 2024 12:54:32 +0900 Subject: [PATCH 3/8] Add limit args --- app/(main)/settings/team/actions.ts | 6 ++++-- app/(main)/settings/team/agent-usage.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/(main)/settings/team/actions.ts b/app/(main)/settings/team/actions.ts index e0d20890..b23724d8 100644 --- a/app/(main)/settings/team/actions.ts +++ b/app/(main)/settings/team/actions.ts @@ -425,7 +425,9 @@ export async function getCurrentUserRole() { } } -export async function getAgentActivities() { +export async function getAgentActivities({ + limit = 50, +}: { limit?: number } = {}) { try { const currentTeam = await fetchCurrentTeam(); @@ -446,7 +448,7 @@ export async function getAgentActivities() { ), ) .orderBy(desc(agentActivities.startedAt)) - .limit(5); + .limit(limit); const formattedActivities = activities.map((activity) => ({ ...activity, diff --git a/app/(main)/settings/team/agent-usage.tsx b/app/(main)/settings/team/agent-usage.tsx index ae1ec888..09d3866a 100644 --- a/app/(main)/settings/team/agent-usage.tsx +++ b/app/(main)/settings/team/agent-usage.tsx @@ -3,7 +3,7 @@ import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils import { getAgentActivities } from "./actions"; export async function AgentUsage() { - const result = await getAgentActivities(); + const result = await getAgentActivities({ limit: 3 }); if (!result.success || !result.data) { return ( From e02320916b2c6e5870bd50da0cfe14a6be98a54c Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Mon, 16 Dec 2024 14:18:49 +0900 Subject: [PATCH 4/8] feat: Enhance agent logs display with dialog view - Add AgentUsageDialog component for viewing detailed logs - Fetch 50 records at once and share between preview and dialog - Add responsive dialog layout - Implement scrollable log view in dialog --- .../settings/team/agent-usage-dialog.tsx | 73 +++++++++++++++++++ app/(main)/settings/team/agent-usage.tsx | 18 ++++- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 app/(main)/settings/team/agent-usage-dialog.tsx diff --git a/app/(main)/settings/team/agent-usage-dialog.tsx b/app/(main)/settings/team/agent-usage-dialog.tsx new file mode 100644 index 00000000..2d50b061 --- /dev/null +++ b/app/(main)/settings/team/agent-usage-dialog.tsx @@ -0,0 +1,73 @@ +import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +type AgentActivity = { + agentId: string; + agentName: string | null; + startTime: Date; + endTime: Date | null; + usedCharge: number; +}; + +type AgentUsageDialogProps = { + activities: AgentActivity[]; +}; + +export function AgentUsageDialog({ activities }: AgentUsageDialogProps) { + return ( + + + + + + + Agent Usage Logs + +
+
+
Agent
+
Start Time
+
End Time
+
Charge
+
+
+
+ {activities.length > 0 ? ( + activities.map((activity) => ( +
+
{activity.agentName ?? activity.agentId}
+
+ {formatTimestamp.toShortDateTime( + new Date(activity.startTime).getTime(), + )} +
+
+ {activity.endTime + ? formatTimestamp.toShortDateTime( + new Date(activity.endTime).getTime(), + ) + : "-"} +
+
{activity.usedCharge} seconds
+
+ )) + ) : ( +
No agent activities
+ )} +
+
+
+
+
+ ); +} diff --git a/app/(main)/settings/team/agent-usage.tsx b/app/(main)/settings/team/agent-usage.tsx index 09d3866a..95bf2cbc 100644 --- a/app/(main)/settings/team/agent-usage.tsx +++ b/app/(main)/settings/team/agent-usage.tsx @@ -1,9 +1,10 @@ import { Card } from "@/app/(main)/settings/components/card"; import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; import { getAgentActivities } from "./actions"; +import { AgentUsageDialog } from "./agent-usage-dialog"; export async function AgentUsage() { - const result = await getAgentActivities({ limit: 3 }); + const result = await getAgentActivities({ limit: 50 }); if (!result.success || !result.data) { return ( @@ -14,9 +15,18 @@ export async function AgentUsage() { } const activities = result.data; + const recentActivities = activities.slice(0, 3); return ( - + 0 ? ( + + ) : null, + }} + >
Agent
@@ -25,8 +35,8 @@ export async function AgentUsage() {
Charge
- {activities.length > 0 ? ( - activities.map((activity) => ( + {recentActivities.length > 0 ? ( + recentActivities.map((activity) => (
Date: Mon, 16 Dec 2024 14:57:11 +0900 Subject: [PATCH 5/8] refactor: Extract agent usage table into separate component - Create AgentUsageTable component for reusability - Share AgentActivity type between components - Add containerClassName prop for flexible styling - Remove duplicate table markup from dialog and main view --- .../settings/team/agent-usage-dialog.tsx | 51 ++-------------- .../settings/team/agent-usage-table.tsx | 59 +++++++++++++++++++ app/(main)/settings/team/agent-usage.tsx | 38 +----------- 3 files changed, 66 insertions(+), 82 deletions(-) create mode 100644 app/(main)/settings/team/agent-usage-table.tsx diff --git a/app/(main)/settings/team/agent-usage-dialog.tsx b/app/(main)/settings/team/agent-usage-dialog.tsx index 2d50b061..377f0e76 100644 --- a/app/(main)/settings/team/agent-usage-dialog.tsx +++ b/app/(main)/settings/team/agent-usage-dialog.tsx @@ -1,4 +1,3 @@ -import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -7,14 +6,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; - -type AgentActivity = { - agentId: string; - agentName: string | null; - startTime: Date; - endTime: Date | null; - usedCharge: number; -}; +import { type AgentActivity, AgentUsageTable } from "./agent-usage-table"; type AgentUsageDialogProps = { activities: AgentActivity[]; @@ -30,43 +22,10 @@ export function AgentUsageDialog({ activities }: AgentUsageDialogProps) { Agent Usage Logs -
-
-
Agent
-
Start Time
-
End Time
-
Charge
-
-
-
- {activities.length > 0 ? ( - activities.map((activity) => ( -
-
{activity.agentName ?? activity.agentId}
-
- {formatTimestamp.toShortDateTime( - new Date(activity.startTime).getTime(), - )} -
-
- {activity.endTime - ? formatTimestamp.toShortDateTime( - new Date(activity.endTime).getTime(), - ) - : "-"} -
-
{activity.usedCharge} seconds
-
- )) - ) : ( -
No agent activities
- )} -
-
-
+ ); diff --git a/app/(main)/settings/team/agent-usage-table.tsx b/app/(main)/settings/team/agent-usage-table.tsx new file mode 100644 index 00000000..7d80364b --- /dev/null +++ b/app/(main)/settings/team/agent-usage-table.tsx @@ -0,0 +1,59 @@ +import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; + +export type AgentActivity = { + agentId: string; + agentName: string | null; + startTime: Date; + endTime: Date | null; + usedCharge: number; +}; + +type AgentUsageTableProps = { + activities: AgentActivity[]; + containerClassName?: string; +}; + +export function AgentUsageTable({ + activities, + containerClassName, +}: AgentUsageTableProps) { + return ( +
+
+
Agent
+
Start Time
+
End Time
+
Charge
+
+
+
+ {activities.length > 0 ? ( + activities.map((activity) => ( +
+
{activity.agentName ?? activity.agentId}
+
+ {formatTimestamp.toShortDateTime( + new Date(activity.startTime).getTime(), + )} +
+
+ {activity.endTime + ? formatTimestamp.toShortDateTime( + new Date(activity.endTime).getTime(), + ) + : "-"} +
+
{activity.usedCharge} seconds
+
+ )) + ) : ( +
No agent activities
+ )} +
+
+
+ ); +} diff --git a/app/(main)/settings/team/agent-usage.tsx b/app/(main)/settings/team/agent-usage.tsx index 95bf2cbc..72d8c081 100644 --- a/app/(main)/settings/team/agent-usage.tsx +++ b/app/(main)/settings/team/agent-usage.tsx @@ -1,7 +1,7 @@ import { Card } from "@/app/(main)/settings/components/card"; -import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; import { getAgentActivities } from "./actions"; import { AgentUsageDialog } from "./agent-usage-dialog"; +import { AgentUsageTable } from "./agent-usage-table"; export async function AgentUsage() { const result = await getAgentActivities({ limit: 50 }); @@ -27,41 +27,7 @@ export async function AgentUsage() { ) : null, }} > -
-
-
Agent
-
Start Time
-
End Time
-
Charge
-
-
- {recentActivities.length > 0 ? ( - recentActivities.map((activity) => ( -
-
{activity.agentName ?? activity.agentId}
-
- {formatTimestamp.toShortDateTime( - new Date(activity.startTime).getTime(), - )} -
-
- {activity.endTime - ? formatTimestamp.toShortDateTime( - new Date(activity.endTime).getTime(), - ) - : "-"} -
-
{activity.usedCharge} seconds
-
- )) - ) : ( -
No recent agent activities
- )} -
-
+ ); } From d190b00ecbecab414f59a363697b11a900d49ca2 Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Tue, 17 Dec 2024 10:49:20 +0900 Subject: [PATCH 6/8] refactor: Add LocalDateTime component to handle locale-aware date formatting - Create LocalDateTime component to handle client-side date formatting - Replace formatTimestamp utility with LocalDateTime in agent usage table - Handle hydration issues by setting locale on client side --- .../settings/team/agent-usage-table.tsx | 16 +++++----- .../team/components/local-date-time.tsx | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 app/(main)/settings/team/components/local-date-time.tsx diff --git a/app/(main)/settings/team/agent-usage-table.tsx b/app/(main)/settings/team/agent-usage-table.tsx index 7d80364b..3064286c 100644 --- a/app/(main)/settings/team/agent-usage-table.tsx +++ b/app/(main)/settings/team/agent-usage-table.tsx @@ -1,4 +1,4 @@ -import { formatTimestamp } from "@/app/(playground)/p/[agentId]/canary/lib/utils"; +import { LocalDateTime } from "./components/local-date-time"; export type AgentActivity = { agentId: string; @@ -35,16 +35,14 @@ export function AgentUsageTable({ >
{activity.agentName ?? activity.agentId}
- {formatTimestamp.toShortDateTime( - new Date(activity.startTime).getTime(), - )} +
- {activity.endTime - ? formatTimestamp.toShortDateTime( - new Date(activity.endTime).getTime(), - ) - : "-"} + {activity.endTime ? ( + + ) : ( + "-" + )}
{activity.usedCharge} seconds
diff --git a/app/(main)/settings/team/components/local-date-time.tsx b/app/(main)/settings/team/components/local-date-time.tsx new file mode 100644 index 00000000..15f07b76 --- /dev/null +++ b/app/(main)/settings/team/components/local-date-time.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { useEffect, useState } from "react"; + +interface LocalDateTimeProps { + utcDateTime: Date; + // Optional format configuration + format?: Intl.DateTimeFormatOptions; +} + +export function LocalDateTime({ + utcDateTime, + format = { + year: "numeric", + month: "long", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + hour12: false, + }, +}: LocalDateTimeProps) { + // Server-side rendering will display "Loading..." until the useEffect hook runs + const [formattedDate, setFormattedDate] = useState("Loading..."); + + useEffect(() => { + // Display using browser's locale and timezone + setFormattedDate(utcDateTime.toLocaleString(undefined, format)); + }, [utcDateTime, format]); + + return ; +} From 17185a2fc0a6fe4c76987615441bccb6cd64c971 Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Tue, 17 Dec 2024 12:28:33 +0900 Subject: [PATCH 7/8] Update formatted date and component props --- .../settings/team/agent-usage-table.tsx | 4 +-- .../team/components/local-date-time.tsx | 26 ++++++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/app/(main)/settings/team/agent-usage-table.tsx b/app/(main)/settings/team/agent-usage-table.tsx index 3064286c..d3321d4b 100644 --- a/app/(main)/settings/team/agent-usage-table.tsx +++ b/app/(main)/settings/team/agent-usage-table.tsx @@ -35,11 +35,11 @@ export function AgentUsageTable({ >
{activity.agentName ?? activity.agentId}
- +
{activity.endTime ? ( - + ) : ( "-" )} diff --git a/app/(main)/settings/team/components/local-date-time.tsx b/app/(main)/settings/team/components/local-date-time.tsx index 15f07b76..2b8551de 100644 --- a/app/(main)/settings/team/components/local-date-time.tsx +++ b/app/(main)/settings/team/components/local-date-time.tsx @@ -1,15 +1,7 @@ "use client"; -import { useEffect, useState } from "react"; - -interface LocalDateTimeProps { - utcDateTime: Date; - // Optional format configuration - format?: Intl.DateTimeFormatOptions; -} - export function LocalDateTime({ - utcDateTime, + date, format = { year: "numeric", month: "long", @@ -17,15 +9,13 @@ export function LocalDateTime({ hour: "2-digit", minute: "2-digit", hour12: false, + timeZoneName: "short", }, -}: LocalDateTimeProps) { - // Server-side rendering will display "Loading..." until the useEffect hook runs - const [formattedDate, setFormattedDate] = useState("Loading..."); - - useEffect(() => { - // Display using browser's locale and timezone - setFormattedDate(utcDateTime.toLocaleString(undefined, format)); - }, [utcDateTime, format]); +}: { + date: Date; + format?: Intl.DateTimeFormatOptions; +}) { + const formattedDate = new Intl.DateTimeFormat("en-US", format).format(date); - return ; + return ; } From 20029cc76cfe8dedfd904100abd0b3f2f1b2fe35 Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Tue, 17 Dec 2024 12:40:17 +0900 Subject: [PATCH 8/8] Update styles --- app/(main)/settings/team/agent-usage-dialog.tsx | 2 +- app/(main)/settings/team/agent-usage-table.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/(main)/settings/team/agent-usage-dialog.tsx b/app/(main)/settings/team/agent-usage-dialog.tsx index 377f0e76..e8832d68 100644 --- a/app/(main)/settings/team/agent-usage-dialog.tsx +++ b/app/(main)/settings/team/agent-usage-dialog.tsx @@ -18,7 +18,7 @@ export function AgentUsageDialog({ activities }: AgentUsageDialogProps) { - + Agent Usage Logs diff --git a/app/(main)/settings/team/agent-usage-table.tsx b/app/(main)/settings/team/agent-usage-table.tsx index d3321d4b..51a55dcd 100644 --- a/app/(main)/settings/team/agent-usage-table.tsx +++ b/app/(main)/settings/team/agent-usage-table.tsx @@ -33,7 +33,9 @@ export function AgentUsageTable({ key={`${activity.agentId}-${activity.startTime}`} className="grid grid-cols-4 gap-4 p-4 items-center text-zinc-200" > -
{activity.agentName ?? activity.agentId}
+
+ {activity.agentName ?? activity.agentId} +