antd#FormProps TypeScript Examples
The following examples show how to use
antd#FormProps.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: FormWizardStore.ts From jmix-frontend with Apache License 2.0 | 5 votes |
setValidateMessages(validateMessages: FormProps['validateMessages']) {
this.validateMessages = validateMessages;
}
Example #2
Source File: FormWizardStore.ts From jmix-frontend with Apache License 2.0 | 5 votes |
validateMessages: FormProps['validateMessages'] = undefined;
Example #3
Source File: SourceEditorFormConfigurationConfigurableFields.tsx From jitsu with MIT License | 5 votes |
SourceEditorFormConfigurationConfigurableFields: React.FC<Props> = memo(
({
initialValues,
configParameters,
availableOauthBackendSecrets,
hideFields,
patchConfig,
setValidator,
setFormReference,
}) => {
const [form] = Form.useForm()
// get form fields from source connector
const handleFormValuesChange = useCallback<(values: PlainObjectWithPrimitiveValues) => void>(values => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
}, [])
const handleFormValuesChangeForm: FormProps<PlainObjectWithPrimitiveValues>["onValuesChange"] = (_, values) => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
}
const handleSetInitialFormValues = useCallback<(values: PlainObjectWithPrimitiveValues) => void>(values => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { doNotSetStateChanged: true })
}, [])
/**
* set validator and form reference on first render
*/
useEffect(() => {
const validateGetErrorsCount: ValidateGetErrorsCount = async () => {
let errorsCount = 0
try {
await form.validateFields()
} catch (error) {
errorsCount = +error?.errorFields?.length
}
return errorsCount
}
setValidator(() => validateGetErrorsCount)
setFormReference(CONFIG_FORM_KEY, form, handleFormValuesChange)
}, [])
return (
<Form
id={"SourceEditorFormConfigurationConfigurableFields"}
form={form}
onValuesChange={handleFormValuesChangeForm}
>
<ConfigurableFieldsForm
form={form}
initialValues={initialValues}
fieldsParamsList={configParameters}
availableOauthBackendSecrets={availableOauthBackendSecrets}
hideFields={hideFields}
setFormValues={handleFormValuesChange}
setInitialFormValues={handleSetInitialFormValues}
/>
</Form>
)
}
)
Example #4
Source File: create-add-on.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
class CreateAddOn extends PureComponent<ICreateAddOnProps & FormProps, any> {
formRef = React.createRef<FormInstance>();
state = {
groups: [],
packUpTabs: new Set(),
searchValue: undefined,
editAddon: null,
selectedAddon: null,
// 是否重置过, 重置过的就不显示警告了
isReset: false,
selectedAddonVersions: [],
versionMap: {},
selectedAddonPlans: [],
};
static getDerivedStateFromProps(nextProps: Readonly<ICreateAddOnProps>, prevState: any): any {
const result = getGroupData(nextProps);
if (
!isEqual(nextProps.groupedAddonList, prevState.propsGroupedAddonList) ||
!isEqual(result, prevState.editAddon)
) {
const { groupedAddonList, groups } = convertData(nextProps.groupedAddonList);
let addonResource: any = null;
if (nextProps.addOn && result) {
addonResource = groupedAddonList.find((item: IAddon) => {
const compareVal = (result.name === 'custom' ? result.alias : result.name).toLowerCase();
// custom的addon不能编辑alias,用alais比对
return item.name.toLowerCase() === compareVal;
});
}
const packUpTabs = new Set();
groups.forEach((i: IAddonGroup) => {
packUpTabs.add(i.groupName);
});
return {
groups,
packUpTabs,
selectedAddon: addonResource,
editAddon: result,
propsGroupedAddonList: nextProps.groupedAddonList,
originGroupedAddonList: groupedAddonList,
};
}
return prevState;
}
componentDidMount() {
const { selectedAddon } = this.state;
if (selectedAddon) {
this.getAddonVersions();
}
}
componentDidUpdate(prevProps, prevState) {
const { selectedAddon } = this.state;
if (selectedAddon && selectedAddon !== prevState.selectedAddon) {
this.getAddonVersions();
}
}
render() {
const { editing, addOn } = this.props;
const { selectedAddon, searchValue, isReset } = this.state;
let alert;
if (addOn && !selectedAddon && !isReset) {
alert = (
<ErdaAlert
className="addon-error-tag"
message={
editing
? i18n.t('dop:the current instance does not exist, please add it again!')
: i18n.t('dop:yml-addon-not-exist-tip')
}
type="error"
/>
);
}
let content = (
<React.Fragment>
<Input.Search
autoFocus
disabled={!editing}
onFocus={this.onFocus}
onClick={this.openSelect}
onChange={this.searchInputChange}
value={searchValue}
className="add-on-input"
placeholder={`${i18n.t('dop:Please select')} Add-on`}
/>
</React.Fragment>
);
if (selectedAddon) {
content = (
<React.Fragment>
<AddOn reselect={editing} reselectFunc={this.clear} addon={selectedAddon} />
</React.Fragment>
);
}
const className = selectedAddon ? 'selected-addon' : null;
const showContent = (
<>
<div className="add-on-head">{content}</div>
{!selectedAddon ? this.renderSelectContent() : this.renderForm()}
</>
);
return (
<div className={classnames('add-on-select', className)}>
{alert ? (
editing ? (
<>
{alert}
{showContent}
</>
) : (
alert
)
) : (
showContent
)}
</div>
);
}
private getAddonVersions = () => {
const { selectedAddon } = this.state;
if (selectedAddon.addonName || selectedAddon.name) {
this.props.getAddonVersions(selectedAddon.addonName || selectedAddon.name).then((data) => {
this.setState({
selectedAddonVersions: map(data, (item) => item.version),
versionMap: keyBy(data, 'version'),
selectedAddonPlans: Object.keys(data[0].spec.plan || { basic: {} }),
});
});
}
};
private renderForm = () => {
const { selectedAddon, editAddon, selectedAddonVersions, versionMap, selectedAddonPlans } = this.state;
const { cancel, editing } = this.props;
const form = this.formRef.current || {};
const { getFieldValue, setFieldsValue } = form;
if (!selectedAddon) {
return null;
}
let nameValue;
let versionValue;
let planValue;
if (editAddon) {
if (editAddon.version) {
versionValue = editAddon.version;
}
nameValue = editAddon.alias || editAddon.name;
if (editAddon.config) {
planValue = editAddon.config;
}
if (selectedAddon && selectedAddonPlans.length && !planValue) {
planValue = selectedAddonPlans[0];
}
if (selectedAddon && selectedAddonVersions.length && !versionValue) {
versionValue = selectedAddonVersions[0];
}
} else if (selectedAddon && selectedAddon.instanceId) {
// 如果是 addon 实例,则只读
nameValue = selectedAddon.name;
versionValue = selectedAddon.version;
planValue = selectedAddon.plan;
}
const name = (
<Item
name="alias"
label={i18n.t('Name')}
initialValue={nameValue}
rules={[
{
required: true,
message: i18n.t('dop:Please enter the name'),
},
]}
>
<Input autoFocus disabled={this.isEditing()} placeholder={i18n.t('dop:Please enter the name')} />
</Item>
);
const version = (
<Item
name="version"
label={i18n.t('Version')}
initialValue={versionValue}
rules={[
{
required: true,
message: i18n.t('dop:please select the version'),
},
]}
>
<Select
disabled={this.isEditing()}
className="w-full"
placeholder={i18n.t('dop:please select the version')}
onSelect={() => setFieldsValue?.({ plan: undefined })}
>
{selectedAddonVersions.map((v: string) => (
<Option key={v}>{v}</Option>
))}
</Select>
</Item>
);
// @ts-ignore
let plans = [];
if (selectedAddon.plan) {
plans.push({
plan: planValue,
planCnName: PLAN_NAME[planValue],
});
} else if (getFieldValue?.('version') && !isEmpty(versionMap)) {
plans = map(versionMap[getFieldValue?.('version')].spec.plan || { basic: {} }, (_, k) => ({
plan: k,
planCnName: PLAN_NAME[k],
}));
} else if (selectedAddonPlans?.length) {
plans = map(selectedAddonPlans, (k) => ({ plan: k, planCnName: PLAN_NAME[k] }));
} else {
plans = map({ basic: {} }, (_, k) => ({
plan: k,
planCnName: PLAN_NAME[k],
}));
}
const plan = (
<Item
name="plan"
label={i18n.t('dop:Configuration')}
initialValue={convertAddonPlan(planValue)}
rules={[
{
required: true,
message: i18n.t('dop:please select configuration'),
},
]}
>
<RadioGroup disabled={this.isEditing()}>
{plans.map((p: any) => (
<RadioButton key={p.plan} value={p.plan}>
{p.planCnName}
</RadioButton>
))}
</RadioGroup>
</Item>
);
return (
<Form ref={this.formRef} layout="vertical" className="add-on-form">
{name}
{version}
{plan}
{editing ? (
<Item className="add-on-form-btn-group">
<Button className="mr-2" onClick={cancel}>
{i18n.t('Cancel')}
</Button>
<Button type="primary" onClick={this.submitAddon}>
{i18n.t('Save')}
</Button>
</Item>
) : null}
</Form>
);
};
private isEditing() {
const { editing } = this.props;
const { editAddon, selectedAddon } = this.state;
return !editing || (editAddon && editAddon.instanceId) || (selectedAddon && selectedAddon.instanceId);
}
private submitAddon = () => {
const { selectedAddon, editAddon } = this.state;
const { onSubmit, cancel } = this.props;
const form = this.formRef.current;
form
?.validateFields()
.then((values: any) => {
onSubmit({
...values,
originName: editAddon ? editAddon.alias : null,
plan: `${selectedAddon.addonName || selectedAddon.name}:${values.plan}`,
});
cancel();
})
.catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
form?.scrollToField(errorFields[0].name);
});
};
private openSelect = () => {
const { editing } = this.props;
if (!editing) {
return;
}
this.setState({
isSelected: !this.state.isSelected,
});
};
private onFocus = (e: any) => {
const { editing } = this.props;
if (!editing) {
return;
}
e.stopPropagation();
};
private clear = () => {
this.setState({
isReset: true,
searchValue: null,
isSelected: false,
selectedAddon: null,
});
};
private renderSelectContent = () => {
const { editing } = this.props;
const { packUpTabs, groups, selectedAddon, searchValue } = this.state;
return groups.map((group: IAddonGroup) => {
let addonsContent = [];
let headClass = 'empty-content';
if (packUpTabs.has(group.groupName)) {
addonsContent = group.data.map((addon: IAddon) => {
if (searchValue && !addon.name.includes(searchValue) && !addon.displayName.includes(searchValue)) {
return null;
}
headClass = null;
let activeClass = null;
// @ts-ignore
if (selectedAddon && selectedAddon.instanceId === addon.instanceId) {
activeClass = 'add-on-selected';
}
return (
<AddOn
editing={editing}
className={activeClass}
addon={addon}
key={addon.instanceId || addon.id}
onClick={this.selectedAddonAction}
/>
);
});
}
const icon = packUpTabs.has(group.groupName) ? (
<ErdaIcon type="down" className="head-icon" size="18px" />
) : (
<ErdaIcon type="up" className="head-icon" size="18px" />
);
const content = packUpTabs.has(group.groupName) ? <div className="addon-group-body">{addonsContent}</div> : null;
return (
<div key={group.groupName} className="yml-addon-group">
<div
className={classnames('addon-group-head', headClass)}
onClick={() => this.triggerGroupTab(group.groupName)}
>
{icon}
<span className="group-name">{group.groupName}</span>
</div>
{content}
</div>
);
});
};
private triggerGroupTab = (name: string) => {
const { packUpTabs } = this.state;
if (packUpTabs.has(name)) {
packUpTabs.delete(name);
} else {
packUpTabs.add(name);
}
this.setState({
packUpTabs: cloneDeep(packUpTabs),
});
};
private searchInputChange = (e: any) => {
const { editing } = this.props;
if (!editing) {
return;
}
this.setState({
searchValue: e.target.value,
});
};
private selectedAddonAction = (addon: IAddon) => {
this.setState({
selectedAddon: addon,
searchValue: addon.name,
});
};
}
Example #5
Source File: edit-global-variable.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
class EditGlobalVariable extends PureComponent<IEditGlobalVariableProps & FormProps, any> {
formRef = React.createRef<FormInstance>();
state = {
globalVariableList: [],
};
UNSAFE_componentWillMount(): void {
const list = convertGlobalVariableList(this.props.globalVariable);
this.setState({
globalVariableList: list.map((i: any) => ({
...i,
id: uniqueId('var-'),
})),
});
}
render() {
const { globalVariableList } = this.state;
const { editing } = this.props;
const content = globalVariableList.map((item: any) => {
const input = <VariableInputGroup lock={false} disabled={!editing} onDelete={this.deleteVariable} />;
return (
<Item
className="mr-0"
key={item.key}
name={item.id}
initialValue={item}
rules={[
{
required: true,
message: i18n.t('dop:environment variables cannot be empty'),
},
]}
>
{input}
</Item>
);
});
return (
<Form ref={this.formRef} className="global-input-form">
<div className="global-input-form-title">
{i18n.t('dop:global environment variable')}
{editing ? (
<ErdaIcon type="plus" className="variable-icon cursor-pointer" onClick={this.addNewVariable} />
) : null}
</div>
{content}
<div className="mt-3">
{editing ? (
<Button type="primary" ghost onClick={this.onSubmit}>
{i18n.t('Save')}
</Button>
) : null}
</div>
</Form>
);
}
private deleteVariable = (key: string) => {
const { globalVariableList } = this.state;
const index = findIndex(globalVariableList, (item: any) => item.key === key);
globalVariableList.splice(index, 1);
this.setState({
globalVariableList: cloneDeep(globalVariableList),
});
};
private onSubmit = () => {
const { onSubmit } = this.props;
const form = this.formRef.current;
form
?.validateFields()
.then((values: any) => {
const object = {};
forEach(values, (item: any, originKey: string) => {
if (item.key !== '') {
object[item.key] = item.value;
} else {
form?.setFields({
[originKey]: {
value: item,
errors: [new Error(i18n.t('dop:environment variables cannot be empty'))],
},
});
}
});
onSubmit(object);
})
.catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
form?.scrollToField(errorFields[0].name);
});
};
private addNewVariable = () => {
const { globalVariableList } = this.state;
globalVariableList.push({
id: uniqueId('var-'),
key: 'key',
value: 'value',
});
this.setState({
globalVariableList: cloneDeep(globalVariableList),
});
};
}
Example #6
Source File: edit-stage.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
EditStage = (props: IEditStageProps & FormProps) => {
const [form] = Form.useForm();
const [state, updater] = useUpdate({
task: {} as IStageTask | {},
actionConfig: {} as DEPLOY.ActionConfig | {},
resource: {},
originType: null as null | string,
originName: null as null | string,
});
const initialValue = React.useRef({});
const { task, actionConfig, resource, originName, originType } = state;
const { actions, otherTaskAlias, editing, isCreateTask, onSubmit: handleSubmit, task: PropsTask } = props;
const { getFieldValue } = form;
const actionConfigs = deployStore.useStore((s) => s.actionConfigs);
const { getActionConfigs } = deployStore.effects;
const [loading] = useLoading(deployStore, ['getActionConfigs']);
React.useEffect(() => {
if (!isEmpty(PropsTask)) {
updater.originName(PropsTask.alias);
updater.originType(PropsTask.type);
updater.task(PropsTask);
}
}, [PropsTask, updater]);
React.useEffect(() => {
let config;
if (actionConfigs.length > 0) {
config = PropsTask.version
? actionConfigs.find((c) => c.version === PropsTask.version)
: getDefaultVersionConfig(actionConfigs);
}
const newResource = getResource(PropsTask, config);
updater.resource(newResource);
updater.actionConfig(config as DEPLOY.ActionConfig);
}, [actionConfigs, PropsTask, updater]);
React.useEffect(() => {
if (isCreateTask) {
updater.actionConfig({});
}
}, [isCreateTask, updater]);
if (!isCreateTask && isEmpty(actionConfig)) {
return null;
}
const type = actionConfig.type || getFieldValue(['resource', 'type']);
const taskInitName =
originType === actionConfig.name
? originName
: otherTaskAlias.includes(actionConfig.name)
? undefined
: actionConfig.name;
const changeResourceType = (value: string) => {
const action = actions.find((a: any) => a.name === value);
if (action) {
getActionConfigs({ actionType: action.name }).then((result: DEPLOY.ActionConfig[]) => {
const config = getDefaultVersionConfig(result);
const mergedResource = mergeActionAndResource(config, {} as any);
updater.resource({
...resource,
...mergedResource,
});
});
}
};
const checkResourceName = (_rule: any, value: string, callback: any) => {
const name = form.getFieldValue(['resource', 'alias']);
if (!value) {
return callback(i18n.t('dop:please enter the task name'));
}
if (otherTaskAlias.includes(name)) {
return callback(i18n.t('dop:An Action with the same name exists.'));
}
callback();
};
const changeActionVersion = (version: string) => {
const selectConfig = actionConfigs.find((config) => config.version === version) as DEPLOY.ActionConfig;
updater.actionConfig(selectConfig);
updater.resource(getResource(task, selectConfig));
};
const taskType = (
<Item
name="resource.type"
initialValue={task.type}
rules={[
{
required: true,
message: `${i18n.t('dop:Please select')}Task Type`,
},
]}
>
<ActionSelect
disabled={!editing}
label={i18n.t('Task type')}
actions={actions}
onChange={changeResourceType}
placeholder={`${i18n.t('dop:Please choose the task type')}`}
/>
</Item>
);
const actionVersion = (
<Item
label="version"
name="resource.version"
initialValue={task.version || actionConfig.version}
rules={[
{
required: true,
message: `${i18n.t('dop:Please select')}Task Version`,
},
]}
>
<Select disabled={!editing} onChange={changeActionVersion} placeholder={`${i18n.t('dop:please choose version')}`}>
{actionConfigs.map((config) => (
<Option key={config.version} value={config.version}>
{config.version}
</Option>
))}
</Select>
</Item>
);
let alert;
if (!isCreateTask && isEmpty(resource)) {
return null;
}
if (!isCreateTask && !actionConfig.type) {
alert = (
<ErdaAlert
className="addon-error-tag"
message={i18n.t('dop:the current action does not exist, please re-select!')}
type="error"
/>
);
}
const taskName = (
<Item
label={i18n.t('dop:task name')}
name="resource.alias"
initialValue={taskInitName}
rules={[
{
required: true,
validator: checkResourceName,
},
]}
>
<Input autoFocus={!type} disabled={!editing} placeholder={i18n.t('dop:please enter the task name')} />
</Item>
);
const renderTaskTypeStructure = () => {
if (isEmpty(resource)) {
return null;
}
const { getFieldsValue } = form;
const resourceForm = getFieldsValue(['resource.alias', 'resource.type']);
if (!resourceForm.resource.type) {
return null;
}
return renderResource(resource, 'resource');
};
const getDataValue = (dataSource: any, key: string) => {
return dataSource ? dataSource[key] : null;
};
const renderResource = (resourceParam: any, parentKey?: string, dataSource?: any) => {
if (resourceParam.data instanceof Array) {
return resourceParam.data.map((item: any) => {
const inputKey = parentKey ? `${parentKey}.${item.name}` : `${item.name}`;
return renderObject(item, inputKey, getDataValue(dataSource, item.name));
});
}
const { params, image, resources } = resourceParam.data;
const parentObjectData = getDataValue(dataSource, 'params');
const paramsContent = map(params, (value: any, itemKey: string) => {
const inputKey = parentKey ? `${parentKey}.params.${itemKey}` : `params.${itemKey}`;
return renderObject(value, inputKey, getDataValue(parentObjectData, itemKey));
});
return (
<>
{actionConfig.name === 'custom-script' ? (
<div>{renderObject(image, 'resource.image', getDataValue(dataSource, 'image'))}</div>
) : null}
<div>
<div className="resource-input-group-title">params: </div>
{paramsContent}
</div>
<div>{renderObject(resources, 'resource.resources', getDataValue(dataSource, 'resources'))}</div>
</>
);
};
const renderObject = (value: any, parentKey: string, dataSource?: any) => {
if (!isObject(value.type)) {
return renderPropertyValue(value, parentKey, dataSource);
}
if (value.type === 'string_array') {
return renderStringArray(value, parentKey);
}
if (value.type === 'struct_array') {
return renderStructArray(value, parentKey);
}
if (value.type === 'map') {
return renderMap(value, parentKey, dataSource);
}
const content = renderResource({ data: value.struct }, parentKey, dataSource);
if (!content || !Object.values(content).some((c) => c)) return null;
return (
<div key={parentKey}>
<span className="resource-input-group-title">{value.name}: </span>
<div>{content}</div>
</div>
);
};
const renderMap = (value: any, parentKey: string, dataSource?: any) => {
let initialValue = isCreateTask ? value.default : value.value || value.default;
if (dataSource) {
initialValue = dataSource;
}
if (!editing && !initialValue) {
return null;
}
const inputField = (
<Item
key={parentKey}
name={parentKey}
initialValue
rules={[
{
required: value.required,
message: i18n.t('dop:this item cannot be empty'),
},
]}
>
{renderTooltip(value.desc, <VariableInput disabled={!editing} label={value.name} />)}
</Item>
);
return inputField;
};
const renderStringArray = (value: any, parentKey: string) => {
const inputField = (
<Item
key={parentKey}
name={parentKey}
initialValue={isCreateTask ? value.default : value.value || value.default}
rules={[
{
required: value.required,
message: i18n.t('dop:this item cannot be empty'),
},
]}
>
{renderTooltip(value.desc, <ListInput disabled={!editing} label={value.name} />)}
</Item>
);
return inputField;
};
const renderPropertyValue = (value: any, parentKey: string, dataSource?: any) => {
let input;
let initialValue = isCreateTask ? value.default : value.value || value.default;
if (dataSource) {
initialValue = dataSource;
}
if (!editing && !initialValue) {
return null;
}
const unit = value.unit ? <span>{value.unit}</span> : null;
switch (value.type) {
case 'float':
case 'int':
input = (
<InputNumber
disabled={!editing || value.readOnly}
className="w-full"
placeholder={i18n.t('dop:please enter data')}
/>
);
break;
default:
input = (
<Input
disabled={!editing || value.readOnly}
placeholder={i18n.t('dop:please enter data')}
addonAfter={unit}
/>
);
break;
}
const inputField = (
<Item
key={parentKey}
label={value.name}
name={parentKey}
initialValue
rules={[
{
required: value.required,
message: i18n.t('dop:this item cannot be empty'),
},
]}
>
{renderTooltip(value.desc, input)}
</Item>
);
return inputField;
};
const renderStructArray = (property: any, parentKey: string) => {
if ((!editing && !property.value) || (!editing && property.value && !property.value.length)) {
return null;
}
const addBtn = editing ? (
<ErdaIcon
type="plus"
className="cursor-pointer"
onClick={() => addNewItemToStructArray(property.value, property.struct[0])}
/>
) : null;
initialValue.current = {
[`${parentKey}-data`]: property.value || [],
};
const data = getFieldValue(`${parentKey}-data`);
const content = data.map((item: any, index: number) => {
const keys = Object.keys(item);
const header = (
<div>
<span>{typeof item[keys[0]] === 'string' ? item[keys[0]] : 'module'}</span>
{editing ? (
<CustomIcon
onClick={() => deleteItemFromStructArray(index, parentKey)}
className="icon-delete"
type="sc1"
/>
) : null}
</div>
);
return (
<Panel key={`${parentKey}.${item.name}`} header={header}>
{renderResource({ data: property.struct }, `${parentKey}[${index}]`, item)}
</Panel>
);
});
return (
<div key={parentKey}>
<span className="resource-input-group-title">
{property.name}:{addBtn}
</span>
{data.length ? (
<Collapse className="collapse-field" accordion>
{content}
</Collapse>
) : null}
</div>
);
};
const deleteItemFromStructArray = (index: number, parentKey: string) => {
const formDatas = form.getFieldValue(`${parentKey}-data`);
formDatas.splice(index, 1);
form.setFieldsValue({
[parentKey]: formDatas,
});
};
const addNewItemToStructArray = (list: any[], struct: any) => {
list.push({
[struct.name]: `module-${list.length + 1}`,
});
updater.resource(cloneDeep(resource));
};
const isObject = (inputType: string) => {
return ['map', 'string_array', 'struct_array', 'struct'].includes(inputType);
};
const renderTooltip = (message: string, text: any) => {
if (!message) {
return text;
}
const msgComp = <pre className="prop-popover">{message}</pre>;
return (
<Popover placement="leftTop" trigger={['focus']} content={msgComp}>
{text}
</Popover>
);
};
const onSubmit = () => {
form
.validateFields()
.then((values: any) => {
let data = cloneDeep(values);
const resources = head(filter(state.resource.data, (item) => item.name === 'resources'));
const originResource = transform(
get(resources, 'struct'),
(result, item: { name: string; default: string | number }) => {
const { name, default: d } = item;
// eslint-disable-next-line no-param-reassign
result[name] = +d;
},
{},
);
const editedResources = get(data, 'resource.resources');
forEach(Object.entries(editedResources), ([key, value]) => {
editedResources[key] = +(value as string);
});
const isResourceDefault = isEqual(editedResources, originResource);
if (isResourceDefault) {
data = omit(data, ['resource.resources']);
}
const filledFieldsData = clearEmptyField(data);
handleSubmit(filledFieldsData);
})
.catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
form.scrollToField(errorFields[0].name);
});
};
const clearEmptyField = (ObjData: any) => {
const filledFields: string[] = [];
const findData = (obj: any, parentArray: string[]) => {
Object.keys(obj).forEach((key) => {
const currentParent = [...parentArray, key];
const value = get(obj, key);
if (typeof value === 'object') {
findData(value, currentParent);
} else if (value || value === 0) {
filledFields.push(currentParent.join('.'));
}
});
};
findData(ObjData, []);
return pick(ObjData, filledFields);
};
return (
<Spin spinning={loading}>
<Form form={form} initialValues={initialValue.current} className="edit-service-container">
{alert}
{taskType}
{type ? taskName : null}
{actionVersion}
{renderTaskTypeStructure()}
{editing ? (
<Button type="primary" ghost onClick={onSubmit}>
{i18n.t('Save')}
</Button>
) : null}
</Form>
</Spin>
);
}
Example #7
Source File: pipeline-node-drawer.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
PurePipelineNodeForm = (props: IEditStageProps & FormProps) => {
const [form] = Form.useForm();
const {
nodeData: propsNodeData,
editing,
isCreate,
otherTaskAlias = [],
onSubmit: handleSubmit = noop,
chosenActionName,
chosenAction,
actionSpec,
} = props;
const [actionConfigs] = appDeployStore.useStore((s) => [s.actionConfigs]);
const { getFieldValue } = form;
const [{ actionConfig, resource, originType, originName, task, changeKey }, updater, update] = useUpdate({
resource: {},
actionConfig: {} as DEPLOY.ActionConfig,
originType: null as null | string,
originName: null as null | string,
task: {} as IStageTask,
changeKey: 0,
});
useEffectOnce(() => {
handleActionSpec();
});
React.useEffect(() => {
if (propsNodeData && !isEmpty(propsNodeData)) {
update({
originName: propsNodeData.alias,
originType: propsNodeData.type,
task: propsNodeData,
});
}
}, [propsNodeData, update]);
React.useEffect(() => {
if (isCreate) {
updater.actionConfig({} as DEPLOY.ActionConfig);
}
}, [isCreate, updater]);
const taskInitName =
originType === actionConfig.name
? originName
: otherTaskAlias.includes(actionConfig.name)
? undefined
: actionConfig.name;
const taskInitVersion = task.version || actionConfig.version;
useUpdateEffect(() => {
const prevResource = form.getFieldValue('resource') || {};
form.setFieldsValue({
resource: {
...prevResource,
type: chosenActionName,
alias: taskInitName,
version: taskInitVersion,
},
});
updater.changeKey((prev: number) => prev + 1);
}, [taskInitName, taskInitVersion, chosenActionName]);
const handleActionSpec = () => {
let _config;
let _resource;
if (propsNodeData && !isEmpty(propsNodeData)) {
if (actionSpec.length > 0) {
_config = propsNodeData.version
? actionSpec.find((c) => c.version === propsNodeData.version)
: getDefaultVersionConfig(actionSpec);
}
_resource = getResource(propsNodeData, _config);
} else {
_config = getDefaultVersionConfig(actionSpec);
const mergedResource = mergeActionAndResource(_config, {});
_resource = { ...resource, ...mergedResource };
}
update({
resource: _resource,
actionConfig: _config || ({} as DEPLOY.ActionConfig),
});
};
useUpdateEffect(() => {
handleActionSpec();
}, [actionSpec]);
if (!isCreate && isEmpty(actionConfig)) {
return null;
}
const type = actionConfig.type || getFieldValue(['resource', 'type']);
const checkResourceName = (_rule: any, value: string, callback: any) => {
const name = form.getFieldValue(['resource', 'alias']);
if (!value) {
return callback(i18n.t('dop:please enter the task name'));
}
if (otherTaskAlias.includes(name)) {
return callback(i18n.t('dop:An Action with the same name exists.'));
}
callback();
};
const changeActionVersion = (version: any) => {
const selectConfig = actionConfigs.find((config) => config.version === version) as DEPLOY.ActionConfig;
updater.actionConfig(selectConfig);
updater.resource(getResource(task as IStageTask, selectConfig));
};
const taskType = (
<Item
className="hidden"
name={['resource', 'type']}
initialValue={chosenActionName}
rules={[
{
required: true,
message: `${i18n.t('dop:Please select')}Task Type`,
},
]}
>
<Input />
</Item>
);
const loopData = (
<Item className="hidden" name={['resource', 'loop']} initialValue={get(actionConfig, 'spec.loop')} />
);
const actionVersion = (
<Item
label={i18nMap.version}
name={['resource', 'version']}
initialValue={task.version || actionConfig.version}
rules={[
{
required: true,
message: `${i18n.t('dop:Please select')}Task Version`,
},
]}
>
<Select disabled={!editing} onChange={changeActionVersion} placeholder={`${i18n.t('dop:please choose version')}`}>
{actionConfigs.map((config) => (
<Option key={config.version} value={config.version}>
{config.version}
</Option>
))}
</Select>
</Item>
);
let alert;
if (!isCreate && !actionConfig.type) {
alert = (
<ErdaAlert
className="addon-error-tag"
showIcon
message={i18n.t('dop:the current action does not exist, please re-select!')}
type="error"
/>
);
}
const taskName = (
<Item
label={i18n.t('dop:task name')}
name={['resource', 'alias']}
initialValue={taskInitName}
rules={[
{
required: true,
validator: checkResourceName,
},
]}
>
<Input autoFocus={!type} disabled={!editing} placeholder={i18n.t('dop:please enter the task name')} />
</Item>
);
const renderTaskTypeStructure = () => {
if (isEmpty(resource)) {
return null;
}
const { getFieldsValue } = form;
const resourceForm = getFieldsValue([
['resource', 'alias'],
['resource', 'type'],
]);
if (!get(resourceForm, 'resource.type')) {
return null;
}
return renderResource(resource, 'resource');
};
const getDataValue = (dataSource: any, key: string) => {
return dataSource ? dataSource[key] : null;
};
const renderResource = (resourceParam: any, parentKey?: string, dataSource?: any) => {
if (resourceParam.data instanceof Array) {
return resourceParam.data.map((item: any) => {
const inputKey = parentKey ? `${parentKey}.${item.name}` : `${item.name}`;
return renderObject(item, inputKey, getDataValue(dataSource, item.name));
});
}
const { params, image, resources } = resourceParam.data;
const parentObjectData = getDataValue(dataSource, 'params');
const paramsContent = map(params, (value: any, itemKey: string) => {
const inputKey = parentKey ? `${parentKey}.params.${itemKey}` : `params.${itemKey}`;
return renderObject(value, inputKey, getDataValue(parentObjectData, itemKey));
});
return (
<>
{actionConfig.name === 'custom-script' ? (
<div>{renderObject(image, 'resource.image', getDataValue(dataSource, 'image'))}</div>
) : null}
<div>
<div className="resource-input-group-title">{i18nMap.params}: </div>
{paramsContent}
</div>
<div>{renderObject(resources, 'resource.resources', getDataValue(dataSource, 'resources'))}</div>
</>
);
};
const renderObject = (value: any, parentKey: string, dataSource?: any) => {
if (!isObject(value.type)) {
return renderPropertyValue(value, parentKey, dataSource);
}
if (value.type === 'string_array') {
return renderStringArray(value, parentKey);
}
if (value.type === 'struct_array') {
return renderStructArray(value, parentKey);
}
if (value.type === 'map') {
return renderMap(value, parentKey, dataSource);
}
const content = renderResource({ data: value.struct }, parentKey, dataSource);
if (!content || !Object.values(content).some((c) => c)) return null;
return (
<div key={parentKey}>
<span className="resource-input-group-title">{i18nMap[value.name] || value.name}: </span>
<div>{content}</div>
</div>
);
};
const renderMap = (value: any, parentKey: string, dataSource?: any) => {
let initialValue = isCreate ? value.default : value.value || value.default;
if (dataSource) {
initialValue = dataSource;
}
if (!editing && !initialValue) {
return null;
}
const inputField = (
<Item
key={parentKey}
name={formKeyFormat(parentKey)}
initialValue={initialValue}
rules={[
{
required: value.required,
message: i18n.t('dop:this item cannot be empty'),
},
]}
>
<VariableInput disabled={!editing} label={getLabel(value.name, value.desc)} />
</Item>
);
return inputField;
};
const renderStringArray = (value: any, parentKey: string) => {
const inputField = (
<Item
key={parentKey}
name={formKeyFormat(parentKey)}
initialValue={isCreate ? value.default : value.value || value.default}
rules={[
{
required: value.required,
message: i18n.t('dop:this item cannot be empty'),
},
]}
getValueFromEvent={(val: Array<{ value: string }>) => {
return val?.length ? val.map((v) => v.value) : val;
}}
>
<ListInput disabled={!editing} label={getLabel(value.name, value.desc)} />
</Item>
);
return inputField;
};
const renderPropertyValue = (value: any, parentKey: string, dataSource?: any) => {
let input;
let initialValue = isCreate ? value.default : value.value || value.default;
if (dataSource) {
initialValue = dataSource;
}
if (!editing && !initialValue) {
return null;
}
const unit = value.unit ? <span>{value.unit}</span> : null;
switch (value.type) {
case 'float':
case 'int':
input = (
<InputNumber
disabled={!editing || value.readOnly}
className="w-full"
placeholder={i18n.t('dop:please enter data')}
/>
);
break;
default:
input = (
<Input
disabled={!editing || value.readOnly}
placeholder={i18n.t('dop:please enter data')}
addonAfter={unit}
/>
);
break;
}
const inputField = (
<Item
key={parentKey}
label={getLabel(value.name, value.desc)}
name={formKeyFormat(parentKey)}
initialValue={initialValue}
rules={[
{
required: value.required,
message: i18n.t('dop:this item cannot be empty'),
},
]}
>
{input}
</Item>
);
return inputField;
};
const getLabel = (label: string, labelTip: string) => {
let _label: any = label;
if (labelTip) {
_label = (
<span>
{_label}
<Tooltip title={labelTip}>
<ErdaIcon type="help" size="14" className="mr-1 align-middle text-icon" />
</Tooltip>
</span>
);
}
return _label;
};
const renderStructArray = (property: any, parentKey: string) => {
if ((!editing && !property.value) || (!editing && property.value && !property.value.length)) {
return null;
}
const addBtn = editing ? (
<ErdaIcon
type="plus"
className="cursor-pointer align-middle"
onClick={() => addNewItemToStructArray(property, property.struct[0])}
/>
) : null;
// getFieldDecorator(`${parentKey}-data`, { initialValue: property.value || [] });
const data = property.value || []; // getFieldValue(`${parentKey}-data`);
const _val = form.getFieldsValue();
const realData = get(_val, `${parentKey}`) || [];
const content = data.map((item: any, index: number) => {
const keys = Object.keys(item);
const curItem = realData[index] || item;
const nameKey = get(property.struct, '[0].name');
const headName = curItem[nameKey] || (typeof curItem[keys[0]] === 'string' ? curItem[keys[0]] : 'module');
if (typeof headName === 'object') {
return (
<div className="p-2 text-black-4">
{i18n.t(
'dop:Rendering multi-layer nested structures is not supported at this time, please go to text mode.',
)}
</div>
);
}
const header = (
<div className="flex items-center justify-between">
<span className="truncate" title={headName}>
{headName}
</span>
{editing ? (
<CustomIcon
onClick={() => deleteItemFromStructArray(property, index, parentKey)}
className="icon-delete"
type="sc1"
/>
) : null}
</div>
);
return (
<Panel key={`${parentKey}.${item.key}-${String(index)}`} header={header} forceRender>
{renderResource({ data: property.struct }, `${parentKey}.[${index}]`, item)}
</Panel>
);
});
return (
<div key={`${parentKey}`}>
<span className="resource-input-group-title">
{property.name}:{addBtn}
</span>
{data.length ? (
<Collapse className="collapse-field my-2" accordion>
{content}
</Collapse>
) : null}
</div>
);
};
const deleteItemFromStructArray = (property: any, index: number, parentKey: string) => {
if (!property.value) {
// eslint-disable-next-line no-param-reassign
property.value = [];
}
property.value.splice(index, 1);
updater.resource(cloneDeep(resource));
const formDatas = form.getFieldValue(`${parentKey}`.split('.'));
formDatas?.splice(index, 1);
const curFormData = form.getFieldsValue();
set(curFormData, parentKey, formDatas);
form.setFieldsValue(curFormData);
};
const addNewItemToStructArray = (property: any, struct: any) => {
if (!property.value) {
// eslint-disable-next-line no-param-reassign
property.value = [];
}
property.value.push({
[struct.name]: `module-${property.value.length + 1}`,
});
updater.resource(cloneDeep(resource));
};
const isObject = (inputType: string) => {
return ['map', 'string_array', 'struct_array', 'struct'].includes(inputType);
};
const onSubmit = () => {
form
.validateFields()
.then((values: any) => {
let data = cloneDeep(values);
const resources = head(filter(resource.data, (item) => item.name === 'resources'));
const originResource = transform(
get(resources, 'struct'),
(result, item: { name: string; default: string | number }) => {
const { name, default: d } = item;
// eslint-disable-next-line no-param-reassign
result[name] = +d;
},
{},
);
const editedResources = get(data, 'resource.resources') || {};
forEach(Object.entries(editedResources), ([key, value]) => {
editedResources[key] = +(value as string);
});
const isResourceDefault = isEqual(editedResources, originResource);
if (isResourceDefault) {
data = omit(data, ['resource.resources']);
}
const filledFieldsData = clearEmptyField(data);
const resData = { ...filledFieldsData, action: chosenAction } as any;
if (data.executionCondition) resData.executionCondition = data.executionCondition;
handleSubmit(resData);
})
.catch(({ errorFields }: { errorFields: Array<{ name: any[]; errors: any[] }> }) => {
form.scrollToField(errorFields[0].name);
});
};
const clearEmptyField = (ObjData: any) => {
const filledFields: string[] = [];
const findData = (obj: any, parentArray: string[]) => {
Object.keys(obj).forEach((key) => {
const currentParent = [...parentArray, key];
const value = get(obj, key);
if (typeof value === 'object' && value !== null) {
findData(value, currentParent);
} else if (value || value === 0) {
filledFields.push(currentParent.join('.'));
}
});
};
findData(ObjData, []);
return pick(ObjData, filledFields);
};
const executionCondition = (
<Item
label={i18n.t('common:execution conditions')}
name={'executionCondition'}
initialValue={get(propsNodeData, 'if') || undefined}
rules={[
{
required: false,
},
]}
>
<Input disabled={!editing} placeholder={i18n.t('common:configure execution conditions')} />
</Item>
);
const onValuesChange = () => {
// use changeKey to tigger a rerender,
updater.changeKey((prev: number) => prev + 1);
};
return (
<Form form={form} onValuesChange={onValuesChange} layout="vertical" className="edit-service-container">
{alert}
{taskType}
{loopData}
{type ? taskName : null}
{actionVersion}
{executionCondition}
{renderTaskTypeStructure()}
{editing ? (
<Button type="primary" ghost onClick={onSubmit}>
{i18n.t('Save')}
</Button>
) : null}
</Form>
);
}
Example #8
Source File: SourceEditorFormConfigurationConfigurableLoadableFields.tsx From jitsu with MIT License | 4 votes |
SourceEditorFormConfigurationConfigurableLoadableFields: React.FC<Props> = memo(
({
editorMode,
initialValues,
sourceDataFromCatalog,
hideFields: _hideFields,
patchConfig,
handleSetControlsDisabled,
handleSetTabsDisabled,
setValidator,
setFormReference,
handleResetOauth,
handleReloadStreams,
}) => {
const [form] = Form.useForm()
const [availableAirbyteImageVersions, setAvailableAirbyteImageVersions] = useState<string[]>([])
const airbyteImageVersion = useRef<string>(initialValues?.config?.image_version ?? "")
const {
isLoading: isLoadingParameters,
data: fieldsParameters,
error: loadingParametersError,
reload: reloadParameters,
} = usePolling<Parameter[]>(
{
configure: () => {
const controlsDisableRequestId = uniqueId("configurableLoadableFields-")
const imageVersion: string = airbyteImageVersion.current
let availableImageVersions: string[] = []
return {
onBeforePollingStart: async () => {
handleSetControlsDisabled(true, controlsDisableRequestId)
editorMode === "edit" && handleSetTabsDisabled(["streams"], "disable")
availableImageVersions = (await pullAvailableAirbyteImageVersions(sourceDataFromCatalog.id)) || []
setAvailableAirbyteImageVersions(availableImageVersions)
},
pollingCallback: (end, fail) => async () => {
try {
const response = await pullAirbyteSpec(
sourceDataFromCatalog.id,
imageVersion || availableImageVersions[0]
)
if (response?.message) throw new Error(response?.message)
if (response?.status && response?.status !== "pending") {
const result = transformAirbyteSpecResponse(response)
end(result)
}
} catch (error) {
fail(error)
}
},
onAfterPollingEnd: () => {
handleSetControlsDisabled(false, controlsDisableRequestId)
editorMode === "edit" && handleSetTabsDisabled(["streams"], "enable")
},
}
},
},
{ interval_ms: 2000 }
)
const hideFields = useMemo<string[]>(() => {
if (!fieldsParameters) return _hideFields
const oauthFieldsParametersNames = fieldsParameters.reduce<string[]>((result, current) => {
if (current.type.typeName === "oauthSecret") result.push(current.id)
return result
}, [])
return [..._hideFields, ...oauthFieldsParametersNames]
}, [_hideFields, fieldsParameters])
const handleFormValuesChange = useCallback(
(values: PlainObjectWithPrimitiveValues): void => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
},
[patchConfig]
)
const handleFormValuesChangeForm = useCallback<FormProps<PlainObjectWithPrimitiveValues>["onValuesChange"]>(
(changedValues, allValues) => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, allValues, { resetErrorsCount: true })
handleIfAirbyteVersionChanged(changedValues)
},
[patchConfig]
)
const handleIfAirbyteVersionChanged = async (changedFormValues: PlainObjectWithPrimitiveValues): Promise<void> => {
const newImageVersion = changedFormValues[AIRBYTE_IMAGE_VERSION_FIELD_ID]
if (newImageVersion && typeof newImageVersion === "string") {
airbyteImageVersion.current = newImageVersion
handleResetOauth()
await reloadParameters()
handleReloadStreams()
}
}
const handleSetInitialFormValues = useCallback(
(values: PlainObjectWithPrimitiveValues): void => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { doNotSetStateChanged: true })
},
[patchConfig]
)
/**
* set validator and form reference to parent component after the first render
*/
useEffect(() => {
const validateGetErrorsCount: ValidateGetErrorsCount = async () => {
let errorsCount = 0
try {
await form.validateFields()
} catch (error) {
errorsCount = +error?.errorFields?.length
}
return errorsCount
}
setValidator(() => validateGetErrorsCount)
setFormReference(CONFIG_FORM_KEY, form, handleFormValuesChange)
}, [])
return loadingParametersError ? (
<Row>
<Col span={4} />
<Col span={20}>
<ErrorCard
title={`Failed to load the source specification data`}
descriptionWithContacts={null}
stackTrace={loadingParametersError.stack}
className={`form-fields-card`}
/>
</Col>
</Row>
) : isLoadingParameters ? (
<Row>
<Col span={4} />
<Col span={20}>
<LoadableFieldsLoadingMessageCard
title="Loading the source configuration"
longLoadingMessage="Loading the spec takes longer than usual. This might happen if you are configuring such source for the first time - Jitsu will need some time to pull a docker image with the connector code"
showLongLoadingMessageAfterMs={5000}
className={`form-fields-card`}
/>
</Col>
</Row>
) : (
<Form form={form} onValuesChange={handleFormValuesChangeForm}>
<AirbyteVersionSelection
key={`Stream Version Selection`}
defaultValue={airbyteImageVersion.current}
options={availableAirbyteImageVersions}
/>
<ConfigurableFieldsForm
fieldsParamsList={fieldsParameters || []}
form={form}
initialValues={initialValues}
availableOauthBackendSecrets={"all_from_config"}
hideFields={hideFields}
setFormValues={handleFormValuesChange}
setInitialFormValues={handleSetInitialFormValues}
/>
</Form>
)
}
)
Example #9
Source File: SourceEditorFormConfigurationStaticFields.tsx From jitsu with MIT License | 4 votes |
SourceEditorFormConfigurationStaticFields: React.FC<Props> = ({
editorMode,
initialValues,
patchConfig,
setValidator,
setFormReference,
}) => {
const [form] = AntdForm.useForm<FormFields>()
const services = useServices()
const subscription = services.currentSubscription?.currentPlan
const sourcesList = sourcesStore.list
const validateUniqueSourceId = (_, value: string) =>
editorMode === "add" && sourcesList?.find((source: SourceData) => source.sourceId === value)
? Promise.reject("SourceId must be unique!")
: Promise.resolve()
const validateSourceIdNoSpaces = (_, value: string) => {
const re = /^[A-Za-z0-9_-]*$/
if (!re.test(value)) {
return Promise.reject("SourceId must contain only letters, numbers, hyphens or the underscore character")
} else {
return Promise.resolve()
}
}
const sourceIdValidationRules = useMemo<AntdFormItemValidationRule[]>(
() => [
{ required: true, message: "SourceId is required field" },
{ validator: validateUniqueSourceId },
{ validator: validateSourceIdNoSpaces },
],
[]
)
const handleFormValuesChange: FormProps<PlainObjectWithPrimitiveValues>["onValuesChange"] = (_, values) => {
patchConfig(CONFIG_INTERNAL_STATE_KEY, values, { resetErrorsCount: true })
}
/**
* set initial state, validator and form reference on first render
*/
useEffect(() => {
const validateGetErrorsCount: ValidateGetErrorsCount = async () => {
let errorsCount = 0
try {
await form.validateFields()
} catch (error) {
errorsCount = +error?.errorFields?.length
}
return errorsCount
}
patchConfig(CONFIG_INTERNAL_STATE_KEY, form.getFieldsValue(), { doNotSetStateChanged: true })
setValidator(() => validateGetErrorsCount)
setFormReference(CONFIG_FORM_KEY, form)
}, [])
return (
<AntdForm name="source-config" form={form} autoComplete="off" onValuesChange={handleFormValuesChange}>
<Row key="sourceId">
<Col span={24}>
<AntdForm.Item
initialValue={initialValues?.sourceId}
className={`form-field_fixed-label ${editorStyles.field}`}
label={<span className="w-full">SourceId</span>}
name="sourceId"
rules={sourceIdValidationRules}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
>
<Input disabled={editorMode === "edit"} autoComplete="off" />
</AntdForm.Item>
</Col>
</Row>
<Row key="schedule">
<Col span={24}>
<AntdForm.Item
initialValue={initialValues?.schedule}
name="schedule"
className={`form-field_fixed-label ${editorStyles.field}`}
label={<span className="w-full">Schedule</span>}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
rules={[{ required: true, message: "You have to choose schedule" }]}
>
<Select>
{COLLECTIONS_SCHEDULES.map(option => {
const available = subscription ? subscription.quota.allowedSchedules.includes(option.id) : true
return (
<Select.Option value={option.value} key={option.value} disabled={!available}>
{option.label}
{!available && " - n/a, upgrade plan"}
</Select.Option>
)
})}
</Select>
</AntdForm.Item>
</Col>
</Row>
</AntdForm>
)
}