Merge remote-tracking branch 'github/feat/1.1.2/ui' into feat/1.1.2/user-center

This commit is contained in:
LinkinStars 2023-04-17 11:08:54 +08:00
commit 2bd0ef8a24
7 changed files with 112 additions and 26 deletions

View File

@ -1432,6 +1432,13 @@ ui:
title: Membership title: Membership
label: Allow new registrations label: Allow new registrations
text: Turn off to prevent anyone from creating a new account. text: Turn off to prevent anyone from creating a new account.
email_registration:
title: Email registration
label: Allow email registration
text: Turn off to prevent anyone creating new account through email.
allowed_email_domains:
title: Allowed email domains
text: Email domains that users must register accounts with. One domain per line. Ignored when empty.
private: private:
title: Private title: Private
label: Login required label: Login required

View File

@ -390,6 +390,8 @@ export interface AdminSettingsCustom {
export interface AdminSettingsLogin { export interface AdminSettingsLogin {
allow_new_registrations: boolean; allow_new_registrations: boolean;
login_required: boolean; login_required: boolean;
allow_email_registrations: boolean;
allow_email_domains: string[];
} }
/** /**

View File

@ -22,6 +22,17 @@ const Index: FC = () => {
description: t('membership.text'), description: t('membership.text'),
default: false, default: false,
}, },
allow_email_registrations: {
type: 'boolean',
title: t('email_registration.title'),
description: t('email_registration.text'),
default: true,
},
allow_email_domains: {
type: 'string',
title: t('allowed_email_domains.title'),
description: t('allowed_email_domains.text'),
},
login_required: { login_required: {
type: 'boolean', type: 'boolean',
title: t('private.title'), title: t('private.title'),
@ -37,6 +48,15 @@ const Index: FC = () => {
label: t('membership.label'), label: t('membership.label'),
}, },
}, },
allow_email_registrations: {
'ui:widget': 'switch',
'ui:options': {
label: t('email_registration.label'),
},
},
allow_email_domains: {
'ui:widget': 'textarea',
},
login_required: { login_required: {
'ui:widget': 'switch', 'ui:widget': 'switch',
'ui:options': { 'ui:options': {
@ -51,8 +71,20 @@ const Index: FC = () => {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
const allowedEmailDomains: string[] = [];
if (formData.allow_email_domains.value) {
const domainList = formData.allow_email_domains.value.split('\n');
domainList.forEach((li) => {
li = li.trim();
if (li) {
allowedEmailDomains.push(li);
}
});
}
const reqParams: Type.AdminSettingsLogin = { const reqParams: Type.AdminSettingsLogin = {
allow_new_registrations: formData.allow_new_registrations.value, allow_new_registrations: formData.allow_new_registrations.value,
allow_email_registrations: formData.allow_email_registrations.value,
allow_email_domains: allowedEmailDomains,
login_required: formData.login_required.value, login_required: formData.login_required.value,
}; };
@ -78,6 +110,13 @@ const Index: FC = () => {
const formMeta = { ...formData }; const formMeta = { ...formData };
formMeta.allow_new_registrations.value = formMeta.allow_new_registrations.value =
setting.allow_new_registrations; setting.allow_new_registrations;
formMeta.allow_email_registrations.value =
setting.allow_email_registrations;
formMeta.allow_email_domains.value = '';
if (Array.isArray(setting.allow_email_domains)) {
formMeta.allow_email_domains.value =
setting.allow_email_domains.join('\n');
}
formMeta.login_required.value = setting.login_required; formMeta.login_required.value = setting.login_required;
setFormData({ ...formMeta }); setFormData({ ...formMeta });
} }

View File

@ -1,4 +1,4 @@
import { FC } from 'react'; import { FC, useEffect, useState } from 'react';
import { Form, Table, Dropdown, Button, Stack } from 'react-bootstrap'; import { Form, Table, Dropdown, Button, Stack } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -21,7 +21,13 @@ import {
useChangePasswordModal, useChangePasswordModal,
useToast, useToast,
} from '@/hooks'; } from '@/hooks';
import { useQueryUsers, addUser, updateUserPassword } from '@/services'; import {
useQueryUsers,
addUser,
updateUserPassword,
getAdminUcAgent,
AdminUcAgent,
} from '@/services';
import { loggedUserInfoStore, userCenterStore } from '@/stores'; import { loggedUserInfoStore, userCenterStore } from '@/stores';
import { formatCount } from '@/utils'; import { formatCount } from '@/utils';
@ -50,6 +56,9 @@ const Users: FC = () => {
const curQuery = urlSearchParams.get('query') || ''; const curQuery = urlSearchParams.get('query') || '';
const currentUser = loggedUserInfoStore((state) => state.user); const currentUser = loggedUserInfoStore((state) => state.user);
const { agent: ucAgent } = userCenterStore(); const { agent: ucAgent } = userCenterStore();
const [adminUcAgent, setAdminUcAgent] = useState<AdminUcAgent>({
user_status_agent_enabled: false,
});
const Toast = useToast(); const Toast = useToast();
const { const {
data, data,
@ -139,6 +148,13 @@ const Users: FC = () => {
urlSearchParams.delete('page'); urlSearchParams.delete('page');
setUrlSearchParams(urlSearchParams); setUrlSearchParams(urlSearchParams);
}; };
useEffect(() => {
if (ucAgent?.enabled) {
getAdminUcAgent().then((resp) => {
setAdminUcAgent(resp);
});
}
}, [ucAgent]);
return ( return (
<> <>
<h3 className="mb-4">{t('title')}</h3> <h3 className="mb-4">{t('title')}</h3>
@ -248,7 +264,8 @@ const Users: FC = () => {
{t('set_new_password')} {t('set_new_password')}
</Dropdown.Item> </Dropdown.Item>
) : null} ) : null}
{!ucAgent?.enabled ? ( {!ucAgent?.enabled ||
!adminUcAgent.user_status_agent_enabled ? (
<Dropdown.Item <Dropdown.Item
onClick={() => handleAction('status', user)}> onClick={() => handleAction('status', user)}>
{t('change_status')} {t('change_status')}

View File

@ -1,4 +1,4 @@
import { FC, ReactNode, useEffect } from 'react'; import { FC, ReactNode, useEffect, useState } from 'react';
import { useLocation, useNavigate, useLoaderData } from 'react-router-dom'; import { useLocation, useNavigate, useLoaderData } from 'react-router-dom';
import { floppyNavigation } from '@/utils'; import { floppyNavigation } from '@/utils';
@ -15,37 +15,49 @@ const RouteGuard: FC<{
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const loaderData = useLoaderData(); const loaderData = useLoaderData();
const gr = onEnter({ const [grOk, setGrOk] = useState(true);
loaderData, const [routeError, setRouteError] = useState<{
path, code: string;
page, msg: string;
}); }>();
const applyGuard = () => {
if (typeof onEnter !== 'function') {
return;
}
const gr = onEnter({
loaderData,
path,
page,
});
let guardError; setGrOk(gr.ok);
const errCode = gr.error?.code; if (gr.error?.code && /403|404|50X/i.test(gr.error.code.toString())) {
if (errCode === '403' || errCode === '404' || errCode === '50X') { setRouteError({
guardError = { code: `${gr.error.code}`,
code: errCode, msg: gr.error.msg || '',
msg: gr.error?.msg, });
}; return;
} }
const handleGuardRedirect = () => { if (gr.redirect) {
const redirectUrl = gr.redirect; floppyNavigation.navigate(gr.redirect, {
if (redirectUrl) {
floppyNavigation.navigate(redirectUrl, {
handler: navigate, handler: navigate,
options: { replace: true }, options: { replace: true },
}); });
} }
}; };
useEffect(() => { useEffect(() => {
handleGuardRedirect(); /**
* NOTICE:
* Must be put in `useEffect`,
* otherwise `guard` may not get `loggedUserInfo` correctly
*/
applyGuard();
}, [location]); }, [location]);
return ( return (
<> <>
{gr.ok ? children : null} {grOk ? children : null}
{!gr.ok && guardError ? ( {!grOk && routeError ? (
<RouteErrorBoundary errCode={guardError.code} /> <RouteErrorBoundary errCode={routeError.code} />
) : null} ) : null}
</> </>
); );

View File

@ -37,11 +37,18 @@ export interface UcBranding {
personal_branding: UcBrandingEntry[]; personal_branding: UcBrandingEntry[];
} }
export interface AdminUcAgent {
user_status_agent_enabled: boolean;
}
export const getUcAgent = () => { export const getUcAgent = () => {
const apiUrl = `/answer/api/v1/user-center/agent`; const apiUrl = `/answer/api/v1/user-center/agent`;
return request.get<UcAgent>(apiUrl); return request.get<UcAgent>(apiUrl);
}; };
export const getAdminUcAgent = () => {
const apiUrl = `/answer/admin/api/user-center/agent`;
return request.get<AdminUcAgent>(apiUrl);
};
export const getUcSettings = () => { export const getUcSettings = () => {
const apiUrl = `/answer/api/v1/user-center/user/settings`; const apiUrl = `/answer/api/v1/user-center/user/settings`;
return request.get<UcSettings>(apiUrl); return request.get<UcSettings>(apiUrl);

View File

@ -11,6 +11,8 @@ const loginSetting = create<IType>((set) => ({
login: { login: {
allow_new_registrations: true, allow_new_registrations: true,
login_required: false, login_required: false,
allow_email_registrations: true,
allow_email_domains: [],
}, },
update: (params) => update: (params) =>
set(() => { set(() => {