Skip to content

Commit

Permalink
feat: add route group (#999)
Browse files Browse the repository at this point in the history
* feat: added route label search

* fix: react key warning

* feat: add label drawer

* feat: add save and cancel button in drawer

* feat: added edit feature in labelDrawer

* fix: lost click event when div child is empty

* feat: update route  transform with labels

* feat: add AutoComplete

* feat: use form.list render labels

* feat: clean code

* fix: warning when click save button

* feat: update fetchLabelList

* feat: update create route

* feat: update typing

* Merge branch master into feat-route-group-1

* Update component.ts

* feat: update code

* feat: update i18n

Co-authored-by: 琚致远 <[email protected]>
  • Loading branch information
LiteSun and juzhiyuan authored Dec 25, 2020
1 parent e0af89e commit d4792fc
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 18 deletions.
1 change: 1 addition & 0 deletions web/src/locales/en-US/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default {
'component.global.loading': 'Loading',
'component.global.list': 'List',
'component.global.description': 'Description',
'component.global.labels': 'Labels',
'component.global.operation': 'Operation',
'component.status.success': 'Successfully',
'component.status.fail': 'Failed',
Expand Down
1 change: 1 addition & 0 deletions web/src/locales/zh-CN/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default {
'component.global.loading': '加载中',
'component.global.list': '列表',
'component.global.description': '描述',
'component.global.labels': '标签',
'component.global.operation': '操作',
'component.status.success': '成功',
'component.status.fail': '失败',
Expand Down
12 changes: 7 additions & 5 deletions web/src/pages/Route/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ const Page: React.FC<Props> = (props) => {
if (action === 'advancedMatchingRulesChange') {
setAdvancedMatchingRules(data);
}
if (action === 'labelsChange') {
form1.setFieldsValue({ ...form1.getFieldsValue(), labels: data })
}
}}
isEdit={props.route.path.indexOf('edit') > 0}
/>
Expand Down Expand Up @@ -253,11 +256,10 @@ const Page: React.FC<Props> = (props) => {
return (
<>
<PageHeaderWrapper
title={`${
(props as any).match.params.rid
? formatMessage({ id: 'component.global.edit' })
: formatMessage({ id: 'component.global.create' })
} ${formatMessage({ id: 'menu.routes' })}`}
title={`${(props as any).match.params.rid
? formatMessage({ id: 'component.global.edit' })
: formatMessage({ id: 'component.global.create' })
} ${formatMessage({ id: 'menu.routes' })}`}
>
<Card bordered={false}>
<Steps current={step - 1} className={styles.steps}>
Expand Down
61 changes: 55 additions & 6 deletions web/src/pages/Route/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useRef, useState } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table';
import { Button, Popconfirm, notification, Tag, Space } from 'antd';
import { Button, Popconfirm, notification, Tag, Space, Select } from 'antd';
import { history, useIntl } from 'umi';
import { PlusOutlined, BugOutlined } from '@ant-design/icons';
import { timestampToLocaleString } from '@/helpers';

import { fetchList, remove, updateRouteStatus } from './service';
import { timestampToLocaleString } from '@/helpers';
import { fetchList, remove, fetchLabelList, updateRouteStatus } from './service';
import { DebugDrawView } from './components/DebugViews';


const { OptGroup, Option } = Select;

const Page: React.FC = () => {
const ref = useRef<ActionType>();
const { formatMessage } = useIntl();

const [labelList, setLabelList] = useState<RouteModule.LabelList>({});

useEffect(() => {
fetchLabelList().then(setLabelList);
}, []);
enum RouteStatus {
Offline = 0,
Publish,
Expand Down Expand Up @@ -90,6 +98,47 @@ const Page: React.FC = () => {
dataIndex: 'desc',
hideInSearch: true,
},
{
title: formatMessage({ id: 'component.global.labels' }),
dataIndex: 'labels',
render: (_, record) => {
return Object.keys(record.labels || {}).map((item) => (
<Tag key={Math.random().toString(36).slice(2)}>
{item}:{record.labels[item]}
</Tag>
));
},
renderFormItem: (_, { type }) => {
if (type === 'form') {
return null;
}

return (
<Select
mode="tags"
style={{ width: '100%' }}
tagRender={(props) => {
const { value, closable, onClose } = props;
return (
<Tag closable={closable} onClose={onClose} style={{ marginRight: 3 }}>
{value}
</Tag>
);
}}
>
{Object.keys(labelList).map((key) => {
return (
<OptGroup label={key} key={Math.random().toString(36).slice(2)}>
{(labelList[key] || []).map((value: string) => (
<Option key={Math.random().toString(36).slice(2)} value={`${key}:${value}`}> {value} </Option>
))}
</OptGroup>
);
})}
</Select>
);
}
},
{
title: formatMessage({ id: 'page.route.status' }),
dataIndex: 'status',
Expand All @@ -98,8 +147,8 @@ const Page: React.FC = () => {
{record.status ? (
<Tag color="green">{formatMessage({ id: 'page.route.published' })}</Tag>
) : (
<Tag color="red">{formatMessage({ id: 'page.route.unpublished' })}</Tag>
)}
<Tag color="red">{formatMessage({ id: 'page.route.unpublished' })}</Tag>
)}
</>
),
},
Expand Down
174 changes: 174 additions & 0 deletions web/src/pages/Route/components/Step1/LabelsDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
import { AutoComplete, Button, Col, Drawer, Form, notification, Row } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { useIntl } from 'umi';

import { transformLableValueToKeyValue } from '../../transform';
import { fetchLabelList } from '../../service';

interface Props extends Pick<RouteModule.Step1PassProps, 'onChange'> {
labelsDataSource: string[];
disabled: boolean;
onClose(): void;
}

const LabelList = (disabled: boolean, labelList: RouteModule.LabelList) => {
const { formatMessage } = useIntl();

const keyOptions = Object.keys(labelList || {}).map((item) => ({ value: item }));
return (
<Form.List name="labels">
{(fields, { add, remove }) => {
return (
<>
{fields.map((field, index) => (
<Form.Item
key={field.key}
label={index === 0 && 'Label'}
labelCol={{ span: index === 0 ? 3 : 0 }}
wrapperCol={{ offset: index === 0 ? 0 : 3 }}
>
<Row style={{ marginBottom: 10 }} gutter={16}>
<Col>
<Form.Item
style={{ marginBottom: 0 }}
name={[field.name, 'labelKey']}
rules={[
{
required: true,
message: 'Please input key',
},
]}
>
<AutoComplete options={keyOptions} style={{ width: 100 }} />
</Form.Item>
</Col>
<Col>
<Form.Item shouldUpdate noStyle>
{({ getFieldValue }) => {
const key = getFieldValue(['labels', field.name, 'labelKey']);
let valueOptions = [{ value: '' }];
if (labelList) {
valueOptions = (labelList[key] || []).map((item) => ({ value: item }));
}

return (
<Form.Item
noStyle
name={[field.name, 'labelValue']}
fieldKey={[field.fieldKey, 'labelValue']}
rules={[
{
required: true,
message: 'Please input value',
},
]}
>
<AutoComplete options={valueOptions} style={{ width: 100 }} />
</Form.Item>
);
}}
</Form.Item>
</Col>
<Col>
{!disabled && <MinusCircleOutlined onClick={() => remove(field.name)} />}
</Col>
</Row>
</Form.Item>
))}
{!disabled && (
<Form.Item wrapperCol={{ offset: 3 }}>
<Button type="dashed" onClick={add}>
<PlusOutlined />
{formatMessage({ id: 'component.global.add' })}
</Button>
</Form.Item>
)}
</>
);
}}
</Form.List>
);
};

const LabelsDrawer: React.FC<Props> = ({
disabled,
labelsDataSource,
onClose,
onChange = () => { },
}) => {
const transformLabel = transformLableValueToKeyValue(labelsDataSource);

const { formatMessage } = useIntl();
const [form] = Form.useForm();
const [labelList, setLabelList] = useState<RouteModule.LabelList>({});
form.setFieldsValue({ labels: transformLabel });

useEffect(() => {
fetchLabelList().then(setLabelList);
}, []);

return (
<Drawer
title="Edit labels"
placement="right"
width={512}
visible
closable
onClose={onClose}
footer={
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button onClick={onClose}>{formatMessage({ id: 'component.global.cancel' })}</Button>
<Button
type="primary"
style={{ marginRight: 8, marginLeft: 8 }}
onClick={(e) => {
e.persist();
form.validateFields().then(({ labels }) => {
const data = labels.map((item: any) => `${item.labelKey}:${item.labelValue}`)
// check for duplicates
if (new Set(data).size !== data.length) {
notification.warning({
message: `Config Error`,
description: 'Please do not enter duplicate labels',
});
return;
}

onChange({
action: 'labelsChange',
data,
});
onClose();
});
}}
>
{formatMessage({ id: 'component.global.confirm' })}
</Button>
</div >
}
>
<Form form={form} layout="horizontal">
{LabelList(disabled, labelList || {})}
</Form>
</Drawer >
);
};

export default LabelsDrawer;
50 changes: 47 additions & 3 deletions web/src/pages/Route/components/Step1/MetaView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,38 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React, { useState } from 'react';
import Form from 'antd/es/form';
import { Input, Switch } from 'antd';
import { Input, Switch, Select, Button, Tag } from 'antd';
import { useIntl } from 'umi';
import { PanelSection } from '@api7-dashboard/ui';

const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, isEdit }) => {
import { FORM_ITEM_WITHOUT_LABEL } from '@/pages/Route/constants';
import LabelsDrawer from './LabelsDrawer';

const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit, onChange, }) => {
const { formatMessage } = useIntl();
const [visible, setVisible] = useState(false);

return (
<PanelSection title={formatMessage({ id: 'page.route.panelSection.title.nameDescription' })}>
{visible && (
<Form.Item shouldUpdate noStyle>
{() => {
if (form.getFieldValue('labels')) {
return (
<LabelsDrawer
labelsDataSource={form.getFieldValue('labels')}
disabled={disabled || false}
onChange={onChange}
onClose={() => setVisible(false)}
/>
);
}
return null;
}}
</Form.Item>
)}
<Form.Item
label={formatMessage({ id: 'component.global.name' })}
name="name"
Expand All @@ -49,6 +70,29 @@ const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, isEdit }) =>
disabled={disabled}
/>
</Form.Item>
<Form.Item label={formatMessage({ id: 'component.global.labels' })} name="labels">
<Select
mode="tags"
style={{ width: '100%' }}
placeholder="--"
disabled={disabled}
open={false}
bordered={false}
tagRender={(props) => {
const { value, closable, onClose } = props;
return (
<Tag closable={closable && !disabled} onClose={onClose} style={{ marginRight: 3 }}>
{value}
</Tag>
);
}}
/>
</Form.Item>
<Form.Item {...FORM_ITEM_WITHOUT_LABEL}>
<Button disabled={disabled} onClick={() => setVisible(true)}>
{formatMessage({ id: 'component.global.edit' })}
</Button>
</Form.Item>
<Form.Item label={formatMessage({ id: 'component.global.description' })} name="desc">
<Input.TextArea
placeholder={formatMessage({ id: 'component.global.input.placeholder.description' })}
Expand Down
1 change: 1 addition & 0 deletions web/src/pages/Route/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const FORM_ITEM_WITHOUT_LABEL = {
export const DEFAULT_STEP_1_DATA: RouteModule.Form1Data = {
name: '',
desc: '',
labels:[],
status: 1,
priority: 0,
websocket: false,
Expand Down
Loading

0 comments on commit d4792fc

Please sign in to comment.