improve: redesign SensitiveKeysPage with better layout and information hierarchy
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user