Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sqllab): giving the query history pane a facelift #31316

Merged
merged 7 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions superset-frontend/src/SqlLab/actions/sqlLab.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,7 @@ export function postStopQuery(query) {
})
.then(() => dispatch(stopQuery(query)))
.then(() => dispatch(addSuccessToast(t('Query was stopped.'))))
.catch(() =>
dispatch(addDangerToast(t('Failed at stopping query. %s', query.id))),
);
.catch(() => dispatch(addDangerToast(t('Failed to stop query.'))));
};
}

Expand Down
96 changes: 69 additions & 27 deletions superset-frontend/src/SqlLab/components/QueryTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import { useMemo } from 'react';
import { useMemo, ReactNode } from 'react';
import moment from 'moment';
import Card from 'src/components/Card';
import ProgressBar from 'src/components/ProgressBar';
import Label from 'src/components/Label';
import { t, useTheme, QueryResponse } from '@superset-ui/core';
import { useDispatch, useSelector } from 'react-redux';

Expand All @@ -35,6 +34,7 @@ import TableView from 'src/components/TableView';
import Button from 'src/components/Button';
import { fDuration } from 'src/utils/dates';
import Icons from 'src/components/Icons';
import Label from 'src/components/Label';
import { Tooltip } from 'src/components/Tooltip';
import { SqlLabRootState } from 'src/SqlLab/types';
import ModalTrigger from 'src/components/ModalTrigger';
Expand All @@ -44,11 +44,16 @@ import HighlightedSql from '../HighlightedSql';
import { StaticPosition, verticalAlign, StyledTooltip } from './styles';

interface QueryTableQuery
extends Omit<QueryResponse, 'state' | 'sql' | 'progress' | 'results'> {
extends Omit<
QueryResponse,
'state' | 'sql' | 'progress' | 'results' | 'duration' | 'started'
> {
state?: Record<string, any>;
sql?: Record<string, any>;
progress?: Record<string, any>;
results?: Record<string, any>;
duration?: ReactNode;
started?: ReactNode;
}

interface QueryTableProps {
Expand Down Expand Up @@ -125,55 +130,95 @@ const QueryTable = ({
const statusAttributes = {
success: {
config: {
icon: <Icons.Check iconColor={theme.colors.success.base} />,
icon: (
<Icons.CheckOutlined
iconColor={theme.colors.success.base}
iconSize="m"
/>
),
// icon: <Icons.Edit iconSize="xl" />,
label: t('Success'),
},
},
failed: {
config: {
icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
icon: (
<Icons.CloseOutlined
iconColor={theme.colors.error.base}
iconSize="m"
/>
),
label: t('Failed'),
},
},
stopped: {
config: {
icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
icon: (
<Icons.CloseOutlined
iconColor={theme.colors.error.base}
iconSize="m"
/>
),
label: t('Failed'),
},
},
running: {
config: {
icon: <Icons.Running iconColor={theme.colors.primary.base} />,
icon: (
<Icons.LoadingOutlined
iconColor={theme.colors.primary.base}
iconSize="m"
/>
),
label: t('Running'),
},
},
fetching: {
config: {
icon: <Icons.Queued iconColor={theme.colors.primary.base} />,
icon: (
<Icons.LoadingOutlined
iconColor={theme.colors.primary.base}
iconSize="m"
/>
),
label: t('Fetching'),
},
},
timed_out: {
config: {
icon: <Icons.Offline iconColor={theme.colors.grayscale.light1} />,
icon: (
<Icons.Clock iconColor={theme.colors.error.base} iconSize="m" />
),
label: t('Offline'),
},
},
scheduled: {
config: {
icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
icon: (
<Icons.LoadingOutlined
iconColor={theme.colors.warning.base}
iconSize="m"
/>
),
label: t('Scheduled'),
},
},
pending: {
config: {
icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
icon: (
<Icons.LoadingOutlined
iconColor={theme.colors.warning.base}
iconSize="m"
/>
),
label: t('Scheduled'),
},
},
error: {
config: {
icon: <Icons.Error iconColor={theme.colors.error.base} />,
icon: (
<Icons.Error iconColor={theme.colors.error.base} iconSize="m" />
),
label: t('Unknown Status'),
},
},
Expand All @@ -187,16 +232,10 @@ const QueryTable = ({
const status = statusAttributes[state] || statusAttributes.error;

if (q.endDttm) {
q.duration = fDuration(q.startDttm, q.endDttm);
q.duration = (
<Label monospace>{fDuration(q.startDttm, q.endDttm)}</Label>
);
}
const time = moment(q.startDttm).format().split('T');
q.time = (
<div>
<span>
{time[0]} <br /> {time[1]}
</span>
</div>
);
q.user = (
<Button
buttonSize="small"
Expand All @@ -215,7 +254,9 @@ const QueryTable = ({
{q.db}
</Button>
);
q.started = moment(q.startDttm).format('L HH:mm:ss');
q.started = (
<Label monospace>{moment(q.startDttm).format('L HH:mm:ss')}</Label>
);
q.querylink = (
<Button
buttonSize="small"
Expand All @@ -241,9 +282,9 @@ const QueryTable = ({
<ModalTrigger
className="ResultsModal"
triggerNode={
<Label type="info" className="pointer">
<Button buttonSize="xsmall" buttonStyle="tertiary">
{t('View')}
</Label>
</Button>
}
modalTitle={t('Data preview')}
beforeOpen={() => openAsyncResults(query, displayLimit)}
Expand Down Expand Up @@ -275,9 +316,7 @@ const QueryTable = ({
<ProgressBar percent={parseInt(progress.toFixed(0), 10)} striped />
);
q.state = (
<Tooltip title={status.config.label} placement="bottom">
<span>{status.config.icon}</span>
</Tooltip>
<Tooltip title={status.config.label}>{status.config.icon}</Tooltip>
);
q.actions = (
<div>
Expand All @@ -287,20 +326,23 @@ const QueryTable = ({
'Overwrite text in the editor with a query on this table',
)}
placement="top"
className="pointer"
>
<Icons.Edit iconSize="xl" />
</StyledTooltip>
<StyledTooltip
onClick={() => openQueryInNewTab(query)}
tooltip={t('Run query in a new tab')}
placement="top"
className="pointer"
>
<Icons.PlusCircleOutlined iconSize="xl" css={verticalAlign} />
</StyledTooltip>
{q.id !== latestQueryId && (
<StyledTooltip
tooltip={t('Remove query from log')}
onClick={() => dispatch(removeQuery(query))}
className="pointer"
>
<Icons.Trash iconSize="xl" />
</StyledTooltip>
Expand Down
13 changes: 11 additions & 2 deletions superset-frontend/src/components/Icons/Icons.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,19 @@ const IconBlock = styled.div`
flex-direction: column;
align-items: center;
padding: ${({ theme }) => theme.gridUnit * 2}px;

span {
margin-top: ${({ theme }) =>
2 * theme.gridUnit}px; // Add spacing between icon and name
font-size: ${({ theme }) =>
theme.typography.sizes.m}; // Optional: adjust font size for elegance
color: ${({ theme }) =>
theme.colors.grayscale.base}; // Optional: subtle color for the name
}
`;

export const InteractiveIcons = ({
showNames,
showNames = true,
...rest
}: IconType & { showNames: boolean }) => (
<IconSet>
Expand All @@ -56,7 +65,7 @@ export const InteractiveIcons = ({
return (
<IconBlock key={k}>
<IconComponent {...rest} />
{showNames && k}
{showNames && <span>{k}</span>}
</IconBlock>
);
})}
Expand Down
9 changes: 7 additions & 2 deletions superset-frontend/src/components/Label/Label.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ export const LabelGallery = () => (
);

export const InteractiveLabel = (args: any) => {
const { hasOnClick, label, ...rest } = args;
const { hasOnClick, label, monospace, ...rest } = args;
return (
<Label onClick={hasOnClick ? action('clicked') : undefined} {...rest}>
<Label
onClick={hasOnClick ? action('clicked') : undefined}
monospace={monospace}
{...rest}
>
{label}
</Label>
);
Expand All @@ -66,4 +70,5 @@ export const InteractiveLabel = (args: any) => {
InteractiveLabel.args = {
hasOnClick: true,
label: 'Example',
monospace: true,
};
54 changes: 33 additions & 21 deletions superset-frontend/src/components/Label/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,20 @@ export interface LabelProps extends HTMLAttributes<HTMLSpanElement> {
style?: CSSProperties;
children?: ReactNode;
role?: string;
monospace?: boolean;
}

export default function Label(props: LabelProps) {
const theme = useTheme();
const { colors, transitionTiming } = theme;
const { type = 'default', onClick, children, ...rest } = props;
const {
type = 'default',
monospace = false,
style,
onClick,
children,
...rest
} = props;
const {
alert,
primary,
Expand Down Expand Up @@ -89,37 +97,41 @@ export default function Label(props: LabelProps) {
} else {
baseColor = primary;
}

backgroundColor = baseColor.base;
backgroundColorHover = onClick ? baseColor.dark1 : baseColor.base;
borderColor = onClick ? baseColor.dark1 : 'transparent';
borderColorHover = onClick ? baseColor.dark2 : 'transparent';
}
const css = {
transition: `background-color ${transitionTiming}s`,
whiteSpace: 'nowrap',
cursor: onClick ? 'pointer' : 'default',
overflow: 'hidden',
textOverflow: 'ellipsis',
backgroundColor,
borderColor,
borderRadius: 21,
padding: '0.35em 0.8em',
lineHeight: 1,
color,
maxWidth: '100%',
'&:hover': {
backgroundColor: backgroundColorHover,
borderColor: borderColorHover,
opacity: 1,
},
};
if (monospace) {
css['font-family'] = theme.typography.families.monospace;
}

return (
<Tag
onClick={onClick}
role={onClick ? 'button' : undefined}
style={style}
{...rest}
css={{
transition: `background-color ${transitionTiming}s`,
whiteSpace: 'nowrap',
cursor: onClick ? 'pointer' : 'default',
overflow: 'hidden',
textOverflow: 'ellipsis',
backgroundColor,
borderColor,
borderRadius: 21,
padding: '0.35em 0.8em',
lineHeight: 1,
color,
maxWidth: '100%',
'&:hover': {
backgroundColor: backgroundColorHover,
borderColor: borderColorHover,
opacity: 1,
},
}}
css={css}
>
{children}
</Tag>
Expand Down
Loading