mirror of https://github.com/kubevela/velaux.git
Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
|
51e71f1feb | |
|
c894356e50 | |
|
df900036d4 | |
|
d631fcee6f | |
|
65af805131 | |
|
02e51bb37e | |
|
d18b2013c5 | |
|
bbe7b2165d | |
|
5ef1a137c4 |
|
@ -5,13 +5,14 @@ import { getDomain } from '../utils/common';
|
||||||
import type {
|
import type {
|
||||||
ApplicationDeployRequest,
|
ApplicationDeployRequest,
|
||||||
Trait,
|
Trait,
|
||||||
Trigger,
|
|
||||||
ApplicationComponentConfig,
|
ApplicationComponentConfig,
|
||||||
ApplicationQuery,
|
ApplicationQuery,
|
||||||
ApplicationCompareRequest,
|
ApplicationCompareRequest,
|
||||||
ApplicationDryRunRequest,
|
ApplicationDryRunRequest,
|
||||||
CreatePolicyRequest,
|
CreatePolicyRequest,
|
||||||
UpdatePolicyRequest,
|
UpdatePolicyRequest,
|
||||||
|
UpdateTriggerRequest,
|
||||||
|
CreateTriggerRequest,
|
||||||
} from '../interface/application';
|
} from '../interface/application';
|
||||||
|
|
||||||
interface TraitQuery {
|
interface TraitQuery {
|
||||||
|
@ -102,12 +103,11 @@ export function createApplicationTemplate(params: any) {
|
||||||
return post(`${url}/${params.name}/template`, params).then((res) => res);
|
return post(`${url}/${params.name}/template`, params).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createApplicationEnv(params: { appName?: string }) {
|
export function createApplicationEnvbinding(params: { appName?: string }) {
|
||||||
delete params.appName;
|
|
||||||
return post(`${url}/${params.appName}/envs`, params).then((res) => res);
|
return post(`${url}/${params.appName}/envs`, params).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateApplicationEnv(params: { appName?: string; name: string }) {
|
export function updateApplicationEnvbinding(params: { appName?: string; name: string }) {
|
||||||
return put(`${url}/${params.appName}/envs/${params.name}`, params).then((res) => res);
|
return put(`${url}/${params.appName}/envs/${params.name}`, params).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,12 +221,20 @@ export function updateApplication(params: any) {
|
||||||
return put(`${url}/${params.name}`, params).then((res) => res);
|
return put(`${url}/${params.name}`, params).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTriggers(params: Trigger, query: { appName: string }) {
|
export function createTrigger(params: CreateTriggerRequest, query: { appName: string }) {
|
||||||
const { appName } = query;
|
const { appName } = query;
|
||||||
return post(`${url}/${appName}/triggers`, params).then((res) => res);
|
return post(`${url}/${appName}/triggers`, params).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteTriggers(params: { appName: string; token: string }) {
|
export function updateTrigger(
|
||||||
|
params: UpdateTriggerRequest,
|
||||||
|
query: { appName: string; token: string },
|
||||||
|
) {
|
||||||
|
const { appName, token } = query;
|
||||||
|
return put(`${url}/${appName}/triggers/${token}`, params).then((res) => res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteTrigger(params: { appName: string; token: string }) {
|
||||||
const { appName, token } = params;
|
const { appName, token } = params;
|
||||||
return rdelete(`${url}/${appName}/triggers/${token}`, {}).then((res) => res);
|
return rdelete(`${url}/${appName}/triggers/${token}`, {}).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,29 @@ export function listPipelineContexts(projectName: string, pipelineName: string)
|
||||||
return get(url, {}).then((res) => res);
|
return get(url, {}).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deletePipelineContext(
|
||||||
|
projectName: string,
|
||||||
|
pipelineName: string,
|
||||||
|
contextName: string,
|
||||||
|
) {
|
||||||
|
const url =
|
||||||
|
base + `/api/v1/projects/${projectName}/pipelines/${pipelineName}/contexts/${contextName}`;
|
||||||
|
return rdelete(url, {}).then((res) => res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updatePipelineContext(
|
||||||
|
projectName: string,
|
||||||
|
pipelineName: string,
|
||||||
|
context: {
|
||||||
|
name: string;
|
||||||
|
values: { key: string; value: string }[];
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const url =
|
||||||
|
base + `/api/v1/projects/${projectName}/pipelines/${pipelineName}/contexts/${context.name}`;
|
||||||
|
return put(url, context).then((res) => res);
|
||||||
|
}
|
||||||
|
|
||||||
export function loadPipelineRunStepOutputs(params: {
|
export function loadPipelineRunStepOutputs(params: {
|
||||||
projectName: string;
|
projectName: string;
|
||||||
pipelineName: string;
|
pipelineName: string;
|
||||||
|
|
|
@ -19,6 +19,25 @@ export function getChartValues(params: {
|
||||||
}).then((res) => res);
|
}).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getChartValueFiles(params: {
|
||||||
|
url: string;
|
||||||
|
repoType: string;
|
||||||
|
secretName?: string;
|
||||||
|
chart: string;
|
||||||
|
version: string;
|
||||||
|
}) {
|
||||||
|
const url = baseURL + repository + '/chart/values';
|
||||||
|
return get(url, {
|
||||||
|
params: {
|
||||||
|
repoUrl: params.url,
|
||||||
|
repoType: params.repoType,
|
||||||
|
secretName: params.secretName,
|
||||||
|
chart: params.chart,
|
||||||
|
version: params.version,
|
||||||
|
},
|
||||||
|
}).then((res) => res);
|
||||||
|
}
|
||||||
|
|
||||||
export function getCharts(params: { url: string; repoType: string; secretName?: string }) {
|
export function getCharts(params: { url: string; repoType: string; secretName?: string }) {
|
||||||
const url = baseURL + repository + '/charts';
|
const url = baseURL + repository + '/charts';
|
||||||
return get(url, {
|
return get(url, {
|
||||||
|
@ -32,9 +51,14 @@ export function getChartVersions(params: {
|
||||||
chart: string;
|
chart: string;
|
||||||
secretName?: string;
|
secretName?: string;
|
||||||
}) {
|
}) {
|
||||||
const url = baseURL + repository + '/charts/' + params.chart + '/versions';
|
const url = baseURL + repository + '/chart/versions';
|
||||||
return get(url, {
|
return get(url, {
|
||||||
params: { repoUrl: params.url, repoType: params.repoType, secretName: params.secretName },
|
params: {
|
||||||
|
repoUrl: params.url,
|
||||||
|
repoType: params.repoType,
|
||||||
|
secretName: params.secretName,
|
||||||
|
chart: params.chart,
|
||||||
|
},
|
||||||
}).then((res) => res);
|
}).then((res) => res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,7 +391,8 @@ a {
|
||||||
.next-btn {
|
.next-btn {
|
||||||
display: block;
|
display: block;
|
||||||
flex: none !important;
|
flex: none !important;
|
||||||
width: 100px !important;
|
width: auto !important;
|
||||||
|
min-width: 100px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,10 +503,6 @@ a {
|
||||||
color: @dangerColor !important;
|
color: @dangerColor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line {
|
|
||||||
border: solid 0.2px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
@ -516,6 +513,7 @@ a {
|
||||||
.next-breadcrumb-separator {
|
.next-breadcrumb-separator {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
|
font-size: 16px;
|
||||||
a {
|
a {
|
||||||
color: @primarycolor;
|
color: @primarycolor;
|
||||||
}
|
}
|
||||||
|
@ -524,7 +522,6 @@ a {
|
||||||
|
|
||||||
.next-btn,
|
.next-btn,
|
||||||
.inline-center {
|
.inline-center {
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Dialog, Message } from '@b-design/ui';
|
import { Button, Dialog, Message } from '@b-design/ui';
|
||||||
import type { ApplicationCompareResponse } from '../../interface/application';
|
import type { ApplicationCompareResponse } from '../../interface/application';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import { DiffEditor } from '../DiffEditor';
|
import { DiffEditor } from '../DiffEditor';
|
||||||
|
@ -11,17 +11,26 @@ type ApplicationDiffProps = {
|
||||||
targetName: string;
|
targetName: string;
|
||||||
compare: ApplicationCompareResponse;
|
compare: ApplicationCompareResponse;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
rollback2Revision?: () => void;
|
||||||
id?: string;
|
id?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ApplicationDiff = (props: ApplicationDiffProps) => {
|
export const ApplicationDiff = (props: ApplicationDiffProps) => {
|
||||||
const { baseName, targetName, compare } = props;
|
const { baseName, targetName, compare, rollback2Revision } = props;
|
||||||
const container = 'revision' + baseName + targetName;
|
const container = 'revision' + baseName + targetName;
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
className={'commonDialog application-diff'}
|
className={'commonDialog application-diff'}
|
||||||
isFullScreen={true}
|
isFullScreen={true}
|
||||||
footer={<div />}
|
footer={
|
||||||
|
<div>
|
||||||
|
<If condition={compare.isDiff && rollback2Revision}>
|
||||||
|
<Button type="primary" onClick={rollback2Revision}>
|
||||||
|
Rollback To Revision
|
||||||
|
</Button>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
id={props.id}
|
id={props.id}
|
||||||
visible={true}
|
visible={true}
|
||||||
onClose={props.onClose}
|
onClose={props.onClose}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
.helmValueDialog {
|
||||||
|
.next-dialog-body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Dialog, Select } from '@b-design/ui';
|
||||||
|
import * as React from 'react';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import DefinitionCode from '../DefinitionCode';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClose: () => void;
|
||||||
|
valueFiles: Record<string, string>;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
const HelmValueShow: React.FC<Props> = (props: Props) => {
|
||||||
|
const [valueFile, setValueFile] = React.useState<string>('values.yaml');
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
className={'commonDialog helmValueDialog'}
|
||||||
|
isFullScreen={true}
|
||||||
|
visible={true}
|
||||||
|
onClose={props.onClose}
|
||||||
|
footer={<div />}
|
||||||
|
title={i18n.t('Show Values File')}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
onChange={(name) => {
|
||||||
|
setValueFile(name);
|
||||||
|
}}
|
||||||
|
defaultValue={valueFile}
|
||||||
|
dataSource={Object.keys(props.valueFiles)}
|
||||||
|
style={{ marginBottom: '8px' }}
|
||||||
|
/>
|
||||||
|
<div id={'chart-' + props.name} className="diff-box">
|
||||||
|
<DefinitionCode
|
||||||
|
language={'yaml'}
|
||||||
|
containerId={'chart-' + props.name}
|
||||||
|
readOnly
|
||||||
|
value={props.valueFiles[valueFile]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default HelmValueShow;
|
|
@ -1,15 +1,23 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
title: string | React.ReactNode;
|
title: string | React.ReactNode;
|
||||||
number?: number;
|
number?: number;
|
||||||
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NumItem: React.FC<Props> = (props) => {
|
const NumItem: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="num-item">
|
<div className="num-item">
|
||||||
<div className="number">{props.number || 0}</div>
|
<div className="number">
|
||||||
|
{props.to ? (
|
||||||
|
<Link to={props.to}>{props.number || 0}</Link>
|
||||||
|
) : (
|
||||||
|
<span>{props.number || 0}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="title">{props.title}</div>
|
<div className="title">{props.title}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -50,7 +50,6 @@ class PipelineGraph extends React.Component<PipelineGraphProps, State> {
|
||||||
<Draggable>
|
<Draggable>
|
||||||
<div
|
<div
|
||||||
className="workflow-graph"
|
className="workflow-graph"
|
||||||
key={name}
|
|
||||||
style={{
|
style={{
|
||||||
transform: `scale(${zoom})`,
|
transform: `scale(${zoom})`,
|
||||||
}}
|
}}
|
||||||
|
@ -66,7 +65,7 @@ class PipelineGraph extends React.Component<PipelineGraphProps, State> {
|
||||||
steps.map((step, i: number) => {
|
steps.map((step, i: number) => {
|
||||||
return (
|
return (
|
||||||
<Step
|
<Step
|
||||||
key={name + step.id}
|
key={name + step.name}
|
||||||
probeState={this.state}
|
probeState={this.state}
|
||||||
step={step}
|
step={step}
|
||||||
group={step.type == 'step-group'}
|
group={step.type == 'step-group'}
|
||||||
|
|
|
@ -14,17 +14,28 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: var(--spacing-4);
|
margin-top: var(--spacing-4);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: solid 1px var(--grey-200);
|
border: dashed 1px var(--grey-200);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.context-name {
|
.context-name {
|
||||||
|
flex-grow: 0;
|
||||||
|
width: 100px;
|
||||||
margin-right: var(--spacing-4);
|
margin-right: var(--spacing-4);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: var(--font-size-normal);
|
font-size: var(--font-size-normal);
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
.context-values {
|
||||||
|
flex: auto;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
&.active {
|
&.active {
|
||||||
border: solid 2px var(--primary-color);
|
border: solid 2px var(--primary-color);
|
||||||
}
|
}
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Checkbox, Dialog, Loading, Message, Tag } from '@b-design/ui';
|
import { Button, Checkbox, Dialog, Loading, Message, Tag } from '@b-design/ui';
|
||||||
import { listPipelineContexts, runPipeline } from '../../api/pipeline';
|
import { deletePipelineContext, listPipelineContexts, runPipeline } from '../../api/pipeline';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import type { KeyValue, PipelineListItem } from '../../interface/pipeline';
|
import type { KeyValue, PipelineListItem } from '../../interface/pipeline';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
@ -10,6 +10,9 @@ import classNames from 'classnames';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import NewContext from './new-context';
|
import NewContext from './new-context';
|
||||||
import Translation from '../Translation';
|
import Translation from '../Translation';
|
||||||
|
import Permission from '../Permission';
|
||||||
|
import { BiCopyAlt } from 'react-icons/bi';
|
||||||
|
import { AiFillDelete, AiFillSetting } from 'react-icons/ai';
|
||||||
|
|
||||||
export interface PipelineProps {
|
export interface PipelineProps {
|
||||||
pipeline: PipelineListItem;
|
pipeline: PipelineListItem;
|
||||||
|
@ -20,11 +23,38 @@ export interface PipelineProps {
|
||||||
const RunPipeline = (props: PipelineProps) => {
|
const RunPipeline = (props: PipelineProps) => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [contexts, setContexts] = useState<Record<string, KeyValue[]>>({});
|
const [contexts, setContexts] = useState<Record<string, KeyValue[]>>({});
|
||||||
|
const [context, setContext] = useState<{ name: string; values: KeyValue[]; clone: boolean }>();
|
||||||
const [noContext, setNoContext] = useState<boolean>();
|
const [noContext, setNoContext] = useState<boolean>();
|
||||||
const [contextName, setSelectContextName] = useState('');
|
const [contextName, setSelectContextName] = useState('');
|
||||||
const [addContext, showAddContext] = useState(false);
|
const [addContext, showAddContext] = useState(false);
|
||||||
const { pipeline } = props;
|
const { pipeline } = props;
|
||||||
|
|
||||||
|
const onClonePipelineContext = (key: string) => {
|
||||||
|
showAddContext(true);
|
||||||
|
setContext({ name: key, values: contexts[key], clone: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEditPipelineContext = (key: string) => {
|
||||||
|
showAddContext(true);
|
||||||
|
setContext({ name: key, values: contexts[key], clone: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeletePipelineContext = (key: string) => {
|
||||||
|
Dialog.confirm({
|
||||||
|
type: 'confirm',
|
||||||
|
content: <Translation>Unrecoverable after deletion, are you sure to delete it?</Translation>,
|
||||||
|
onOk: () => {
|
||||||
|
deletePipelineContext(pipeline.project.name, pipeline.name, key).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
Message.success(i18n.t('The Pipeline context removed successfully'));
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
locale: locale().Dialog,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
listPipelineContexts(pipeline.project.name, pipeline.name)
|
listPipelineContexts(pipeline.project.name, pipeline.name)
|
||||||
|
@ -86,20 +116,98 @@ const RunPipeline = (props: PipelineProps) => {
|
||||||
setSelectContextName('');
|
setSelectContextName('');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
title={
|
||||||
|
contextName === key ? i18n.t('Click and deselect') : i18n.t('Click and select')
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className="context-name">{key}</div>
|
<div className="context-name">{key}</div>
|
||||||
<div className="context-values">
|
<div className="context-values">
|
||||||
{Array.isArray(contexts[key]) &&
|
{Array.isArray(contexts[key]) &&
|
||||||
contexts[key].map((item) => {
|
contexts[key].map((item) => {
|
||||||
return <Tag>{`${item.key}=${item.value}`}</Tag>;
|
return (
|
||||||
|
<Tag
|
||||||
|
style={{ marginBottom: '8px' }}
|
||||||
|
key={item.key}
|
||||||
|
>{`${item.key}=${item.value}`}</Tag>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="actions">
|
||||||
|
<Permission
|
||||||
|
request={{
|
||||||
|
resource: `project:${pipeline.project.name}/pipeline:${pipeline.name}/context:${key}`,
|
||||||
|
action: 'update',
|
||||||
|
}}
|
||||||
|
project={pipeline.project.name}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="margin-left-10"
|
||||||
|
text={true}
|
||||||
|
component={'a'}
|
||||||
|
size={'medium'}
|
||||||
|
ghost={true}
|
||||||
|
onClick={(event) => {
|
||||||
|
onEditPipelineContext(key);
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AiFillSetting />
|
||||||
|
<Translation>Edit</Translation>
|
||||||
|
</Button>
|
||||||
|
<span className="line" />
|
||||||
|
</Permission>
|
||||||
|
<Permission
|
||||||
|
project={pipeline.project.name}
|
||||||
|
resource={{
|
||||||
|
resource: `project:${pipeline.project.name}/pipeline:${pipeline.name}/context:${key}`,
|
||||||
|
action: 'create',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
text
|
||||||
|
size={'medium'}
|
||||||
|
ghost={true}
|
||||||
|
component={'a'}
|
||||||
|
onClick={(event) => {
|
||||||
|
onClonePipelineContext(key);
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BiCopyAlt /> <Translation>Clone</Translation>
|
||||||
|
</Button>
|
||||||
|
<span className="line" />
|
||||||
|
</Permission>
|
||||||
|
<Permission
|
||||||
|
project={pipeline.project.name}
|
||||||
|
resource={{
|
||||||
|
resource: `project:${pipeline.project.name}/pipeline:${pipeline.name}/context:${key}`,
|
||||||
|
action: 'delete',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
text
|
||||||
|
size={'medium'}
|
||||||
|
ghost={true}
|
||||||
|
className={'danger-btn'}
|
||||||
|
component={'a'}
|
||||||
|
onClick={(event) => {
|
||||||
|
onDeletePipelineContext(key);
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AiFillDelete />
|
||||||
|
<Translation>Remove</Translation>
|
||||||
|
</Button>
|
||||||
|
</Permission>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<If condition={addContext}>
|
<If condition={addContext}>
|
||||||
<div className="context-item">
|
<div className="context-item">
|
||||||
<NewContext
|
<NewContext
|
||||||
|
clone={context?.clone}
|
||||||
|
context={context}
|
||||||
pipeline={props.pipeline}
|
pipeline={props.pipeline}
|
||||||
onCancel={() => showAddContext(false)}
|
onCancel={() => showAddContext(false)}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Field } from '@b-design/ui';
|
import { Field, Loading } from '@b-design/ui';
|
||||||
import { Button, Form, Grid, Input } from '@b-design/ui';
|
import { Button, Form, Grid, Input } from '@b-design/ui';
|
||||||
import KV from '../../extends/KV';
|
import KV from '../../extends/KV';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import Translation from '../Translation';
|
import Translation from '../Translation';
|
||||||
import { createPipelineContext } from '../../api/pipeline';
|
import { createPipelineContext, updatePipelineContext } from '../../api/pipeline';
|
||||||
import type { KeyValue, PipelineListItem } from '../../interface/pipeline';
|
import type { KeyValue, PipelineListItem } from '../../interface/pipeline';
|
||||||
import { checkName } from '../../utils/common';
|
import { checkName } from '../../utils/common';
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ export interface NewContextProps {
|
||||||
pipeline: PipelineListItem;
|
pipeline: PipelineListItem;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
context?: { name: string; values: KeyValue[] };
|
||||||
|
clone?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewContext extends React.Component<NewContextProps> {
|
class NewContext extends React.Component<NewContextProps> {
|
||||||
|
@ -23,7 +25,29 @@ class NewContext extends React.Component<NewContextProps> {
|
||||||
super(props);
|
super(props);
|
||||||
this.field = new Field(this);
|
this.field = new Field(this);
|
||||||
}
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
const { clone, context } = this.props;
|
||||||
|
if (context) {
|
||||||
|
const editValues: Record<string, string> = {};
|
||||||
|
context.values.map((v) => {
|
||||||
|
editValues[v.key] = v.value;
|
||||||
|
});
|
||||||
|
if (clone) {
|
||||||
|
this.field.setValues({
|
||||||
|
name: context.name + '-clone',
|
||||||
|
values: editValues,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.field.setValues({
|
||||||
|
name: context.name,
|
||||||
|
values: editValues,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
submitContext = () => {
|
submitContext = () => {
|
||||||
|
const { context, clone } = this.props;
|
||||||
|
const editMode = context && !clone;
|
||||||
this.field.validate((errs, values: any) => {
|
this.field.validate((errs, values: any) => {
|
||||||
if (errs) {
|
if (errs) {
|
||||||
return;
|
return;
|
||||||
|
@ -33,17 +57,33 @@ class NewContext extends React.Component<NewContextProps> {
|
||||||
Object.keys(values.values).map((key) => {
|
Object.keys(values.values).map((key) => {
|
||||||
keyValues.push({ key: key, value: values.values[key] });
|
keyValues.push({ key: key, value: values.values[key] });
|
||||||
});
|
});
|
||||||
createPipelineContext(project.name, name, { name: values.name, values: keyValues }).then(
|
if (editMode) {
|
||||||
(res) => {
|
updatePipelineContext(project.name, name, { name: values.name, values: keyValues }).then(
|
||||||
if (res) {
|
(res) => {
|
||||||
this.props.onSuccess();
|
if (res) {
|
||||||
}
|
this.props.onSuccess();
|
||||||
},
|
}
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
createPipelineContext(project.name, name, { name: values.name, values: keyValues }).then(
|
||||||
|
(res) => {
|
||||||
|
if (res) {
|
||||||
|
this.props.onSuccess();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
const { init } = this.field;
|
const { init } = this.field;
|
||||||
|
const { context, clone } = this.props;
|
||||||
|
const editMode = context && !clone;
|
||||||
|
// waiting init the values
|
||||||
|
if (context && Object.keys(this.field.getValues()).length === 0) {
|
||||||
|
return <Loading visible />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Form field={this.field} style={{ width: '100%' }}>
|
<Form field={this.field} style={{ width: '100%' }}>
|
||||||
<Row style={{ width: '100%' }} wrap>
|
<Row style={{ width: '100%' }} wrap>
|
||||||
|
@ -63,6 +103,7 @@ class NewContext extends React.Component<NewContextProps> {
|
||||||
],
|
],
|
||||||
})}
|
})}
|
||||||
placeholder={i18n.t('Please input the context name')}
|
placeholder={i18n.t('Please input the context name')}
|
||||||
|
disabled={editMode}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -47,8 +47,19 @@ export const Step = (props: StepProps) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showAlias(step.name, step.alias)}
|
{showAlias(step.name, step.alias)}
|
||||||
|
<span className="step-delete">
|
||||||
|
<Icon
|
||||||
|
size={14}
|
||||||
|
type="ashbin"
|
||||||
|
onClick={(event) => {
|
||||||
|
onDelete(step.name);
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
title="Delete this step group."
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="groups asd" style={{ width: stepWidth + 'px' }}>
|
<div className="groups" style={{ width: stepWidth + 'px' }}>
|
||||||
{step.subSteps?.map((subStep) => {
|
{step.subSteps?.map((subStep) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -10,19 +10,23 @@ import Translation from '../Translation';
|
||||||
import type { Rule } from '@alifd/meet-react/lib/field';
|
import type { Rule } from '@alifd/meet-react/lib/field';
|
||||||
import type { WorkflowStepBase } from '../../interface/pipeline';
|
import type { WorkflowStepBase } from '../../interface/pipeline';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
|
import Item from '../Item';
|
||||||
|
|
||||||
const { Row, Col } = Grid;
|
const { Row, Col } = Grid;
|
||||||
|
|
||||||
interface DefinitionCatalog {
|
interface DefinitionCatalog {
|
||||||
title: string;
|
title: string;
|
||||||
|
sort: number;
|
||||||
description?: string;
|
description?: string;
|
||||||
definitions: DefinitionBase[];
|
definitions: DefinitionBase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultDefinitionCatalog: Record<string, string> = {
|
const defaultDefinitionCatalog: Record<string, string> = {
|
||||||
|
'step-group': 'Group',
|
||||||
deploy: 'Delivery',
|
deploy: 'Delivery',
|
||||||
'apply-app': 'Delivery',
|
'apply-app': 'Delivery',
|
||||||
'apply-deployment': 'Delivery',
|
'apply-deployment': 'Delivery',
|
||||||
|
'apply-component': 'Delivery',
|
||||||
'addon-operation': 'Delivery',
|
'addon-operation': 'Delivery',
|
||||||
'deploy-cloud-resource': 'Delivery',
|
'deploy-cloud-resource': 'Delivery',
|
||||||
'share-cloud-resource': 'Delivery',
|
'share-cloud-resource': 'Delivery',
|
||||||
|
@ -34,34 +38,47 @@ const defaultDefinitionCatalog: Record<string, string> = {
|
||||||
'list-config': 'Config Management',
|
'list-config': 'Config Management',
|
||||||
'read-config': 'Config Management',
|
'read-config': 'Config Management',
|
||||||
'export-data': 'Config Management',
|
'export-data': 'Config Management',
|
||||||
|
export2config: 'Config Management',
|
||||||
|
export2secret: 'Config Management',
|
||||||
suspend: 'Notification',
|
suspend: 'Notification',
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultCatalog: Record<string, DefinitionCatalog> = {
|
const defaultCatalog: Record<string, DefinitionCatalog> = {
|
||||||
|
Group: {
|
||||||
|
title: 'Group',
|
||||||
|
description: 'Merge a set of steps.',
|
||||||
|
definitions: [],
|
||||||
|
sort: 1,
|
||||||
|
},
|
||||||
Delivery: {
|
Delivery: {
|
||||||
title: 'Delivery',
|
title: 'Delivery',
|
||||||
description: 'Delivery the Application or workloads to the Targets',
|
description: 'Delivery the Application or workloads to the Targets',
|
||||||
definitions: [],
|
definitions: [],
|
||||||
|
sort: 2,
|
||||||
},
|
},
|
||||||
Approval: {
|
Approval: {
|
||||||
title: 'Approval',
|
title: 'Approval',
|
||||||
description: 'Approval or reject the changes during Workflow or Pipeline progress',
|
description: 'Approval or reject the changes during Workflow or Pipeline progress',
|
||||||
definitions: [],
|
definitions: [],
|
||||||
|
sort: 3,
|
||||||
},
|
},
|
||||||
'Config Management': {
|
'Config Management': {
|
||||||
title: 'Config Management',
|
title: 'Config Management',
|
||||||
description: 'Create or read the config.',
|
description: 'Create or read the config.',
|
||||||
definitions: [],
|
definitions: [],
|
||||||
|
sort: 4,
|
||||||
},
|
},
|
||||||
Notification: {
|
Notification: {
|
||||||
title: 'Approval',
|
title: 'Notification',
|
||||||
description: 'Send messages to users or other applications.',
|
description: 'Send messages to users or other applications.',
|
||||||
definitions: [],
|
definitions: [],
|
||||||
|
sort: 5,
|
||||||
},
|
},
|
||||||
Custom: {
|
Custom: {
|
||||||
title: 'Custom',
|
title: 'Custom',
|
||||||
description: 'Custom Workflow or Pipeline steps',
|
description: 'Custom Workflow or Pipeline steps',
|
||||||
definitions: [],
|
definitions: [],
|
||||||
|
sort: 1000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,10 +110,12 @@ const buildDefinitionCatalog = (defs: DefinitionBase[]) => {
|
||||||
if (catalogMap[catalog]) {
|
if (catalogMap[catalog]) {
|
||||||
catalogMap[catalog].definitions.push(def);
|
catalogMap[catalog].definitions.push(def);
|
||||||
} else {
|
} else {
|
||||||
catalogMap[catalog] = { title: catalog, definitions: [def] };
|
catalogMap[catalog] = { title: catalog, definitions: [def], sort: 100 };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Object.values(catalogMap);
|
return Object.values(catalogMap).sort((a, b) => {
|
||||||
|
return a.sort - b.sort;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -107,7 +126,7 @@ type Props = {
|
||||||
addSub?: boolean;
|
addSub?: boolean;
|
||||||
};
|
};
|
||||||
type State = {
|
type State = {
|
||||||
selectType?: string;
|
selectType?: DefinitionBase;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TypeSelect extends React.Component<Props, State> {
|
class TypeSelect extends React.Component<Props, State> {
|
||||||
|
@ -126,7 +145,12 @@ class TypeSelect extends React.Component<Props, State> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { name, alias, description } = values;
|
const { name, alias, description } = values;
|
||||||
this.props.addStep({ type: selectType, name: name, alias: alias, description: description });
|
this.props.addStep({
|
||||||
|
type: selectType.name,
|
||||||
|
name: name,
|
||||||
|
alias: alias,
|
||||||
|
description: description,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -136,6 +160,7 @@ class TypeSelect extends React.Component<Props, State> {
|
||||||
const catalogs = buildDefinitionCatalog(
|
const catalogs = buildDefinitionCatalog(
|
||||||
definitions?.filter((def) => !addSub || def.name != 'step-group') || [],
|
definitions?.filter((def) => !addSub || def.name != 'step-group') || [],
|
||||||
);
|
);
|
||||||
|
console.log(catalogs);
|
||||||
const { init } = this.field;
|
const { init } = this.field;
|
||||||
const checkStepNameRule = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
const checkStepNameRule = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||||
if (checkStepName(value)) {
|
if (checkStepName(value)) {
|
||||||
|
@ -153,7 +178,7 @@ class TypeSelect extends React.Component<Props, State> {
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onCancel={onClose}
|
onCancel={onClose}
|
||||||
onOk={this.onSubmit}
|
onOk={this.onSubmit}
|
||||||
title={i18n.t('Select Step Type')}
|
title={selectType ? i18n.t('Set Step Basic Info') : i18n.t('Select Step Type')}
|
||||||
visible
|
visible
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
@ -175,7 +200,7 @@ class TypeSelect extends React.Component<Props, State> {
|
||||||
<div
|
<div
|
||||||
className="icon"
|
className="icon"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({ selectType: def.name });
|
this.setState({ selectType: def });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StepTypeIcon type={def.name} />
|
<StepTypeIcon type={def.name} />
|
||||||
|
@ -198,6 +223,17 @@ class TypeSelect extends React.Component<Props, State> {
|
||||||
})}
|
})}
|
||||||
{selectType && (
|
{selectType && (
|
||||||
<Form field={this.field}>
|
<Form field={this.field}>
|
||||||
|
<Row wrap>
|
||||||
|
<Col span={24}>
|
||||||
|
<Item
|
||||||
|
label={i18n.t('Select Type')}
|
||||||
|
value={showAlias(selectType.name, selectType.alias)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<Item label={i18n.t('Type Description')} value={selectType.description} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={12} style={{ padding: '0 8px' }}>
|
<Col span={12} style={{ padding: '0 8px' }}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
||||||
import type { WorkflowStep } from '../../interface/pipeline';
|
import type { WorkflowStep } from '../../interface/pipeline';
|
||||||
import DefinitionCode from '../DefinitionCode';
|
import DefinitionCode from '../DefinitionCode';
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
|
import { Message } from '@b-design/ui';
|
||||||
|
import Translation from '../Translation';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
steps?: WorkflowStep[];
|
steps?: WorkflowStep[];
|
||||||
|
@ -12,21 +14,36 @@ export const WorkflowYAML = (props: Props) => {
|
||||||
const id = 'workflow:' + props.name;
|
const id = 'workflow:' + props.name;
|
||||||
const content = yaml.dump(props.steps);
|
const content = yaml.dump(props.steps);
|
||||||
return (
|
return (
|
||||||
<div id={id} style={{ height: 'calc(100vh - 340px)' }}>
|
<div>
|
||||||
<DefinitionCode
|
<Message type="help">
|
||||||
onChange={(value: string) => {
|
<div>
|
||||||
try {
|
<Translation>The workflow step spec reference document</Translation>:
|
||||||
const newSteps: any = yaml.load(value);
|
<a
|
||||||
props.onChange(newSteps);
|
href="http://kubevela.net/docs/end-user/workflow/built-in-workflow-defs"
|
||||||
} catch (err) {
|
target="_blank"
|
||||||
console.log(err);
|
rel="noopener noreferrer"
|
||||||
}
|
style={{ marginLeft: '16px' }}
|
||||||
}}
|
>
|
||||||
value={content}
|
<Translation>Click here</Translation>
|
||||||
language="yaml"
|
</a>
|
||||||
theme="hc-black"
|
</div>
|
||||||
containerId={id}
|
</Message>
|
||||||
/>
|
<div id={id} style={{ height: 'calc(100vh - 340px)' }}>
|
||||||
|
<DefinitionCode
|
||||||
|
onChange={(value: string) => {
|
||||||
|
try {
|
||||||
|
const newSteps: any = yaml.load(value);
|
||||||
|
props.onChange(newSteps);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={content}
|
||||||
|
language="yaml"
|
||||||
|
theme="hc-black"
|
||||||
|
containerId={id}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,27 +37,37 @@ class HelmRepoSelect extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (!this.props.helm?.repoType || this.props.helm?.repoType == 'helm') {
|
if (!this.props.helm?.repoType || this.props.helm?.repoType == 'helm') {
|
||||||
this.onLoadRepos();
|
this.onLoadRepos(this.props.helm?.repoType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
componentWillReceiveProps(nextProps: Props) {
|
||||||
const repoType = nextProps.helm?.repoType;
|
const repoType = nextProps.helm?.repoType;
|
||||||
if (repoType !== this.props.helm?.repoType && repoType == 'helm') {
|
if (repoType !== this.props.helm?.repoType && (repoType == 'helm' || repoType == 'oci')) {
|
||||||
this.onLoadRepos();
|
this.onLoadRepos(repoType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadRepos = () => {
|
onLoadRepos = (repoType?: string) => {
|
||||||
const { project } = this.props;
|
const { project } = this.props;
|
||||||
const defaultRepos = [{ url: 'https://charts.bitnami.com/bitnami', type: 'helm' }];
|
const defaultRepos = [{ url: 'https://charts.bitnami.com/bitnami', type: 'helm' }];
|
||||||
this.setState({ loading: true, repos: defaultRepos });
|
this.setState({ loading: true, repos: repoType === 'helm' ? defaultRepos : [] });
|
||||||
|
console.log(repoType);
|
||||||
getChartRepos({ project: project }).then((res) => {
|
getChartRepos({ project: project }).then((res) => {
|
||||||
let repos: HelmRepo[] = [];
|
let repos: HelmRepo[] = [];
|
||||||
if (res && res.repos) {
|
if (res && res.repos) {
|
||||||
repos = repos.concat(res.repos);
|
res.repos.map((repo: HelmRepo) => {
|
||||||
|
if (repoType == 'oci' && repo.url.startsWith('oci://')) {
|
||||||
|
repos.push(repo);
|
||||||
|
}
|
||||||
|
if (repoType == 'helm' && !repo.url.startsWith('oci://')) {
|
||||||
|
repos.push(repo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (repoType == 'helm') {
|
||||||
|
repos = repos.concat(defaultRepos);
|
||||||
}
|
}
|
||||||
repos = repos.concat(defaultRepos);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
repos: repos,
|
repos: repos,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
@ -4,9 +4,12 @@ import _ from 'lodash';
|
||||||
import type { UIParam } from '../../interface/application';
|
import type { UIParam } from '../../interface/application';
|
||||||
import type { HelmRepo } from '../../interface/repository';
|
import type { HelmRepo } from '../../interface/repository';
|
||||||
import KV from '../KV';
|
import KV from '../KV';
|
||||||
import { getChartValues } from '../../api/repository';
|
import { getChartValueFiles } from '../../api/repository';
|
||||||
import { Loading } from '@b-design/ui';
|
import { Button, Loading } from '@b-design/ui';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
|
import YAML from 'js-yaml';
|
||||||
|
import Translation from '../../components/Translation';
|
||||||
|
import HelmValueShow from '../../components/HelmValueShow';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value?: any;
|
value?: any;
|
||||||
|
@ -62,6 +65,8 @@ type State = {
|
||||||
version: string;
|
version: string;
|
||||||
};
|
};
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
valueFiles?: Record<string, string>;
|
||||||
|
showValuesFile?: boolean;
|
||||||
};
|
};
|
||||||
@connect((store: any) => {
|
@connect((store: any) => {
|
||||||
return { ...store.uischema };
|
return { ...store.uischema };
|
||||||
|
@ -81,11 +86,19 @@ class HelmValues extends Component<Props, State> {
|
||||||
loadChartValues = () => {
|
loadChartValues = () => {
|
||||||
const { helm, repo } = this.props;
|
const { helm, repo } = this.props;
|
||||||
if (helm?.chart && helm.version && helm.url) {
|
if (helm?.chart && helm.version && helm.url) {
|
||||||
getChartValues({ ...helm, secretName: repo?.secretName }).then((re) => {
|
getChartValueFiles({ ...helm, secretName: repo?.secretName }).then(
|
||||||
if (re) {
|
(re: Record<string, string>) => {
|
||||||
this.setState({ values: re.BusinessCode ? undefined : re, helm: helm, loading: false });
|
if (re) {
|
||||||
}
|
try {
|
||||||
});
|
const defaultValueFile = re['values.yaml'];
|
||||||
|
const defaultValue = YAML.load(defaultValueFile);
|
||||||
|
const newValues: any = {};
|
||||||
|
getValues(defaultValue, '', newValues);
|
||||||
|
this.setState({ values: newValues, valueFiles: re, helm: helm, loading: false });
|
||||||
|
} catch (err) {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,7 +128,7 @@ class HelmValues extends Component<Props, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { helm } = this.props;
|
const { helm } = this.props;
|
||||||
const { values, loading, helm: stateHelm } = this.state;
|
const { values, loading, helm: stateHelm, showValuesFile, valueFiles } = this.state;
|
||||||
if (this.renderHelmKey(helm) != this.renderHelmKey(stateHelm)) {
|
if (this.renderHelmKey(helm) != this.renderHelmKey(stateHelm)) {
|
||||||
this.loadChartValues();
|
this.loadChartValues();
|
||||||
}
|
}
|
||||||
|
@ -131,6 +144,26 @@ class HelmValues extends Component<Props, State> {
|
||||||
subParameters={this.props.subParameters}
|
subParameters={this.props.subParameters}
|
||||||
id={this.props.id}
|
id={this.props.id}
|
||||||
/>
|
/>
|
||||||
|
{valueFiles && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ showValuesFile: true });
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type="secondary"
|
||||||
|
>
|
||||||
|
<Translation>Show Values File</Translation>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{showValuesFile && valueFiles && (
|
||||||
|
<HelmValueShow
|
||||||
|
name={helm?.chart || 'default'}
|
||||||
|
valueFiles={valueFiles}
|
||||||
|
onClose={() => {
|
||||||
|
this.setState({ showValuesFile: false });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Loading>
|
</Loading>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,6 @@ class KV extends Component<Props, State> {
|
||||||
this.form.setValue('envValue-' + key, value[label]);
|
this.form.setValue('envValue-' + key, value[label]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ items: newItems });
|
this.setState({ items: newItems });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -353,6 +353,26 @@ export interface Trigger {
|
||||||
componentName?: string;
|
componentName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateTriggerRequest {
|
||||||
|
name: string;
|
||||||
|
alias?: string;
|
||||||
|
description?: string;
|
||||||
|
workflowName: string;
|
||||||
|
type: 'webhook';
|
||||||
|
payloadType?: 'custom' | 'dockerHub' | 'ACR' | 'harbor' | 'artifactory';
|
||||||
|
registry?: string;
|
||||||
|
componentName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateTriggerRequest {
|
||||||
|
alias?: string;
|
||||||
|
description?: string;
|
||||||
|
workflowName: string;
|
||||||
|
payloadType?: 'custom' | 'dockerHub' | 'ACR' | 'harbor' | 'artifactory';
|
||||||
|
registry?: string;
|
||||||
|
componentName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApplicationComponentConfig {
|
export interface ApplicationComponentConfig {
|
||||||
name: string;
|
name: string;
|
||||||
alias: string;
|
alias: string;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export interface HelmRepo {
|
export interface HelmRepo {
|
||||||
url: string;
|
url: string;
|
||||||
type: string;
|
type?: string;
|
||||||
secretName?: string;
|
secretName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@ import { withTranslation } from 'react-i18next';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import { Dialog, Field, Form, Grid, Message, Select, Button } from '@b-design/ui';
|
import { Dialog, Field, Form, Grid, Message, Select, Button } from '@b-design/ui';
|
||||||
import type { ApplicationDetail, EnvBinding } from '../../../../interface/application';
|
import type { ApplicationDetail, EnvBinding } from '../../../../interface/application';
|
||||||
import { createApplicationEnv, updateApplicationEnv } from '../../../../api/application';
|
import {
|
||||||
|
createApplicationEnvbinding,
|
||||||
|
updateApplicationEnvbinding,
|
||||||
|
} from '../../../../api/application';
|
||||||
import Translation from '../../../../components/Translation';
|
import Translation from '../../../../components/Translation';
|
||||||
import locale from '../../../../utils/locale';
|
import locale from '../../../../utils/locale';
|
||||||
import { getEnvs } from '../../../../api/env';
|
import { getEnvs } from '../../../../api/env';
|
||||||
|
@ -11,6 +14,8 @@ import type { Env } from '../../../../interface/env';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import EnvDialog from '../../../../pages/EnvPage/components/EnvDialog';
|
import EnvDialog from '../../../../pages/EnvPage/components/EnvDialog';
|
||||||
import type { LoginUserInfo } from '../../../../interface/user';
|
import type { LoginUserInfo } from '../../../../interface/user';
|
||||||
|
import { showAlias } from '../../../../utils/common';
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
@ -19,7 +24,7 @@ interface Props {
|
||||||
envbinding?: EnvBinding[];
|
envbinding?: EnvBinding[];
|
||||||
targets?: [];
|
targets?: [];
|
||||||
userInfo?: LoginUserInfo;
|
userInfo?: LoginUserInfo;
|
||||||
dispatch?: ({}) => {};
|
dispatch?: Dispatch<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -53,9 +58,9 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
|
|
||||||
loadEnvs = async (callback?: Callback) => {
|
loadEnvs = async (callback?: Callback) => {
|
||||||
const { applicationDetail, envbinding } = this.props;
|
const { applicationDetail, envbinding } = this.props;
|
||||||
if (applicationDetail) {
|
if (applicationDetail && applicationDetail.project?.name) {
|
||||||
//Temporary logic
|
//Temporary logic
|
||||||
getEnvs({ project: applicationDetail.project?.name || 'default', page: 0 }).then((re) => {
|
getEnvs({ project: applicationDetail.project?.name, page: 0 }).then((re) => {
|
||||||
const existEnvs =
|
const existEnvs =
|
||||||
envbinding?.map((eb) => {
|
envbinding?.map((eb) => {
|
||||||
return eb.name;
|
return eb.name;
|
||||||
|
@ -65,7 +70,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
this.setState({ envs: canAdd });
|
this.setState({ envs: canAdd });
|
||||||
const envOption = canAdd?.map((env: { name: string; alias: string }) => {
|
const envOption = canAdd?.map((env: { name: string; alias: string }) => {
|
||||||
return {
|
return {
|
||||||
label: env.alias ? `${env.alias}(${env.name})` : env.name,
|
label: showAlias(env.name, env.alias),
|
||||||
value: env.name,
|
value: env.name,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -78,19 +83,19 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
|
|
||||||
onSubmit = () => {
|
onSubmit = () => {
|
||||||
const { applicationDetail } = this.props;
|
const { applicationDetail } = this.props;
|
||||||
|
if (!applicationDetail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { isEdit } = this.state;
|
const { isEdit } = this.state;
|
||||||
this.field.validate((error: any, values: any) => {
|
this.field.validate((error: any, values: any) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
const { name, alias, description, targetNames } = values;
|
const { name } = values;
|
||||||
const params = {
|
const params = {
|
||||||
appName: applicationDetail && applicationDetail.name,
|
appName: applicationDetail && applicationDetail.name,
|
||||||
name,
|
name,
|
||||||
alias,
|
|
||||||
targetNames,
|
|
||||||
description,
|
|
||||||
};
|
};
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
this.onUpdateApplicationEnv(params);
|
this.onUpdateApplicationEnv(params);
|
||||||
|
@ -101,7 +106,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onCreateApplicationEnv(params: any) {
|
onCreateApplicationEnv(params: any) {
|
||||||
createApplicationEnv(params)
|
createApplicationEnvbinding(params)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success(<Translation>Environment bound successfully</Translation>);
|
Message.success(<Translation>Environment bound successfully</Translation>);
|
||||||
|
@ -114,7 +119,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateApplicationEnv(params: any) {
|
onUpdateApplicationEnv(params: any) {
|
||||||
updateApplicationEnv(params)
|
updateApplicationEnvbinding(params)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success(<Translation>Environment bound successfully</Translation>);
|
Message.success(<Translation>Environment bound successfully</Translation>);
|
||||||
|
@ -207,7 +212,7 @@ class EnvBindPlanDialog extends Component<Props, State> {
|
||||||
this.changeEnvDialog(true);
|
this.changeEnvDialog(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Translation>New environment</Translation>
|
<Translation>New Environment</Translation>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
required={true}
|
required={true}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
font-size: var(--font-size-normal);
|
font-size: var(--font-size-normal);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
border: var(--grey-200) dashed 1px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
&.active {
|
&.active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,9 @@ class TabsContent extends Component<Props, State> {
|
||||||
trigger={
|
trigger={
|
||||||
<Link
|
<Link
|
||||||
key={item.name + 'link'}
|
key={item.name + 'link'}
|
||||||
className={classNames('top-menu-item', { active: activeKey === item.name })}
|
className={classNames('top-menu-item', 'item-env', {
|
||||||
|
active: activeKey === item.name,
|
||||||
|
})}
|
||||||
to={`/applications/${applicationDetail?.name}/envbinding/${item.name}/workflow`}
|
to={`/applications/${applicationDetail?.name}/envbinding/${item.name}/workflow`}
|
||||||
>
|
>
|
||||||
<span title={item.description}>{item.alias ? item.alias : item.name}</span>
|
<span title={item.description}>{item.alias ? item.alias : item.name}</span>
|
||||||
|
|
|
@ -155,15 +155,13 @@ class ApplicationHeader extends Component<Props, State> {
|
||||||
<If condition={applicationDetail?.readOnly}>
|
<If condition={applicationDetail?.readOnly}>
|
||||||
<Message
|
<Message
|
||||||
type="notice"
|
type="notice"
|
||||||
title={i18n
|
title={i18n.t('This application is managed by the addon, and it is readonly')}
|
||||||
.t('This application is managed by the addon, and it is readonly')
|
|
||||||
.toString()}
|
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<If condition={sourceOfTrust === 'from-k8s-resource'}>
|
<If condition={sourceOfTrust === 'from-k8s-resource'}>
|
||||||
<Message
|
<Message
|
||||||
type="warning"
|
type="warning"
|
||||||
title={i18n.t('The application is synchronizing from the cluster.').toString()}
|
title={i18n.t('The application is synchronizing from the cluster.')}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<Permission
|
<Permission
|
||||||
|
|
|
@ -36,6 +36,11 @@ class Menu extends Component<Props, any> {
|
||||||
label: <Translation>Revisions</Translation>,
|
label: <Translation>Revisions</Translation>,
|
||||||
to: `/applications/${appName}/revisions`,
|
to: `/applications/${appName}/revisions`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'workflows',
|
||||||
|
label: <Translation>Workflows</Translation>,
|
||||||
|
to: `/applications/${appName}/workflows`,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
envPage: [
|
envPage: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,8 @@ import PipelineListPage from '../../pages/PipelineListPage';
|
||||||
import ApplicationWorkflowStudio from '../../pages/ApplicationWorkflowStudio';
|
import ApplicationWorkflowStudio from '../../pages/ApplicationWorkflowStudio';
|
||||||
import PipelineStudio from '../../pages/PipelineStudio';
|
import PipelineStudio from '../../pages/PipelineStudio';
|
||||||
import ProjectPipelines from '../../pages/ProjectPipelines';
|
import ProjectPipelines from '../../pages/ProjectPipelines';
|
||||||
|
import ApplicationEnvRoute from '../../pages/ApplicationEnvRoute';
|
||||||
|
import ApplicationWorkflowList from '../../pages/ApplicationWorkflowList';
|
||||||
|
|
||||||
export default function Content() {
|
export default function Content() {
|
||||||
return (
|
return (
|
||||||
|
@ -73,6 +75,28 @@ export default function Content() {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/applications/:appName/envbinding"
|
||||||
|
render={(props: any) => {
|
||||||
|
return (
|
||||||
|
<ApplicationLayout {...props}>
|
||||||
|
<ApplicationEnvRoute {...props} />;
|
||||||
|
</ApplicationLayout>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/applications/:appName/workflows"
|
||||||
|
render={(props: any) => {
|
||||||
|
return (
|
||||||
|
<ApplicationLayout {...props}>
|
||||||
|
<ApplicationWorkflowList {...props} />
|
||||||
|
</ApplicationLayout>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/applications/:appName/envbinding/:envName"
|
path="/applications/:appName/envbinding/:envName"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
:root {
|
:root {
|
||||||
--min-layout-width: 1400px;
|
--min-layout-width: 1500px;
|
||||||
--primary-color: #1b58f4;
|
--primary-color: #1b58f4;
|
||||||
--warning-color: var(--message-warning-color-icon-inline, #fac800);
|
--warning-color: var(--message-warning-color-icon-inline, #fac800);
|
||||||
--hover-color: #f7bca9;
|
--hover-color: #f7bca9;
|
||||||
|
|
|
@ -195,6 +195,7 @@
|
||||||
"New Environment": "新增环境",
|
"New Environment": "新增环境",
|
||||||
"New Trait": "新增运维特征",
|
"New Trait": "新增运维特征",
|
||||||
"New Trigger": "新增触发器",
|
"New Trigger": "新增触发器",
|
||||||
|
"Edit Trigger": "编辑触发器",
|
||||||
"New Workflow": "新增工作流",
|
"New Workflow": "新增工作流",
|
||||||
"Next Step": "下一步",
|
"Next Step": "下一步",
|
||||||
"No version record": "暂无版本记录",
|
"No version record": "暂无版本记录",
|
||||||
|
@ -596,5 +597,25 @@
|
||||||
"Launch Workflow Studio": "工作流画板",
|
"Launch Workflow Studio": "工作流画板",
|
||||||
"Available Targets": "可用的交付目标",
|
"Available Targets": "可用的交付目标",
|
||||||
"Select Step Type": "选择步骤类型",
|
"Select Step Type": "选择步骤类型",
|
||||||
"Unsaved changes": "变更未保存"
|
"Unsaved changes": "变更未保存",
|
||||||
|
"DAG": "并行执行",
|
||||||
|
"StepByStep": "串行执行",
|
||||||
|
"Mode": "执行模式",
|
||||||
|
"Sub Mode": "子步骤",
|
||||||
|
"The workflow step spec reference document": "工作流部署规范参考文档",
|
||||||
|
"Click here": "点击这里",
|
||||||
|
"Select Contexts": "选择上下文参数",
|
||||||
|
"The context is the runtime inputs for the Pipeline": "上下文参数作为工作流的运行时输入",
|
||||||
|
"New Context": "新建上下文参数",
|
||||||
|
"No context is required for the execution of this Pipeline": "工作流执行无必需的上下文参数",
|
||||||
|
"Run Pipeline": "执行工作流",
|
||||||
|
"Click and deselect": "点击取消选中",
|
||||||
|
"Click and select": "点击选中",
|
||||||
|
"Edit Pipeline": "编辑流水线",
|
||||||
|
"Pipeline loaded successfully and is ready to clone.": "流水线配置加载完成,可以开始克隆。",
|
||||||
|
"Clone Pipeline": "克隆流水线",
|
||||||
|
"Includes": "包括",
|
||||||
|
"contexts": "上下文参数",
|
||||||
|
"Are you sure to rerun this Pipeline?": "确定要重新运行流水线吗?",
|
||||||
|
"Show Values File": "查看 Value 文件"
|
||||||
}
|
}
|
|
@ -58,6 +58,7 @@ type State = {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
status: 'disabled' | 'enabled' | 'enabling' | 'suspend' | 'disabling' | '';
|
status: 'disabled' | 'enabled' | 'enabling' | 'suspend' | 'disabling' | '';
|
||||||
statusLoading: boolean;
|
statusLoading: boolean;
|
||||||
|
enableLoading?: boolean;
|
||||||
upgradeLoading: boolean;
|
upgradeLoading: boolean;
|
||||||
args?: any;
|
args?: any;
|
||||||
addonsStatus?: ApplicationStatus;
|
addonsStatus?: ApplicationStatus;
|
||||||
|
@ -251,7 +252,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
Message.warning(i18n.t('Please firstly select at least one cluster.'));
|
Message.warning(i18n.t('Please firstly select at least one cluster.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ statusLoading: true }, () => {
|
this.setState({ statusLoading: true, enableLoading: true }, () => {
|
||||||
if (this.state.version) {
|
if (this.state.version) {
|
||||||
const params: EnableAddonRequest = {
|
const params: EnableAddonRequest = {
|
||||||
name: this.props.addonName,
|
name: this.props.addonName,
|
||||||
|
@ -262,9 +263,13 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
if (this.state.addonDetailInfo?.deployTo?.runtimeCluster) {
|
if (this.state.addonDetailInfo?.deployTo?.runtimeCluster) {
|
||||||
params.clusters = this.state.clusters;
|
params.clusters = this.state.clusters;
|
||||||
}
|
}
|
||||||
enableAddon(params).then(() => {
|
enableAddon(params)
|
||||||
this.loadAddonStatus();
|
.then(() => {
|
||||||
});
|
this.loadAddonStatus();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({ enableLoading: false });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -311,6 +316,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
status,
|
status,
|
||||||
statusLoading,
|
statusLoading,
|
||||||
upgradeLoading,
|
upgradeLoading,
|
||||||
|
enableLoading,
|
||||||
addonsStatus,
|
addonsStatus,
|
||||||
showStatusVisible,
|
showStatusVisible,
|
||||||
version,
|
version,
|
||||||
|
@ -390,7 +396,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
project={''}
|
project={''}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
loading={status === 'enabling'}
|
loading={status === 'enabling' || enableLoading}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={this.onEnable}
|
onClick={this.onEnable}
|
||||||
style={{ marginLeft: '16px' }}
|
style={{ marginLeft: '16px' }}
|
||||||
|
@ -462,7 +468,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
||||||
{`${i18n.t('Addon status is ')}${addonsStatus?.status || 'Init'}`}
|
{`${i18n.t('Addon status is ')}${addonsStatus?.status || 'Init'}`}
|
||||||
<Link
|
<Link
|
||||||
style={{ marginLeft: '16px' }}
|
style={{ marginLeft: '16px' }}
|
||||||
to={`/applications/addon-${addonDetailInfo?.name}/envbinding/system/workflow`}
|
to={`/applications/addon-${addonDetailInfo?.name}/envbinding`}
|
||||||
>
|
>
|
||||||
<Translation>Check the details</Translation>
|
<Translation>Check the details</Translation>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
.components-list-title {
|
.components-list-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
color: #1b58f4;
|
color: var(--primary-color);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import Permission from '../../../../components/Permission';
|
||||||
import terraform from '../../../../assets/terraform.svg';
|
import terraform from '../../../../assets/terraform.svg';
|
||||||
import kubernetes from '../../../../assets/kubernetes.svg';
|
import kubernetes from '../../../../assets/kubernetes.svg';
|
||||||
import helm from '../../../../assets/helm.svg';
|
import helm from '../../../../assets/helm.svg';
|
||||||
|
import { showAlias } from '../../../../utils/common';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application?: ApplicationBase;
|
application?: ApplicationBase;
|
||||||
|
@ -57,8 +58,8 @@ class ComponentsList extends Component<Props> {
|
||||||
<div className="list-warper">
|
<div className="list-warper">
|
||||||
<div className="box">
|
<div className="box">
|
||||||
{(components || []).map((item: ApplicationComponentBase) => (
|
{(components || []).map((item: ApplicationComponentBase) => (
|
||||||
<Row wrap={true} className="box-item">
|
<Row key={item.name} wrap={true} className="box-item">
|
||||||
<Col span={24} key={item.name}>
|
<Col span={24}>
|
||||||
<Card locale={locale().Card} contentHeight="auto">
|
<Card locale={locale().Card} contentHeight="auto">
|
||||||
<div className="components-list-nav">
|
<div className="components-list-nav">
|
||||||
<div
|
<div
|
||||||
|
@ -68,7 +69,7 @@ class ComponentsList extends Component<Props> {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{this.getComponentTypeIcon(item)}
|
{this.getComponentTypeIcon(item)}
|
||||||
{item.alias ? `${item.alias}(${item.name})` : item.name}
|
{showAlias(item)}
|
||||||
</div>
|
</div>
|
||||||
<If condition={item.main != true}>
|
<If condition={item.main != true}>
|
||||||
<div className="components-list-operation">
|
<div className="components-list-operation">
|
||||||
|
@ -98,7 +99,7 @@ class ComponentsList extends Component<Props> {
|
||||||
</If>
|
</If>
|
||||||
<Row wrap={true}>
|
<Row wrap={true}>
|
||||||
{item.traits?.map((trait) => {
|
{item.traits?.map((trait) => {
|
||||||
const label = trait.alias ? trait.alias + '(' + trait.type + ')' : trait.type;
|
const label = showAlias(trait.type, trait.alias);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={trait.type}
|
key={trait.type}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Grid, Field, Form, Select, Message, Button, Input } from '@b-design/ui';
|
import { Grid, Field, Form, Select, Message, Button, Input } from '@b-design/ui';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { createTriggers } from '../../../../api/application';
|
import { createTrigger, updateTrigger } from '../../../../api/application';
|
||||||
import { getPayloadType } from '../../../../api/payload';
|
import { getPayloadType } from '../../../../api/payload';
|
||||||
import { detailComponentDefinition } from '../../../../api/definitions';
|
import { detailComponentDefinition } from '../../../../api/definitions';
|
||||||
import type {
|
import type {
|
||||||
|
@ -9,6 +9,8 @@ import type {
|
||||||
Trigger,
|
Trigger,
|
||||||
UIParam,
|
UIParam,
|
||||||
ApplicationComponentBase,
|
ApplicationComponentBase,
|
||||||
|
CreateTriggerRequest,
|
||||||
|
UpdateTriggerRequest,
|
||||||
} from '../../../../interface/application';
|
} from '../../../../interface/application';
|
||||||
import DrawerWithFooter from '../../../../components/Drawer';
|
import DrawerWithFooter from '../../../../components/Drawer';
|
||||||
import Translation from '../../../../components/Translation';
|
import Translation from '../../../../components/Translation';
|
||||||
|
@ -16,6 +18,7 @@ import { checkName } from '../../../../utils/common';
|
||||||
import locale from '../../../../utils/locale';
|
import locale from '../../../../utils/locale';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import i18n from '../../../../i18n';
|
import i18n from '../../../../i18n';
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
@ -23,8 +26,9 @@ type Props = {
|
||||||
workflows?: Workflow[];
|
workflows?: Workflow[];
|
||||||
onOK: (params: Trigger) => void;
|
onOK: (params: Trigger) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
dispatch?: ({}) => {};
|
dispatch?: Dispatch<any>;
|
||||||
components: ApplicationComponentBase[];
|
components: ApplicationComponentBase[];
|
||||||
|
trigger?: Trigger;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -32,6 +36,7 @@ type State = {
|
||||||
payloadTypes: string[];
|
payloadTypes: string[];
|
||||||
hasImage: boolean;
|
hasImage: boolean;
|
||||||
component?: ApplicationComponentBase;
|
component?: ApplicationComponentBase;
|
||||||
|
submitLoading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TriggerDialog extends React.Component<Props, State> {
|
class TriggerDialog extends React.Component<Props, State> {
|
||||||
|
@ -47,6 +52,10 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
const { trigger } = this.props;
|
||||||
|
if (trigger) {
|
||||||
|
this.field.setValues(trigger);
|
||||||
|
}
|
||||||
const type = this.field.getValue('type');
|
const type = this.field.getValue('type');
|
||||||
if (type === 'webhook') {
|
if (type === 'webhook') {
|
||||||
this.onGetPayloadType();
|
this.onGetPayloadType();
|
||||||
|
@ -90,6 +99,8 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit = () => {
|
onSubmit = () => {
|
||||||
|
const { trigger } = this.props;
|
||||||
|
const editMode = trigger != undefined;
|
||||||
this.field.validate((error: any, values: any) => {
|
this.field.validate((error: any, values: any) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return;
|
||||||
|
@ -106,38 +117,68 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
registry = '',
|
registry = '',
|
||||||
} = values;
|
} = values;
|
||||||
const query = { appName };
|
const query = { appName };
|
||||||
const params: Trigger = {
|
this.setState({ submitLoading: true });
|
||||||
name,
|
if (editMode) {
|
||||||
alias,
|
const params: UpdateTriggerRequest = {
|
||||||
description,
|
alias,
|
||||||
type,
|
description,
|
||||||
payloadType,
|
payloadType,
|
||||||
workflowName,
|
workflowName,
|
||||||
token: '',
|
componentName,
|
||||||
componentName,
|
registry,
|
||||||
registry,
|
};
|
||||||
};
|
updateTrigger(params, { appName, token: trigger.token })
|
||||||
createTriggers(params, query).then((res: any) => {
|
.then((res: any) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
Message.success({
|
Message.success({
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
content: 'Trigger created successfully.',
|
content: 'Trigger updated successfully.',
|
||||||
|
});
|
||||||
|
this.props.onOK(res);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({ submitLoading: false });
|
||||||
});
|
});
|
||||||
this.props.onOK(res);
|
} else {
|
||||||
}
|
const params: CreateTriggerRequest = {
|
||||||
});
|
name,
|
||||||
|
alias,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
payloadType,
|
||||||
|
workflowName,
|
||||||
|
componentName,
|
||||||
|
registry,
|
||||||
|
};
|
||||||
|
createTrigger(params, query)
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res) {
|
||||||
|
Message.success({
|
||||||
|
duration: 4000,
|
||||||
|
content: 'Trigger created successfully.',
|
||||||
|
});
|
||||||
|
this.props.onOK(res);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({ submitLoading: false });
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
extButtonList = () => {
|
extButtonList = () => {
|
||||||
const { onClose } = this.props;
|
const { onClose, trigger } = this.props;
|
||||||
|
const { submitLoading } = this.state;
|
||||||
|
const editMode = trigger != undefined;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button type="secondary" onClick={onClose} className="margin-right-10">
|
<Button type="secondary" onClick={onClose} className="margin-right-10">
|
||||||
<Translation>Cancel</Translation>
|
<Translation>Cancel</Translation>
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={this.onSubmit}>
|
<Button loading={submitLoading} type="primary" onClick={this.onSubmit}>
|
||||||
<Translation>Create</Translation>
|
<Translation>{editMode ? 'Update' : 'Create'}</Translation>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -180,7 +221,8 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
const init = this.field.init;
|
const init = this.field.init;
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
const { Row, Col } = Grid;
|
const { Row, Col } = Grid;
|
||||||
const { workflows, onClose, components } = this.props;
|
const { workflows, onClose, components, trigger } = this.props;
|
||||||
|
const editMode = trigger != undefined;
|
||||||
const { payloadTypes } = this.state;
|
const { payloadTypes } = this.state;
|
||||||
const workflowOption = workflows?.map((workflow) => {
|
const workflowOption = workflows?.map((workflow) => {
|
||||||
return {
|
return {
|
||||||
|
@ -205,7 +247,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerWithFooter
|
<DrawerWithFooter
|
||||||
title={i18n.t('Add Trigger')}
|
title={editMode ? i18n.t('Edit Trigger') : i18n.t('Add Trigger')}
|
||||||
placement="right"
|
placement="right"
|
||||||
width={800}
|
width={800}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
@ -217,6 +259,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
<FormItem label={<Translation>Name</Translation>} required>
|
<FormItem label={<Translation>Name</Translation>} required>
|
||||||
<Input
|
<Input
|
||||||
name="name"
|
name="name"
|
||||||
|
disabled={editMode}
|
||||||
placeholder={i18n.t('Please enter the name').toString()}
|
placeholder={i18n.t('Please enter the name').toString()}
|
||||||
{...init('name', {
|
{...init('name', {
|
||||||
rules: [
|
rules: [
|
||||||
|
@ -274,6 +317,7 @@ class TriggerDialog extends React.Component<Props, State> {
|
||||||
<Select
|
<Select
|
||||||
name="type"
|
name="type"
|
||||||
locale={locale().Select}
|
locale={locale().Select}
|
||||||
|
disabled={editMode}
|
||||||
dataSource={[{ label: 'On Webhook Event', value: 'webhook' }]}
|
dataSource={[{ label: 'On Webhook Event', value: 'webhook' }]}
|
||||||
{...init('type', {
|
{...init('type', {
|
||||||
initValue: 'webhook',
|
initValue: 'webhook',
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
.trigger-list-title {
|
.trigger-list-title {
|
||||||
|
color: var(--primary-color);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
ApplicationDetail,
|
ApplicationDetail,
|
||||||
} from '../../../../interface/application';
|
} from '../../../../interface/application';
|
||||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||||
import { momentDate } from '../../../../utils/common';
|
import { momentDate, showAlias } from '../../../../utils/common';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import Empty from '../../../../components/Empty';
|
import Empty from '../../../../components/Empty';
|
||||||
|
@ -24,6 +24,7 @@ type Props = {
|
||||||
components: ApplicationComponentBase[];
|
components: ApplicationComponentBase[];
|
||||||
applicationDetail?: ApplicationDetail;
|
applicationDetail?: ApplicationDetail;
|
||||||
onDeleteTrigger: (token: string) => void;
|
onDeleteTrigger: (token: string) => void;
|
||||||
|
onEditTrigger: (t: Trigger) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -135,12 +136,17 @@ class TriggerList extends Component<Props, State> {
|
||||||
<div className="list-warper">
|
<div className="list-warper">
|
||||||
<div className="box">
|
<div className="box">
|
||||||
{(triggers || []).map((item: Trigger) => (
|
{(triggers || []).map((item: Trigger) => (
|
||||||
<Row wrap={true} className="box-item">
|
<Row wrap={true} key={item.type} className="box-item">
|
||||||
<Col span={24} key={item.type}>
|
<Col span={24}>
|
||||||
<Card free={true} style={{ padding: '16px' }} locale={locale().Card}>
|
<Card free={true} style={{ padding: '16px' }} locale={locale().Card}>
|
||||||
<div className="trigger-list-nav">
|
<div className="trigger-list-nav">
|
||||||
<div className="trigger-list-title">
|
<div
|
||||||
{item.alias ? `${item.alias}(${item.name})` : item.name}
|
onClick={() => {
|
||||||
|
this.props.onEditTrigger(item);
|
||||||
|
}}
|
||||||
|
className="trigger-list-title"
|
||||||
|
>
|
||||||
|
{showAlias(item)}
|
||||||
</div>
|
</div>
|
||||||
<div className="trigger-list-operation">
|
<div className="trigger-list-operation">
|
||||||
<Permission
|
<Permission
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { If } from 'tsx-control-statements/components';
|
||||||
import {
|
import {
|
||||||
deleteTrait,
|
deleteTrait,
|
||||||
getApplicationTriggers,
|
getApplicationTriggers,
|
||||||
deleteTriggers,
|
deleteTrigger,
|
||||||
deleteComponent,
|
deleteComponent,
|
||||||
deleteApplication,
|
deleteApplication,
|
||||||
deletePolicy,
|
deletePolicy,
|
||||||
|
@ -76,6 +76,7 @@ type State = {
|
||||||
mainComponent?: ApplicationComponent;
|
mainComponent?: ApplicationComponent;
|
||||||
traitItem: Trait;
|
traitItem: Trait;
|
||||||
triggers: Trigger[];
|
triggers: Trigger[];
|
||||||
|
trigger?: Trigger;
|
||||||
visibleTrigger: boolean;
|
visibleTrigger: boolean;
|
||||||
createTriggerInfo: Trigger;
|
createTriggerInfo: Trigger;
|
||||||
showEditApplication: boolean;
|
showEditApplication: boolean;
|
||||||
|
@ -211,6 +212,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
onTriggerClose = () => {
|
onTriggerClose = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
visibleTrigger: false,
|
visibleTrigger: false,
|
||||||
|
trigger: undefined,
|
||||||
});
|
});
|
||||||
this.onLoadApplicationComponents();
|
this.onLoadApplicationComponents();
|
||||||
};
|
};
|
||||||
|
@ -219,6 +221,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
this.onGetApplicationTrigger();
|
this.onGetApplicationTrigger();
|
||||||
this.setState({
|
this.setState({
|
||||||
visibleTrigger: false,
|
visibleTrigger: false,
|
||||||
|
trigger: undefined,
|
||||||
createTriggerInfo: res,
|
createTriggerInfo: res,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -229,8 +232,12 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
appName,
|
appName,
|
||||||
token,
|
token,
|
||||||
};
|
};
|
||||||
deleteTriggers(params).then((res: any) => {
|
deleteTrigger(params).then((res: any) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
|
Message.success({
|
||||||
|
duration: 4000,
|
||||||
|
content: 'Trigger deleted successfully.',
|
||||||
|
});
|
||||||
this.onGetApplicationTrigger();
|
this.onGetApplicationTrigger();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -462,6 +469,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
componentName = '',
|
componentName = '',
|
||||||
traitItem,
|
traitItem,
|
||||||
triggers,
|
triggers,
|
||||||
|
trigger,
|
||||||
visibleTrigger,
|
visibleTrigger,
|
||||||
createTriggerInfo,
|
createTriggerInfo,
|
||||||
showEditApplication,
|
showEditApplication,
|
||||||
|
@ -481,7 +489,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Row className="flex-row" wrap={true}>
|
<Row className="flex-row" wrap={true}>
|
||||||
<Col xl={16} m={24} s={24} style={{ padding: '0 8px' }}>
|
<Col xl={16} l={24} s={24} style={{ padding: '0 8px' }}>
|
||||||
<Card
|
<Card
|
||||||
locale={locale().Card}
|
locale={locale().Card}
|
||||||
contentHeight="auto"
|
contentHeight="auto"
|
||||||
|
@ -525,7 +533,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col l={8} xxs={24}>
|
<Col l={8} xs={24}>
|
||||||
<Item
|
<Item
|
||||||
label={<Translation>Project</Translation>}
|
label={<Translation>Project</Translation>}
|
||||||
value={
|
value={
|
||||||
|
@ -538,7 +546,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col l={8} xxs={24}>
|
<Col l={8} xs={24}>
|
||||||
<Item
|
<Item
|
||||||
label={<Translation>Create Time</Translation>}
|
label={<Translation>Create Time</Translation>}
|
||||||
value={
|
value={
|
||||||
|
@ -555,7 +563,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col l={8} xxs={24}>
|
<Col l={8} xs={24}>
|
||||||
<Item
|
<Item
|
||||||
label={<Translation>Update Time</Translation>}
|
label={<Translation>Update Time</Translation>}
|
||||||
value={
|
value={
|
||||||
|
@ -578,6 +586,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
if (applicationDetail?.labels) {
|
if (applicationDetail?.labels) {
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
|
key={key}
|
||||||
style={{ margin: '4px' }}
|
style={{ margin: '4px' }}
|
||||||
color="blue"
|
color="blue"
|
||||||
>{`${key}=${applicationDetail?.labels[key]}`}</Tag>
|
>{`${key}=${applicationDetail?.labels[key]}`}</Tag>
|
||||||
|
@ -588,7 +597,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xl={8} m={24} s={24} style={{ padding: '0 8px' }}>
|
<Col xl={8} l={24} s={24} style={{ padding: '0 8px' }}>
|
||||||
<Card locale={locale().Card} contentHeight="auto" style={{ height: '100%' }}>
|
<Card locale={locale().Card} contentHeight="auto" style={{ height: '100%' }}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={6} style={{ padding: '22px 0' }}>
|
<Col span={6} style={{ padding: '22px 0' }}>
|
||||||
|
@ -606,12 +615,14 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
<Col span={6} style={{ padding: '22px 0' }}>
|
<Col span={6} style={{ padding: '22px 0' }}>
|
||||||
<NumItem
|
<NumItem
|
||||||
number={statistics?.revisionCount}
|
number={statistics?.revisionCount}
|
||||||
|
to={`/applications/${applicationDetail.name}/revisions`}
|
||||||
title={i18n.t('Revision Count').toString()}
|
title={i18n.t('Revision Count').toString()}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={6} style={{ padding: '22px 0' }}>
|
<Col span={6} style={{ padding: '22px 0' }}>
|
||||||
<NumItem
|
<NumItem
|
||||||
number={statistics?.workflowCount}
|
number={statistics?.workflowCount}
|
||||||
|
to={`/applications/${applicationDetail.name}/workflows`}
|
||||||
title={i18n.t('Workflow Count').toString()}
|
title={i18n.t('Workflow Count').toString()}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -621,7 +632,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row wrap={true} className="app-spec">
|
<Row wrap={true} className="app-spec">
|
||||||
<Col xl={8} xs={24} className="app-spec-item">
|
<Col xl={8} xxs={24} className="app-spec-item">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} className="padding16">
|
<Col span={24} className="padding16">
|
||||||
<Title
|
<Title
|
||||||
|
@ -672,7 +683,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
changeTraitStats={this.changeTraitStats}
|
changeTraitStats={this.changeTraitStats}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xl={8} xs={24} className="app-spec-item">
|
<Col xl={8} xxs={24} className="app-spec-item">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} className="padding16">
|
<Col span={24} className="padding16">
|
||||||
<Title
|
<Title
|
||||||
|
@ -713,7 +724,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xl={8} xs={24} className="app-spec-item">
|
<Col xl={8} xxs={24} className="app-spec-item">
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24} className="padding16">
|
<Col span={24} className="padding16">
|
||||||
<Title
|
<Title
|
||||||
|
@ -751,6 +762,9 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
}}
|
}}
|
||||||
createTriggerInfo={createTriggerInfo}
|
createTriggerInfo={createTriggerInfo}
|
||||||
applicationDetail={applicationDetail}
|
applicationDetail={applicationDetail}
|
||||||
|
onEditTrigger={(t: Trigger) => {
|
||||||
|
this.setState({ visibleTrigger: true, trigger: t });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -780,6 +794,7 @@ class ApplicationConfig extends Component<Props, State> {
|
||||||
<TriggerDialog
|
<TriggerDialog
|
||||||
visible={visibleTrigger}
|
visible={visibleTrigger}
|
||||||
appName={appName}
|
appName={appName}
|
||||||
|
trigger={trigger}
|
||||||
workflows={workflows}
|
workflows={workflows}
|
||||||
components={components || []}
|
components={components || []}
|
||||||
onClose={this.onTriggerClose}
|
onClose={this.onTriggerClose}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Loading } from '@b-design/ui';
|
||||||
|
import { connect } from 'dva';
|
||||||
|
import { routerRedux } from 'dva/router';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
import { getApplicationEnvbinding } from '../../api/application';
|
||||||
|
|
||||||
|
const EnvRoute = (props: { match: { params: { appName: string } }; dispatch: Dispatch<any> }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
getApplicationEnvbinding({ appName: props.match.params.appName }).then((res) => {
|
||||||
|
if (res && Array.isArray(res.envBindings) && res.envBindings.length > 0) {
|
||||||
|
props.dispatch(
|
||||||
|
routerRedux.push(
|
||||||
|
`/applications/${props.match.params.appName}/envbinding/${res.envBindings[0].name}/workflow`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
props.dispatch(routerRedux.push(`/applications/${props.match.params.appName}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return <Loading visible={true} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect()(EnvRoute);
|
|
@ -434,7 +434,7 @@ class AppDialog extends React.Component<Props, State> {
|
||||||
this.changeEnvDialog(true);
|
this.changeEnvDialog(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Translation>New environment</Translation>
|
<Translation>New Environment</Translation>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
required={true}
|
required={true}
|
||||||
|
|
|
@ -32,6 +32,7 @@ type Props = {
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
compare?: ApplicationCompareResponse;
|
compare?: ApplicationCompareResponse;
|
||||||
|
revision?: ApplicationRevision;
|
||||||
visibleApplicationDiff: boolean;
|
visibleApplicationDiff: boolean;
|
||||||
diffMode: 'latest' | 'cluster';
|
diffMode: 'latest' | 'cluster';
|
||||||
};
|
};
|
||||||
|
@ -80,22 +81,27 @@ class TableList extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadChanges = (revision: string, mode: 'latest' | 'cluster') => {
|
loadChanges = (revision: ApplicationRevision, mode: 'latest' | 'cluster') => {
|
||||||
const { applicationDetail } = this.props;
|
const { applicationDetail } = this.props;
|
||||||
if (!revision || !applicationDetail) {
|
if (!revision || !applicationDetail) {
|
||||||
this.setState({ compare: undefined });
|
this.setState({ compare: undefined });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let params: ApplicationCompareRequest = {
|
let params: ApplicationCompareRequest = {
|
||||||
compareRevisionWithLatest: { revision: revision },
|
compareRevisionWithLatest: { revision: revision.version },
|
||||||
};
|
};
|
||||||
if (mode === 'cluster') {
|
if (mode === 'cluster') {
|
||||||
params = {
|
params = {
|
||||||
compareRevisionWithRunning: { revision: revision },
|
compareRevisionWithRunning: { revision: revision.version },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
compareApplication(applicationDetail?.name, params).then((res: ApplicationCompareResponse) => {
|
compareApplication(applicationDetail?.name, params).then((res: ApplicationCompareResponse) => {
|
||||||
this.setState({ compare: res, visibleApplicationDiff: true, diffMode: mode });
|
this.setState({
|
||||||
|
revision: revision,
|
||||||
|
compare: res,
|
||||||
|
visibleApplicationDiff: true,
|
||||||
|
diffMode: mode,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -223,14 +229,14 @@ class TableList extends Component<Props, State> {
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.loadChanges(record.version, 'cluster');
|
this.loadChanges(record, 'cluster');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Translation>Diff With Deployed Application</Translation>
|
<Translation>Diff With Deployed Application</Translation>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.loadChanges(record.version, 'latest');
|
this.loadChanges(record, 'latest');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Translation>Diff With Latest Configuration</Translation>
|
<Translation>Diff With Latest Configuration</Translation>
|
||||||
|
@ -271,7 +277,7 @@ class TableList extends Component<Props, State> {
|
||||||
const { Column } = Table;
|
const { Column } = Table;
|
||||||
const columns = this.getColumns();
|
const columns = this.getColumns();
|
||||||
const { list } = this.props;
|
const { list } = this.props;
|
||||||
const { visibleApplicationDiff, compare, diffMode } = this.state;
|
const { visibleApplicationDiff, compare, diffMode, revision } = this.state;
|
||||||
const baseName = 'Current Revision';
|
const baseName = 'Current Revision';
|
||||||
let targetName = 'Latest Application Configuration';
|
let targetName = 'Latest Application Configuration';
|
||||||
if (diffMode == 'cluster') {
|
if (diffMode == 'cluster') {
|
||||||
|
@ -298,8 +304,19 @@ class TableList extends Component<Props, State> {
|
||||||
{compare && (
|
{compare && (
|
||||||
<ApplicationDiff
|
<ApplicationDiff
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
this.setState({ visibleApplicationDiff: false, compare: undefined });
|
this.setState({
|
||||||
|
visibleApplicationDiff: false,
|
||||||
|
compare: undefined,
|
||||||
|
revision: undefined,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
|
rollback2Revision={
|
||||||
|
diffMode === 'cluster' && revision
|
||||||
|
? () => {
|
||||||
|
this.onRollback(revision);
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
baseName={baseName}
|
baseName={baseName}
|
||||||
targetName={targetName}
|
targetName={targetName}
|
||||||
compare={compare}
|
compare={compare}
|
||||||
|
|
|
@ -76,8 +76,8 @@ class ApplicationRevisionList extends React.Component<Props, State> {
|
||||||
listRevisions(params).then((res) => {
|
listRevisions(params).then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
this.setState({
|
this.setState({
|
||||||
revisionsList: res && res.revisions,
|
revisionsList: res.revisions || [],
|
||||||
revisionsListTotal: res && res.total,
|
revisionsListTotal: res.total || 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'dva';
|
||||||
|
import { Button, Dialog, Message, Table } from '@b-design/ui';
|
||||||
|
import type { ApplicationDetail, EnvBinding, Workflow } from '../../interface/application';
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
import { deleteWorkflow, listWorkflow } from '../../api/workflows';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import { momentDate } from '../../utils/common';
|
||||||
|
import Permission from '../../components/Permission';
|
||||||
|
import { AiFillDelete } from 'react-icons/ai';
|
||||||
|
import Translation from '../../components/Translation';
|
||||||
|
import { Link } from 'dva/router';
|
||||||
|
import { If } from 'tsx-control-statements/components';
|
||||||
|
import locale from '../../utils/locale';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
revisions: [];
|
||||||
|
applicationDetail?: ApplicationDetail;
|
||||||
|
envbinding?: EnvBinding[];
|
||||||
|
dispatch: Dispatch<any>;
|
||||||
|
match: {
|
||||||
|
params: {
|
||||||
|
appName: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
workflowList: Workflow[];
|
||||||
|
};
|
||||||
|
|
||||||
|
@connect((store: any) => {
|
||||||
|
return { ...store.application };
|
||||||
|
})
|
||||||
|
class ApplicationWorkflowList extends React.Component<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
workflowList: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getWorkflowList();
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorkflowList = async () => {
|
||||||
|
const { applicationDetail } = this.props;
|
||||||
|
if (applicationDetail) {
|
||||||
|
const params = {
|
||||||
|
appName: applicationDetail?.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
listWorkflow(params).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
this.setState({
|
||||||
|
workflowList: res.workflows || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onDeleteWorkflow = (name: string) => {
|
||||||
|
const { applicationDetail } = this.props;
|
||||||
|
if (applicationDetail) {
|
||||||
|
Dialog.confirm({
|
||||||
|
type: 'confirm',
|
||||||
|
content: (
|
||||||
|
<Translation>Unrecoverable after deletion, are you sure to delete it?</Translation>
|
||||||
|
),
|
||||||
|
onOk: () => {
|
||||||
|
deleteWorkflow({
|
||||||
|
appName: applicationDetail.name,
|
||||||
|
name: name,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
Message.success(i18n.t('The Workflow removed successfully'));
|
||||||
|
this.getWorkflowList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
locale: locale().Dialog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { workflowList } = this.state;
|
||||||
|
const { applicationDetail } = this.props;
|
||||||
|
const projectName = applicationDetail?.project?.name;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Message type="notice" style={{ marginBottom: '8px' }}>
|
||||||
|
<Translation>One environment corresponds to one workflow</Translation>
|
||||||
|
</Message>
|
||||||
|
<Table dataSource={workflowList}>
|
||||||
|
<Table.Column dataIndex="name" title={i18n.t('Name')} />
|
||||||
|
<Table.Column
|
||||||
|
dataIndex="envName"
|
||||||
|
title={i18n.t('Environment')}
|
||||||
|
cell={(v: string) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={`/applications/${applicationDetail?.name}/envbinding/${v}/workflow/studio`}
|
||||||
|
>
|
||||||
|
{v}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Table.Column dataIndex="mode" title={i18n.t('Mode')} />
|
||||||
|
<Table.Column
|
||||||
|
dataIndex="steps"
|
||||||
|
title={i18n.t('Step Number')}
|
||||||
|
cell={(steps: []) => {
|
||||||
|
return steps ? steps.length : 0;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
dataIndex="createTime"
|
||||||
|
title={i18n.t('Create Time')}
|
||||||
|
cell={(v: string) => {
|
||||||
|
return momentDate(v);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
dataIndex="updateTime"
|
||||||
|
title={i18n.t('Update Time')}
|
||||||
|
cell={(v: string) => {
|
||||||
|
return momentDate(v);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Table.Column
|
||||||
|
dataIndex="name"
|
||||||
|
title={i18n.t('Action')}
|
||||||
|
cell={(v: string, w: Workflow) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<If condition={v === w.envName + '-workflow'}>
|
||||||
|
<Permission
|
||||||
|
project={projectName}
|
||||||
|
resource={{
|
||||||
|
resource: `project:${projectName}/application:${applicationDetail?.name}/workflow:${v}`,
|
||||||
|
action: 'delete',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
text
|
||||||
|
size={'medium'}
|
||||||
|
ghost={true}
|
||||||
|
className={'danger-btn'}
|
||||||
|
component={'a'}
|
||||||
|
onClick={() => {
|
||||||
|
this.onDeleteWorkflow(v);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AiFillDelete />
|
||||||
|
<Translation>Remove</Translation>
|
||||||
|
</Button>
|
||||||
|
</Permission>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ApplicationWorkflowList;
|
|
@ -445,7 +445,7 @@ class ApplicationWorkflowRecord extends React.Component<Props, State> {
|
||||||
<div className={classNames('studio')}>
|
<div className={classNames('studio')}>
|
||||||
{showRecord && (
|
{showRecord && (
|
||||||
<PipelineGraph
|
<PipelineGraph
|
||||||
name={`${showRecord?.name}/${showRecord?.status}`}
|
name={`${showRecord?.name}`}
|
||||||
zoom={zoom}
|
zoom={zoom}
|
||||||
onNodeClick={this.onStepClick}
|
onNodeClick={this.onStepClick}
|
||||||
steps={showRecord?.steps}
|
steps={showRecord?.steps}
|
||||||
|
@ -534,7 +534,7 @@ class ApplicationWorkflowRecord extends React.Component<Props, State> {
|
||||||
<Icon type="refresh" />
|
<Icon type="refresh" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="logBox">
|
<div className="log-content">
|
||||||
{logs?.map((line, i: number) => {
|
{logs?.map((line, i: number) => {
|
||||||
return (
|
return (
|
||||||
<div key={`log-${i}`} className="logLine">
|
<div key={`log-${i}`} className="logLine">
|
||||||
|
|
|
@ -47,6 +47,19 @@ type State = {
|
||||||
editMode: 'visual' | 'yaml';
|
editMode: 'visual' | 'yaml';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const WorkflowModeOptions = [
|
||||||
|
{
|
||||||
|
value: 'DAG',
|
||||||
|
label: i18n.t('DAG'),
|
||||||
|
description: 'Workflows will be executed in parallel in DAG mode based on dependencies.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'StepByStep',
|
||||||
|
label: i18n.t('StepByStep'),
|
||||||
|
description: 'The workflow will be executed serially step by step .',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
@connect((store: any) => {
|
@connect((store: any) => {
|
||||||
return { ...store.application };
|
return { ...store.application };
|
||||||
})
|
})
|
||||||
|
@ -214,18 +227,22 @@ class ApplicationWorkflowStudio extends React.Component<Props, State> {
|
||||||
<Translation>Unsaved changes</Translation>
|
<Translation>Unsaved changes</Translation>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Form.Item label="Mode" labelAlign="inset" style={{ marginRight: '8px' }}>
|
<Form.Item label={i18n.t('Mode')} labelAlign="inset" style={{ marginRight: '8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale().Select}
|
locale={locale().Select}
|
||||||
defaultValue="StepByStep"
|
defaultValue="StepByStep"
|
||||||
value={mode}
|
value={mode}
|
||||||
dataSource={[{ value: 'StepByStep' }, { value: 'DAG' }]}
|
dataSource={WorkflowModeOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
this.setState({ mode: value, changed: this.state.mode !== value });
|
this.setState({ mode: value, changed: this.state.mode !== value });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Sub Mode" labelAlign="inset" style={{ marginRight: '8px' }}>
|
<Form.Item
|
||||||
|
label={i18n.t('Sub Mode')}
|
||||||
|
labelAlign="inset"
|
||||||
|
style={{ marginRight: '8px' }}
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
locale={locale().Select}
|
locale={locale().Select}
|
||||||
defaultValue="DAG"
|
defaultValue="DAG"
|
||||||
|
@ -233,7 +250,7 @@ class ApplicationWorkflowStudio extends React.Component<Props, State> {
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
this.setState({ subMode: value, changed: this.state.subMode !== value });
|
this.setState({ subMode: value, changed: this.state.subMode !== value });
|
||||||
}}
|
}}
|
||||||
dataSource={[{ value: 'DAG' }, { value: 'StepByStep' }]}
|
dataSource={WorkflowModeOptions}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -44,6 +44,7 @@ class TableList extends Component<Props> {
|
||||||
key: 'name',
|
key: 'name',
|
||||||
title: <Translation>Name(Alias)</Translation>,
|
title: <Translation>Name(Alias)</Translation>,
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
|
width: '150px',
|
||||||
cell: (v: string, i: number, env: Env) => {
|
cell: (v: string, i: number, env: Env) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
|
@ -60,6 +61,7 @@ class TableList extends Component<Props> {
|
||||||
key: 'project',
|
key: 'project',
|
||||||
title: <Translation>Project</Translation>,
|
title: <Translation>Project</Translation>,
|
||||||
dataIndex: 'project',
|
dataIndex: 'project',
|
||||||
|
width: '100px',
|
||||||
cell: (v: Project) => {
|
cell: (v: Project) => {
|
||||||
if (v && v.name) {
|
if (v && v.name) {
|
||||||
return <Link to={`/projects/${v.name}/summary`}>{v.alias || v.name}</Link>;
|
return <Link to={`/projects/${v.name}/summary`}>{v.alias || v.name}</Link>;
|
||||||
|
@ -72,6 +74,7 @@ class TableList extends Component<Props> {
|
||||||
key: 'namespace',
|
key: 'namespace',
|
||||||
title: <Translation>Namespace</Translation>,
|
title: <Translation>Namespace</Translation>,
|
||||||
dataIndex: 'namespace',
|
dataIndex: 'namespace',
|
||||||
|
width: '100px',
|
||||||
cell: (v: string) => {
|
cell: (v: string) => {
|
||||||
return <span>{v}</span>;
|
return <span>{v}</span>;
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,12 @@ import * as yaml from 'js-yaml';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import type { LoginUserInfo } from '../../../../interface/user';
|
import type { LoginUserInfo } from '../../../../interface/user';
|
||||||
import { checkPermission } from '../../../../utils/permission';
|
import { checkPermission } from '../../../../utils/permission';
|
||||||
import { createPipeline, loadPipeline, updatePipeline } from '../../../../api/pipeline';
|
import {
|
||||||
|
createPipeline,
|
||||||
|
createPipelineContext,
|
||||||
|
loadPipeline,
|
||||||
|
updatePipeline,
|
||||||
|
} from '../../../../api/pipeline';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import type {
|
import type {
|
||||||
PipelineBase,
|
PipelineBase,
|
||||||
|
@ -18,6 +23,7 @@ import type {
|
||||||
import { checkName } from '../../../../utils/common';
|
import { checkName } from '../../../../utils/common';
|
||||||
import locale from '../../../../utils/locale';
|
import locale from '../../../../utils/locale';
|
||||||
import { templates } from './pipeline-template';
|
import { templates } from './pipeline-template';
|
||||||
|
import { If } from 'tsx-control-statements/components';
|
||||||
|
|
||||||
const FormItem = Form.Item;
|
const FormItem = Form.Item;
|
||||||
|
|
||||||
|
@ -33,6 +39,8 @@ export interface PipelineProps {
|
||||||
type State = {
|
type State = {
|
||||||
configError?: string[];
|
configError?: string[];
|
||||||
containerId: string;
|
containerId: string;
|
||||||
|
defaultContext?: Record<string, string>;
|
||||||
|
loading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@connect((store: any) => {
|
@connect((store: any) => {
|
||||||
|
@ -97,24 +105,45 @@ class CreatePipeline extends React.Component<PipelineProps, State> {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
this.setState({ loading: true });
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
updatePipeline(request).then((res) => {
|
updatePipeline(request)
|
||||||
if (res) {
|
.then((res) => {
|
||||||
Message.success(i18n.t('Pipeline updated successfully'));
|
if (res) {
|
||||||
if (this.props.onSuccess) {
|
Message.success(i18n.t('Pipeline updated successfully'));
|
||||||
this.props.onSuccess(res);
|
if (this.props.onSuccess) {
|
||||||
|
this.props.onSuccess(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.finally(() => {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
createPipeline(request).then((res) => {
|
createPipeline(request)
|
||||||
if (res) {
|
.then((res) => {
|
||||||
Message.success(i18n.t('Pipeline created successfully'));
|
if (res) {
|
||||||
if (this.props.onSuccess) {
|
// Create the default context
|
||||||
this.props.onSuccess(res);
|
const { defaultContext } = this.state;
|
||||||
|
if (defaultContext) {
|
||||||
|
const contextValues: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}[] = [];
|
||||||
|
Object.keys(defaultContext).map((key) => {
|
||||||
|
contextValues.push({ key: key, value: defaultContext[key] });
|
||||||
|
});
|
||||||
|
createPipelineContext(project, name, { name: 'default', values: contextValues });
|
||||||
|
}
|
||||||
|
Message.success(i18n.t('Pipeline created successfully'));
|
||||||
|
if (this.props.onSuccess) {
|
||||||
|
this.props.onSuccess(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.finally(() => {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -161,6 +190,7 @@ class CreatePipeline extends React.Component<PipelineProps, State> {
|
||||||
const { init } = this.field;
|
const { init } = this.field;
|
||||||
const { userInfo, pipeline } = this.props;
|
const { userInfo, pipeline } = this.props;
|
||||||
let defaultProject = '';
|
let defaultProject = '';
|
||||||
|
const editMode = pipeline != undefined;
|
||||||
const projectOptions: { label: string; value: string }[] = [];
|
const projectOptions: { label: string; value: string }[] = [];
|
||||||
(userInfo?.projects || []).map((project) => {
|
(userInfo?.projects || []).map((project) => {
|
||||||
if (
|
if (
|
||||||
|
@ -180,12 +210,13 @@ class CreatePipeline extends React.Component<PipelineProps, State> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const modeOptions = [{ value: 'StepByStep' }, { value: 'DAG' }];
|
const modeOptions = [{ value: 'StepByStep' }, { value: 'DAG' }];
|
||||||
|
const { loading } = this.state;
|
||||||
return (
|
return (
|
||||||
<DrawerWithFooter
|
<DrawerWithFooter
|
||||||
title={i18n.t(pipeline == undefined ? 'New Pipeline' : 'Edit Pipeline')}
|
title={i18n.t(!editMode ? 'New Pipeline' : 'Edit Pipeline')}
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
onOk={this.onSubmit}
|
onOk={this.onSubmit}
|
||||||
|
onOkButtonLoading={loading}
|
||||||
>
|
>
|
||||||
<Form field={this.field}>
|
<Form field={this.field}>
|
||||||
<Row wrap>
|
<Row wrap>
|
||||||
|
@ -193,7 +224,7 @@ class CreatePipeline extends React.Component<PipelineProps, State> {
|
||||||
<FormItem required label={<Translation>Name</Translation>}>
|
<FormItem required label={<Translation>Name</Translation>}>
|
||||||
<Input
|
<Input
|
||||||
name="name"
|
name="name"
|
||||||
disabled={pipeline != undefined}
|
disabled={editMode}
|
||||||
{...init('name', {
|
{...init('name', {
|
||||||
initValue: '',
|
initValue: '',
|
||||||
rules: [
|
rules: [
|
||||||
|
@ -235,6 +266,7 @@ class CreatePipeline extends React.Component<PipelineProps, State> {
|
||||||
dataSource={projectOptions}
|
dataSource={projectOptions}
|
||||||
filterLocal={true}
|
filterLocal={true}
|
||||||
hasClear={true}
|
hasClear={true}
|
||||||
|
disabled={editMode}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
{...init('project', {
|
{...init('project', {
|
||||||
initValue: defaultProject,
|
initValue: defaultProject,
|
||||||
|
@ -287,20 +319,24 @@ class CreatePipeline extends React.Component<PipelineProps, State> {
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24} style={{ padding: '0 8px' }}>
|
<If condition={!editMode}>
|
||||||
<FormItem label={<Translation>Template</Translation>}>
|
<Col span={24} style={{ padding: '0 8px' }}>
|
||||||
<Select
|
<FormItem label={<Translation>Template</Translation>}>
|
||||||
locale={locale().Select}
|
<Select
|
||||||
name="template"
|
locale={locale().Select}
|
||||||
dataSource={templates}
|
name="template"
|
||||||
hasClear
|
dataSource={templates}
|
||||||
placeholder="Select a template"
|
hasClear
|
||||||
onChange={(value) => {
|
placeholder="Select a template"
|
||||||
this.field.setValue('steps', value);
|
onChange={(value) => {
|
||||||
}}
|
this.field.setValue('steps', value);
|
||||||
/>
|
const template = templates.find((t) => t.value == value);
|
||||||
</FormItem>
|
this.setState({ defaultContext: template?.defaultContext });
|
||||||
</Col>
|
}}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
</Col>
|
||||||
|
</If>
|
||||||
</Row>
|
</Row>
|
||||||
</Form>
|
</Form>
|
||||||
</DrawerWithFooter>
|
</DrawerWithFooter>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
export const templates = [
|
export const templates = [
|
||||||
{
|
{
|
||||||
label: 'Observability Template',
|
label: 'Observability Template',
|
||||||
|
defaultContext: {
|
||||||
|
readConfig: 'false',
|
||||||
|
},
|
||||||
value: `
|
value: `
|
||||||
- name: Enable Prism
|
- name: Enable Prism
|
||||||
type: addon-operation
|
type: addon-operation
|
||||||
|
|
|
@ -4,8 +4,13 @@ import i18n from '../../../../i18n';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import type { LoginUserInfo } from '../../../../interface/user';
|
import type { LoginUserInfo } from '../../../../interface/user';
|
||||||
import { checkPermission } from '../../../../utils/permission';
|
import { checkPermission } from '../../../../utils/permission';
|
||||||
import { createPipeline, loadPipeline } from '../../../../api/pipeline';
|
import {
|
||||||
import type { PipelineListItem, PipelineDetail } from '../../../../interface/pipeline';
|
createPipeline,
|
||||||
|
createPipelineContext,
|
||||||
|
listPipelineContexts,
|
||||||
|
loadPipeline,
|
||||||
|
} from '../../../../api/pipeline';
|
||||||
|
import type { PipelineListItem, PipelineDetail, KeyValue } from '../../../../interface/pipeline';
|
||||||
import Translation from '../../../../components/Translation';
|
import Translation from '../../../../components/Translation';
|
||||||
import { checkName } from '../../../../utils/common';
|
import { checkName } from '../../../../utils/common';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
|
@ -25,7 +30,10 @@ export interface PipelineProps {
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
loadingContext: boolean;
|
||||||
pipelineDetail?: PipelineDetail;
|
pipelineDetail?: PipelineDetail;
|
||||||
|
contexts?: Record<string, KeyValue[]>;
|
||||||
|
cloneLoading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@connect((store: any) => {
|
@connect((store: any) => {
|
||||||
|
@ -37,6 +45,7 @@ class ClonePipeline extends React.Component<PipelineProps, State> {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
|
loadingContext: true,
|
||||||
};
|
};
|
||||||
this.field = new Field(this);
|
this.field = new Field(this);
|
||||||
}
|
}
|
||||||
|
@ -44,19 +53,34 @@ class ClonePipeline extends React.Component<PipelineProps, State> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { pipeline } = this.props;
|
const { pipeline } = this.props;
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
loadPipeline({ projectName: pipeline.project.name, pipelineName: pipeline.name })
|
this.onLoadingPipeline(pipeline);
|
||||||
.then((res: PipelineDetail) => {
|
this.onLoadingPipelineContexts(pipeline);
|
||||||
this.setState({ pipelineDetail: res });
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.setState({ loading: false });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLoadingPipeline = async (pipeline: PipelineListItem) => {
|
||||||
|
loadPipeline({ projectName: pipeline.project.name, pipelineName: pipeline.name })
|
||||||
|
.then((res: PipelineDetail) => {
|
||||||
|
this.setState({ pipelineDetail: res });
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoadingPipelineContexts = async (pipeline: PipelineListItem) => {
|
||||||
|
listPipelineContexts(pipeline.project.name, pipeline.name)
|
||||||
|
.then((res) => {
|
||||||
|
this.setState({ contexts: res && res.contexts ? res.contexts : {} });
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.setState({ loadingContext: false });
|
||||||
|
});
|
||||||
|
};
|
||||||
onSubmit = () => {
|
onSubmit = () => {
|
||||||
const { pipelineDetail } = this.state;
|
const { pipelineDetail, contexts } = this.state;
|
||||||
if (pipelineDetail) {
|
if (pipelineDetail) {
|
||||||
|
this.setState({ cloneLoading: true });
|
||||||
this.field.validate((errs: any, values: any) => {
|
this.field.validate((errs: any, values: any) => {
|
||||||
if (errs) {
|
if (errs) {
|
||||||
return;
|
return;
|
||||||
|
@ -69,20 +93,29 @@ class ClonePipeline extends React.Component<PipelineProps, State> {
|
||||||
name: name,
|
name: name,
|
||||||
spec: pipelineDetail?.spec,
|
spec: pipelineDetail?.spec,
|
||||||
};
|
};
|
||||||
createPipeline(request).then((res) => {
|
createPipeline(request)
|
||||||
if (res) {
|
.then((res) => {
|
||||||
Message.success(i18n.t('Pipeline cloned successfully'));
|
if (res) {
|
||||||
if (this.props.onSuccess) {
|
if (contexts) {
|
||||||
this.props.onSuccess();
|
Object.keys(contexts).map((key) => {
|
||||||
|
createPipelineContext(project, name, { name: key, values: contexts[key] });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Message.success(i18n.t('Pipeline cloned successfully'));
|
||||||
|
if (this.props.onSuccess) {
|
||||||
|
this.props.onSuccess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {
|
||||||
|
this.setState({ cloneLoading: false });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, pipelineDetail } = this.state;
|
const { loading, pipelineDetail, loadingContext, contexts, cloneLoading } = this.state;
|
||||||
const { userInfo } = this.props;
|
const { userInfo } = this.props;
|
||||||
const { init } = this.field;
|
const { init } = this.field;
|
||||||
const projectOptions: { label: string; value: string }[] = [];
|
const projectOptions: { label: string; value: string }[] = [];
|
||||||
|
@ -100,9 +133,15 @@ class ClonePipeline extends React.Component<PipelineProps, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const message = contexts
|
||||||
|
? i18n.t('Includes') + ` ${Object.keys(contexts).length} ` + i18n.t('contexts')
|
||||||
|
: '';
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
onOk={this.onSubmit}
|
onOk={this.onSubmit}
|
||||||
|
okProps={{
|
||||||
|
loading: cloneLoading,
|
||||||
|
}}
|
||||||
onClose={this.props.onClose}
|
onClose={this.props.onClose}
|
||||||
onCancel={this.props.onClose}
|
onCancel={this.props.onClose}
|
||||||
locale={locale().Dialog}
|
locale={locale().Dialog}
|
||||||
|
@ -110,11 +149,11 @@ class ClonePipeline extends React.Component<PipelineProps, State> {
|
||||||
className="commonDialog"
|
className="commonDialog"
|
||||||
title="Clone Pipeline"
|
title="Clone Pipeline"
|
||||||
>
|
>
|
||||||
<Loading visible={loading}>
|
<Loading visible={loading || loadingContext}>
|
||||||
<If condition={pipelineDetail}>
|
<If condition={pipelineDetail && contexts}>
|
||||||
<Message
|
<Message
|
||||||
type="success"
|
type="success"
|
||||||
title={i18n.t('Pipeline loaded successfully and is ready to clone.')}
|
title={i18n.t('Pipeline loaded successfully and is ready to clone.') + message}
|
||||||
/>
|
/>
|
||||||
<Form field={this.field}>
|
<Form field={this.field}>
|
||||||
<Row wrap>
|
<Row wrap>
|
||||||
|
|
|
@ -17,7 +17,7 @@ import locale from '../../utils/locale';
|
||||||
import { Link, routerRedux } from 'dva/router';
|
import { Link, routerRedux } from 'dva/router';
|
||||||
import type { NameAlias } from '../../interface/env';
|
import type { NameAlias } from '../../interface/env';
|
||||||
import { deletePipeline, listPipelines } from '../../api/pipeline';
|
import { deletePipeline, listPipelines } from '../../api/pipeline';
|
||||||
import { AiFillCaretRight, AiFillDelete } from 'react-icons/ai';
|
import { AiFillCaretRight, AiFillDelete, AiFillSetting } from 'react-icons/ai';
|
||||||
import { BiCopyAlt } from 'react-icons/bi';
|
import { BiCopyAlt } from 'react-icons/bi';
|
||||||
import { HiViewList } from 'react-icons/hi';
|
import { HiViewList } from 'react-icons/hi';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
@ -111,6 +111,10 @@ class PipelineListPage extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onEditPipeline = (pipeline: PipelineListItem) => {
|
||||||
|
this.setState({ showNewPipeline: true, pipeline: pipeline });
|
||||||
|
};
|
||||||
|
|
||||||
renderPipelineTable = () => {
|
renderPipelineTable = () => {
|
||||||
const { pipelines } = this.state;
|
const { pipelines } = this.state;
|
||||||
return (
|
return (
|
||||||
|
@ -236,7 +240,7 @@ class PipelineListPage extends Component<Props, State> {
|
||||||
key={'actions'}
|
key={'actions'}
|
||||||
title={i18n.t('Actions')}
|
title={i18n.t('Actions')}
|
||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
width={'360px'}
|
width={'420px'}
|
||||||
cell={(name: string, i: number, pipeline: PipelineListItem) => {
|
cell={(name: string, i: number, pipeline: PipelineListItem) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -301,6 +305,26 @@ class PipelineListPage extends Component<Props, State> {
|
||||||
</Button>
|
</Button>
|
||||||
<span className="line" />
|
<span className="line" />
|
||||||
</Permission>
|
</Permission>
|
||||||
|
<Permission
|
||||||
|
project={pipeline.project.name}
|
||||||
|
resource={{
|
||||||
|
resource: `project:${pipeline.project.name}/pipeline:${pipeline.name}`,
|
||||||
|
action: 'update',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
text
|
||||||
|
size={'medium'}
|
||||||
|
ghost={true}
|
||||||
|
component={'a'}
|
||||||
|
onClick={() => {
|
||||||
|
this.onEditPipeline(pipeline);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AiFillSetting /> <Translation>Edit</Translation>
|
||||||
|
</Button>
|
||||||
|
<span className="line" />
|
||||||
|
</Permission>
|
||||||
<Permission
|
<Permission
|
||||||
project={pipeline.project.name}
|
project={pipeline.project.name}
|
||||||
resource={{
|
resource={{
|
||||||
|
|
|
@ -53,7 +53,7 @@ class Header extends Component<Props, State> {
|
||||||
onRerun = () => {
|
onRerun = () => {
|
||||||
Dialog.confirm({
|
Dialog.confirm({
|
||||||
type: 'alert',
|
type: 'alert',
|
||||||
content: 'Are you sure to rerun this Pipeline?',
|
content: i18n.t('Are you sure to rerun this Pipeline?'),
|
||||||
locale: locale().Dialog,
|
locale: locale().Dialog,
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
const { pipeline, runBase } = this.props;
|
const { pipeline, runBase } = this.props;
|
||||||
|
|
|
@ -29,24 +29,34 @@
|
||||||
.detail-page {
|
.detail-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100vh - 250px);
|
justify-content: space-between;
|
||||||
|
height: calc(100vh - 300px);
|
||||||
.step-info {
|
.step-info {
|
||||||
flex: auto;
|
height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
.step-log {
|
.step-log {
|
||||||
height: 600px;
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
background: #000;
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
padding: 4px 16px;
|
padding: 4px 16px;
|
||||||
color: var(--grey-300);
|
color: var(--grey-300);
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
background: #090909;
|
background: #090909;
|
||||||
border-bottom: 1px solid var(--grey-800);
|
border-bottom: 1px solid var(--grey-800);
|
||||||
}
|
}
|
||||||
|
.log-content {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
padding: 16px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.step-info {
|
.step-info {
|
||||||
|
|
|
@ -128,7 +128,8 @@ class PipelineRunPage extends Component<Props, State> {
|
||||||
stepName: stepStatus?.name,
|
stepName: stepStatus?.name,
|
||||||
})
|
})
|
||||||
.then((res: { log: string }) => {
|
.then((res: { log: string }) => {
|
||||||
this.setState({ logs: res && res.log ? res.log.split('\n') : [] });
|
const logLines = res && res.log ? res.log.split('\n') : [];
|
||||||
|
this.setState({ logs: logLines });
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.setState({ logLoading: false });
|
this.setState({ logLoading: false });
|
||||||
|
@ -287,7 +288,7 @@ class PipelineRunPage extends Component<Props, State> {
|
||||||
<div className={classNames('studio')}>
|
<div className={classNames('studio')}>
|
||||||
{runStatus && (
|
{runStatus && (
|
||||||
<PipelineGraph
|
<PipelineGraph
|
||||||
name={`${runBase?.pipelineRunName}+${runStatus.status}`}
|
name={`${runBase?.pipelineRunName}`}
|
||||||
steps={runStatus.steps}
|
steps={runStatus.steps}
|
||||||
zoom={1}
|
zoom={1}
|
||||||
onNodeClick={this.onStepClick}
|
onNodeClick={this.onStepClick}
|
||||||
|
@ -380,7 +381,7 @@ class PipelineRunPage extends Component<Props, State> {
|
||||||
<Icon type="refresh" />
|
<Icon type="refresh" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="logBox">
|
<div className="log-content">
|
||||||
{logs?.map((line, i: number) => {
|
{logs?.map((line, i: number) => {
|
||||||
return (
|
return (
|
||||||
<div key={`log-${i}`} className="logLine">
|
<div key={`log-${i}`} className="logLine">
|
||||||
|
|
|
@ -21,6 +21,7 @@ import i18n from '../../i18n';
|
||||||
import { If } from 'tsx-control-statements/components';
|
import { If } from 'tsx-control-statements/components';
|
||||||
import RunPipeline from '../../components/RunPipeline';
|
import RunPipeline from '../../components/RunPipeline';
|
||||||
import { routerRedux } from 'dva/router';
|
import { routerRedux } from 'dva/router';
|
||||||
|
import { WorkflowModeOptions } from '../ApplicationWorkflowStudio';
|
||||||
|
|
||||||
const { Row, Col } = Grid;
|
const { Row, Col } = Grid;
|
||||||
|
|
||||||
|
@ -234,18 +235,22 @@ class PipelineStudio extends React.Component<Props, State> {
|
||||||
<Translation>Unsaved changes</Translation>
|
<Translation>Unsaved changes</Translation>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Form.Item label="Mode" labelAlign="inset" style={{ marginRight: '8px' }}>
|
<Form.Item label={i18n.t('Mode')} labelAlign="inset" style={{ marginRight: '8px' }}>
|
||||||
<Select
|
<Select
|
||||||
locale={locale().Select}
|
locale={locale().Select}
|
||||||
defaultValue="StepByStep"
|
defaultValue="StepByStep"
|
||||||
value={mode}
|
value={mode}
|
||||||
dataSource={[{ value: 'StepByStep' }, { value: 'DAG' }]}
|
dataSource={WorkflowModeOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
this.setState({ mode: value, changed: this.state.mode !== value });
|
this.setState({ mode: value, changed: this.state.mode !== value });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Sub Mode" labelAlign="inset" style={{ marginRight: '8px' }}>
|
<Form.Item
|
||||||
|
label={i18n.t('Sub Mode')}
|
||||||
|
labelAlign="inset"
|
||||||
|
style={{ marginRight: '8px' }}
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
locale={locale().Select}
|
locale={locale().Select}
|
||||||
defaultValue="DAG"
|
defaultValue="DAG"
|
||||||
|
@ -253,7 +258,7 @@ class PipelineStudio extends React.Component<Props, State> {
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
this.setState({ subMode: value, changed: this.state.subMode !== value });
|
this.setState({ subMode: value, changed: this.state.subMode !== value });
|
||||||
}}
|
}}
|
||||||
dataSource={[{ value: 'DAG' }, { value: 'StepByStep' }]}
|
dataSource={WorkflowModeOptions}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -295,9 +295,20 @@ export function convertAny(data?: any): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showAlias(name: string, alias?: string) {
|
export function showAlias(name: string, alias?: string): string;
|
||||||
if (alias) {
|
export function showAlias(item: { name: string; alias?: string }): string;
|
||||||
return `${name}(${alias})`;
|
export function showAlias(item: { name: string; alias?: string } | string, alias?: string) {
|
||||||
|
if (typeof item == 'string') {
|
||||||
|
if (alias) {
|
||||||
|
return `${item}(${alias})`;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
return name;
|
if (!item) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (item.alias) {
|
||||||
|
return `${item.name}(${item.alias})`;
|
||||||
|
}
|
||||||
|
return item.name;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue