Files
superset2/superset-frontend/src/features/tasks/TaskStatusIcon.tsx

147 lines
4.5 KiB
TypeScript

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { useTheme, SupersetTheme } from '@apache-superset/core/theme';
import { t } from '@apache-superset/core/translation';
import { Icons } from '@superset-ui/core/components/Icons';
import { Tooltip } from '@superset-ui/core/components';
import { TaskStatus } from './types';
import { formatProgressTooltip } from './timeUtils';
function getStatusColor(status: TaskStatus, theme: SupersetTheme): string {
switch (status) {
case TaskStatus.Pending:
return theme.colorPrimaryText;
case TaskStatus.InProgress:
return theme.colorPrimaryText;
case TaskStatus.Success:
return theme.colorSuccessText;
case TaskStatus.Failure:
return theme.colorErrorText;
case TaskStatus.TimedOut:
return theme.colorErrorText;
case TaskStatus.Aborting:
return theme.colorWarningText;
case TaskStatus.Aborted:
return theme.colorWarningText;
default:
return theme.colorText;
}
}
const statusIcons = {
[TaskStatus.Pending]: Icons.ClockCircleOutlined,
[TaskStatus.InProgress]: Icons.LoadingOutlined,
[TaskStatus.Success]: Icons.CheckCircleOutlined,
[TaskStatus.Failure]: Icons.CloseCircleOutlined,
[TaskStatus.TimedOut]: Icons.ClockCircleOutlined, // Clock to indicate timeout
[TaskStatus.Aborting]: Icons.LoadingOutlined, // Spinning to show in-progress abort
[TaskStatus.Aborted]: Icons.StopOutlined,
};
const statusLabels = {
[TaskStatus.Pending]: t('Pending'),
[TaskStatus.InProgress]: t('In Progress'),
[TaskStatus.Success]: t('Success'),
[TaskStatus.Failure]: t('Failed'),
[TaskStatus.TimedOut]: t('Timed Out'),
[TaskStatus.Aborting]: t('Aborting'),
[TaskStatus.Aborted]: t('Aborted'),
};
interface TaskStatusIconProps {
status: TaskStatus;
progressPercent?: number | null;
progressCurrent?: number | null;
progressTotal?: number | null;
durationSeconds?: number | null;
errorMessage?: string | null;
exceptionType?: string | null;
}
export default function TaskStatusIcon({
status,
progressPercent,
progressCurrent,
progressTotal,
durationSeconds,
errorMessage,
exceptionType,
}: TaskStatusIconProps) {
const theme = useTheme();
const IconComponent = statusIcons[status];
const label = statusLabels[status];
// Build tooltip content based on status
let tooltipContent: React.ReactNode;
if (status === TaskStatus.InProgress || status === TaskStatus.Aborting) {
// Progress tooltip for active tasks (multiline)
const lines = formatProgressTooltip(
label,
progressCurrent,
progressTotal,
progressPercent,
durationSeconds,
);
tooltipContent = (
<>
{lines.map((line, index) => (
<React.Fragment key={index}>
{index > 0 && <br />}
{line}
</React.Fragment>
))}
</>
);
} else if (
(status === TaskStatus.Failure || status === TaskStatus.TimedOut) &&
(exceptionType || errorMessage)
) {
// Error tooltip for failed/timed out tasks: "Label (ExceptionType): message"
if (exceptionType && errorMessage) {
tooltipContent = `${label} (${exceptionType}): ${errorMessage}`;
} else if (exceptionType) {
tooltipContent = `${label} (${exceptionType})`;
} else if (errorMessage) {
tooltipContent = `${label}: ${errorMessage}`;
} else {
tooltipContent = label;
}
} else {
tooltipContent = label;
}
// Spin for in-progress and aborting states
const shouldSpin =
status === TaskStatus.InProgress || status === TaskStatus.Aborting;
return (
<Tooltip title={tooltipContent} placement="top">
<span>
<IconComponent
iconSize="l"
iconColor={getStatusColor(status, theme)}
spin={shouldSpin}
/>
</span>
</Tooltip>
);
}