settings menu reworks (#5864)

This commit is contained in:
Anthony Stirling
2026-03-05 16:20:20 +00:00
committed by GitHub
parent ba2d10a75b
commit 0f7ee5c5b0
18 changed files with 640 additions and 580 deletions
@@ -0,0 +1,42 @@
import { Button, Group, Text } from '@mantine/core';
import { useTranslation } from 'react-i18next';
interface SettingsStickyFooterProps {
isDirty: boolean;
saving: boolean;
loginEnabled: boolean;
onSave: () => void;
onDiscard: () => void;
}
export function SettingsStickyFooter({
isDirty,
saving,
loginEnabled,
onSave,
onDiscard,
}: SettingsStickyFooterProps) {
const { t } = useTranslation();
if (!isDirty || !loginEnabled) {
return null;
}
return (
<div className="settings-sticky-footer">
<Group justify="space-between" w="100%">
<Text size="sm" c="dimmed">
{t('admin.settings.unsavedChanges.hint', 'You have unsaved changes')}
</Text>
<Group gap="sm">
<Button variant="default" onClick={onDiscard} size="sm">
{t('admin.settings.discard', 'Discard')}
</Button>
<Button onClick={onSave} loading={saving} size="sm">
{t('admin.settings.save', 'Save Changes')}
</Button>
</Group>
</Group>
</div>
);
}
@@ -23,7 +23,9 @@ interface ProviderCardProps {
settings?: Record<string, any>;
onSave?: (settings: Record<string, any>) => void;
onDisconnect?: () => void;
onChange?: (settings: Record<string, any>) => void;
disabled?: boolean;
readOnly?: boolean;
}
export default function ProviderCard({
@@ -32,18 +34,19 @@ export default function ProviderCard({
settings = {},
onSave,
onDisconnect,
onChange,
disabled = false,
readOnly = false,
}: ProviderCardProps) {
const { t } = useTranslation();
const [expanded, setExpanded] = useState(false);
const [localSettings, setLocalSettings] = useState<Record<string, any>>(settings);
// Keep local settings in sync with incoming settings (values loaded from settings.yml)
// Update whenever parent settings change, whether expanded or not (important for Discard to work)
useEffect(() => {
if (!expanded) {
setLocalSettings(settings);
}
}, [settings, expanded]);
setLocalSettings(settings);
}, [settings]);
// Initialize local settings with defaults when opening an unconfigured provider
const handleConnectToggle = () => {
@@ -63,7 +66,12 @@ export default function ProviderCard({
const handleFieldChange = (key: string, value: any) => {
if (disabled) return; // Block changes when disabled
setLocalSettings((prev) => ({ ...prev, [key]: value }));
const updated = { ...localSettings, [key]: value };
setLocalSettings(updated);
// Notify parent of changes if onChange callback provided
if (onChange) {
onChange(updated);
}
};
const handleSave = () => {
@@ -225,22 +233,26 @@ export default function ProviderCard({
<Stack gap="md" mt="xs">
{provider.fields.map((field) => renderField(field))}
<Group justify="flex-end" mt="sm">
{onDisconnect && (
<Button
variant="outline"
color="red"
size="sm"
onClick={onDisconnect}
disabled={disabled}
>
{t('admin.settings.connections.disconnect', 'Disconnect')}
</Button>
)}
<Button size="sm" onClick={handleSave} disabled={disabled}>
{t('admin.settings.save', 'Save Changes')}
</Button>
</Group>
{!readOnly && (onSave || onDisconnect) && (
<Group justify="flex-end" mt="sm">
{onDisconnect && (
<Button
variant="outline"
color="red"
size="sm"
onClick={onDisconnect}
disabled={disabled}
>
{t('admin.settings.connections.disconnect', 'Disconnect')}
</Button>
)}
{onSave && (
<Button size="sm" onClick={handleSave} disabled={disabled}>
{t('admin.settings.save', 'Save Changes')}
</Button>
)}
</Group>
)}
</Stack>
</Collapse>
</Stack>