config-ui/src/pages/blueprint/detail/configuration-panel.tsx (204 lines of code) (raw):

/* * 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 { useState, useEffect, useMemo } from 'react'; import { Link } from 'react-router-dom'; import { Button, Intent } from '@blueprintjs/core'; import { IconButton, Table, NoData, Buttons } from '@/components'; import { getCron } from '@/config'; import { useConnections } from '@/hooks'; import { getPluginConfig } from '@/plugins'; import { formatTime, operator } from '@/utils'; import { BlueprintType, FromEnum } from '../types'; import { ModeEnum } from '../types'; import { validRawPlan } from '../utils'; import { AdvancedEditor, UpdateNameDialog, UpdatePolicyDialog, AddConnectionDialog } from './components'; import * as API from './api'; import * as S from './styled'; interface Props { from: FromEnum; blueprint: BlueprintType; onRefresh: () => void; onChangeTab: (tab: string) => void; } export const ConfigurationPanel = ({ from, blueprint, onRefresh, onChangeTab }: Props) => { const [type, setType] = useState<'name' | 'policy' | 'add-connection'>(); const [rawPlan, setRawPlan] = useState(''); const [operating, setOperating] = useState(false); useEffect(() => { setRawPlan(JSON.stringify(blueprint.plan, null, ' ')); }, [blueprint]); const { onGet } = useConnections(); const connections = useMemo( () => blueprint.settings?.connections .filter((cs) => cs.plugin !== 'webhook') .map((cs: any) => { const unique = `${cs.plugin}-${cs.connectionId}`; const plugin = getPluginConfig(cs.plugin); const connection = onGet(unique); return { unique, icon: plugin.icon, name: connection.name, scope: cs.scopes, }; }) .filter(Boolean), [blueprint], ); const handleCancel = () => { setType(undefined); }; const handleShowNameDialog = () => { setType('name'); }; const handleShowPolicyDialog = () => { setType('policy'); }; const handleShowAddConnectionDialog = () => { setType('add-connection'); }; const handleUpdate = async (payload: any) => { const [success] = await operator( () => API.updateBlueprint(blueprint.id, { ...blueprint, ...payload, }), { setOperating, formatMessage: () => 'Update blueprint successful.', }, ); if (success) { onRefresh(); handleCancel(); } }; const handleRun = async () => { const [success] = await operator(() => API.runBlueprint(blueprint.id, false), { setOperating, formatMessage: () => 'Trigger blueprint successful.', }); if (success) { onRefresh(); onChangeTab('status'); } }; return ( <S.ConfigurationPanel> <div className="block"> <h3>Blueprint Name</h3> <div> <span>{blueprint.name}</span> <IconButton icon="annotation" tooltip="Edit" onClick={handleShowNameDialog} /> </div> </div> <div className="block"> <h3> <span>Sync Policy</span> <IconButton icon="annotation" tooltip="Edit" onClick={handleShowPolicyDialog} /> </h3> <Table columns={[ { title: 'Data Time Range', dataIndex: 'timeRange', key: 'timeRange', render: (val) => (blueprint.mode === ModeEnum.normal ? `${formatTime(val)} to Now` : 'N/A'), }, { title: 'Sync Frequency', dataIndex: 'frequency', key: 'frequency', align: 'center', render: (val, row) => { const cron = getCron(row.isManual, val); return `${cron.label}${cron.description}`; }, }, { title: 'Skip Failed Tasks', dataIndex: 'skipFailed', key: 'skipFailed', align: 'center', render: (val) => (val ? 'Enabled' : 'Disabled'), }, ]} dataSource={[ { timeRange: blueprint?.settings?.timeAfter, frequency: blueprint.cronConfig, isManual: blueprint.isManual, skipFailed: blueprint.skipOnFail, }, ]} /> </div> {blueprint.mode === ModeEnum.normal && ( <div className="block"> <h3>Data Connections</h3> {!connections.length ? ( <NoData text={ <> If you have not created data connections yet, please <Link to="/connections">create connections</Link>{' '} first and then add them to the project. </> } action={ <Button intent={Intent.PRIMARY} icon="add" text="Add a Connection" onClick={handleShowAddConnectionDialog} /> } /> ) : ( <> <Buttons> <Button intent={Intent.PRIMARY} icon="add" text="Add a Connection" onClick={handleShowAddConnectionDialog} /> </Buttons> <S.ConnectionList> {connections.map((cs) => ( <S.ConnectionItem key={cs.unique}> <div className="title"> <img src={cs.icon} alt="" /> <span>{cs.name}</span> </div> <div className="count"> <span>{cs.scope.length} data scope</span> </div> <div className="link"> <Link to={ from === FromEnum.blueprint ? `/blueprints/${blueprint.id}/${cs.unique}` : `/projects/${blueprint.projectName}/${cs.unique}` } > Edit Data Scope and Scope Config </Link> </div> </S.ConnectionItem> ))} </S.ConnectionList> <Buttons position="bottom" align="center"> <Button intent={Intent.PRIMARY} text="Collect Data" onClick={handleRun} /> </Buttons> </> )} </div> )} {blueprint.mode === ModeEnum.advanced && ( <div className="block"> <h3>JSON Configuration</h3> <AdvancedEditor value={rawPlan} onChange={setRawPlan} /> <div className="btns"> <Button intent={Intent.PRIMARY} text="Save" onClick={() => handleUpdate({ plan: !validRawPlan(rawPlan) ? JSON.parse(rawPlan) : JSON.stringify([[]], null, ' '), }) } /> </div> </div> )} {type === 'name' && ( <UpdateNameDialog name={blueprint.name} operating={operating} onCancel={handleCancel} onSubmit={(name) => handleUpdate({ name })} /> )} {type === 'policy' && ( <UpdatePolicyDialog blueprint={blueprint} isManual={blueprint.isManual} cronConfig={blueprint.cronConfig} skipOnFail={blueprint.skipOnFail} timeAfter={blueprint.settings?.timeAfter} operating={operating} onCancel={handleCancel} onSubmit={(payload) => handleUpdate(payload)} /> )} {type === 'add-connection' && ( <AddConnectionDialog disabled={connections.map((cs) => cs.unique)} onCancel={handleCancel} onSubmit={(connection) => handleUpdate({ settings: { ...blueprint.settings, connections: [...blueprint.settings.connections, connection] }, }) } /> )} </S.ConfigurationPanel> ); };