feat: add default roles and ConfirmDialog to OIDC config

Adds a Default Roles section with Tag components for viewing/removing roles and an Input+Button for adding new ones. Replaces the plain delete button with a ConfirmDialog requiring typed confirmation. Introduces OidcConfigPage.module.css for CSS module layout classes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-23 18:25:14 +01:00
parent a86f56f588
commit 8e27f45a2b
4 changed files with 188 additions and 39 deletions

View File

@@ -0,0 +1,28 @@
.section {
display: grid;
gap: 0.5rem;
}
.section h3 {
font-size: 0.875rem;
font-weight: 600;
margin: 0;
}
.tagRow {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
min-height: 2rem;
align-items: center;
}
.addRow {
display: flex;
gap: 0.5rem;
align-items: center;
}
.addRow input {
flex: 1;
}

View File

@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { Button, Input, Toggle, FormField, Card, Alert, SectionHeader } from '@cameleer/design-system';
import { Button, Input, Toggle, FormField, Card, Alert, SectionHeader, Tag, ConfirmDialog } from '@cameleer/design-system';
import { adminFetch } from '../../api/queries/admin/admin-api';
import styles from './OidcConfigPage.module.css';
interface OidcConfig {
enabled: boolean;
@@ -18,6 +19,8 @@ export default function OidcConfigPage() {
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const [newRole, setNewRole] = useState('');
const [deleteOpen, setDeleteOpen] = useState(false);
useEffect(() => {
adminFetch<OidcConfig>('/oidc')
@@ -64,15 +67,44 @@ export default function OidcConfigPage() {
<FormField label="Display Name Claim"><Input value={config.displayNameClaim} onChange={(e) => setConfig({ ...config, displayNameClaim: e.target.value })} /></FormField>
<Toggle checked={config.autoSignup} onChange={(e) => setConfig({ ...config, autoSignup: e.target.checked })} label="Auto Signup" />
<div className={styles.section}>
<h3>Default Roles</h3>
<div className={styles.tagRow}>
{(config.defaultRoles || []).map(role => (
<Tag key={role} label={role} onRemove={() => {
setConfig(prev => ({ ...prev!, defaultRoles: prev!.defaultRoles.filter(r => r !== role) }));
}} />
))}
</div>
<div className={styles.addRow}>
<Input placeholder="Add role..." value={newRole} onChange={e => setNewRole(e.target.value)} />
<Button onClick={() => {
if (newRole.trim() && !config.defaultRoles?.includes(newRole.trim())) {
setConfig(prev => ({ ...prev!, defaultRoles: [...(prev!.defaultRoles || []), newRole.trim()] }));
setNewRole('');
}
}}>Add</Button>
</div>
</div>
<div style={{ display: 'flex', gap: '0.75rem' }}>
<Button variant="primary" onClick={handleSave} disabled={saving}>{saving ? 'Saving...' : 'Save'}</Button>
<Button variant="danger" onClick={handleDelete}>Remove Config</Button>
<Button variant="danger" onClick={() => setDeleteOpen(true)}>Delete Configuration</Button>
</div>
{error && <Alert variant="error">{error}</Alert>}
{success && <Alert variant="success">Configuration saved</Alert>}
</div>
</Card>
<ConfirmDialog
open={deleteOpen}
onClose={() => setDeleteOpen(false)}
onConfirm={handleDelete}
title="Delete OIDC Configuration"
message="Delete OIDC configuration? All OIDC users will lose access."
confirmText="DELETE"
/>
</div>
);
}