mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-08-28 19:46:41 +08:00
feat: update SystemSettingsPage to remove AuthSettingsTab and enhance AppSettingsTab with registration settings
This commit is contained in:
@@ -2,21 +2,20 @@ import { Alert, message, Tabs, Space } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import PageCard from '../../components/PageCard';
|
||||
import { getAllConfig, setConfig } from '../../api/config';
|
||||
import { AppstoreOutlined, RobotOutlined, DatabaseOutlined, SkinOutlined, MailOutlined, CloudSyncOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { AppstoreOutlined, RobotOutlined, DatabaseOutlined, SkinOutlined, MailOutlined, CloudSyncOutlined } from '@ant-design/icons';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import '../../styles/settings-tabs.css';
|
||||
import { useI18n } from '../../i18n';
|
||||
import AppearanceSettingsTab from './components/AppearanceSettingsTab';
|
||||
import AppSettingsTab from './components/AppSettingsTab';
|
||||
import AuthSettingsTab from './components/AuthSettingsTab';
|
||||
import AiSettingsTab from './components/AiSettingsTab';
|
||||
import VectorDbSettingsTab from './components/VectorDbSettingsTab';
|
||||
import EmailSettingsTab from './components/EmailSettingsTab';
|
||||
import ProtocolMappingsTab from './components/ProtocolMappingsTab';
|
||||
|
||||
type TabKey = 'appearance' | 'app' | 'auth' | 'email' | 'ai' | 'vector-db' | 'mappings';
|
||||
type TabKey = 'appearance' | 'app' | 'email' | 'ai' | 'vector-db' | 'mappings';
|
||||
|
||||
const TAB_KEYS: TabKey[] = ['appearance', 'app', 'auth', 'email', 'ai', 'vector-db', 'mappings'];
|
||||
const TAB_KEYS: TabKey[] = ['appearance', 'app', 'email', 'ai', 'vector-db', 'mappings'];
|
||||
const DEFAULT_TAB: TabKey = 'appearance';
|
||||
|
||||
const isValidTab = (key?: string): key is TabKey => !!key && (TAB_KEYS as string[]).includes(key);
|
||||
@@ -169,22 +168,6 @@ export default function SystemSettingsPage({ tabKey, onTabNavigate }: SystemSett
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'auth',
|
||||
label: (
|
||||
<span>
|
||||
<UserOutlined style={{ marginRight: 8 }} />
|
||||
{t('Registration Settings')}
|
||||
</span>
|
||||
),
|
||||
children: (
|
||||
<AuthSettingsTab
|
||||
config={config}
|
||||
loading={loading}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
label: (
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Form, Input, Button } from 'antd';
|
||||
import { Alert, Button, Divider, Form, Input, Select, Switch, message } from 'antd';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { rolesApi, type RoleInfo } from '../../../api/roles';
|
||||
import { useI18n } from '../../../i18n';
|
||||
|
||||
interface AppConfigKey {
|
||||
@@ -21,14 +23,56 @@ export default function AppSettingsTab({
|
||||
configKeys,
|
||||
}: AppSettingsTabProps) {
|
||||
const { t } = useI18n();
|
||||
const [rolesLoading, setRolesLoading] = useState(false);
|
||||
const [roles, setRoles] = useState<RoleInfo[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
async function loadRoles() {
|
||||
setRolesLoading(true);
|
||||
try {
|
||||
const list = await rolesApi.list();
|
||||
if (!mounted) return;
|
||||
setRoles(list);
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('Load failed'));
|
||||
} finally {
|
||||
if (mounted) setRolesLoading(false);
|
||||
}
|
||||
}
|
||||
loadRoles();
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [t]);
|
||||
|
||||
const initialValues = useMemo(() => {
|
||||
const allowRegister = (config.AUTH_ALLOW_REGISTER || '').trim().toLowerCase() === 'true';
|
||||
const roleIdRaw = (config.AUTH_DEFAULT_REGISTER_ROLE_ID || '').trim();
|
||||
const roleId = roleIdRaw ? Number(roleIdRaw) : undefined;
|
||||
return {
|
||||
...Object.fromEntries(configKeys.map(({ key, default: def }) => [key, config[key] ?? def ?? ''])),
|
||||
AUTH_ALLOW_REGISTER: allowRegister,
|
||||
AUTH_DEFAULT_REGISTER_ROLE_ID: Number.isFinite(roleId) ? roleId : undefined,
|
||||
};
|
||||
}, [config, configKeys]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
...Object.fromEntries(configKeys.map(({ key, default: def }) => [key, config[key] ?? def ?? ''])),
|
||||
initialValues={initialValues}
|
||||
onFinish={async (vals) => {
|
||||
const payload: Record<string, unknown> = {};
|
||||
for (const { key } of configKeys) {
|
||||
payload[key] = vals[key];
|
||||
}
|
||||
const allow = !!vals.AUTH_ALLOW_REGISTER;
|
||||
payload.AUTH_ALLOW_REGISTER = allow ? 'true' : 'false';
|
||||
if (allow) {
|
||||
payload.AUTH_DEFAULT_REGISTER_ROLE_ID = String(vals.AUTH_DEFAULT_REGISTER_ROLE_ID);
|
||||
}
|
||||
await onSave(payload);
|
||||
}}
|
||||
onFinish={onSave}
|
||||
style={{ marginTop: 24 }}
|
||||
key={JSON.stringify(config)}
|
||||
>
|
||||
@@ -37,6 +81,45 @@ export default function AppSettingsTab({
|
||||
<Input size="large" />
|
||||
</Form.Item>
|
||||
))}
|
||||
|
||||
<Divider orientation="left">{t('Registration Settings')}</Divider>
|
||||
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
message={t('Enabling registration allows new users to sign up and assigns them the default role')}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
name="AUTH_ALLOW_REGISTER"
|
||||
label={t('Enable Registration')}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item dependencies={['AUTH_ALLOW_REGISTER']} noStyle>
|
||||
{({ getFieldValue }) => {
|
||||
const enabled = !!getFieldValue('AUTH_ALLOW_REGISTER');
|
||||
return (
|
||||
<Form.Item
|
||||
name="AUTH_DEFAULT_REGISTER_ROLE_ID"
|
||||
label={t('Default Role for New Registrations')}
|
||||
rules={enabled ? [{ required: true, message: t('Please select default role') }] : []}
|
||||
>
|
||||
<Select
|
||||
size="large"
|
||||
loading={rolesLoading}
|
||||
disabled={!enabled || rolesLoading}
|
||||
placeholder={t('Select roles')}
|
||||
options={roles.map((r) => ({ value: r.id, label: r.name }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" loading={loading} block>
|
||||
{t('Save')}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import { Alert, Button, Form, Select, Switch, message } from 'antd';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { rolesApi, type RoleInfo } from '../../../api/roles';
|
||||
import { useI18n } from '../../../i18n';
|
||||
|
||||
interface AuthSettingsTabProps {
|
||||
config: Record<string, string>;
|
||||
loading: boolean;
|
||||
onSave: (values: Record<string, unknown>) => Promise<void>;
|
||||
}
|
||||
|
||||
export default function AuthSettingsTab({
|
||||
config,
|
||||
loading,
|
||||
onSave,
|
||||
}: AuthSettingsTabProps) {
|
||||
const { t } = useI18n();
|
||||
const [rolesLoading, setRolesLoading] = useState(false);
|
||||
const [roles, setRoles] = useState<RoleInfo[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
async function loadRoles() {
|
||||
setRolesLoading(true);
|
||||
try {
|
||||
const list = await rolesApi.list();
|
||||
if (!mounted) return;
|
||||
setRoles(list);
|
||||
} catch (e: any) {
|
||||
message.error(e.message || t('Load failed'));
|
||||
} finally {
|
||||
if (mounted) setRolesLoading(false);
|
||||
}
|
||||
}
|
||||
loadRoles();
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [t]);
|
||||
|
||||
const initialValues = useMemo(() => {
|
||||
const allowRegister = (config.AUTH_ALLOW_REGISTER || '').trim().toLowerCase() === 'true';
|
||||
const roleIdRaw = (config.AUTH_DEFAULT_REGISTER_ROLE_ID || '').trim();
|
||||
const roleId = roleIdRaw ? Number(roleIdRaw) : undefined;
|
||||
return {
|
||||
AUTH_ALLOW_REGISTER: allowRegister,
|
||||
AUTH_DEFAULT_REGISTER_ROLE_ID: Number.isFinite(roleId) ? roleId : undefined,
|
||||
};
|
||||
}, [config]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
layout="vertical"
|
||||
initialValues={initialValues}
|
||||
onFinish={async (vals) => {
|
||||
const allow = !!vals.AUTH_ALLOW_REGISTER;
|
||||
const payload: Record<string, unknown> = {
|
||||
AUTH_ALLOW_REGISTER: allow ? 'true' : 'false',
|
||||
};
|
||||
if (allow) {
|
||||
payload.AUTH_DEFAULT_REGISTER_ROLE_ID = String(vals.AUTH_DEFAULT_REGISTER_ROLE_ID);
|
||||
}
|
||||
await onSave(payload);
|
||||
}}
|
||||
style={{ marginTop: 24 }}
|
||||
key={'auth-settings-' + (config.AUTH_ALLOW_REGISTER ?? '') + '-' + (config.AUTH_DEFAULT_REGISTER_ROLE_ID ?? '')}
|
||||
>
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
message={t('Enabling registration allows new users to sign up and assigns them the default role')}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
name="AUTH_ALLOW_REGISTER"
|
||||
label={t('Enable Registration')}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item dependencies={['AUTH_ALLOW_REGISTER']} noStyle>
|
||||
{({ getFieldValue }) => {
|
||||
const enabled = !!getFieldValue('AUTH_ALLOW_REGISTER');
|
||||
return (
|
||||
<Form.Item
|
||||
name="AUTH_DEFAULT_REGISTER_ROLE_ID"
|
||||
label={t('Default Role for New Registrations')}
|
||||
rules={enabled ? [{ required: true, message: t('Please select default role') }] : []}
|
||||
>
|
||||
<Select
|
||||
size="large"
|
||||
loading={rolesLoading}
|
||||
disabled={!enabled || rolesLoading}
|
||||
placeholder={t('Select roles')}
|
||||
options={roles.map((r) => ({ value: r.id, label: r.name }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" loading={loading} block>
|
||||
{t('Save')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user