This commit is contained in:
seb
2026-10-07 11:40:55 +02:00
parent a2c18841c1
commit 22115cb583
6 changed files with 446 additions and 47 deletions

View File

@@ -3,6 +3,7 @@ import Alert from '@mui/material/Alert';
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import IconButton from '@mui/material/IconButton';
import MenuItem from '@mui/material/MenuItem';
import Stack from '@mui/material/Stack';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
@@ -28,7 +29,7 @@ const SETTABLE_KEYS = [
'keys.tavily',
'keys.openrouter',
'keys.serper',
'default-model',
'modelslugimage',
'systemprompt-prefix',
'systemprompt-suffix',
];
@@ -38,11 +39,17 @@ const KEY_LABELS = {
'keys.tavily': 'Tavily API-Key',
'keys.openrouter': 'OpenRouter API-Key',
'keys.serper': 'Serper API-Key',
'default-model': 'Modellslug',
'modelslugimage': 'Modellslug Bild',
'systemprompt-prefix': 'Prä Prompt',
'systemprompt-suffix': 'Post Prompt',
};
export const DEFAULT_SETTINGS = {
modelslugimage: 'meta/muse-image',
};
const ENUM_OPTIONS = {};
const MULTILINE_KEYS = new Set([
'systemprompt-prefix',
'systemprompt-suffix',
@@ -80,12 +87,17 @@ export default class SettingsKeysPanel extends Component {
try {
const data = await settingApi.list();
requireObject(data, 'GET /api/company/setting');
const settings = requireArray(data.settings, 'settings');
const fetched = requireArray(data.settings, 'settings');
const byKey = new Map(fetched.map((s) => [s.key, s]));
const settings = SETTABLE_KEYS.map((key) => {
if (byKey.has(key)) return byKey.get(key);
return { key, value: DEFAULT_SETTINGS[key] ?? null };
});
const drafts = Object.fromEntries(
settings.map((s) => [
s.key,
{
value: s.value ?? '',
value: s.value ?? (DEFAULT_SETTINGS[s.key] ?? ''),
busy: false,
saveError: null,
},
@@ -117,14 +129,8 @@ export default class SettingsKeysPanel extends Component {
const row = this.state.settings.find((s) => s.key === key);
if (!row) return;
const body = {};
if (draft.value !== (row.value ?? '')) {
body.value = draft.value;
}
if (Object.keys(body).length === 0) {
return;
}
const valToSave = draft.value !== '' ? draft.value : (DEFAULT_SETTINGS[key] ?? '');
const body = { value: valToSave };
this.setState((prev) => ({
drafts: {
@@ -146,7 +152,7 @@ export default class SettingsKeysPanel extends Component {
drafts: {
...this.state.drafts,
[key]: {
value: updated.value ?? '',
value: updated.value ?? valToSave,
busy: false,
saveError: null,
},
@@ -198,6 +204,7 @@ export default class SettingsKeysPanel extends Component {
const draft = row ? drafts[row.key] : null;
if (!row || !draft) return null;
const isMultiline = MULTILINE_KEYS.has(row.key);
const enumOpts = ENUM_OPTIONS[row.key];
return (
<TableRow key={row.key} hover>
<TableCell
@@ -211,23 +218,49 @@ export default class SettingsKeysPanel extends Component {
<Typography variant="body2">{keyLabel(row.key)}</Typography>
</TableCell>
<TableCell sx={{ verticalAlign: 'top' }}>
<TextField
size="small"
fullWidth
multiline={isMultiline}
minRows={isMultiline ? 2 : undefined}
maxRows={isMultiline ? 6 : undefined}
value={draft.value}
disabled={draft.busy}
onChange={(e) => this.handleChange(row.key, 'value', e.target.value)}
onKeyDown={(e) => {
if (!isMultiline && e.key === 'Enter') {
e.preventDefault();
void this.handleSave(row.key);
}
}}
autoComplete="off"
/>
{enumOpts ? (
<TextField
select
size="small"
fullWidth
value={draft.value !== undefined && draft.value !== null ? draft.value : (DEFAULT_SETTINGS[row.key] ?? '')}
disabled={draft.busy}
onChange={(e) => this.handleChange(row.key, 'value', e.target.value)}
slotProps={{ htmlInput: { spellCheck: false } }}
inputProps={{ spellCheck: false }}
>
{enumOpts.map((opt) => {
const val = typeof opt === 'object' ? opt.value : opt;
const lbl = typeof opt === 'object' ? opt.label : opt;
return (
<MenuItem key={val} value={val}>
{lbl}
</MenuItem>
);
})}
</TextField>
) : (
<TextField
size="small"
fullWidth
multiline={isMultiline}
minRows={isMultiline ? 2 : undefined}
maxRows={isMultiline ? 6 : undefined}
placeholder={DEFAULT_SETTINGS[row.key] ?? undefined}
value={draft.value}
disabled={draft.busy}
onChange={(e) => this.handleChange(row.key, 'value', e.target.value)}
onKeyDown={(e) => {
if (!isMultiline && e.key === 'Enter') {
e.preventDefault();
void this.handleSave(row.key);
}
}}
autoComplete="off"
slotProps={{ htmlInput: { spellCheck: false, autoCorrect: 'off', autoCapitalize: 'off' } }}
inputProps={{ spellCheck: false, autoCorrect: 'off', autoCapitalize: 'off' }}
/>
)}
</TableCell>
<TableCell align="right" sx={{ verticalAlign: 'top', pt: 1, width: 60 }}>
<Stack