fix(plugin-workflow): fix status button styles (#2516)
This commit is contained in:
parent
96caf9befa
commit
17f438ac39
@ -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} />,
|
||||
};
|
||||
}),
|
||||
}}
|
||||
|
@ -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) => ({
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -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}" })}}`,
|
||||
},
|
||||
];
|
||||
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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`
|
||||
|
Loading…
Reference in New Issue
Block a user