mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-30 03:00:41 -04:00
fixing Agent Table phase 1
This commit is contained in:
@@ -1,19 +1,28 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { AgentTable } from "./AgentTable";
|
||||
import { AgentTableRowProps } from "./AgentTableRow";
|
||||
import { userEvent, within, expect } from "@storybook/test";
|
||||
import { userEvent, within, expect, fn } from "@storybook/test";
|
||||
import { StatusType } from "./Status";
|
||||
|
||||
const meta: Meta<typeof AgentTable> = {
|
||||
const meta = {
|
||||
title: "AGPT UI/Agent Table",
|
||||
component: AgentTable,
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
};
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="container mx-auto p-4">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} satisfies Meta<typeof AgentTable>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof AgentTable>;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const sampleAgents: AgentTableRowProps[] = [
|
||||
const sampleAgents = [
|
||||
{
|
||||
id: 43,
|
||||
agentName: "Super Coder",
|
||||
@@ -22,17 +31,13 @@ const sampleAgents: AgentTableRowProps[] = [
|
||||
"https://ddz4ak4pa3d19.cloudfront.net/cache/53/b2/53b2bc7d7900f0e1e60bf64ebf38032d.jpg",
|
||||
],
|
||||
dateSubmitted: "2023-05-15",
|
||||
status: "approved",
|
||||
status: "approved" as StatusType,
|
||||
runs: 1500,
|
||||
rating: 4.8,
|
||||
agent_id: "43",
|
||||
agent_version: 1,
|
||||
sub_heading: "Super Coder",
|
||||
date_submitted: "2023-05-15",
|
||||
onEditSubmission: () => console.log("Edit Super Coder"),
|
||||
onDeleteSubmission: () => console.log("Delete Super Coder"),
|
||||
selectedAgents: new Set(),
|
||||
setSelectedAgents: () => {},
|
||||
},
|
||||
{
|
||||
id: 44,
|
||||
@@ -42,17 +47,13 @@ const sampleAgents: AgentTableRowProps[] = [
|
||||
"https://ddz4ak4pa3d19.cloudfront.net/cache/40/f7/40f7bc97c952f8df0f9c88d29defe8d4.jpg",
|
||||
],
|
||||
dateSubmitted: "2023-05-10",
|
||||
status: "awaiting_review",
|
||||
status: "awaiting_review" as StatusType,
|
||||
runs: 1200,
|
||||
rating: 4.5,
|
||||
agent_id: "44",
|
||||
agent_version: 1,
|
||||
sub_heading: "Data Analyzer",
|
||||
date_submitted: "2023-05-10",
|
||||
onEditSubmission: () => console.log("Edit Data Analyzer"),
|
||||
onDeleteSubmission: () => console.log("Delete Data Analyzer"),
|
||||
selectedAgents: new Set(),
|
||||
setSelectedAgents: () => {},
|
||||
},
|
||||
{
|
||||
id: 45,
|
||||
@@ -62,48 +63,118 @@ const sampleAgents: AgentTableRowProps[] = [
|
||||
"https://ddz4ak4pa3d19.cloudfront.net/cache/14/9e/149ebb9014aa8c0097e72ed89845af0e.jpg",
|
||||
],
|
||||
dateSubmitted: "2023-05-05",
|
||||
status: "draft",
|
||||
status: "draft" as StatusType,
|
||||
runs: 800,
|
||||
rating: 4.2,
|
||||
agent_id: "45",
|
||||
agent_version: 1,
|
||||
sub_heading: "UI Designer",
|
||||
date_submitted: "2023-05-05",
|
||||
onEditSubmission: () => console.log("Edit UI Designer"),
|
||||
onDeleteSubmission: () => console.log("Delete UI Designer"),
|
||||
selectedAgents: new Set(),
|
||||
setSelectedAgents: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
agents: sampleAgents,
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyTable: Story = {
|
||||
args: {
|
||||
agents: [],
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
// Tests
|
||||
export const InteractionTest: Story = {
|
||||
...Default,
|
||||
play: async ({ canvasElement }) => {
|
||||
export const LongAgentNames: Story = {
|
||||
args: {
|
||||
agents: [
|
||||
{
|
||||
...sampleAgents[0],
|
||||
agentName:
|
||||
"Super Advanced Artificial Intelligence Code Generator and Optimizer with Machine Learning Capabilities",
|
||||
sub_heading:
|
||||
"A very advanced AI system that can generate and optimize code using cutting-edge machine learning techniques",
|
||||
},
|
||||
...sampleAgents.slice(1),
|
||||
],
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export const ManyAgents: Story = {
|
||||
args: {
|
||||
agents: Array(20)
|
||||
.fill(null)
|
||||
.map((_, index) => ({
|
||||
...sampleAgents[index % 3],
|
||||
id: 100 + index,
|
||||
agent_id: `${100 + index}`,
|
||||
agentName: `Test Agent ${index + 1}`,
|
||||
})),
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export const WithInteraction: Story = {
|
||||
args: {
|
||||
agents: sampleAgents,
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const editButtons = await canvas.findAllByText("Edit");
|
||||
await userEvent.click(editButtons[0]);
|
||||
// You would typically assert something here, but console.log is used in the mocked function
|
||||
|
||||
const table = canvas.getByRole("table");
|
||||
await expect(table).toBeInTheDocument();
|
||||
|
||||
const checkboxes = canvas.getAllByTestId("dropdown-button");
|
||||
await expect(checkboxes.length).toBeGreaterThan(0);
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyTableTest: Story = {
|
||||
...EmptyTable,
|
||||
args: {
|
||||
agents: [],
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const emptyMessage = canvas.getByText("No agents found");
|
||||
expect(emptyMessage).toBeTruthy();
|
||||
const emptyMessages = canvas.getAllByText(
|
||||
"No agents available. Create your first agent to get started!",
|
||||
);
|
||||
await expect(emptyMessages.length).toBeGreaterThan(0);
|
||||
await expect(emptyMessages[0]).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const ResponsiveTest: Story = {
|
||||
args: {
|
||||
agents: sampleAgents,
|
||||
onEditSubmission: fn(),
|
||||
onDeleteSubmission: fn(),
|
||||
},
|
||||
parameters: {
|
||||
viewport: {
|
||||
defaultViewport: "mobile2",
|
||||
},
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// In mobile view, cards should be visible instead of table
|
||||
// Check for at least one card
|
||||
const cards = canvas.getAllByTestId("agent-table-card");
|
||||
await expect(cards.length).toBe(3);
|
||||
|
||||
// Table should be hidden
|
||||
const tables = canvasElement.querySelectorAll("table");
|
||||
await expect(tables.length).toBe(1);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,6 +4,15 @@ import * as React from "react";
|
||||
import { AgentTableRow, AgentTableRowProps } from "./AgentTableRow";
|
||||
import { AgentTableCard } from "./AgentTableCard";
|
||||
import { StoreSubmissionRequest } from "@/lib/autogpt-server-api/types";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
export interface AgentTableProps {
|
||||
agents: Omit<
|
||||
@@ -41,78 +50,90 @@ export const AgentTable: React.FC<AgentTableProps> = ({
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{/* Table header - Hide on mobile */}
|
||||
<div className="hidden flex-col md:flex">
|
||||
<div className="border-t border-neutral-300 dark:border-neutral-700" />
|
||||
<div className="flex items-center px-4 py-2">
|
||||
<div className="flex items-center">
|
||||
<div className="flex min-w-[120px] items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="selectAllAgents"
|
||||
aria-label="Select all agents"
|
||||
className="mr-4 h-5 w-5 rounded border-2 border-neutral-400 dark:border-neutral-600"
|
||||
checked={
|
||||
selectedAgents.size === agents.length && agents.length > 0
|
||||
}
|
||||
onChange={handleSelectAll}
|
||||
/>
|
||||
<label
|
||||
htmlFor="selectAllAgents"
|
||||
className="text-sm font-medium text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
Select all
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-2 grid w-full grid-cols-[400px,150px,150px,100px,100px,50px] items-center">
|
||||
<div className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
Agent info
|
||||
</div>
|
||||
<div className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
Date submitted
|
||||
</div>
|
||||
<div className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
Status
|
||||
</div>
|
||||
<div className="text-right text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
Runs
|
||||
</div>
|
||||
<div className="text-right text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
Reviews
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b border-neutral-300 dark:border-neutral-700" />
|
||||
{/* Table for desktop view */}
|
||||
<div className="hidden md:block">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>
|
||||
<Checkbox
|
||||
id="selectAllAgents"
|
||||
aria-label="Select all agents"
|
||||
checked={
|
||||
selectedAgents.size === agents.length && agents.length > 0
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setSelectedAgents(
|
||||
new Set(agents.map((agent) => agent.agent_id)),
|
||||
);
|
||||
} else {
|
||||
setSelectedAgents(new Set());
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TableHead>
|
||||
<TableHead className="font-sans text-sm font-medium text-neutral-800">
|
||||
Agent info
|
||||
</TableHead>
|
||||
<TableHead className="font-sans text-sm font-medium text-neutral-800">
|
||||
Date submitted
|
||||
</TableHead>
|
||||
<TableHead className="font-sans text-sm font-medium text-neutral-800">
|
||||
Status
|
||||
</TableHead>
|
||||
<TableHead className="font-sans text-sm font-medium text-neutral-800">
|
||||
Runs
|
||||
</TableHead>
|
||||
<TableHead className="font-sans text-sm font-medium text-neutral-800">
|
||||
Reviews
|
||||
</TableHead>
|
||||
<TableHead className="font-sans text-sm font-medium text-neutral-800"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{agents.length > 0 ? (
|
||||
agents.map((agent) => (
|
||||
<AgentTableRow
|
||||
key={agent.id}
|
||||
{...agent}
|
||||
selectedAgents={selectedAgents}
|
||||
setSelectedAgents={setSelectedAgents}
|
||||
onEditSubmission={onEditSubmission}
|
||||
onDeleteSubmission={onDeleteSubmission}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="py-4 text-center">
|
||||
<span className="font-sans text-base text-neutral-600 dark:text-neutral-400">
|
||||
No agents available. Create your first agent to get started!
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Table body */}
|
||||
{agents.length > 0 ? (
|
||||
<div className="flex flex-col">
|
||||
{agents.map((agent, index) => (
|
||||
<div key={agent.id} className="md:block">
|
||||
<AgentTableRow
|
||||
{/* Mobile view with cards */}
|
||||
<div className="md:hidden">
|
||||
{agents.length > 0 ? (
|
||||
<div className="flex flex-col">
|
||||
{agents.map((agent) => (
|
||||
<AgentTableCard
|
||||
key={agent.id}
|
||||
{...agent}
|
||||
selectedAgents={selectedAgents}
|
||||
setSelectedAgents={setSelectedAgents}
|
||||
onEditSubmission={onEditSubmission}
|
||||
onDeleteSubmission={onDeleteSubmission}
|
||||
/>
|
||||
<div className="block md:hidden">
|
||||
<AgentTableCard
|
||||
{...agent}
|
||||
onEditSubmission={onEditSubmission}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4 text-center font-sans text-base text-neutral-600 dark:text-neutral-400">
|
||||
No agents available. Create your first agent to get started!
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4 text-center font-sans text-base text-neutral-600 dark:text-neutral-400">
|
||||
No agents available. Create your first agent to get started!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { AgentTableCard } from "./AgentTableCard";
|
||||
import { userEvent, within, expect } from "@storybook/test";
|
||||
import { type StatusType } from "./Status";
|
||||
|
||||
const meta: Meta<typeof AgentTableCard> = {
|
||||
title: "AGPT UI/Agent Table Card",
|
||||
component: AgentTableCard,
|
||||
tags: ["autodocs"],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof AgentTableCard>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
agentName: "Super Coder",
|
||||
description: "An AI agent that writes clean, efficient code",
|
||||
imageSrc: [
|
||||
"https://ddz4ak4pa3d19.cloudfront.net/cache/53/b2/53b2bc7d7900f0e1e60bf64ebf38032d.jpg",
|
||||
],
|
||||
dateSubmitted: "2023-05-15",
|
||||
status: "ACTIVE" as StatusType,
|
||||
runs: 1500,
|
||||
rating: 4.8,
|
||||
},
|
||||
};
|
||||
|
||||
export const NoRating: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
rating: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const NoRuns: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
runs: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const InactiveAgent: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
status: "INACTIVE" as StatusType,
|
||||
},
|
||||
};
|
||||
|
||||
export const LongDescription: Story = {
|
||||
args: {
|
||||
...Default.args,
|
||||
description:
|
||||
"This is a very long description that should wrap to multiple lines. It contains detailed information about the agent and its capabilities.",
|
||||
},
|
||||
};
|
||||
|
||||
export const InteractionTest: Story = {
|
||||
...Default,
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const moreButton = canvas.getByRole("button");
|
||||
await userEvent.click(moreButton);
|
||||
},
|
||||
};
|
||||
@@ -50,7 +50,10 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border-b border-neutral-300 p-4 dark:border-neutral-700">
|
||||
<div
|
||||
className="border-b border-neutral-300 p-4 dark:border-neutral-700"
|
||||
data-testid="agent-table-card"
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
<div className="relative h-[56px] w-[100px] overflow-hidden rounded-lg bg-[#d9d9d9] dark:bg-neutral-800">
|
||||
<Image
|
||||
@@ -61,10 +64,10 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-[15px] font-medium text-neutral-800 dark:text-neutral-200">
|
||||
<h3 className="font-sans text-sm font-medium text-neutral-600">
|
||||
{agentName}
|
||||
</h3>
|
||||
<p className="line-clamp-2 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<p className="font-sans text-sm font-normal text-neutral-600">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -76,16 +79,16 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex flex-wrap gap-4">
|
||||
<div className="mt-4 flex flex-wrap items-center gap-4">
|
||||
<Status status={status} />
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<div className="font-sans text-sm font-normal text-neutral-600">
|
||||
{dateSubmitted}
|
||||
</div>
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<div className="font-sans text-sm font-normal text-neutral-600">
|
||||
{runs.toLocaleString()} runs
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
<span className="font-sans text-sm font-normal text-neutral-600">
|
||||
{rating.toFixed(1)}
|
||||
</span>
|
||||
<IconStarFilled className="h-4 w-4 text-neutral-800 dark:text-neutral-200" />
|
||||
|
||||
@@ -7,6 +7,8 @@ import { Status, StatusType } from "./Status";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { TrashIcon } from "@radix-ui/react-icons";
|
||||
import { StoreSubmissionRequest } from "@/lib/autogpt-server-api/types";
|
||||
import { TableCell, TableRow } from "../ui/table";
|
||||
import { Checkbox } from "../ui/checkbox";
|
||||
|
||||
export interface AgentTableRowProps {
|
||||
agent_id: string;
|
||||
@@ -82,28 +84,22 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
|
||||
}, [agent_id, selectedAgents, setSelectedAgents]);
|
||||
|
||||
return (
|
||||
<div className="hidden items-center border-b border-neutral-300 px-4 py-4 hover:bg-neutral-50 dark:border-neutral-700 dark:hover:bg-neutral-800 md:flex">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={checkboxId}
|
||||
aria-label={`Select ${agentName}`}
|
||||
className="mr-4 h-5 w-5 rounded border-2 border-neutral-400 dark:border-neutral-600"
|
||||
checked={selectedAgents.has(agent_id)}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
{/* Single label instead of multiple */}
|
||||
<label htmlFor={checkboxId} className="sr-only">
|
||||
Select {agentName}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<TableRow className="space-x-2.5 hover:bg-neutral-50 dark:hover:bg-neutral-800">
|
||||
<TableCell className="w-[40px]">
|
||||
<Checkbox
|
||||
id={checkboxId}
|
||||
aria-label={`Select ${agentName}`}
|
||||
checked={selectedAgents.has(agent_id)}
|
||||
onCheckedChange={handleCheckboxChange}
|
||||
/>
|
||||
<label htmlFor={checkboxId} className="sr-only">
|
||||
Select {agentName}
|
||||
</label>
|
||||
</TableCell>
|
||||
|
||||
<div className="grid w-full grid-cols-[minmax(400px,1fr),180px,140px,100px,100px,40px] items-center gap-4">
|
||||
{/* Agent info column */}
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative h-[70px] w-[125px] overflow-hidden rounded-[10px] bg-[#d9d9d9] dark:bg-neutral-700">
|
||||
<div className="relative aspect-video w-[125px] overflow-hidden rounded-[10px] bg-[#d9d9d9] dark:bg-neutral-700">
|
||||
<Image
|
||||
src={imageSrc?.[0] ?? "/nada.png"}
|
||||
alt={agentName}
|
||||
@@ -112,74 +108,69 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-[15px] font-medium text-neutral-800 dark:text-neutral-200">
|
||||
<h3 className="font-sans text-sm font-medium text-neutral-800">
|
||||
{agentName}
|
||||
</h3>
|
||||
<p className="line-clamp-2 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
<p className="line-clamp-2 font-sans text-sm font-normal text-neutral-600">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Date column */}
|
||||
<div className="pl-14 text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{dateSubmitted}
|
||||
</div>
|
||||
<TableCell className="font-sans text-sm font-normal text-neutral-600">
|
||||
{dateSubmitted}
|
||||
</TableCell>
|
||||
|
||||
{/* Status column */}
|
||||
<div>
|
||||
<Status status={status} />
|
||||
</div>
|
||||
<TableCell>
|
||||
<Status status={status} />
|
||||
</TableCell>
|
||||
|
||||
{/* Runs column */}
|
||||
<div className="text-right text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{runs?.toLocaleString() ?? "0"}
|
||||
</div>
|
||||
<TableCell className="text-right font-sans text-sm font-normal text-neutral-600">
|
||||
{runs?.toLocaleString() ?? "-"}
|
||||
</TableCell>
|
||||
|
||||
{/* Reviews column */}
|
||||
<div className="text-right">
|
||||
{rating ? (
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<span className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
{rating.toFixed(1)}
|
||||
</span>
|
||||
<IconStarFilled className="h-4 w-4 text-neutral-800 dark:text-neutral-200" />
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
No reviews
|
||||
<TableCell className="text-right font-sans text-sm font-normal text-neutral-600">
|
||||
{rating ? (
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<span className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
{rating.toFixed(1)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<IconStarFilled className="h-4 w-4 text-neutral-800 dark:text-neutral-200" />
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
No reviews
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
{/* Actions - Three dots menu */}
|
||||
<div className="flex justify-end">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<button className="rounded-full p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700">
|
||||
<IconMore className="h-5 w-5 text-neutral-800 dark:text-neutral-200" />
|
||||
</button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content className="z-10 rounded-xl border bg-white p-1 shadow-md dark:bg-gray-800">
|
||||
<DropdownMenu.Item
|
||||
onSelect={handleEdit}
|
||||
className="flex cursor-pointer items-center rounded-md px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<IconEdit className="mr-2 h-5 w-5 dark:text-gray-100" />
|
||||
<span className="dark:text-gray-100">Edit</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator className="my-1 h-px bg-gray-300 dark:bg-gray-600" />
|
||||
<DropdownMenu.Item
|
||||
onSelect={handleDelete}
|
||||
className="flex cursor-pointer items-center rounded-md px-3 py-2 text-red-500 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<TrashIcon className="mr-2 h-5 w-5 text-red-500 dark:text-red-400" />
|
||||
<span className="dark:text-red-400">Delete</span>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TableCell className="text-right font-sans text-sm font-normal text-neutral-600">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<button className="rounded-full p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700">
|
||||
<IconMore className="h-5 w-5 text-neutral-800 dark:text-neutral-200" />
|
||||
</button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content className="z-10 rounded-xl border bg-white p-1 shadow-md dark:bg-gray-800">
|
||||
<DropdownMenu.Item
|
||||
onSelect={handleEdit}
|
||||
className="flex cursor-pointer items-center rounded-md px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<IconEdit className="mr-2 h-5 w-5 dark:text-gray-100" />
|
||||
<span className="dark:text-gray-100">Edit</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator className="my-1 h-px bg-gray-300 dark:bg-gray-600" />
|
||||
<DropdownMenu.Item
|
||||
onSelect={handleDelete}
|
||||
className="flex cursor-pointer items-center rounded-md px-3 py-2 text-red-500 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<TrashIcon className="mr-2 h-5 w-5 text-red-500 dark:text-red-400" />
|
||||
<span className="dark:text-red-400">Delete</span>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,46 +12,31 @@ const statusConfig: Record<
|
||||
bgColor: string;
|
||||
dotColor: string;
|
||||
text: string;
|
||||
darkBgColor: string;
|
||||
darkDotColor: string;
|
||||
}
|
||||
> = {
|
||||
draft: {
|
||||
bgColor: "bg-blue-50",
|
||||
dotColor: "bg-blue-500",
|
||||
text: "Draft",
|
||||
darkBgColor: "dark:bg-blue-900",
|
||||
darkDotColor: "dark:bg-blue-300",
|
||||
},
|
||||
awaiting_review: {
|
||||
bgColor: "bg-amber-50",
|
||||
dotColor: "bg-amber-500",
|
||||
text: "Awaiting review",
|
||||
darkBgColor: "dark:bg-amber-900",
|
||||
darkDotColor: "dark:bg-amber-300",
|
||||
},
|
||||
approved: {
|
||||
bgColor: "bg-green-50",
|
||||
dotColor: "bg-green-500",
|
||||
text: "Approved",
|
||||
darkBgColor: "dark:bg-green-900",
|
||||
darkDotColor: "dark:bg-green-300",
|
||||
},
|
||||
rejected: {
|
||||
bgColor: "bg-red-50",
|
||||
dotColor: "bg-red-500",
|
||||
text: "Rejected",
|
||||
darkBgColor: "dark:bg-red-900",
|
||||
darkDotColor: "dark:bg-red-300",
|
||||
},
|
||||
};
|
||||
|
||||
export const Status: React.FC<StatusProps> = ({ status }) => {
|
||||
/**
|
||||
* Status component displays a badge with a colored dot and text indicating the agent's status
|
||||
* @param status - The current status of the agent
|
||||
* Valid values: 'draft', 'awaiting_review', 'approved', 'rejected'
|
||||
*/
|
||||
if (!status) {
|
||||
return <Status status="awaiting_review" />;
|
||||
} else if (!statusConfig[status]) {
|
||||
@@ -62,12 +47,10 @@ export const Status: React.FC<StatusProps> = ({ status }) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`px-2.5 py-1 ${config.bgColor} ${config.darkBgColor} flex items-center gap-1.5 rounded-[26px]`}
|
||||
className={`px-2.5 py-1 ${config.bgColor} flex w-fit items-center gap-1.5 rounded-3xl`}
|
||||
>
|
||||
<div
|
||||
className={`h-3 w-3 ${config.dotColor} ${config.darkDotColor} rounded-full`}
|
||||
/>
|
||||
<div className="font-sans text-sm font-normal leading-tight text-neutral-600 dark:text-neutral-300">
|
||||
<div className={`h-3 w-3 ${config.dotColor} rounded-full`} />
|
||||
<div className="font-sans text-sm font-normal text-neutral-600">
|
||||
{config.text}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,7 +73,7 @@ const TableHead = React.forwardRef<
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"h-10 px-2 text-left align-middle font-medium text-neutral-500 dark:text-neutral-400 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
"h-10 pr-2.5 text-left align-middle font-medium text-neutral-500 dark:text-neutral-400 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -88,7 +88,7 @@ const TableCell = React.forwardRef<
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
"py-3 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user