refactor(ui): move the tag and vector store views off tremor (#37311)

* refactor(ui): move the search tool, tag and vector store views off tremor

Swaps the tremor Button, TextInput, Text, Title, Card, Badge, Accordion and
TabGroup usages in the search tools, tag management and vector store views for
the shadcn primitives, following the tremor conversion cookbook. Both tab panels
in the vector store info view carry keepMounted so the tester's state survives
switching to Details and back, and a test pins that contract.

tremor's Button renders a bare button element with no type, so inside the antd
Forms here the Test Connection button in the create search tool modal and the
Cancel buttons in the tag editor and the vector store form were implicit submit
buttons. The shadcn Button defaults to type="button", so they can no longer
submit, and every button that is meant to submit now carries an explicit
type="submit". Clicking Test Connection and Cancel against a live proxy on the
merge base already only ran the connection test and only cancelled, so this
closes a latent trap rather than changing what the pages do.

Decrements the seven no-restricted-imports suppression counts these files no
longer need, leaving the antd half of each entry in place for the antd pass.

* refactor(ui): use the line tab strip in the vector store detail view

The detail view's tabs kept the default pill TabsList, so it no longer matched
the underline strip tremor rendered before the swap or the one the vector store
list view already uses.

* test(ui): pin the tag and vector store form save and cancel buttons

Cancel in the tag editor used to submit the form and save the tag because the
tremor button carried no type; nothing in the suite failed if it started doing
that again. Each form now has a pair of cases: Save Changes and Create still
submit, and Cancel leaves the record alone.

* fix(ui): keep the reveal toggle on the search tool API key

tremor's TextInput drew its own show/hide button whenever the type was
password, and the shadcn Input is a plain native input, so the straight
prop pass-through silently deleted that affordance from the create search
tool form's API key field.

Puts it on antd's Input.Password instead, which is what the sibling edit
form in the same directory (SearchTools.tsx) already uses for the very
same field, so the reveal survives and the two forms behave the same.
The file already imports antd, so this adds no import and no suppression.
This commit is contained in:
ryan-crabbe-berri 2026-08-18 14:29:06 -07:00 committed by GitHub
parent 573aa61084
commit ef72e1afd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 282 additions and 169 deletions

View file

@ -1406,7 +1406,7 @@
"count": 1
},
"no-restricted-imports": {
"count": 3
"count": 1
},
"react-hooks/set-state-in-effect": {
"count": 1
@ -1513,7 +1513,7 @@
},
"src/app/(dashboard)/vector-stores/_components/CreateVectorStore.tsx": {
"no-restricted-imports": {
"count": 3
"count": 2
}
},
"src/app/(dashboard)/vector-stores/_components/S3VectorsConfig.tsx": {
@ -1541,9 +1541,6 @@
"local/filename-pascal-case": {
"count": 1
},
"no-restricted-imports": {
"count": 1
},
"react-hooks/set-state-in-effect": {
"count": 1
}

View file

@ -136,4 +136,17 @@ describe("TagInfoView save payload", () => {
expect(mockTagUpdateCall).toHaveBeenCalledWith("sk-test", expected);
});
it("leaves the tag untouched and returns to the detail view when Cancel is clicked", async () => {
const { user } = await renderEditor();
const descriptionInput = screen.getByLabelText("Description");
await user.clear(descriptionInput);
await user.type(descriptionInput, "abandoned description");
await user.click(screen.getByRole("button", { name: "Cancel" }));
expect(await screen.findByText("Tag Details")).toBeInTheDocument();
expect(mockTagUpdateCall).not.toHaveBeenCalled();
});
});

View file

@ -1,7 +1,6 @@
"use client";
import React, { useState, useEffect } from "react";
import { Card, Text, Title, Button as TremorButton, Badge } from "@tremor/react";
import { Tooltip, Button as AntdButton } from "antd";
import { z } from "zod/v4";
import { fetchUserModels } from "@/components/organisms/create_key_button";
@ -14,7 +13,9 @@ import BudgetDurationDropdown from "@/components/common_components/budget_durati
import { FieldGroup } from "@/components/shared/form/field";
import { FormField } from "@/components/shared/form/FormField";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardTitle } from "@/components/ui/card";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
@ -220,11 +221,11 @@ const TagInfoView: React.FC<TagInfoViewProps> = ({ tagId, onClose, accessToken,
<div className="p-4">
<div className="flex justify-between items-center mb-6">
<div>
<TremorButton onClick={onClose} className="mb-4">
<Button onClick={onClose} className="mb-4">
Back to Tags
</TremorButton>
</Button>
<div className="flex items-center gap-2">
<Text className="font-medium">Tag Name:</Text>
<span className="text-sm font-medium">Tag Name:</span>
<span className="font-mono px-2 py-1 bg-muted rounded-sm text-sm border border-border">
{tagDetails.name}
</span>
@ -240,91 +241,97 @@ const TagInfoView: React.FC<TagInfoViewProps> = ({ tagId, onClose, accessToken,
}`}
/>
</div>
<Text className="text-muted-foreground">{tagDetails.description || "No description"}</Text>
<p className="text-sm text-muted-foreground">{tagDetails.description || "No description"}</p>
</div>
{is_admin && !isEditing && <TremorButton onClick={() => setIsEditing(true)}>Edit Tag</TremorButton>}
{is_admin && !isEditing && <Button onClick={() => setIsEditing(true)}>Edit Tag</Button>}
</div>
{isEditing ? (
<Card>
<TagEditForm
tag={tagDetails}
seedBudgetFields={editTag}
userModels={userModels}
onCancel={() => setIsEditing(false)}
onSave={handleSave}
/>
<CardContent>
<TagEditForm
tag={tagDetails}
seedBudgetFields={editTag}
userModels={userModels}
onCancel={() => setIsEditing(false)}
onSave={handleSave}
/>
</CardContent>
</Card>
) : (
<div className="space-y-6">
<Card>
<Title>Tag Details</Title>
<div className="space-y-4 mt-4">
<div>
<Text className="font-medium">Name</Text>
<Text>{tagDetails.name}</Text>
</div>
<div>
<Text className="font-medium">Description</Text>
<Text>{tagDetails.description || "-"}</Text>
</div>
<div>
<Text className="font-medium">Allowed Models</Text>
<div className="flex flex-wrap gap-2 mt-2">
{!tagDetails.models || tagDetails.models.length === 0 ? (
<Badge color="red">All Models</Badge>
) : (
tagDetails.models.map((modelId) => (
<Badge key={modelId} color="blue">
<Tooltip title={`ID: ${modelId}`}>{tagDetails.model_info?.[modelId] || modelId}</Tooltip>
</Badge>
))
)}
<CardContent>
<CardTitle>Tag Details</CardTitle>
<div className="space-y-4 mt-4">
<div>
<p className="font-medium">Name</p>
<p>{tagDetails.name}</p>
</div>
<div>
<p className="font-medium">Description</p>
<p>{tagDetails.description || "-"}</p>
</div>
<div>
<p className="font-medium">Allowed Models</p>
<div className="flex flex-wrap gap-2 mt-2">
{!tagDetails.models || tagDetails.models.length === 0 ? (
<Badge variant="secondary">All Models</Badge>
) : (
tagDetails.models.map((modelId) => (
<Badge key={modelId} variant="secondary">
<Tooltip title={`ID: ${modelId}`}>{tagDetails.model_info?.[modelId] || modelId}</Tooltip>
</Badge>
))
)}
</div>
</div>
<div>
<p className="font-medium">Created</p>
<p>{tagDetails.created_at ? new Date(tagDetails.created_at).toLocaleString() : "-"}</p>
</div>
<div>
<p className="font-medium">Last Updated</p>
<p>{tagDetails.updated_at ? new Date(tagDetails.updated_at).toLocaleString() : "-"}</p>
</div>
</div>
<div>
<Text className="font-medium">Created</Text>
<Text>{tagDetails.created_at ? new Date(tagDetails.created_at).toLocaleString() : "-"}</Text>
</div>
<div>
<Text className="font-medium">Last Updated</Text>
<Text>{tagDetails.updated_at ? new Date(tagDetails.updated_at).toLocaleString() : "-"}</Text>
</div>
</div>
</CardContent>
</Card>
{tagDetails.litellm_budget_table && (
<Card>
<Title>Budget & Rate Limits</Title>
<div className="space-y-4 mt-4">
{tagDetails.litellm_budget_table.max_budget !== undefined &&
tagDetails.litellm_budget_table.max_budget !== null && (
<CardContent>
<CardTitle>Budget & Rate Limits</CardTitle>
<div className="space-y-4 mt-4">
{tagDetails.litellm_budget_table.max_budget !== undefined &&
tagDetails.litellm_budget_table.max_budget !== null && (
<div>
<p className="font-medium">Max Budget</p>
<p>${tagDetails.litellm_budget_table.max_budget}</p>
</div>
)}
{tagDetails.litellm_budget_table.budget_duration && (
<div>
<Text className="font-medium">Max Budget</Text>
<Text>${tagDetails.litellm_budget_table.max_budget}</Text>
<p className="font-medium">Budget Duration</p>
<p>{tagDetails.litellm_budget_table.budget_duration}</p>
</div>
)}
{tagDetails.litellm_budget_table.budget_duration && (
<div>
<Text className="font-medium">Budget Duration</Text>
<Text>{tagDetails.litellm_budget_table.budget_duration}</Text>
</div>
)}
{tagDetails.litellm_budget_table.tpm_limit !== undefined &&
tagDetails.litellm_budget_table.tpm_limit !== null && (
<div>
<Text className="font-medium">TPM Limit</Text>
<Text>{tagDetails.litellm_budget_table.tpm_limit.toLocaleString()}</Text>
</div>
)}
{tagDetails.litellm_budget_table.rpm_limit !== undefined &&
tagDetails.litellm_budget_table.rpm_limit !== null && (
<div>
<Text className="font-medium">RPM Limit</Text>
<Text>{tagDetails.litellm_budget_table.rpm_limit.toLocaleString()}</Text>
</div>
)}
</div>
{tagDetails.litellm_budget_table.tpm_limit !== undefined &&
tagDetails.litellm_budget_table.tpm_limit !== null && (
<div>
<p className="font-medium">TPM Limit</p>
<p>{tagDetails.litellm_budget_table.tpm_limit.toLocaleString()}</p>
</div>
)}
{tagDetails.litellm_budget_table.rpm_limit !== undefined &&
tagDetails.litellm_budget_table.rpm_limit !== null && (
<div>
<p className="font-medium">RPM Limit</p>
<p>{tagDetails.litellm_budget_table.rpm_limit.toLocaleString()}</p>
</div>
)}
</div>
</CardContent>
</Card>
)}
</div>

View file

@ -1,5 +1,4 @@
import React, { useState } from "react";
import { Card, Title, Text } from "@tremor/react";
import { Upload, Alert } from "antd";
import { toast } from "@/lib/toast";
import { InboxOutlined } from "@ant-design/icons";
@ -18,6 +17,7 @@ import {
import { Logo } from "@/components/molecules/logo/Logo";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
@ -210,47 +210,53 @@ const CreateVectorStore: React.FC<CreateVectorStoreProps> = ({ accessToken, onSu
<TooltipProvider>
<div className="space-y-6">
<div>
<Title>Create Vector Store</Title>
<Text className="text-muted-foreground">
<h3 className="text-lg font-medium">Create Vector Store</h3>
<p className="text-sm text-muted-foreground">
Upload documents and select a provider to create a new vector store with embedded content.
</Text>
</p>
</div>
{/* Upload Area */}
<Card>
<div className="mb-4">
<Text className="font-medium">Step 1: Upload Documents</Text>
<Text className="text-sm text-muted-foreground block mt-1">
Upload one or more documents (PDF, TXT, DOCX, MD). Maximum file size: 50MB per file.
</Text>
</div>
<Dragger {...uploadProps}>
<p className="ant-upload-drag-icon">
<InboxOutlined style={{ fontSize: "48px", color: "#1890ff" }} />
</p>
<p className="ant-upload-text">Click or drag files to this area to upload</p>
<p className="ant-upload-hint">Support for single or bulk upload. Supported formats: PDF, TXT, DOCX, MD</p>
</Dragger>
<CardContent>
<div className="mb-4">
<p className="font-medium">Step 1: Upload Documents</p>
<p className="text-sm text-muted-foreground block mt-1">
Upload one or more documents (PDF, TXT, DOCX, MD). Maximum file size: 50MB per file.
</p>
</div>
<Dragger {...uploadProps}>
<p className="ant-upload-drag-icon">
<InboxOutlined style={{ fontSize: "48px", color: "#1890ff" }} />
</p>
<p className="ant-upload-text">Click or drag files to this area to upload</p>
<p className="ant-upload-hint">
Support for single or bulk upload. Supported formats: PDF, TXT, DOCX, MD
</p>
</Dragger>
</CardContent>
</Card>
{/* Documents Table */}
{documents.length > 0 && (
<Card>
<div className="mb-4">
<Text className="font-medium">Uploaded Documents ({documents.length})</Text>
</div>
<DocumentsTable documents={documents} onRemove={handleRemoveDocument} />
<CardContent>
<div className="mb-4">
<p className="font-medium">Uploaded Documents ({documents.length})</p>
</div>
<DocumentsTable documents={documents} onRemove={handleRemoveDocument} />
</CardContent>
</Card>
)}
{/* Provider Selection and Vector Store Details */}
<Card>
<div className="space-y-4">
<CardContent className="space-y-4">
<div>
<Text className="font-medium">Step 2: Configure Vector Store</Text>
<Text className="text-sm text-muted-foreground block mt-1">
<p className="font-medium">Step 2: Configure Vector Store</p>
<p className="text-sm text-muted-foreground block mt-1">
Choose the provider and optionally provide a name and description for your vector store.
</Text>
</p>
</div>
<FieldGroup>
@ -342,7 +348,7 @@ const CreateVectorStore: React.FC<CreateVectorStoreProps> = ({ accessToken, onSu
{isCreating ? "Creating Vector Store..." : "Create Vector Store"}
</Button>
</div>
</div>
</CardContent>
</Card>
{/* Success Message */}

View file

@ -1,17 +1,26 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { CredentialItem } from "@/components/networking";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { CredentialItem, vectorStoreCreateCall } from "@/components/networking";
import { Providers, providerLogoMap } from "@/components/provider_info_helpers";
import { VectorStoreProviders } from "@/components/vector_store_providers";
import VectorStoreForm from "./VectorStoreForm";
vi.mock("@/components/networking");
const renderForm = () =>
vi.mock("@/components/molecules/notifications_manager", () => ({
__esModule: true,
default: {
success: vi.fn(),
fromBackend: vi.fn(),
},
}));
const renderForm = (onCancel: () => void = vi.fn()) =>
render(
<VectorStoreForm
isVisible={true}
onCancel={vi.fn()}
onCancel={onCancel}
onSuccess={vi.fn()}
accessToken="test-token"
credentials={[] as CredentialItem[]}
@ -19,6 +28,10 @@ const renderForm = () =>
);
describe("VectorStoreForm", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should render the form when visible", () => {
renderForm();
@ -31,4 +44,27 @@ describe("VectorStoreForm", () => {
const logo = screen.getByRole("img", { name: `${VectorStoreProviders.Bedrock} logo` });
expect(logo).toHaveAttribute("src", providerLogoMap[Providers.Bedrock]);
});
it("creates the vector store when Create is clicked on a filled form", async () => {
const user = userEvent.setup();
renderForm();
await user.type(screen.getByLabelText(/Vector Store ID/), "vs-created");
await user.click(screen.getByRole("button", { name: "Create" }));
await vi.waitFor(() => expect(vectorStoreCreateCall).toHaveBeenCalledTimes(1));
expect(vi.mocked(vectorStoreCreateCall).mock.calls[0][1]).toMatchObject({ vector_store_id: "vs-created" });
});
it("cancels without creating the vector store when Cancel is clicked on a filled form", async () => {
const user = userEvent.setup();
const onCancel = vi.fn();
renderForm(onCancel);
await user.type(screen.getByLabelText(/Vector Store ID/), "vs-abandoned");
await user.click(screen.getByRole("button", { name: "Cancel" }));
expect(onCancel).toHaveBeenCalledTimes(1);
expect(vectorStoreCreateCall).not.toHaveBeenCalled();
});
});

View file

@ -12,7 +12,21 @@ vi.mock("@/components/networking", () => ({
credentialListCall: vi.fn(),
}));
vi.mock("./VectorStoreTester", () => ({ __esModule: true, default: () => null }));
vi.mock("./VectorStoreTester", async () => {
const { useState } = await import("react");
const VectorStoreTesterStub = () => {
const [searchesRun, setSearchesRun] = useState(0);
return (
<div>
<button type="button" onClick={() => setSearchesRun((count) => count + 1)}>
Run search
</button>
<p>Searches run: {searchesRun}</p>
</div>
);
};
return { __esModule: true, default: VectorStoreTesterStub };
});
const mockVectorStoreInfoCall = vi.mocked(vectorStoreInfoCall);
const mockCredentialListCall = vi.mocked(credentialListCall);
@ -65,6 +79,37 @@ describe("VectorStoreInfoView", () => {
expect(onClose).toHaveBeenCalled();
});
it("keeps the test panel's search state when switching to Details and back", async () => {
const user = userEvent.setup();
mockVectorStoreInfoCall.mockResolvedValue({
vector_store: {
vector_store_id: "vs-1",
vector_store_name: "support-docs-store",
custom_llm_provider: "bedrock",
created_at: "2024-01-01T00:00:00Z",
updated_at: "2024-01-01T00:00:00Z",
},
});
render(
<VectorStoreInfoView
vectorStoreId="vs-1"
onClose={vi.fn()}
accessToken="sk-test"
is_admin={true}
editVectorStore={false}
/>,
);
expect(await screen.findByText("Vector Store ID: vs-1")).toBeInTheDocument();
await user.click(screen.getByRole("tab", { name: "Test Vector Store" }));
await user.click(screen.getByRole("button", { name: "Run search" }));
expect(screen.getByText("Searches run: 1")).toBeInTheDocument();
await user.click(screen.getByRole("tab", { name: "Details" }));
await user.click(screen.getByRole("tab", { name: "Test Vector Store" }));
expect(screen.getByText("Searches run: 1")).toBeInTheDocument();
});
it("should show the not-found state when the fetch resolves without a vector store", async () => {
mockVectorStoreInfoCall.mockResolvedValue({ vector_store: null });
render(

View file

@ -1,7 +1,5 @@
import React, { useState, useEffect } from "react";
import { Card, Text, Title, Button, Badge, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react";
import { CircleHelp } from "lucide-react";
import { ArrowLeftIcon } from "@heroicons/react/outline";
import { ArrowLeft, CircleHelp } from "lucide-react";
import { z } from "zod/v4";
import {
vectorStoreInfoCall,
@ -17,7 +15,9 @@ import VectorStoreTester from "./VectorStoreTester";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button as ShadcnButton } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
Combobox,
ComboboxContent,
@ -28,6 +28,7 @@ import {
} from "@/components/ui/combobox";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { useZodForm } from "@/lib/forms/useZodForm";
@ -183,13 +184,14 @@ const VectorStoreInfoView: React.FC<VectorStoreInfoViewProps> = ({
if (loadFailed) {
return (
<div className="p-4 max-w-full">
<Button icon={ArrowLeftIcon} variant="light" className="mb-4" onClick={onClose}>
<Button variant="ghost" className="mb-4" onClick={onClose}>
<ArrowLeft />
Back to Vector Stores
</Button>
<Title>Vector store not found</Title>
<Text className="text-muted-foreground">
<h1 className="text-xl font-semibold">Vector store not found</h1>
<p className="text-sm text-muted-foreground">
Vector store {vectorStoreId} could not be loaded. It may have been deleted.
</Text>
</p>
</div>
);
}
@ -202,31 +204,36 @@ const VectorStoreInfoView: React.FC<VectorStoreInfoViewProps> = ({
<div className="p-4 max-w-full">
<div className="flex justify-between items-center mb-6">
<div>
<Button icon={ArrowLeftIcon} variant="light" className="mb-4" onClick={onClose}>
<Button variant="ghost" className="mb-4" onClick={onClose}>
<ArrowLeft />
Back to Vector Stores
</Button>
<Title>Vector Store ID: {vectorStoreDetails.vector_store_id}</Title>
<Text className="text-muted-foreground">
<h1 className="text-xl font-semibold">Vector Store ID: {vectorStoreDetails.vector_store_id}</h1>
<p className="text-sm text-muted-foreground">
{vectorStoreDetails.vector_store_description || "No description"}
</Text>
</p>
</div>
{is_admin && !isEditing && <Button onClick={startEditing}>Edit Vector Store</Button>}
</div>
<TabGroup>
<TabList className="mb-6">
<Tab>Details</Tab>
<Tab>Test Vector Store</Tab>
</TabList>
<Tabs defaultValue="details">
<TabsList variant="line" className="mb-6 h-auto w-full justify-start rounded-none p-0">
<TabsTrigger value="details" className="flex-none rounded-none px-4 py-2">
Details
</TabsTrigger>
<TabsTrigger value="test" className="flex-none rounded-none px-4 py-2">
Test Vector Store
</TabsTrigger>
</TabsList>
<TabPanels>
<TabPanel>
{isEditing ? (
<div>
<div className="flex justify-between items-center mb-4">
<Title>Edit Vector Store</Title>
</div>
<Card>
<TabsContent value="details" keepMounted>
{isEditing ? (
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-medium">Edit Vector Store</h3>
</div>
<Card>
<CardContent>
<TooltipProvider>
<form onSubmit={form.handleSubmit(handleSave)}>
<FieldGroup>
@ -287,9 +294,9 @@ const VectorStoreInfoView: React.FC<VectorStoreInfoViewProps> = ({
)}
</FormField>
<Text className="text-sm text-muted-foreground">
<p className="text-sm text-muted-foreground">
Either select existing credentials OR enter provider credentials below
</Text>
</p>
<FormField control={form.control} name="litellm_credential_name" label="Existing Credentials">
{({
@ -352,37 +359,39 @@ const VectorStoreInfoView: React.FC<VectorStoreInfoViewProps> = ({
</FieldGroup>
<div className="mt-6 flex justify-end space-x-2">
<ShadcnButton type="button" variant="outline" onClick={() => setIsEditing(false)}>
<Button type="button" variant="outline" onClick={() => setIsEditing(false)}>
Cancel
</ShadcnButton>
<ShadcnButton type="submit">Save Changes</ShadcnButton>
</Button>
<Button type="submit">Save Changes</Button>
</div>
</form>
</TooltipProvider>
</Card>
</CardContent>
</Card>
</div>
) : (
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-medium">Vector Store Details</h3>
{is_admin && <Button onClick={startEditing}>Edit Vector Store</Button>}
</div>
) : (
<div>
<div className="flex justify-between items-center mb-4">
<Title>Vector Store Details</Title>
{is_admin && <Button onClick={startEditing}>Edit Vector Store</Button>}
</div>
<Card>
<Card>
<CardContent>
<div className="space-y-4">
<div>
<Text className="font-medium">ID</Text>
<Text>{vectorStoreDetails.vector_store_id}</Text>
<p className="font-medium">ID</p>
<p>{vectorStoreDetails.vector_store_id}</p>
</div>
<div>
<Text className="font-medium">Name</Text>
<Text>{vectorStoreDetails.vector_store_name || "-"}</Text>
<p className="font-medium">Name</p>
<p>{vectorStoreDetails.vector_store_name || "-"}</p>
</div>
<div>
<Text className="font-medium">Description</Text>
<Text>{vectorStoreDetails.vector_store_description || "-"}</Text>
<p className="font-medium">Description</p>
<p>{vectorStoreDetails.vector_store_description || "-"}</p>
</div>
<div>
<Text className="font-medium">Provider</Text>
<p className="font-medium">Provider</p>
<div className="flex items-center space-x-2 mt-1">
{(() => {
const provider = vectorStoreDetails.custom_llm_provider || "bedrock";
@ -391,41 +400,41 @@ const VectorStoreInfoView: React.FC<VectorStoreInfoViewProps> = ({
return (
<>
<Logo src={logo} label={displayName} className="w-5 h-5" />
<Badge color="blue">{displayName}</Badge>
<Badge variant="secondary">{displayName}</Badge>
</>
);
})()}
</div>
</div>
<div>
<Text className="font-medium">Metadata</Text>
<p className="font-medium">Metadata</p>
<div className="bg-muted p-3 rounded-sm mt-2 font-mono text-xs overflow-auto max-h-48">
<pre>{metadataString}</pre>
</div>
</div>
<div>
<Text className="font-medium">Created</Text>
<Text>
<p className="font-medium">Created</p>
<p>
{vectorStoreDetails.created_at ? new Date(vectorStoreDetails.created_at).toLocaleString() : "-"}
</Text>
</p>
</div>
<div>
<Text className="font-medium">Last Updated</Text>
<Text>
<p className="font-medium">Last Updated</p>
<p>
{vectorStoreDetails.updated_at ? new Date(vectorStoreDetails.updated_at).toLocaleString() : "-"}
</Text>
</p>
</div>
</div>
</Card>
</div>
)}
</TabPanel>
</CardContent>
</Card>
</div>
)}
</TabsContent>
<TabPanel>
<VectorStoreTester vectorStoreId={vectorStoreDetails.vector_store_id} accessToken={accessToken || ""} />
</TabPanel>
</TabPanels>
</TabGroup>
<TabsContent value="test" keepMounted>
<VectorStoreTester vectorStoreId={vectorStoreDetails.vector_store_id} accessToken={accessToken || ""} />
</TabsContent>
</Tabs>
</div>
);
};