feat(edge/stacks): ui for status [EE-5593] (#9214)

pull/9231/head
Chaim Lev-Ari 2023-07-19 18:26:18 +03:00 committed by GitHub
parent 03b9a9b65d
commit 4f0f53b9aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 123 deletions

View File

@ -1,28 +0,0 @@
.root {
padding: 2px 10px;
border-radius: 10px;
}
.status-acknowledged {
color: #337ab7;
background-color: rgba(51, 122, 183, 0.1);
}
.status-images-pulled {
color: #e1a800;
background-color: rgba(238, 192, 32, 0.1);
}
.status-ok {
color: #23ae89;
background-color: rgba(35, 174, 137, 0.1);
}
.status-error {
color: #ae2323;
background-color: rgba(174, 35, 35, 0.1);
}
.status-total {
background-color: rgba(168, 167, 167, 0.1);
}

View File

@ -1,28 +1,30 @@
import clsx from 'clsx';
import { ReactNode } from 'react';
import { Link } from '@@/Link';
import { TooltipWithChildren } from '@@/Tip/TooltipWithChildren';
import { EdgeStack, StatusType } from '../../types';
import styles from './DeploymentCounter.module.css';
export function DeploymentCounterLink({
count,
total,
type,
stackId,
}: {
count: number;
total: number;
type: StatusType;
stackId: EdgeStack['Id'];
}) {
return (
<div className="text-center">
<div className="w-full text-center">
<Link
className="hover:no-underline"
to="edge.stacks.edit"
params={{ stackId, tab: 1, status: type }}
>
<DeploymentCounter count={count} type={type} />
<DeploymentCounter count={count} type={type} total={total} />
</Link>
</div>
);
@ -30,22 +32,48 @@ export function DeploymentCounterLink({
export function DeploymentCounter({
count,
total,
type,
}: {
count: number;
total: number;
type?: StatusType;
}) {
return (
<span
className={clsx(styles.root, {
[styles.statusOk]: type === StatusType.Running,
[styles.statusError]: type === StatusType.Error,
[styles.statusAcknowledged]: type === StatusType.Acknowledged,
[styles.statusImagesPulled]: type === StatusType.ImagesPulled,
[styles.statusTotal]: type === undefined,
})}
>
&bull; {count}
</span>
<TooltipWithChildren message={getTooltip(count, total, type)}>
<div className="h-2 w-full overflow-hidden rounded-lg bg-gray-4">
<div
style={{ width: `${(count / total) * 100}%` }}
className={clsx('h-full rounded-lg', {
'bg-success-7': type === StatusType.Running,
'bg-error-7': type === StatusType.Error,
'bg-blue-9': type === StatusType.Acknowledged,
'bg-yellow-7': type === StatusType.ImagesPulled,
})}
/>
</div>
</TooltipWithChildren>
);
}
function getTooltip(count: number, total: number, type?: StatusType) {
const label = getLabel(type);
return `${count} of ${total} ${label}`;
function getLabel(type?: StatusType): ReactNode {
switch (type) {
case StatusType.Running:
return 'deployments running';
case StatusType.DeploymentReceived:
return 'deployments received';
case StatusType.Error:
return 'deployments failed';
case StatusType.Acknowledged:
return 'deployments acknowledged';
case StatusType.ImagesPulled:
return 'images pre-pulled';
default:
return '';
}
}
}

View File

@ -50,15 +50,12 @@ export function EdgeStacksDatatable() {
}
function aggregateStackStatus(stackStatus: EdgeStack['Status']) {
const aggregateStatus = { ok: 0, error: 0, acknowledged: 0, imagesPulled: 0 };
const aggregateStatus: Partial<Record<StatusType, number>> = {};
return Object.values(stackStatus).reduce(
(acc, envStatus) =>
envStatus.Status.reduce((acc, status) => {
const { Type } = status;
acc.ok += Number(Type === StatusType.Running);
acc.error += Number(Type === StatusType.Error);
acc.acknowledged += Number(Type === StatusType.Acknowledged);
acc.imagesPulled += Number(Type === StatusType.ImagesPulled);
acc[Type] = (acc[Type] || 0) + 1;
return acc;
}, acc),
aggregateStatus

View File

@ -5,12 +5,13 @@ import { isoDateFromTimestamp } from '@/portainer/filters/filters';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { buildNameColumn } from '@@/datatables/NameCell';
import { Link } from '@@/Link';
import { StatusType } from '../../types';
import { EdgeStackStatus } from './EdgeStacksStatus';
import { DecoratedEdgeStack } from './types';
import { DeploymentCounter, DeploymentCounterLink } from './DeploymentCounter';
import { DeploymentCounter } from './DeploymentCounter';
const columnHelper = createColumnHelper<DecoratedEdgeStack>();
@ -21,29 +22,52 @@ export const columns = _.compact([
'edge.stacks.edit',
'stackId'
),
columnHelper.accessor('aggregatedStatus.acknowledged', {
header: 'Acknowledged',
enableSorting: false,
enableHiding: false,
cell: ({ getValue, row }) => (
<DeploymentCounterLink
count={getValue()}
type={StatusType.Acknowledged}
stackId={row.original.Id}
/>
),
meta: {
className: '[&>*]:justify-center',
},
}),
isBE &&
columnHelper.accessor('aggregatedStatus.imagesPulled', {
header: 'Images Pre-pulled',
columnHelper.accessor(
(item) => item.aggregatedStatus[StatusType.Acknowledged] || 0,
{
header: 'Acknowledged',
enableSorting: false,
enableHiding: false,
cell: ({ getValue, row }) => (
<DeploymentCounterLink
<DeploymentCounter
count={getValue()}
type={StatusType.ImagesPulled}
stackId={row.original.Id}
type={StatusType.Acknowledged}
total={row.original.NumDeployments}
/>
),
meta: {
className: '[&>*]:justify-center',
},
}
),
isBE &&
columnHelper.accessor(
(item) => item.aggregatedStatus[StatusType.ImagesPulled] || 0,
{
header: 'Images pre-pulled',
cell: ({ getValue, row }) => (
<DeploymentCounter
count={getValue()}
type={StatusType.ImagesPulled}
total={row.original.NumDeployments}
/>
),
enableSorting: false,
enableHiding: false,
meta: {
className: '[&>*]:justify-center',
},
}
),
columnHelper.accessor(
(item) => item.aggregatedStatus[StatusType.DeploymentReceived] || 0,
{
header: 'Deployments received',
cell: ({ getValue, row }) => (
<DeploymentCounter
count={getValue()}
type={StatusType.Running}
total={row.original.NumDeployments}
/>
),
enableSorting: false,
@ -51,37 +75,45 @@ export const columns = _.compact([
meta: {
className: '[&>*]:justify-center',
},
}),
columnHelper.accessor('aggregatedStatus.ok', {
header: 'Deployed',
cell: ({ getValue, row }) => (
<DeploymentCounterLink
count={getValue()}
type={StatusType.Running}
stackId={row.original.Id}
/>
),
enableSorting: false,
enableHiding: false,
meta: {
className: '[&>*]:justify-center',
},
}),
columnHelper.accessor('aggregatedStatus.error', {
header: 'Failed',
cell: ({ getValue, row }) => (
<DeploymentCounterLink
count={getValue()}
type={StatusType.Error}
stackId={row.original.Id}
/>
),
enableSorting: false,
enableHiding: false,
meta: {
className: '[&>*]:justify-center',
},
}),
}
),
columnHelper.accessor(
(item) => item.aggregatedStatus[StatusType.Error] || 0,
{
header: 'Deployments failed',
cell: ({ getValue, row }) => {
const count = getValue();
return (
<div className="flex items-center gap-2">
<DeploymentCounter
count={count}
type={StatusType.Error}
total={row.original.NumDeployments}
/>
{count > 0 && (
<Link
className="hover:no-underline"
to="edge.stacks.edit"
params={{
stackId: row.original.Id,
tab: 1,
status: StatusType.Error,
}}
>
({count}/{row.original.NumDeployments})
</Link>
)}
</div>
);
},
enableSorting: false,
enableHiding: false,
meta: {
className: '[&>*]:justify-center',
},
}
),
columnHelper.accessor('Status', {
header: 'Status',
cell: ({ row }) => (
@ -95,19 +127,6 @@ export const columns = _.compact([
className: '[&>*]:justify-center',
},
}),
columnHelper.accessor('NumDeployments', {
header: 'Deployments',
cell: ({ getValue }) => (
<div className="text-center">
<DeploymentCounter count={getValue()} />
</div>
),
enableSorting: false,
enableHiding: false,
meta: {
className: '[&>*]:justify-center',
},
}),
columnHelper.accessor('CreationDate', {
header: 'Creation Date',
cell: ({ getValue }) => isoDateFromTimestamp(getValue()),

View File

@ -1,12 +1,5 @@
import { EdgeStack } from '../../types';
interface AggregateStackStatus {
ok: number;
error: number;
acknowledged: number;
imagesPulled: number;
}
import { EdgeStack, StatusType } from '../../types';
export type DecoratedEdgeStack = EdgeStack & {
aggregatedStatus: AggregateStackStatus;
aggregatedStatus: Partial<Record<StatusType, number>>;
};