Appearance
Admin UI Components API
This document describes the reusable UI components available for building admin interfaces in Veriprompt.
Components Overview
| Component | Import Path | Purpose |
|---|---|---|
Breadcrumbs | @/components/ui/breadcrumbs | Navigation breadcrumbs |
ExportButton | @/components/ui/export-button | Data export functionality |
CloneButton | @/components/ui/clone-button | Item duplication |
Utilities
| Utility | Import Path | Purpose |
|---|---|---|
cross-links | @/lib/cross-links | Related page navigation |
Breadcrumbs Component
Displays hierarchical navigation showing the user's current location.
Import
tsx
import { Breadcrumbs } from '@/components/ui/breadcrumbs';Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | BreadcrumbItem[] | [] | Array of breadcrumb items |
showHome | boolean | true | Show home icon as first item |
homeHref | string | /dashboard | URL for home link |
separator | ReactNode | <ChevronRight /> | Custom separator element |
className | string | - | Additional CSS classes |
autoGenerate | boolean | false | Auto-generate from URL path |
BreadcrumbItem Interface
typescript
interface BreadcrumbItem {
label: string; // Display text
href?: string; // Link URL (optional for last item)
icon?: ReactNode; // Optional icon
}Usage Examples
Manual Items:
tsx
<Breadcrumbs
items={[
{ label: 'Admin', href: '/admin' },
{ label: 'Settings', href: '/admin/settings' },
{ label: 'API Keys' } // Current page (no href)
]}
/>Auto-Generated:
tsx
<Breadcrumbs autoGenerate />
// URL: /admin/routing-policies
// Output: Home > Admin > Routing PoliciesCustom Separator:
tsx
<Breadcrumbs
items={items}
separator={<span className="mx-2">/</span>}
/>Path Labels
The component includes predefined labels for common paths:
| Path Segment | Label |
|---|---|
admin | Admin |
routing-policies | Routing Policies |
dynamic-ranking | Dynamic Ranking |
throttling | Throttling |
api-console | API Console |
ai-providers | AI Providers |
analytics | Analytics |
ExportButton Component
Button component for exporting data to files.
Import
tsx
import { ExportButton } from '@/components/ui/export-button';Props
| Prop | Type | Default | Description |
|---|---|---|---|
data | unknown | Required | Data to export |
filename | string | 'export' | Filename without extension |
format | 'json' | 'csv' | 'txt' | 'json' | Export format |
onExport | (data, format) => void | - | Custom export handler |
onExportSuccess | () => void | - | Success callback |
onExportError | (error) => void | - | Error callback |
showIcon | boolean | true | Show format icon |
label | string | 'Export {FORMAT}' | Button label |
successLabel | string | 'Exported!' | Success state label |
loadingLabel | string | 'Exporting...' | Loading state label |
Inherits all ButtonProps except onClick and onError.
Usage Examples
Basic JSON Export:
tsx
<ExportButton
data={apiResponse}
filename="routing-policies"
format="json"
/>CSV Export with Callbacks:
tsx
<ExportButton
data={analyticsData}
filename="monthly-report"
format="csv"
onExportSuccess={() => toast.success('Report exported!')}
onExportError={(err) => toast.error(err.message)}
/>Custom Export Handler:
tsx
<ExportButton
data={encryptedData}
format="json"
onExport={(data, format) => {
const decrypted = decrypt(data);
downloadFile(decrypted, format);
}}
/>Format Handling
| Format | MIME Type | Conversion |
|---|---|---|
json | application/json | JSON.stringify(data, null, 2) |
csv | text/csv | Converts array of objects to CSV |
txt | text/plain | String or JSON stringified |
CloneButton Component
Button component for duplicating items.
Import
tsx
import { CloneButton } from '@/components/ui/clone-button';Props
| Prop | Type | Default | Description |
|---|---|---|---|
onClone | () => Promise<void> | void | Required | Clone handler function |
label | string | 'Clone' | Button label |
successLabel | string | 'Cloned!' | Success state label |
loadingLabel | string | 'Cloning...' | Loading state label |
showIcon | boolean | true | Show copy icon |
confirmMessage | string | 'Are you sure...' | Confirmation dialog text |
requireConfirm | boolean | false | Show confirmation dialog |
Inherits all ButtonProps except onClick.
Usage Examples
Simple Clone:
tsx
<CloneButton
onClone={async () => {
await api.clonePolicy(policyId);
refreshList();
}}
/>With Confirmation:
tsx
<CloneButton
onClone={handleClone}
requireConfirm={true}
confirmMessage="Clone this routing policy? A new policy will be created."
/>Custom Labels:
tsx
<CloneButton
onClone={handleDuplicate}
label="Duplicate"
successLabel="Duplicated!"
loadingLabel="Duplicating..."
/>Cross-Links Utility
Utility functions for managing page relationships and navigation.
Import
typescript
import {
getRelatedPages,
getAllCrossLinks,
buildCrossLink,
getPageMetadata,
isValidCrossLink
} from '@/lib/cross-links';Functions
getRelatedPages
Returns related pages for the current path.
typescript
function getRelatedPages(currentPath: string): CrossLinkDefinition[]Example:
typescript
const related = getRelatedPages('/admin/routing-policies');
// Returns:
// [
// { label: 'Dynamic Ranking', href: '/admin/dynamic-ranking', ... },
// { label: 'AI Providers', href: '/admin/ai-providers', ... },
// { label: 'Throttling', href: '/admin/throttling', ... }
// ]getAllCrossLinks
Returns all cross-links grouped by category.
typescript
function getAllCrossLinks(): CrossLinkGroup[]Example:
typescript
const groups = getAllCrossLinks();
// Returns:
// [
// { title: 'Routing & Optimization', links: [...] },
// { title: 'Provider Management', links: [...] },
// { title: 'Testing & Development', links: [...] },
// { title: 'Monitoring & Analytics', links: [...] }
// ]buildCrossLink
Builds a URL with optional query parameters.
typescript
function buildCrossLink(
basePath: string,
params?: Record<string, string | number | boolean | undefined>
): stringExample:
typescript
const url = buildCrossLink('/admin/analytics', {
startDate: '2024-01-01',
provider: 'openai'
});
// Returns: '/admin/analytics?startDate=2024-01-01&provider=openai'getPageMetadata
Returns metadata for a page path.
typescript
function getPageMetadata(path: string): {
label: string;
description: string;
icon?: string;
} | nullisValidCrossLink
Checks if a path is a valid cross-link destination.
typescript
function isValidCrossLink(path: string): booleanInterfaces
typescript
interface CrossLinkDefinition {
label: string;
href: string;
description?: string;
icon?: string;
}
interface CrossLinkGroup {
title: string;
links: CrossLinkDefinition[];
}Usage in Components
tsx
import { getRelatedPages } from '@/lib/cross-links';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
export function RelatedPages() {
const pathname = usePathname();
const relatedPages = getRelatedPages(pathname);
if (relatedPages.length === 0) return null;
return (
<div className="flex gap-2">
<span>Related:</span>
{relatedPages.map(page => (
<Link key={page.href} href={page.href}>
{page.label}
</Link>
))}
</div>
);
}Complete Example
Here's a complete example combining all components:
tsx
'use client';
import { useState } from 'react';
import { Breadcrumbs } from '@/components/ui/breadcrumbs';
import { ExportButton } from '@/components/ui/export-button';
import { CloneButton } from '@/components/ui/clone-button';
import { getRelatedPages } from '@/lib/cross-links';
import Link from 'next/link';
interface Policy {
id: string;
name: string;
weights: Record<string, number>;
}
export default function PoliciesPage() {
const [policies, setPolicies] = useState<Policy[]>([]);
const relatedPages = getRelatedPages('/admin/routing-policies');
const handleClone = async (policy: Policy) => {
const cloned = { ...policy, id: crypto.randomUUID(), name: `${policy.name} (Copy)` };
setPolicies([...policies, cloned]);
};
return (
<div>
{/* Breadcrumbs */}
<Breadcrumbs
items={[
{ label: 'Admin', href: '/admin' },
{ label: 'Routing Policies' }
]}
/>
{/* Related Pages */}
<div className="flex gap-2 text-sm">
<span>Related:</span>
{relatedPages.map(page => (
<Link key={page.href} href={page.href} className="text-blue-600">
{page.label}
</Link>
))}
</div>
{/* Export All */}
<ExportButton
data={policies}
filename="routing-policies"
format="json"
/>
{/* Policy List */}
{policies.map(policy => (
<div key={policy.id} className="flex items-center gap-2">
<span>{policy.name}</span>
<CloneButton
onClone={() => handleClone(policy)}
label="Clone"
size="sm"
/>
<ExportButton
data={policy}
filename={`policy-${policy.id}`}
format="json"
size="sm"
/>
</div>
))}
</div>
);
}