From 32d6f75212e07004bcbf2c34973160c0ded2023a Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Aug 2020 16:03:06 -0700 Subject: [PATCH] feat(ui): Add links to wft, cwf, or cwft to workflow list and details. Closes #3621 (#3662) --- .../cluster-workflow-template-link.tsx | 9 ++++++++ .../components/cron-workflow-link.tsx | 9 ++++++++ .../cron-workflow-summary-panel.tsx | 17 +++----------- ui/src/app/shared/components/link-button.tsx | 8 +++++++ .../resource-editor/resource-editor.tsx | 6 ++++- .../components/workflow-template-link.tsx | 9 ++++++++ .../components/submit-workflow-panel.tsx | 5 ++++- .../workflow-drawer/workflow-drawer.tsx | 6 +++++ .../workflows/components/workflow-from.tsx | 19 ++++++++++++++++ .../workflow-labels/workflow-labels.scss | 3 +-- .../workflow-labels/workflow-labels.tsx | 9 ++++---- .../workflows/components/workflow-link.tsx | 9 ++++++++ .../components/workflow-summary-panel.tsx | 22 +++++++++++++++++-- ui/src/models/workflows.ts | 6 ++++- 14 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-link.tsx create mode 100644 ui/src/app/cron-workflows/components/cron-workflow-link.tsx create mode 100644 ui/src/app/shared/components/link-button.tsx create mode 100644 ui/src/app/workflow-templates/components/workflow-template-link.tsx create mode 100644 ui/src/app/workflows/components/workflow-from.tsx create mode 100644 ui/src/app/workflows/components/workflow-link.tsx diff --git a/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-link.tsx b/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-link.tsx new file mode 100644 index 000000000000..b41e70f2ef8a --- /dev/null +++ b/ui/src/app/cluster-workflow-templates/components/cluster-workflow-template-link.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import {uiUrl} from '../../shared/base'; +import {LinkButton} from '../../shared/components/link-button'; + +export const ClusterWorkflowTemplateLink = (props: {name: string}) => ( + + {props.name} + +); diff --git a/ui/src/app/cron-workflows/components/cron-workflow-link.tsx b/ui/src/app/cron-workflows/components/cron-workflow-link.tsx new file mode 100644 index 000000000000..fa691c69bef4 --- /dev/null +++ b/ui/src/app/cron-workflows/components/cron-workflow-link.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import {uiUrl} from '../../shared/base'; +import {LinkButton} from '../../shared/components/link-button'; + +export const CronWorkflowLink = (props: {namespace: string; name: string}) => ( + + {props.name} + +); diff --git a/ui/src/app/cron-workflows/components/cron-workflow-summary-panel.tsx b/ui/src/app/cron-workflows/components/cron-workflow-summary-panel.tsx index 5bb9227d72ac..1a01a5869dce 100644 --- a/ui/src/app/cron-workflows/components/cron-workflow-summary-panel.tsx +++ b/ui/src/app/cron-workflows/components/cron-workflow-summary-panel.tsx @@ -1,13 +1,12 @@ import * as React from 'react'; import * as kubernetes from 'argo-ui/src/models/kubernetes'; -import {Link} from 'react-router-dom'; import {CronWorkflow} from '../../../models'; -import {uiUrl} from '../../shared/base'; import {ResourceEditor} from '../../shared/components/resource-editor/resource-editor'; import {Timestamp} from '../../shared/components/timestamp'; import {ConditionsPanel} from '../../shared/conditions-panel'; import {services} from '../../shared/services'; +import {WorkflowLink} from '../../workflows/components/workflow-link'; const jsonMergePatch = require('json-merge-patch'); @@ -86,16 +85,6 @@ export const CronWorkflowSummaryPanel = (props: Props) => { ); }; -function getCronWorkflowActiveWorkflowList(active: kubernetes.ObjectReference[]): JSX.Element { - return ( -
- {active.reverse().map(activeWf => ( -
- - {activeWf.namespace}/{activeWf.name} - -
- ))} -
- ); +function getCronWorkflowActiveWorkflowList(active: kubernetes.ObjectReference[]) { + return active.reverse().map(activeWf => ); } diff --git a/ui/src/app/shared/components/link-button.tsx b/ui/src/app/shared/components/link-button.tsx new file mode 100644 index 000000000000..bffffaef046a --- /dev/null +++ b/ui/src/app/shared/components/link-button.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; +import {ReactNode} from 'react'; + +export const LinkButton = (props: {to: string; children?: ReactNode}) => ( + +); diff --git a/ui/src/app/shared/components/resource-editor/resource-editor.tsx b/ui/src/app/shared/components/resource-editor/resource-editor.tsx index 5db616a467af..4dcf285bdf61 100644 --- a/ui/src/app/shared/components/resource-editor/resource-editor.tsx +++ b/ui/src/app/shared/components/resource-editor/resource-editor.tsx @@ -29,7 +29,11 @@ const LOCAL_STORAGE_KEY = 'ResourceEditorLang'; export class ResourceEditor extends React.Component, State> { private set lang(lang: string) { - this.setState({lang, value: stringify(parse(this.state.value), lang)}); + try { + this.setState({lang, error: null, value: stringify(parse(this.state.value), lang)}); + } catch (error) { + this.setState({error}); + } } private static saveLang(newLang: string) { diff --git a/ui/src/app/workflow-templates/components/workflow-template-link.tsx b/ui/src/app/workflow-templates/components/workflow-template-link.tsx new file mode 100644 index 000000000000..155969c85f1e --- /dev/null +++ b/ui/src/app/workflow-templates/components/workflow-template-link.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import {uiUrl} from '../../shared/base'; +import {LinkButton} from '../../shared/components/link-button'; + +export const WorkflowTemplateLink = (props: {namespace: string; name: string}) => ( + + {props.name} + +); diff --git a/ui/src/app/workflows/components/submit-workflow-panel.tsx b/ui/src/app/workflows/components/submit-workflow-panel.tsx index 979e956a889f..fe0f100c7e7e 100644 --- a/ui/src/app/workflows/components/submit-workflow-panel.tsx +++ b/ui/src/app/workflows/components/submit-workflow-panel.tsx @@ -78,7 +78,10 @@ export class SubmitWorkflowPanel extends React.Component { ))} ) : ( - + <> +
+ + )}
diff --git a/ui/src/app/workflows/components/workflow-drawer/workflow-drawer.tsx b/ui/src/app/workflows/components/workflow-drawer/workflow-drawer.tsx index 864a2b073706..79fcb85148f5 100644 --- a/ui/src/app/workflows/components/workflow-drawer/workflow-drawer.tsx +++ b/ui/src/app/workflows/components/workflow-drawer/workflow-drawer.tsx @@ -5,6 +5,7 @@ import {Loading} from '../../../shared/components/loading'; import {ConditionsPanel} from '../../../shared/conditions-panel'; import {formatDuration} from '../../../shared/duration'; import {services} from '../../../shared/services'; +import {WorkflowFrom} from '../workflow-from'; import {WorkflowLabels} from '../workflow-labels/workflow-labels'; require('./workflow-drawer.scss'); @@ -74,6 +75,11 @@ export class WorkflowDrawer extends React.Component
)} +
+
+ +
+
LABELS
diff --git a/ui/src/app/workflows/components/workflow-from.tsx b/ui/src/app/workflows/components/workflow-from.tsx new file mode 100644 index 000000000000..69e3e4a7a51f --- /dev/null +++ b/ui/src/app/workflows/components/workflow-from.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import {labels} from '../../../models'; +import {ClusterWorkflowTemplateLink} from '../../cluster-workflow-templates/components/cluster-workflow-template-link'; +import {CronWorkflowLink} from '../../cron-workflows/components/cron-workflow-link'; +import {WorkflowTemplateLink} from '../../workflow-templates/components/workflow-template-link'; + +export const WorkflowFrom = (props: {namespace: string; labels: {[name: string]: string}}) => { + const workflowTemplate = props.labels[labels.workflowTemplate]; + const clusterWorkflowTemplate = props.labels[labels.clusterWorkflowTemplate]; + const cronWorkflow = props.labels[labels.cronWorkflow]; + return ( + <> + {workflowTemplate && } + {clusterWorkflowTemplate && } + {cronWorkflow && } + {!workflowTemplate && !clusterWorkflowTemplate && !cronWorkflow && '-'} + + ); +}; diff --git a/ui/src/app/workflows/components/workflow-labels/workflow-labels.scss b/ui/src/app/workflows/components/workflow-labels/workflow-labels.scss index ac06ff12ef8e..a3ea53ecd818 100644 --- a/ui/src/app/workflows/components/workflow-labels/workflow-labels.scss +++ b/ui/src/app/workflows/components/workflow-labels/workflow-labels.scss @@ -6,8 +6,6 @@ align-items: center; height: 100%; $gutter: 0.5em; - background-color: $argo-color-gray-1; - margin-top: -8px; border-radius: 0 0 3px 3px; .tag { @@ -19,6 +17,7 @@ margin-right: 10px; display: flex; overflow: hidden; + cursor: pointer; &:hover { .key { diff --git a/ui/src/app/workflows/components/workflow-labels/workflow-labels.tsx b/ui/src/app/workflows/components/workflow-labels/workflow-labels.tsx index 289a5374bca5..2b064cb5f539 100644 --- a/ui/src/app/workflows/components/workflow-labels/workflow-labels.tsx +++ b/ui/src/app/workflows/components/workflow-labels/workflow-labels.tsx @@ -5,7 +5,7 @@ require('./workflow-labels.scss'); interface WorkflowLabelsProps { workflow: models.Workflow; - onChange: (key: string) => void; + onChange: (key: string, value: string) => void; } export class WorkflowLabels extends React.Component { @@ -18,16 +18,17 @@ export class WorkflowLabels extends React.Component { const w = this.props.workflow; if (w.metadata.labels) { labels.push( - Object.keys(w.metadata.labels).map(key => ( + Object.entries(w.metadata.labels).map(([key, value]) => (
{ e.preventDefault(); - this.props.onChange(key); + this.props.onChange(key, value); }}>
{key}
-
{w.metadata.labels[key]}
+
{value}
)) ); diff --git a/ui/src/app/workflows/components/workflow-link.tsx b/ui/src/app/workflows/components/workflow-link.tsx new file mode 100644 index 000000000000..fa134592033d --- /dev/null +++ b/ui/src/app/workflows/components/workflow-link.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import {uiUrl} from '../../shared/base'; +import {LinkButton} from '../../shared/components/link-button'; + +export const WorkflowLink = (props: {namespace: string; name: string}) => ( + + {props.name} + +); diff --git a/ui/src/app/workflows/components/workflow-summary-panel.tsx b/ui/src/app/workflows/components/workflow-summary-panel.tsx index edf2387bf859..e8d2ab52b8db 100644 --- a/ui/src/app/workflows/components/workflow-summary-panel.tsx +++ b/ui/src/app/workflows/components/workflow-summary-panel.tsx @@ -1,12 +1,16 @@ import {Ticker} from 'argo-ui'; import * as React from 'react'; -import {NODE_PHASE, Workflow} from '../../../models'; +import {labels, NODE_PHASE, Workflow} from '../../../models'; +import {uiUrl} from '../../shared/base'; import {Phase} from '../../shared/components/phase'; import {Timestamp} from '../../shared/components/timestamp'; import {ConditionsPanel} from '../../shared/conditions-panel'; +import {Consumer} from '../../shared/context'; import {formatDuration, wfDuration} from '../../shared/duration'; import {ResourcesDuration} from '../../shared/resources-duration'; +import {WorkflowFrom} from './workflow-from'; +import {WorkflowLabels} from './workflow-labels/workflow-labels'; export const WorkflowSummaryPanel = (props: {workflow: Workflow}) => ( @@ -16,11 +20,25 @@ export const WorkflowSummaryPanel = (props: {workflow: Workflow}) => ( {title: 'Message', value: props.workflow.status.message}, {title: 'Name', value: props.workflow.metadata.name}, {title: 'Namespace', value: props.workflow.metadata.namespace}, + {title: 'From', value: }, + { + title: 'Labels', + value: ( + + {ctx => ( + ctx.navigation.goto(uiUrl(`workflows/${props.workflow.metadata.namespace}?label=${key}=${value}`))} + /> + )} + + ) + }, {title: 'Started', value: }, {title: 'Finished ', value: }, {title: 'Duration', value: formatDuration(wfDuration(props.workflow.status))} ]; - const creator = props.workflow.metadata.labels['workflows.argoproj.io/creator']; + const creator = props.workflow.metadata.labels[labels.creator]; if (creator) { attributes.push({title: 'Creator', value: creator}); } diff --git a/ui/src/models/workflows.ts b/ui/src/models/workflows.ts index cb3cd60546a4..2d1ba4ad6968 100644 --- a/ui/src/models/workflows.ts +++ b/ui/src/models/workflows.ts @@ -1,7 +1,11 @@ import * as kubernetes from 'argo-ui/src/models/kubernetes'; export const labels = { - completed: 'workflows.argoproj.io/completed' + clusterWorkflowTemplate: 'workflows.argoproj.io/cluster-workflow-template', + completed: 'workflows.argoproj.io/completed', + creator: 'workflows.argoproj.io/creator', + cronWorkflow: 'workflows.argoproj.io/cron-workflow', + workflowTemplate: 'workflows.argoproj.io/workflow-template' }; /**