improve: redesign SensitiveKeysPage with better layout and information hierarchy
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m29s
CI / docker (push) Successful in 1m11s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 37s

Show agent built-in defaults as reference Badge pills, separate editable keys
section with count badge, amber-highlighted push toggle, right-aligned save
button. Fix info text: keys add to defaults, not replace. Add ClaimMapping
controller to CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-14 19:03:45 +02:00
parent 9ac8e3604c
commit 92d7f5809b
3 changed files with 125 additions and 35 deletions

View File

@@ -1,27 +1,71 @@
.page {
display: flex;
flex-direction: column;
gap: var(--space-lg);
gap: var(--space-md);
max-width: 720px;
}
.infoBanner {
font-size: var(--font-size-sm);
.sectionTitle {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: var(--text-secondary);
background: var(--surface-secondary);
padding: var(--space-md);
border-radius: var(--radius-md);
line-height: 1.5;
letter-spacing: 0.01em;
}
.defaultsList {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
}
.hint {
display: flex;
align-items: flex-start;
gap: 6px;
font-size: 12px;
color: var(--text-muted);
line-height: 1.4;
padding-top: 4px;
border-top: 1px solid var(--border-subtle);
}
.hint svg {
flex-shrink: 0;
margin-top: 1px;
}
.keyCount {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 9px;
background: var(--bg-hover);
color: var(--text-muted);
font-size: 12px;
font-weight: 500;
}
.pillList {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
gap: 6px;
min-height: 36px;
align-items: center;
}
.emptyState {
color: var(--text-tertiary);
font-size: 12px;
font-style: italic;
}
.inputRow {
display: flex;
gap: var(--space-sm);
@@ -32,8 +76,20 @@
flex: 1;
}
.footer {
display: flex;
gap: var(--space-sm);
align-items: center;
.inputHint {
font-size: 12px;
color: var(--text-muted);
line-height: 1.4;
}
.pushToggle {
background: color-mix(in srgb, var(--amber) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--amber) 20%, transparent);
border-radius: var(--radius-md);
padding: 8px 12px;
}
.saveRow {
display: flex;
justify-content: flex-end;
}

View File

@@ -1,10 +1,16 @@
import { useState, useEffect, useCallback } from 'react';
import { Button, SectionHeader, Tag, Input, Toggle, Label, useToast } from '@cameleer/design-system';
import { Shield, Info } from 'lucide-react';
import { Button, SectionHeader, Tag, Badge, Input, Toggle, useToast } from '@cameleer/design-system';
import { PageLoader } from '../../components/PageLoader';
import { useSensitiveKeys, useUpdateSensitiveKeys } from '../../api/queries/admin/sensitive-keys';
import styles from './SensitiveKeysPage.module.css';
import sectionStyles from '../../styles/section-card.module.css';
const AGENT_DEFAULTS = [
'Authorization', 'Cookie', 'Set-Cookie',
'X-API-Key', 'X-Auth-Token', 'Proxy-Authorization',
];
export default function SensitiveKeysPage() {
const { data, isLoading } = useSensitiveKeys();
const updateKeys = useUpdateSensitiveKeys();
@@ -65,24 +71,46 @@ export default function SensitiveKeysPage() {
if (isLoading) return <PageLoader />;
const isConfigured = draft.length > 0;
return (
<div className={styles.page}>
<SectionHeader>Sensitive Keys</SectionHeader>
<div className={styles.infoBanner}>
Agents ship with built-in defaults (Authorization, Cookie, Set-Cookie, X-API-Key,
X-Auth-Token, Proxy-Authorization). Configuring keys here replaces agent defaults for
all applications. Leave unconfigured to use agent defaults.
</div>
{/* Agent defaults reference */}
<section className={sectionStyles.section}>
<div className={styles.sectionTitle}>
<Shield size={14} />
<span>Agent built-in defaults</span>
</div>
<div className={styles.defaultsList}>
{AGENT_DEFAULTS.map((key) => (
<Badge key={key} label={key} variant="outlined" />
))}
</div>
<div className={styles.hint}>
<Info size={12} />
<span>
{isConfigured
? 'Your keys below are added to these defaults for all applications.'
: 'These headers are masked automatically. Add keys below to extend the list.'}
</span>
</div>
</section>
{/* Editable keys */}
<section className={sectionStyles.section}>
<div className={styles.sectionTitle}>
<span>Global sensitive keys</span>
{isConfigured && <span className={styles.keyCount}>{draft.length}</span>}
</div>
<div className={sectionStyles.section}>
<Label>Global sensitive keys</Label>
<div className={styles.pillList}>
{draft.map((key, i) => (
<Tag key={`${key}-${i}`} label={key} onRemove={() => removeKey(i)} />
))}
{draft.length === 0 && (
<span style={{ color: 'var(--text-tertiary)', fontSize: 'var(--font-size-sm)' }}>
{!isConfigured && (
<span className={styles.emptyState}>
No keys configured agents use built-in defaults
</span>
)}
@@ -99,18 +127,23 @@ export default function SensitiveKeysPage() {
Add
</Button>
</div>
</div>
<div className={styles.inputHint}>
Supports exact header names and glob patterns. Per-app additions can be configured on each application's settings page.
</div>
<div className={styles.footer}>
<Toggle
checked={pushToAgents}
onChange={(e) => setPushToAgents((e.target as HTMLInputElement).checked)}
label="Push to all connected agents immediately"
/>
<Button variant="primary" onClick={handleSave} loading={updateKeys.isPending}>
Save
</Button>
</div>
<div className={styles.pushToggle}>
<Toggle
checked={pushToAgents}
onChange={(e) => setPushToAgents((e.target as HTMLInputElement).checked)}
label="Push to all connected agents immediately"
/>
</div>
<div className={styles.saveRow}>
<Button variant="primary" onClick={handleSave} loading={updateKeys.isPending}>
Save
</Button>
</div>
</section>
</div>
);
}