fix(plugin-workflow): fix status button styles (#2516)

This commit is contained in:
Junyi 2023-08-23 16:30:21 +07:00 committed by GitHub
parent 96caf9befa
commit 17f438ac39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 102 deletions

View File

@ -1,6 +1,6 @@
import {
ActionContextProvider,
css,
cx,
SchemaComponent,
useAPIClient,
useCompile,
@ -12,15 +12,14 @@ import { Breadcrumb, Dropdown, Space, Tag } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { CanvasContent } from './CanvasContent';
import { ExecutionStatusOptionsMap, JobStatusOptions, JobStatusOptionsMap } from './constants';
import { ExecutionStatusOptionsMap, JobStatusOptions } from './constants';
import { FlowContext, useFlowContext } from './FlowContext';
import { lang, NAMESPACE } from './locale';
import { instructions } from './nodes';
import useStyles from './style';
import { linkNodes } from './utils';
import { DownOutlined } from '@ant-design/icons';
import { StatusIcon } from './components/StatusIcon';
import classnames from 'classnames';
import { StatusButton } from './components/StatusButton';
function attachJobs(nodes, jobs: any[] = []): void {
const nodesMap = new Map();
@ -174,18 +173,23 @@ function ExecutionsDropdown(props) {
return execution ? (
<Dropdown
menu={{
className: styles.executionsDropdownRowClass,
onClick,
defaultSelectedKeys: [`${execution.id}`],
className: cx(styles.dropdownClass, styles.executionsDropdownRowClass),
items: [...executionsAfter, execution, ...executionsBefore].map((item) => {
return {
key: item.id,
label: (
<span className={classnames('row', { current: item.id === execution.id })}>
<>
<span className="id">{`#${item.id}`}</span>
<time>{str2moment(item.createdAt).format('YYYY-MM-DD HH:mm:ss')}</time>
</>
),
icon: (
<span>
<StatusButton statusMap={ExecutionStatusOptionsMap} status={item.status} />
</span>
),
icon: <StatusIcon status={item.status} />,
};
}),
}}

View File

@ -159,7 +159,7 @@ export function WorkflowCanvas() {
menu={{
onClick: onSwitchVersion,
defaultSelectedKeys: [`${workflow.id}`],
className: cx(styles.workflowVersionDropdownClass),
className: cx(styles.dropdownClass, styles.workflowVersionDropdownClass),
items: revisions
.sort((a, b) => b.id - a.id)
.map((item, index) => ({

View File

@ -0,0 +1,42 @@
import React from 'react';
import { Button, Tag } from 'antd';
import classnames from 'classnames';
import { createStyles } from '@nocobase/client';
const useStyles = createStyles(({ css, token }) => ({
statusButtonClass: css`
border: none;
.ant-tag {
width: 100%;
height: 100%;
padding: 0;
margin-right: 0;
border-radius: 50%;
text-align: center;
}
`,
noStatusButtonClass: css`
border-width: 2px;
`,
}));
export function StatusButton(props) {
const { styles } = useStyles();
let tag = null;
if (typeof props.status !== 'undefined' && props.statusMap?.[props.status]) {
const { icon, color } = props.statusMap[props.status];
tag = <Tag color={color}>{icon}</Tag>;
}
return (
<Button
{...props}
shape="circle"
size="small"
className={classnames(tag ? styles.statusButtonClass : styles.noStatusButtonClass, props.className)}
>
{tag}
</Button>
);
}

View File

@ -1,30 +0,0 @@
import React from 'react';
import { Tag } from 'antd';
import classnames from 'classnames';
import { createStyles } from '@nocobase/client';
import { JobStatusOptionsMap } from '../constants';
const useStyles = createStyles(({ css, token }) => ({
statusIconClass: css`
padding: 0;
width: ${token.sizeLG}px;
height: ${token.sizeLG}px;
line-height: ${token.sizeLG}px;
margin-right: 0;
border-radius: 50%;
text-align: center;
`,
}));
export function StatusIcon(props) {
const { icon, color } = JobStatusOptionsMap[props.status];
const { styles } = useStyles();
return (
<Tag color={color} className={classnames(styles.statusIconClass, props.className)}>
{icon}
</Tag>
);
}

View File

@ -5,6 +5,8 @@ import {
CheckOutlined,
MinusOutlined,
ExclamationOutlined,
HourglassOutlined,
LoadingOutlined,
} from '@ant-design/icons';
import { NAMESPACE } from './locale';
@ -24,48 +26,56 @@ export const ExecutionStatusOptions = [
value: EXECUTION_STATUS.QUEUEING,
label: `{{t("Queueing", { ns: "${NAMESPACE}" })}}`,
color: 'blue',
icon: <HourglassOutlined />,
description: `{{t("Triggered but still waiting in queue to execute.", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.STARTED,
label: `{{t("On going", { ns: "${NAMESPACE}" })}}`,
color: 'gold',
icon: <LoadingOutlined />,
description: `{{t("Started and executing, maybe waiting for an async callback (manual, delay etc.).", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.RESOLVED,
label: `{{t("Resolved", { ns: "${NAMESPACE}" })}}`,
color: 'green',
icon: <CheckOutlined />,
description: `{{t("Successfully finished.", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.FAILED,
label: `{{t("Failed", { ns: "${NAMESPACE}" })}}`,
color: 'red',
icon: <ExclamationOutlined />,
description: `{{t("Failed to satisfy node configurations.", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.ERROR,
label: `{{t("Error", { ns: "${NAMESPACE}" })}}`,
color: 'red',
icon: <CloseOutlined />,
description: `{{t("Some node meets error.", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.ABORTED,
label: `{{t("Aborted", { ns: "${NAMESPACE}" })}}`,
color: 'red',
icon: <MinusOutlined rotate={90} />,
description: `{{t("Running of some node was aborted by program flow.", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.CANCELED,
label: `{{t("Canceled", { ns: "${NAMESPACE}" })}}`,
color: 'volcano',
icon: <MinusOutlined rotate={45} />,
description: `{{t("Manually canceled whole execution when waiting.", { ns: "${NAMESPACE}" })}}`,
},
{
value: EXECUTION_STATUS.REJECTED,
label: `{{t("Rejected", { ns: "${NAMESPACE}" })}}`,
color: 'volcano',
icon: <MinusOutlined />,
description: `{{t("Rejected from a manual node.", { ns: "${NAMESPACE}" })}}`,
},
];

View File

@ -36,7 +36,7 @@ import query from './query';
import request from './request';
import sql from './sql';
import update from './update';
import { StatusIcon } from '../components/StatusIcon';
import { StatusButton } from '../components/StatusButton';
export interface Instruction {
title: string;
@ -237,16 +237,6 @@ export function RemoveButton() {
);
}
function InnerJobButton({ job, ...props }) {
const { styles } = useStyles();
return (
<Button {...props} shape="circle" size="small" className={cx(styles.nodeJobButtonClass, props.className)}>
<StatusIcon status={job.status} />
</Button>
);
}
export function JobButton() {
const { execution, setViewJob } = useFlowContext();
const { jobs } = useNodeContext() ?? {};
@ -257,20 +247,7 @@ export function JobButton() {
}
if (!jobs.length) {
return (
<span
className={cx(
styles.nodeJobButtonClass,
css`
border: 2px solid #d9d9d9;
border-radius: 50%;
cursor: not-allowed;
width: 24px;
height: 24px;
`,
)}
/>
);
return <StatusButton className={styles.nodeJobButtonClass} disabled />;
}
function onOpenJob({ key }) {
@ -285,31 +262,30 @@ export function JobButton() {
return {
key: job.id,
label: (
<div
className={css`
display: flex;
align-items: center;
gap: 0.5em;
time {
color: #999;
font-size: 0.8em;
}
`}
>
<StatusIcon status={job.status} />
<>
<StatusButton statusMap={JobStatusOptionsMap} status={job.status} />
<time>{str2moment(job.updatedAt).format('YYYY-MM-DD HH:mm:ss')}</time>
</div>
</>
),
};
}),
onClick: onOpenJob,
className: styles.dropdownClass,
}}
>
<InnerJobButton job={jobs[jobs.length - 1]} />
<StatusButton
statusMap={JobStatusOptionsMap}
status={jobs[jobs.length - 1].status}
className={styles.nodeJobButtonClass}
/>
</Dropdown>
) : (
<InnerJobButton job={jobs[0]} onClick={() => setViewJob(jobs[0])} />
<StatusButton
statusMap={JobStatusOptionsMap}
status={jobs[0].status}
onClick={() => setViewJob(jobs[0])}
className={styles.nodeJobButtonClass}
/>
);
}

View File

@ -15,12 +15,11 @@ const useStyles = createStyles(({ css, token }) => {
position: relative;
padding: 0.5rem 1rem;
background: ${token.colorBgContainer};
border-bottom: 1px solid ${token.colorBorder};
border-bottom: 1px solid ${token.colorBorderSecondary};
header {
display: flex;
align-items: center;
min-height: 2rem;
}
aside {
@ -55,7 +54,7 @@ const useStyles = createStyles(({ css, token }) => {
}
`,
workflowVersionDropdownClass: css`
dropdownClass: css`
.ant-dropdown-menu-item {
justify-content: flex-end;
.ant-dropdown-menu-title-content {
@ -65,11 +64,16 @@ const useStyles = createStyles(({ css, token }) => {
text-align: right;
time {
width: 12em;
color: ${token.colorText};
width: 14em;
font-size: 80%;
}
}
}
`,
workflowVersionDropdownClass: css`
.ant-dropdown-menu-item {
.ant-dropdown-menu-title-content {
strong {
font-weight: normal;
}
@ -90,24 +94,11 @@ const useStyles = createStyles(({ css, token }) => {
`,
executionsDropdownRowClass: css`
.row {
display: flex;
align-items: baseline;
&.current {
font-weight: bold;
}
.ant-dropdown-menu-item {
.id {
flex-grow: 1;
text-align: right;
}
time {
width: 12em;
color: ${token.colorText};
font-size: 80%;
}
}
`,
@ -283,10 +274,6 @@ const useStyles = createStyles(({ css, token }) => {
justify-content: center;
align-items: center;
color: ${token.colorTextLightSolid};
&[type='button'] {
border: none;
}
`,
nodeHeaderClass: css`