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:
Ajax Davis 2026-01-14 07:18:42 +10:00
parent 9e1fe2be43
commit f8bfbc3ed2
3 changed files with 90 additions and 49 deletions

View file

@ -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>
}
/>

View file

@ -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>
}
/>

View file

@ -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" />