blob: deaa343f4ed3ddb673e18323fb5ad8c2c80939d6 [file] [log] [blame]
/*
* 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 Form from 'antd/es/form';
import { Input, Switch, Select, Button, Tag, AutoComplete, Row, Col, notification } from 'antd';
import { useIntl } from 'umi';
import PanelSection from '@/components/PanelSection';
import { FORM_ITEM_WITHOUT_LABEL } from '@/pages/Route/constants';
import LabelsDrawer from '@/components/LabelsfDrawer';
import { fetchLabelList, fetchServiceList } from '../../service';
const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit, upstreamForm, onChange = () => { } }) => {
const { formatMessage } = useIntl();
const [visible, setVisible] = useState(false);
const [labelList, setLabelList] = useState<LabelList>({});
const [serviceList, setServiceList] = useState<ServiceModule.ResponseBody[]>([]);
useEffect(() => {
fetchLabelList().then(setLabelList);
fetchServiceList().then(({ data }) => setServiceList(data));
}, []);
const NormalLabelComponent = () => {
const field = 'custom_normal_labels';
return (
<React.Fragment>
<Form.Item label={formatMessage({ id: 'component.global.labels' })} name={field} tooltip={formatMessage({ id: 'page.route.configuration.normal-labels.tooltip' })}>
<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 type="dashed" disabled={disabled} onClick={() => setVisible(true)}>
{formatMessage({ id: 'component.global.manage' })}
</Button>
</Form.Item>
{visible && (
<Form.Item shouldUpdate noStyle>
{() => {
const labels = form.getFieldValue(field) || [];
return (
<LabelsDrawer
title={formatMessage({ id: "component.label-manager" })}
actionName={field}
dataSource={labels}
disabled={disabled || false}
onChange={onChange}
onClose={() => setVisible(false)}
filterList={["API_VERSION"]}
fetchLabelList={fetchLabelList}
/>
);
}}
</Form.Item>
)}
</React.Fragment>
);
};
const VersionLabelComponent = () => {
return (
<Form.Item
label={formatMessage({ id: 'component.global.version' })} tooltip={formatMessage({ id: "page.route.configuration.version.tooltip" })}>
<Row>
<Col span={10}>
<Form.Item
noStyle
name="custom_version_label"
>
<AutoComplete
options={(labelList.API_VERSION || []).map((item) => ({ value: item }))}
disabled={disabled}
placeholder={formatMessage({ id: "page.route.configuration.version.placeholder" })}
/>
</Form.Item>
</Col>
</Row>
</Form.Item>
);
};
const Name: React.FC = () => (
<Form.Item label={formatMessage({ id: 'component.global.name' })} required tooltip={formatMessage({ id: 'page.route.form.itemRulesPatternMessage.apiNameRule' })}>
<Row>
<Col span={10}>
<Form.Item
noStyle
name="name"
rules={[
{
required: true,
message: formatMessage({ id: 'page.route.configuration.name.rules.required.description' }),
},
{
pattern: new RegExp(/^[a-zA-Z][a-zA-Z0-9_-]{0,100}$/, 'g'),
message: formatMessage({ id: 'page.route.form.itemRulesPatternMessage.apiNameRule' }),
},
]}
>
<Input
placeholder={formatMessage({ id: 'page.route.configuration.name.placeholder' })}
disabled={disabled}
/>
</Form.Item>
</Col>
</Row>
</Form.Item>
)
const Description: React.FC = () => (
<Form.Item label={formatMessage({ id: 'component.global.description' })}>
<Row>
<Col span={10}>
<Form.Item noStyle name="desc">
<Input.TextArea
placeholder={formatMessage({ id: 'component.global.input.placeholder.description' })}
disabled={disabled}
showCount
maxLength={256}
/>
</Form.Item>
</Col>
</Row>
</Form.Item>
)
const Publish: React.FC = () => (
<Form.Item label={formatMessage({ id: 'page.route.publish' })} tooltip={formatMessage({ id: 'page.route.configuration.publish.tooltip' })}>
<Row>
<Col>
<Form.Item
noStyle
name="status"
valuePropName="checked"
>
<Switch disabled={isEdit} />
</Form.Item>
</Col>
</Row>
</Form.Item>
)
const WebSocket: React.FC = () => (
<Form.Item label="WebSocket">
<Row>
<Col>
<Form.Item noStyle valuePropName="checked" name="enable_websocket">
<Switch disabled={disabled} />
</Form.Item>
</Col>
</Row>
</Form.Item>
)
const Redirect: React.FC = () => {
const list = [
{
value: "forceHttps",
label: formatMessage({ id: 'page.route.select.option.enableHttps' })
}, {
value: "customRedirect",
label: formatMessage({ id: 'page.route.select.option.configCustom' })
}, {
value: "disabled",
label: formatMessage({ id: 'page.route.select.option.forbidden' })
}
]
return (
<Form.Item label={formatMessage({ id: 'page.route.form.itemLabel.redirect' })} tooltip={formatMessage({ id: 'page.route.fields.custom.redirectOption.tooltip' })}>
<Row>
<Col span={5}>
<Form.Item
name="redirectOption"
noStyle
>
<Select
disabled={disabled}
data-cy='route-redirect'
onChange={(params) => {
onChange({ action: 'redirectOptionChange', data: params });
}}
>
{list.map(item => (
<Select.Option value={item.value} key={item.value}>
{item.label}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
</Row>
</Form.Item>
)
}
const CustomRedirect: React.FC = () => (
<Form.Item
noStyle
shouldUpdate={(prev, next) => {
if (prev.redirectOption !== next.redirectOption) {
onChange({ action: 'redirectOptionChange', data: next.redirectOption });
}
return prev.redirectOption !== next.redirectOption;
}}
>
{() => {
if (form.getFieldValue('redirectOption') === 'customRedirect') {
return (
<Form.Item
label={formatMessage({ id: 'page.route.form.itemLabel.redirectCustom' })}
required
style={{ marginBottom: 0 }}
>
<Row gutter={10}>
<Col span={5}>
<Form.Item
name="redirectURI"
rules={[
{
required: true,
message: `${formatMessage({
id: 'component.global.pleaseEnter',
})}${formatMessage({
id: 'page.route.form.itemLabel.redirectURI',
})}`,
},
]}
>
<Input
placeholder={formatMessage({
id: 'page.route.input.placeholder.redirectCustom',
})}
disabled={disabled}
/>
</Form.Item>
</Col>
<Col span={5}>
<Form.Item name="ret_code" rules={[{ required: true }]}>
<Select disabled={disabled} data-cy='redirect_code'>
<Select.Option value={301}>
{formatMessage({ id: 'page.route.select.option.redirect301' })}
</Select.Option>
<Select.Option value={302}>
{formatMessage({ id: 'page.route.select.option.redirect302' })}
</Select.Option>
</Select>
</Form.Item>
</Col>
</Row>
</Form.Item>
);
}
return null;
}}
</Form.Item>
)
const ServiceSelector: React.FC = () => (
<React.Fragment>
<Form.Item label={formatMessage({ id: 'page.route.service' })} tooltip={formatMessage({ id: 'page.route.fields.service_id.tooltip' })}>
<Row>
<Col span={5}>
<Form.Item noStyle name="service_id">
<Select disabled={disabled}>
{/* TODO: value === '' means no service_id select, need to find a better way */}
<Select.Option value="" key={Math.random().toString(36).substring(7)}>
{formatMessage({ id: "page.route.service.none" })}
</Select.Option>
{serviceList.map((item) => {
return (
<Select.Option value={item.id} key={item.id}>
{item.name}
</Select.Option>
);
})}
</Select>
</Form.Item>
</Col>
</Row>
</Form.Item>
<Form.Item noStyle shouldUpdate={(prev, next) => {
// route with redirect plugin can be edit without service and upstream
if (next.redirectOption === 'customRedirect') {
return false;
}
if (next.service_id === '') {
const upstream_id = upstreamForm?.getFieldValue('upstream_id')
if (upstream_id === 'None') {
notification.warning({
message: formatMessage({ id: 'page.route.fields.service_id.invalid' }),
description: formatMessage({ id: 'page.route.fields.service_id.without-upstream' })
})
}
}
return prev.service_id !== next.service_id
}}>
{() => null}
</Form.Item>
</React.Fragment>
)
return (
<PanelSection title={formatMessage({ id: 'page.route.panelSection.title.nameDescription' })}>
<Name />
<NormalLabelComponent />
<VersionLabelComponent />
<Description />
<Redirect />
<CustomRedirect />
<ServiceSelector />
<WebSocket />
<Publish />
</PanelSection>
);
};
export default MetaView;