diff --git a/src/components/DeleteModal/reset.less b/src/components/DeleteModal/reset.less
index a2c1669..d5a560d 100644
--- a/src/components/DeleteModal/reset.less
+++ b/src/components/DeleteModal/reset.less
@@ -1,5 +1,9 @@
.queryLogo {
svg {
fill: #f4ce5d;
+
+ path {
+ fill: #faad14;
+ }
}
}
diff --git a/src/components/LabelWithHelp/index.tsx b/src/components/LabelWithHelp/index.tsx
new file mode 100644
index 0000000..a3a86fb
--- /dev/null
+++ b/src/components/LabelWithHelp/index.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+
+import CommonIcon from '../CommonIcon';
+import { ReactComponent as IconHelp } from '../../images/icon/icon_label_query.svg';
+import styled from 'styled-components';
+
+const LabelWrap = styled.div`
+ display: flex;
+ align-items: center;
+ position: relative;
+
+ .help {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ top: 50%;
+ left: 100%;
+ transform: translate(12px, -50%);
+
+ svg {
+ &:hover {
+ path {
+ fill: #36435c;
+ }
+ }
+ }
+ }
+`;
+
+interface IProp {
+ label: string;
+ tip: string;
+}
+
+const LabelWithHelp = ({ label, tip }: IProp) => {
+ return (
+
+ {label}
+
+
+
+
+ );
+};
+
+export default LabelWithHelp;
diff --git a/src/components/MainHeader/index.tsx b/src/components/MainHeader/index.tsx
index 09dd854..233a8de 100644
--- a/src/components/MainHeader/index.tsx
+++ b/src/components/MainHeader/index.tsx
@@ -52,6 +52,8 @@ import { ReactComponent as IconUser } from '../../images/icon/icon_user.svg';
import { ReactComponent as IconTip } from '../../images/icon/icon_label_tips.svg';
import { ReactComponent as IconClose } from '../../images/icon/icon_close.svg';
import { ReactComponent as IconLink } from '../../images/icon/icon_external_link.svg';
+import { ReactComponent as IconSetNormal } from '../../images/icon/icon_set_normal.svg';
+import { ReactComponent as IconSetActive } from '../../images/icon/icon_set_elected.svg';
import ImageVersionInfo from '../../images/icon_logoWords.svg';
import AddCluster from '../../components/AddCluster';
@@ -285,6 +287,15 @@ function MainHeader() {
style={{ fontSize: '24px' }}
>
+
history.push('/dashboard/setting')}>
+
+
\ No newline at end of file
diff --git a/src/images/bg_left_unselect.svg b/src/images/bg_left_unselect.svg
new file mode 100644
index 0000000..0bcbabf
--- /dev/null
+++ b/src/images/bg_left_unselect.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/bg_right_select.svg b/src/images/bg_right_select.svg
new file mode 100644
index 0000000..5b37e21
--- /dev/null
+++ b/src/images/bg_right_select.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/bg_right_unselect.svg b/src/images/bg_right_unselect.svg
new file mode 100644
index 0000000..6602696
--- /dev/null
+++ b/src/images/bg_right_unselect.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/icon/icon_arrow_right.svg b/src/images/icon/icon_arrow_right.svg
new file mode 100644
index 0000000..807d95f
--- /dev/null
+++ b/src/images/icon/icon_arrow_right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/icon/icon_ldap.svg b/src/images/icon/icon_ldap.svg
new file mode 100644
index 0000000..0435511
--- /dev/null
+++ b/src/images/icon/icon_ldap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/icon/icon_set_elected.svg b/src/images/icon/icon_set_elected.svg
new file mode 100644
index 0000000..adb545e
--- /dev/null
+++ b/src/images/icon/icon_set_elected.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/icon/icon_set_normal.svg b/src/images/icon/icon_set_normal.svg
new file mode 100644
index 0000000..29ca6d8
--- /dev/null
+++ b/src/images/icon/icon_set_normal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/icon_set_elected.svg b/src/images/icon_set_elected.svg
new file mode 100644
index 0000000..adb545e
--- /dev/null
+++ b/src/images/icon_set_elected.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/images/icon_set_normal.svg b/src/images/icon_set_normal.svg
new file mode 100644
index 0000000..29ca6d8
--- /dev/null
+++ b/src/images/icon_set_normal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index 7b6b877..3478695 100644
--- a/src/index.css
+++ b/src/index.css
@@ -331,3 +331,7 @@ input:-webkit-autofill:active {
.ant-tooltip-arrow-content {
background-color: #1a2433;
}
+
+.ant-form-item-required {
+ font-weight: normal;
+}
diff --git a/src/pages/Application/index.tsx b/src/pages/Application/index.tsx
index b95fef6..4e0c581 100644
--- a/src/pages/Application/index.tsx
+++ b/src/pages/Application/index.tsx
@@ -23,7 +23,6 @@ import DeleteModal from '../../components/DeleteModal';
import LabelSelect from '../../components/LabelSelect';
// import { applictionOptions } from './const';
import { useHistory } from 'react-router-dom';
-import { UserType } from '../User/const';
import CommonIcon from '../../components/CommonIcon';
import { ReactComponent as IconNormalEdit } from '../../images/icon/icon_btn_normal_edit.svg';
import { ReactComponent as IconSelectedEdit } from '../../images/icon/icon_btn_elected_edit.svg';
@@ -42,7 +41,6 @@ import { ReactComponent as IconAdd } from '../../images/icon/icon_add.svg';
function Application() {
const [data, setData] = useState([]);
const [copyData, setCopyData] = useState([]);
- const [userList, setUserList] = useState([]);
const [tableLoading, setTableLoading] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const [filterValue, setFilterValue] = useState({ name: '', type: 'all' });
@@ -60,12 +58,7 @@ function Application() {
const { user } = useContext(UserContext);
const history = useHistory();
const { t } = useTranslation();
- const getUser = async () => {
- const result = await HTTP.get('users');
- if (result.code === 0) {
- setUserList(result.data || []);
- }
- };
+
const getApplication = async () => {
setTableLoading(true);
const result = await HTTP.get('application', {
@@ -81,7 +74,6 @@ function Application() {
};
useEffect(() => {
getApplication();
- getUser();
}, []);
const showTotal = (total: number) => {
return `共${total}条`;
@@ -230,16 +222,8 @@ function Application() {
},
{
title: t('resources.application.fields.user'),
+ dataIndex: 'user_name',
key: '5',
- // eslint-disable-next-line react/display-name
- render: (...args: any) => {
- const record = args[1];
- const user_id = record?.user_id;
- const item: UserType = userList.find((el: UserType) => el.id === user_id) || {
- name: '',
- };
- return {item?.name};
- },
},
{
title: t('common.operation'),
diff --git a/src/pages/Clusters/components/ListItem.tsx b/src/pages/Clusters/components/ListItem.tsx
index 465cf31..ee2e1ea 100644
--- a/src/pages/Clusters/components/ListItem.tsx
+++ b/src/pages/Clusters/components/ListItem.tsx
@@ -496,7 +496,7 @@ const ListItem: FC = ({ data, onSubmit }: IProps) => {
component={IconProfile}
style={{ fontSize: 24, marginRight: 6 }}
/>
- {data.userName}
+ {data.user_name}
diff --git a/src/pages/Clusters/index.tsx b/src/pages/Clusters/index.tsx
index daf51eb..b7786a2 100644
--- a/src/pages/Clusters/index.tsx
+++ b/src/pages/Clusters/index.tsx
@@ -6,7 +6,6 @@ import { ContentTitle, ClusterCount, FlexContainer, LoadingBox } from './style-c
import { Button, Spin } from 'antd';
import i18n from '../../i18n/i18n';
import AddCluster from '../../components/AddCluster';
-import { queryAllUser } from '../../services';
import { useTranslation } from 'react-i18next';
import NotData from '../../components/NotData';
import Icon from '@ant-design/icons';
@@ -23,17 +22,11 @@ const Clusters: FC<{}> = () => {
async function queryClusters() {
setClusterLoading(true);
- const nameMap = await queryAllUser();
+
const response = await HTTP.get('cluster');
setClusterLoading(false);
if (response.code === 0) {
- const tmpList = response.data.map((item: any) => {
- return {
- ...item,
- userName: nameMap.get(item.user_id),
- };
- });
- setClusterList(tmpList);
+ setClusterList(response.data);
}
}
diff --git a/src/pages/DevSpace/components/DevspaceForm.tsx b/src/pages/DevSpace/components/DevspaceForm.tsx
index 7754032..d6f66a3 100644
--- a/src/pages/DevSpace/components/DevspaceForm.tsx
+++ b/src/pages/DevSpace/components/DevspaceForm.tsx
@@ -10,7 +10,7 @@ import { ReactComponent as IconAdmin } from '../../../images/icon/icon_admin.svg
import { ReactComponent as IconResource } from '../../../images/icon/icon_resource.svg';
import { ReactComponent as IconBaseSpace } from '../../../images/icon/icon_switch_baseSpace.svg';
-import { queryAllCluster, queryAllUser } from '../../../services';
+import { queryAllCluster, queryUserList } from '../../../services';
const FormFlexBox = styled(FlexBox)`
flex: 1;
@@ -112,6 +112,7 @@ const DevspaceForm = ({
const [isAdmin, setIsAdmin] = useState(false);
const [userList, setUserList] = useState([]);
+ const [filterUserList, setFilterUserList] = useState([]);
const [clusterList, setClusterList] = useState([]);
// 校验相关
@@ -183,15 +184,9 @@ const DevspaceForm = ({
}
async function getUsers() {
- const userMap = await queryAllUser();
- const tmpList = Array.from(userMap).map((item) => {
- return {
- value: item[0],
- text: item[1],
- label: item[1],
- };
- });
- setUserList(tmpList);
+ const list = await queryUserList();
+ setUserList(list);
+ setFilterUserList(list.slice(0, 10));
}
const handleSubmit = async (values: any) => {
@@ -290,6 +285,11 @@ const DevspaceForm = ({
}
};
+ const handleSearchUser = (value: string) => {
+ const list = userList.filter((item: any) => item.label.indexOf(value) > -1);
+ setFilterUserList(list.slice(0, 10));
+ };
+
return (
<>
diff --git a/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx b/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx
index 34d70d5..210ea31 100644
--- a/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx
+++ b/src/pages/DevSpace/components/MeshSpace/BaseSpace.tsx
@@ -20,9 +20,7 @@ import { ReactComponent as WayLine } from '../../../../images/mesh-icon/way1.svg
import { ReactComponent as BlueArrow } from '../../../../images/mesh-icon/arrow.svg';
import { ReactComponent as IconTracingHeader } from '../../../../images/icon/icon_normal_tracingHeaders.svg';
import { ReactComponent as IconActiveTracingHeader } from '../../../../images/icon/icon_active_tracingHeaders.svg';
-
import CommonIcon from '../../../../components/CommonIcon';
-
import { ReactComponent as IconPath } from '../../../../images/mesh-icon/icon_path.svg';
import {
windowAnimationStartHandle,
diff --git a/src/pages/EnvList/components/KubeConfig.tsx b/src/pages/EnvList/components/KubeConfig.tsx
index 9478912..fb6a7ea 100644
--- a/src/pages/EnvList/components/KubeConfig.tsx
+++ b/src/pages/EnvList/components/KubeConfig.tsx
@@ -69,7 +69,9 @@ const KubeConfig = (props: PropParam) => {
const handleDownload = () => {
// download
- const blob = new Blob([kubeConfig]);
+ const blob = new Blob([kubeConfig], {
+ type: 'application/octet-stream',
+ });
const aLink = document.createElement('a');
aLink.style.display = 'none';
aLink.setAttribute('download', 'config');
diff --git a/src/pages/EnvList/index.tsx b/src/pages/EnvList/index.tsx
index 0f54ca7..0bfdf74 100644
--- a/src/pages/EnvList/index.tsx
+++ b/src/pages/EnvList/index.tsx
@@ -252,12 +252,12 @@ const EnvList = () => {
return record?.cooper_user?.length > 0 || record?.viewer_user?.length > 0 ? (
}>
- {record.user_name}
+ {record?.owner?.name}
) : (
- {record.user_name}
+ {record?.owner?.name}
);
},
},
@@ -609,13 +609,6 @@ const EnvList = () => {
/>
- {/*
-
- */}
{!id && (
<>
{!!user.is_admin && (
diff --git a/src/pages/Home.tsx/routes.tsx b/src/pages/Home.tsx/routes.tsx
index abe359a..c4ed855 100644
--- a/src/pages/Home.tsx/routes.tsx
+++ b/src/pages/Home.tsx/routes.tsx
@@ -7,6 +7,7 @@ import EnvList from '../EnvList';
import DevSpace from '../DevSpace';
import DevSpaceOperation from '../DevSpace/components/DevspaceOperation';
import MeshSpace from '../DevSpace/components/MeshSpace';
+import Setting from '../Setting';
import ImportUser from '../User/Import';
import ImportDevSpace from '../DevSpace/import';
@@ -64,4 +65,8 @@ export const routes = [
path: '/dashboard/devspace/mesh-space',
component: MeshSpace,
},
+ {
+ path: '/dashboard/setting',
+ component: Setting,
+ },
];
diff --git a/src/pages/Setting/components/ConfigService.tsx b/src/pages/Setting/components/ConfigService.tsx
new file mode 100644
index 0000000..9cf01b2
--- /dev/null
+++ b/src/pages/Setting/components/ConfigService.tsx
@@ -0,0 +1,380 @@
+import React, { useEffect, useState } from 'react';
+
+import { ConfigServiceWrap } from '../styled-component';
+import { useTranslation } from 'react-i18next';
+import { Form, Input, Button, Checkbox } from 'antd';
+import { ConfigInfo } from '../../../types';
+import LabelWithHelp from '../../../components/LabelWithHelp';
+import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg';
+import { ReactComponent as IconArrowRight } from '../../../images/icon/icon_arrow_right.svg';
+
+import HTTP from '../../../api/fetch';
+
+interface IProp {
+ configData: ConfigInfo | null;
+ onClose: () => void;
+ handleSyncData: () => void;
+ closeAndSync: () => void;
+}
+
+interface SearchData {
+ bind_dn: string;
+ email: string;
+ user_name: string;
+}
+
+const ConfigService = ({ configData, onClose, handleSyncData, closeAndSync }: IProp) => {
+ const { t } = useTranslation();
+ const [currentStep, setCurrentStep] = useState(configData?.enable ? 2 : 1);
+ const [folded, setFolded] = useState(true);
+ const [form] = Form.useForm();
+ const [isLoading, setIsLoading] = useState(false);
+ // eslint-disable-next-line no-undef
+ const [step1Info, setStep1Info] = useState | null>(configData);
+ const [searchData, setSearchData] = useState(null);
+ const [advancedSearchData, setAdvancedSearchData] = useState(null);
+
+ useEffect(() => {
+ if (configData) {
+ const security = [];
+ if (configData.tls) {
+ security.push('tls');
+ }
+ if (configData?.md5) {
+ security.push('md5');
+ }
+ form.setFieldsValue({
+ server: configData?.server,
+ bind_dn: configData?.bind_dn,
+ password: configData?.password,
+ security,
+ base_dn: configData?.base_dn,
+ filter: configData?.filter,
+ email_attr: configData?.email_attr,
+ user_name_attr: configData?.user_name_attr,
+ admin_base_dn: configData?.admin_base_dn,
+ admin_filter: configData?.admin_filter,
+ });
+ validateSearch();
+ advanceSearch();
+ }
+ }, []);
+
+ const handleNextStep = async () => {
+ form.validateFields().then(async () => {
+ const { bind_dn, password, server, security } = form.getFieldsValue();
+ setIsLoading(true);
+ setStep1Info({
+ ...configData,
+ bind_dn,
+ password,
+ server,
+ tls: security.indexOf('tls') > -1 ? 1 : 0,
+ md5: security.indexOf('md5') ? 1 : 0,
+ });
+ const response = await HTTP.put('ldap/bind', {
+ bind_dn,
+ password,
+ server,
+ });
+ setIsLoading(false);
+ if (response.code === 0) {
+ setCurrentStep(2);
+ }
+ });
+ };
+
+ const validateSearch = async () => {
+ const {
+ email_attr,
+ user_name_attr,
+ base_dn,
+ filter,
+ admin_base_dn,
+ admin_filter,
+ } = form.getFieldsValue();
+ if (email_attr && base_dn) {
+ const resp = await HTTP.put('ldap/search', {
+ ...step1Info,
+ email_attr,
+ user_name_attr,
+ base_dn,
+ filter,
+ admin_base_dn,
+ admin_filter,
+ });
+ if (resp.code === 0) {
+ setSearchData(resp.data);
+ }
+ return resp;
+ }
+ };
+
+ const advanceSearch = async () => {
+ const {
+ email_attr,
+ user_name_attr,
+ base_dn,
+ filter,
+ admin_base_dn,
+ admin_filter,
+ } = form.getFieldsValue();
+ if (email_attr && admin_base_dn) {
+ const resp = await HTTP.put('ldap/search', {
+ ...step1Info,
+ email_attr,
+ user_name_attr,
+ base_dn,
+ filter,
+ admin_base_dn,
+ admin_filter,
+ });
+ if (resp.code === 0) {
+ setAdvancedSearchData(resp.data);
+ }
+ return resp;
+ }
+ };
+
+ const saveConfig = async () => {
+ const {
+ email_attr,
+ user_name_attr,
+ base_dn,
+ filter,
+ admin_base_dn,
+ admin_filter,
+ } = form.getFieldsValue();
+ return await HTTP.put('ldap/config/set', {
+ ...step1Info,
+ email_attr,
+ user_name_attr,
+ base_dn,
+ filter,
+ admin_base_dn,
+ admin_filter,
+ });
+ };
+
+ const handleSubmit = async () => {
+ form.validateFields().then(async () => {
+ setIsLoading(true);
+ const saveInfo = await saveConfig();
+ if (saveInfo.code === 0) {
+ await handleSyncData();
+ setIsLoading(false);
+ closeAndSync();
+ } else {
+ setIsLoading(false);
+ }
+ });
+ };
+
+ return (
+
+ {t('settings.configDesc')}
+
+
+
1
+
+
{t('settings.serverConfig')}
+
{t('settings.serverConfigTip')}
+
+
+
+
2
+
+
{t('settings.ruleConfig')}
+
{t('settings.ruleConfigTip')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enable TLS
+ Enable Digest MD5 Bind
+
+
+ >
+ )}
+
+ {currentStep === 2 && (
+ <>
+
+ }
+ name="email_attr"
+ rules={[
+ {
+ required: true,
+ },
+ ]}
+ required={true}
+ >
+
+
+
+ }
+ name="user_name_attr"
+ >
+
+
+
+
+
{t('settings.memberRule')}
+
+
+
+
+
+
+
+
{`Example: ${t('settings.exampleTip')}`}
+
DN: {searchData?.bind_dn ?? '--'}
+
Email: {searchData?.email ?? '--'}
+
Username: {searchData?.user_name ?? '--'}
+
+
+
+
+
setFolded((curr) => !curr)}
+ >
+ {folded ? : }
+
+
+ {!folded && (
+
+
{t('settings.adminRule')}
+
+
+
+
+
+
+
+
{`Example: ${t('settings.exampleTip')}`}
+
DN: {advancedSearchData?.bind_dn ?? '- -'}
+
Email: {advancedSearchData?.email ?? '- -'}
+
+ Username: {advancedSearchData?.user_name ?? '- -'}
+
+
+
+ )}
+
+ >
+ )}
+
+
+
+
+ {currentStep === 1 && (
+
+ )}
+ {currentStep === 2 && (
+ <>
+
+
+ >
+ )}
+
+
+ );
+};
+
+export default ConfigService;
diff --git a/src/pages/Setting/components/ThirdAccount.tsx b/src/pages/Setting/components/ThirdAccount.tsx
new file mode 100644
index 0000000..8962691
--- /dev/null
+++ b/src/pages/Setting/components/ThirdAccount.tsx
@@ -0,0 +1,136 @@
+import React, { useState } from 'react';
+import { ThirdAccountWrap, PopupWrap } from '../styled-component';
+import { Button, Popover } from 'antd';
+import { useTranslation } from 'react-i18next';
+
+import { ReactComponent as IconLdap } from '../../../images/icon/icon_ldap.svg';
+import { ReactComponent as IconArrowDown } from '../../../images/icon/icon_arrow_down.svg';
+import { ReactComponent as IconExplain } from '../../../images/icon/icon_label_explain.svg';
+import Icon from '@ant-design/icons';
+import DeleteModal from '../../../components/DeleteModal';
+import CommonIcon from '../../../components/CommonIcon';
+
+interface ThirdAccountProp {
+ status: 'unallocated' | 'configured';
+ showConfig: () => void;
+ handleSyncData: () => any;
+ handleDeleteConfig: () => void;
+ configData: any;
+}
+
+const ThirdAccount = ({
+ status,
+ showConfig,
+ handleSyncData,
+ handleDeleteConfig,
+ configData,
+}: ThirdAccountProp) => {
+ const { t } = useTranslation();
+ const [visible, setVisible] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [showDelete, setShowDelete] = useState(false);
+ const [showSyncTip, setShowSyncTip] = useState(Boolean(configData?.last_sync_err_msg));
+ const [syncTip, setSyncTip] = useState(configData?.last_sync_err_msg ?? '');
+
+ const handleEditConfig = () => {
+ setVisible(false);
+ showConfig();
+ };
+
+ const handleDelete = () => {
+ setVisible(false);
+ handleDeleteConfig();
+ };
+
+ const handleClick = async () => {
+ setIsLoading(true);
+ const resp = await handleSyncData();
+ if (resp?.data?.last_sync_err_msg) {
+ setSyncTip(resp?.data?.last_sync_err_msg ?? '');
+ setShowSyncTip(true);
+ }
+
+ setIsLoading(false);
+ };
+
+ const onClickDelete = () => {
+ setVisible(false);
+ setShowDelete(true);
+ };
+
+ const PopupContent = () => {
+ return (
+
+
+ {t('settings.modifyConfig')}
+
+
+ {t('settings.deleteConfig')}
+
+
+ );
+ };
+
+ return (
+
+
+
+
+
+
+
+
LDAP
+
{t(`settings.${status}`)}
+
+
+
+
+
+ {showSyncTip && (
+
+ )}
+ {status === 'unallocated' && (
+
+ )}
+ {status === 'configured' && (
+ <>
+
+
}
+ visible={visible}
+ onVisibleChange={(curr: boolean) => setVisible(curr)}
+ trigger="click"
+ >
+
+
+ >
+ )}
+
+ setShowDelete(false)}
+ onConfirm={handleDelete}
+ />
+
+ );
+};
+
+export default ThirdAccount;
diff --git a/src/pages/Setting/index.tsx b/src/pages/Setting/index.tsx
new file mode 100644
index 0000000..3456c08
--- /dev/null
+++ b/src/pages/Setting/index.tsx
@@ -0,0 +1,112 @@
+import React, { useState, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { SettingWrap } from './styled-component';
+import ThirdAccount from './components/ThirdAccount';
+import ConfigService from './components/ConfigService';
+import { message, Modal } from 'antd';
+import HTTP from '../../api/fetch';
+import { ConfigInfo } from '../../types';
+
+const CONFIG_MENU_LIST = ['thirdAccount'];
+
+const Settings = () => {
+ const { t } = useTranslation();
+ const [currentIndex, setCurrentIndex] = useState(0);
+ const [showModal, setShowModal] = useState(false);
+ const [status, setStatus] = useState<'unallocated' | 'configured'>('unallocated');
+ const [configData, setConfigData] = useState(null);
+
+ const showConfig = () => {
+ setShowModal(true);
+ };
+
+ const handleSyncData = async () => {
+ const response = await HTTP.post('ldap/trigger', {});
+ if (response.code === 0) {
+ message.success(t('common.message.success'));
+ }
+
+ return response;
+ };
+
+ // delete config
+ const handleDeleteConfig = async () => {
+ const resp = await HTTP.put('ldap/config/disable', configData);
+ if (resp.code === 0) {
+ await getConfig();
+ }
+ };
+
+ const getConfig = async () => {
+ const response = await HTTP.get('ldap/config');
+ if (response.code === 0) {
+ const { data } = response;
+ if (data) {
+ setStatus(data?.enable ? 'configured' : 'unallocated');
+ setConfigData(data);
+ }
+ }
+ };
+
+ const closeAndSync = async () => {
+ setShowModal(false);
+ await getConfig();
+ };
+
+ useEffect(() => {
+ getConfig();
+ }, []);
+
+ return (
+ <>
+
+
+
{t('settings.settingCenter')}
+
+ {CONFIG_MENU_LIST.map((item, index) => {
+ return (
+ - setCurrentIndex(index)}
+ key={index}
+ className={`menu-list-item${
+ currentIndex === index ? ' active' : ''
+ }`}
+ >
+ {t(`settings.${item}`)}
+
+ );
+ })}
+
+
+
+
{t(`settings.${CONFIG_MENU_LIST[currentIndex]}`)}
+
+
+
+ setShowModal(false)}
+ footer={null}
+ bodyStyle={{ paddingTop: 0 }}
+ >
+ setShowModal(false)}
+ closeAndSync={closeAndSync}
+ handleSyncData={handleSyncData}
+ />
+
+ >
+ );
+};
+
+export default Settings;
diff --git a/src/pages/Setting/styled-component.tsx b/src/pages/Setting/styled-component.tsx
new file mode 100644
index 0000000..e6247af
--- /dev/null
+++ b/src/pages/Setting/styled-component.tsx
@@ -0,0 +1,326 @@
+import styled from 'styled-components';
+import step1active from '../../images/bg_left_select.svg';
+import step1normal from '../../images/bg_left_unselect.svg';
+import step2active from '../../images/bg_right_select.svg';
+import step2normal from '../../images/bg_right_unselect.svg';
+
+export const SettingWrap = styled.div`
+ display: flex;
+ min-height: 604px;
+ background: #ffffff;
+
+ .menu {
+ width: 200px;
+ border-right: 1px solid #dae1e8;
+ font-family: PingFangSC-Semibold;
+ font-size: 16px;
+ font-weight: 600;
+ color: #36435c;
+
+ .title {
+ padding: 16px 0 16px 20px;
+ }
+
+ &-list {
+ font-size: 14px;
+
+ &-item {
+ height: 40px;
+ padding: 0 20px;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+
+ &.active,
+ &:hover {
+ color: #0066ff;
+ border-right: 2px solid #0066ff;
+ background: #f3f6fa;
+ }
+ }
+ }
+ }
+
+ .content {
+ flex: 1;
+ padding: 0 20px;
+
+ .title {
+ padding: 16px 0;
+ font-family: PingFangSC-Semibold;
+ font-size: 16px;
+ font-weight: 600;
+ color: #36435c;
+ }
+ }
+`;
+
+interface IThirdAccount {
+ status: string;
+}
+
+interface IConfigService {
+ step: number;
+}
+
+export const ThirdAccountWrap = styled.div`
+ display: flex;
+ padding: 30px 20px;
+ border: 1px solid #dae1e8;
+ border-radius: 4px;
+ justify-content: space-between;
+ align-items: center;
+
+ .left {
+ display: flex;
+ align-items: center;
+
+ .logo {
+ width: 40px;
+ height: 40px;
+ }
+
+ .content {
+ .sub-title {
+ color: #202d40;
+ display: flex;
+ align-items: center;
+ font-family: PingFangSC-Semibold;
+ font-weight: 600;
+ font-size: 16px;
+
+ .status {
+ height: 18px;
+ padding: 0 4px;
+ display: flex;
+ align-items: center;
+ margin-left: 8px;
+ font-size: 12px;
+ color: #ffffff;
+ border-radius: 2px;
+ background: ${(props) =>
+ props.status === 'unallocated' ? '#ffac52' : '#4ac285'};
+ }
+ }
+
+ .desc {
+ margin-top: 10px;
+ font-size: 12px;
+ color: #79879c;
+ }
+ }
+ }
+
+ .btn-box {
+ display: flex;
+ align-items: center;
+
+ .popup-btn {
+ display: flex;
+ align-items: center;
+ }
+
+ svg {
+ &:hover {
+ path {
+ fill: #faad14;
+ }
+ }
+ }
+ }
+`;
+
+export const ConfigServiceWrap = styled.div`
+ display: block;
+ overflow: hidden;
+
+ .desc {
+ font-family: PingFangSC-Regular;
+ font-size: 14px;
+ font-weight: normal;
+ color: #79879c;
+ }
+
+ .progress {
+ height: 68px;
+ margin-top: 20px;
+ display: flex;
+ align-items: center;
+
+ .step {
+ height: 68px;
+ width: 50%;
+ padding: 14px;
+ flex-basis: 50%;
+ display: flex;
+ position: relative;
+ background-size: cover;
+
+ &::after {
+ content: '';
+ width: 0;
+ height: 0;
+ display: block;
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ border: 10px solid transparent;
+ border-bottom: 10px solid #f9fbfd;
+ transform: translate(-10px, -10px);
+ }
+
+ &:nth-child(1) {
+ background: url(${(props) => (props.step === 1 ? step1active : step1normal)})
+ no-repeat;
+ position: absolute;
+ z-index: 10;
+
+ &::after {
+ visibility: ${(props) => (props.step === 1 ? 'visible' : 'hidden')};
+ }
+ }
+
+ &:nth-child(2) {
+ width: 314px;
+ position: absolute;
+ padding-left: 40px;
+ padding-right: 0;
+ left: 50%;
+ background: url(${(props) => (props.step === 2 ? step2active : step2normal)})
+ no-repeat;
+
+ &::after {
+ visibility: ${(props) => (props.step === 2 ? 'visible' : 'hidden')};
+ }
+ }
+
+ .icon {
+ height: 24px;
+ width: 24px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgb(239, 244, 249);
+ border: 2px solid rgb(255, 255, 255);
+ box-shadow: 0px 3px 8px 0px rgba(54, 67, 92, 0.2);
+ border-radius: 50%;
+ font-family: PingFangSC-Semibold;
+ font-size: 12px;
+ font-weight: 600;
+ color: #b6c2cd;
+
+ &.active {
+ background: rgb(0, 128, 255);
+ border: 2px solid rgb(255, 255, 255);
+ box-shadow: 0px 3px 8px 0px rgba(0, 128, 255, 0.3);
+ color: #ffffff;
+ }
+ }
+
+ .content {
+ margin-left: 6px;
+
+ .title {
+ color: rgb(32, 45, 64);
+ font-family: PingFangSC-Semibold;
+ font-size: 16px;
+ font-weight: 600;
+ }
+
+ .desc {
+ color: rgb(121, 135, 156);
+ font-family: PingFangSC-Regular;
+ font-size: 12px;
+ }
+ }
+ }
+ }
+
+ .form-wrap {
+ margin-top: 10px;
+ padding: 20px 10px;
+ background: #f9fbfd;
+ font-size: 14px;
+
+ .member-rule {
+ padding: 20px 10px 20px 0;
+ position: relative;
+ min-height: 184px;
+ border-radius: 4px;
+ border: 1px solid rgb(218, 225, 232);
+
+ .rule-label {
+ padding: 0 4px;
+ position: absolute;
+ background: #f9fbfd;
+ top: 0;
+ left: 0;
+ transform: translate(10px, -50%);
+ font-family: PingFangSC-Semibold;
+ font-size: 14px;
+ font-weight: 600;
+ }
+
+ .rule-tip {
+ padding: 0 10px;
+ font-size: 12px;
+ color: #79879c;
+ }
+ }
+
+ .advance {
+ &-setting {
+ margin-top: 20px;
+ }
+
+ &-label {
+ margin-bottom: 20px;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ }
+ }
+
+ .ant-form-item-label {
+ padding-left: 10px;
+ }
+ }
+
+ .btn-box {
+ margin-top: 24px;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ }
+
+ .ant-form-item-control-input {
+ box-shadow: none;
+ }
+`;
+
+export const PopupWrap = styled.ul`
+ .list-item {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 106px;
+ height: 32px;
+ cursor: pointer;
+
+ &:hover {
+ background: #f3f6fa;
+ }
+
+ font-family: PingFangSC-Regular;
+ font-size: 14px;
+ font-weight: normal;
+
+ &.modify {
+ color: rgb(32, 45, 64);
+ }
+
+ &.del {
+ color: #ff3f3f;
+ }
+ }
+`;
diff --git a/src/services/index.ts b/src/services/index.ts
index e97db9b..6d77dad 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -12,6 +12,20 @@ export const queryAllUser = async () => {
return nameMap;
};
+export const queryUserList = async () => {
+ const response = await HTTP.get('users');
+ let list = [];
+ if (response.code === 0) {
+ list = response?.data?.map((item: any) => {
+ return {
+ label: item.name,
+ value: item.id,
+ text: item.name,
+ };
+ });
+ }
+ return list;
+};
export interface UserType {
id: number;
name: string;
diff --git a/src/types.ts b/src/types.ts
deleted file mode 100644
index 947567a..0000000
--- a/src/types.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export interface LoginToken {
- email: string;
- exp: number;
- iat: number;
- is_admin: number;
- nbf: number;
- user_id: number;
- username: string;
- uuid: string;
-}
diff --git a/src/types/index.ts b/src/types/index.ts
index 062aa0c..37ba4b8 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -1,3 +1,13 @@
+export interface LoginToken {
+ email: string;
+ exp: number;
+ iat: number;
+ is_admin: number;
+ nbf: number;
+ user_id: number;
+ username: string;
+ uuid: string;
+}
export interface ClusterItemInfo {
id: number;
info: string;
@@ -8,6 +18,7 @@ export interface ClusterItemInfo {
users_count: number;
created_at: string;
user_id: number;
+ user_name: string;
userName: string;
modifiable: boolean;
resources: {
@@ -18,3 +29,29 @@ export interface ClusterItemInfo {
used: number;
}[];
}
+
+export interface ConfigInfo {
+ admin_base_dn: string;
+ admin_filter: string;
+ base_dn: string;
+ bind_dn: string;
+ costs: number;
+ created_at: string;
+ deletes: number;
+ email_attr: string;
+ enable: number;
+ entries: number;
+ fails: number;
+ filter: string;
+ id: number;
+ inserts: number;
+ last_sync_err_msg: string;
+ ldap_gen: number;
+ md5: number;
+ password: string;
+ server: string;
+ sync_protection_ts: number;
+ tls: number;
+ updates: number;
+ user_name_attr: string;
+}