feat: improve API keys UX and navigation clarity
- Reorganize sidebar into sections with headers (main items, Settings) - Rename "API Keys" to "AI Provider Keys" with clear description - Rename "TPMJS API Keys" to "Platform API Keys" - Add info banners explaining the difference: - AI Provider Keys: credentials for OpenAI, Anthropic, etc. - Platform API Keys: authentication for TPMJS platform (tpmjs_sk_...) - Update empty states with clearer messaging - Use distinct icons (puzzle for AI providers, key for platform)
This commit is contained in:
parent
9e1fe2be43
commit
f8bfbc3ed2
3 changed files with 90 additions and 49 deletions
|
|
@ -144,7 +144,7 @@ export default function ApiKeysPage(): React.ReactElement {
|
|||
|
||||
if (error) {
|
||||
return (
|
||||
<DashboardLayout title="API Keys">
|
||||
<DashboardLayout title="AI Provider Keys">
|
||||
<div className="text-center py-16">
|
||||
<Icon icon="alertCircle" size="lg" className="mx-auto text-error mb-4" />
|
||||
<h2 className="text-lg font-medium text-foreground mb-2">Error</h2>
|
||||
|
|
@ -157,8 +157,8 @@ export default function ApiKeysPage(): React.ReactElement {
|
|||
|
||||
return (
|
||||
<DashboardLayout
|
||||
title="API Keys"
|
||||
subtitle={keys.length > 0 ? `${keys.length} key${keys.length !== 1 ? 's' : ''}` : undefined}
|
||||
title="AI Provider Keys"
|
||||
subtitle="Credentials for OpenAI, Anthropic, Google, and other AI services"
|
||||
actions={
|
||||
!showAddForm &&
|
||||
!showImport && (
|
||||
|
|
@ -175,6 +175,22 @@ export default function ApiKeysPage(): React.ReactElement {
|
|||
)
|
||||
}
|
||||
>
|
||||
{/* Info banner */}
|
||||
<div className="bg-blue-500/5 border border-blue-500/20 rounded-lg p-4 mb-6">
|
||||
<div className="flex gap-3">
|
||||
<Icon icon="info" size="sm" className="text-blue-500 flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm">
|
||||
<p className="text-foreground font-medium mb-1">
|
||||
These are your AI service credentials
|
||||
</p>
|
||||
<p className="text-foreground-secondary">
|
||||
Add API keys from providers like OpenAI, Anthropic, or Google. Your agents use these
|
||||
keys to make AI calls. Keys are encrypted and never exposed to clients.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Import from .env */}
|
||||
{showImport && (
|
||||
<div className="bg-surface border border-border rounded-lg p-6 mb-6">
|
||||
|
|
@ -272,16 +288,16 @@ ANOTHER_SECRET=xyz789"
|
|||
<TableEmpty
|
||||
colSpan={4}
|
||||
icon={
|
||||
<div className="w-16 h-16 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
<Icon icon="key" size="lg" className="text-primary" />
|
||||
<div className="w-16 h-16 rounded-full bg-blue-500/10 flex items-center justify-center">
|
||||
<Icon icon="puzzle" size="lg" className="text-blue-500" />
|
||||
</div>
|
||||
}
|
||||
title="No API keys yet"
|
||||
description="Add your AI provider API keys to enable agent functionality. Keys are encrypted and stored securely."
|
||||
title="No AI provider keys yet"
|
||||
description="Add your OpenAI, Anthropic, or other AI provider keys so your agents can make AI calls."
|
||||
action={
|
||||
<Button onClick={() => setShowAddForm(true)}>
|
||||
<Icon icon="plus" size="sm" className="mr-2" />
|
||||
Add Your First Key
|
||||
Add Provider Key
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ export default function TpmjsApiKeysPage(): React.ReactElement {
|
|||
|
||||
if (error) {
|
||||
return (
|
||||
<DashboardLayout title="TPMJS API Keys">
|
||||
<DashboardLayout title="Platform API Keys">
|
||||
<div className="text-center py-16">
|
||||
<Icon icon="alertCircle" size="lg" className="mx-auto text-error mb-4" />
|
||||
<h2 className="text-lg font-medium text-foreground mb-2">Error</h2>
|
||||
|
|
@ -212,8 +212,8 @@ export default function TpmjsApiKeysPage(): React.ReactElement {
|
|||
|
||||
return (
|
||||
<DashboardLayout
|
||||
title="TPMJS API Keys"
|
||||
subtitle={keys.length > 0 ? `${keys.length} key${keys.length !== 1 ? 's' : ''}` : undefined}
|
||||
title="Platform API Keys"
|
||||
subtitle="Authentication keys for accessing TPMJS programmatically"
|
||||
actions={
|
||||
!showCreateForm &&
|
||||
!newlyCreatedKey && (
|
||||
|
|
@ -230,11 +230,11 @@ export default function TpmjsApiKeysPage(): React.ReactElement {
|
|||
<Icon icon="info" size="sm" className="text-primary flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm">
|
||||
<p className="text-foreground font-medium mb-1">
|
||||
Use API keys to access TPMJS programmatically
|
||||
These keys authenticate you to the TPMJS platform
|
||||
</p>
|
||||
<p className="text-foreground-secondary">
|
||||
API keys allow you to call MCP endpoints, chat with agents, and connect via the bridge
|
||||
without a browser session. Keep your keys secure and never share them.
|
||||
Use platform API keys (tpmjs_sk_...) to access MCP servers, chat with agents from scripts,
|
||||
or connect via the bridge. These are different from AI provider keys which power the AI calls themselves.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -342,12 +342,12 @@ export default function TpmjsApiKeysPage(): React.ReactElement {
|
|||
<Icon icon="key" size="lg" className="text-primary" />
|
||||
</div>
|
||||
}
|
||||
title="No API keys yet"
|
||||
description="Create an API key to access TPMJS programmatically from your applications, scripts, or CI/CD pipelines."
|
||||
title="No platform API keys yet"
|
||||
description="Create a key to access your TPMJS collections and agents programmatically from scripts or CI/CD pipelines."
|
||||
action={
|
||||
<Button onClick={() => setShowCreateForm(true)}>
|
||||
<Icon icon="plus" size="sm" className="mr-2" />
|
||||
Create Your First Key
|
||||
Create Platform Key
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -15,15 +15,29 @@ interface NavItem {
|
|||
badge?: number;
|
||||
}
|
||||
|
||||
const navItems: NavItem[] = [
|
||||
{ href: '/dashboard', label: 'Overview', icon: 'home' },
|
||||
{ href: '/dashboard/agents', label: 'Agents', icon: 'terminal' },
|
||||
{ href: '/dashboard/collections', label: 'Collections', icon: 'folder' },
|
||||
{ href: '/dashboard/usage', label: 'Usage', icon: 'globe' },
|
||||
{ href: '/dashboard/settings/profile', label: 'Profile', icon: 'user' },
|
||||
{ href: '/dashboard/settings/tpmjs-api-keys', label: 'TPMJS API Keys', icon: 'key' },
|
||||
{ href: '/dashboard/settings/api-keys', label: 'Provider Keys', icon: 'edit' },
|
||||
{ href: '/dashboard/settings/bridge', label: 'Bridge', icon: 'link' },
|
||||
interface NavSection {
|
||||
title?: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
const navSections: NavSection[] = [
|
||||
{
|
||||
items: [
|
||||
{ href: '/dashboard', label: 'Overview', icon: 'home' },
|
||||
{ href: '/dashboard/agents', label: 'Agents', icon: 'terminal' },
|
||||
{ href: '/dashboard/collections', label: 'Collections', icon: 'folder' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
items: [
|
||||
{ href: '/dashboard/settings/profile', label: 'Profile', icon: 'user' },
|
||||
{ href: '/dashboard/settings/api-keys', label: 'AI Provider Keys', icon: 'puzzle' },
|
||||
{ href: '/dashboard/settings/tpmjs-api-keys', label: 'Platform API Keys', icon: 'key' },
|
||||
{ href: '/dashboard/settings/bridge', label: 'Bridge', icon: 'link' },
|
||||
{ href: '/dashboard/usage', label: 'Usage', icon: 'globe' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const likesNavItems: NavItem[] = [
|
||||
|
|
@ -152,32 +166,43 @@ export function DashboardLayout({
|
|||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
|
||||
`}
|
||||
>
|
||||
<nav className="p-4 space-y-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors
|
||||
${
|
||||
isActive(item.href)
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-foreground-secondary hover:text-foreground hover:bg-surface'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Icon icon={item.icon} size="sm" />
|
||||
<span>{item.label}</span>
|
||||
{item.badge !== undefined && item.badge > 0 && (
|
||||
<span className="ml-auto text-xs bg-surface-secondary px-1.5 py-0.5 rounded-full">
|
||||
{item.badge}
|
||||
</span>
|
||||
<nav className="p-4 space-y-6 overflow-y-auto h-[calc(100%-80px)]">
|
||||
{navSections.map((section, sectionIdx) => (
|
||||
<div key={section.title || `section-${sectionIdx}`}>
|
||||
{section.title && (
|
||||
<h3 className="px-3 mb-2 text-xs font-semibold text-foreground-tertiary uppercase tracking-wider">
|
||||
{section.title}
|
||||
</h3>
|
||||
)}
|
||||
</Link>
|
||||
<div className="space-y-1">
|
||||
{section.items.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`
|
||||
flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors
|
||||
${
|
||||
isActive(item.href)
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-foreground-secondary hover:text-foreground hover:bg-surface'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Icon icon={item.icon} size="sm" />
|
||||
<span>{item.label}</span>
|
||||
{item.badge !== undefined && item.badge > 0 && (
|
||||
<span className="ml-auto text-xs bg-surface-secondary px-1.5 py-0.5 rounded-full">
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Likes Section - Collapsible */}
|
||||
<div className="pt-2 mt-2 border-t border-border">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLikesExpanded(!likesExpanded)}
|
||||
|
|
@ -224,7 +249,7 @@ export function DashboardLayout({
|
|||
</nav>
|
||||
|
||||
{/* User section at bottom */}
|
||||
<div className="absolute bottom-0 left-0 right-0 p-4 border-t border-border">
|
||||
<div className="absolute bottom-0 left-0 right-0 p-4 border-t border-border bg-background">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
<Icon icon="user" size="sm" className="text-primary" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue