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(ui): Individual freight view #1741

Merged
merged 2 commits into from
Apr 2, 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
1 change: 1 addition & 0 deletions ui/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const App = () => (
element={<Project tab='analysisTemplates' />}
/>
<Route path={paths.stage} element={<Project />} />
<Route path={paths.freight} element={<Project />} />
</Route>
</Route>
<Route path={paths.login} element={<Login />} />
Expand Down
1 change: 1 addition & 0 deletions ui/src/config/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const paths = {
projectCredentials: '/project/:name/credentials',
projectAnalysisTemplates: '/project/:name/analysis-templates',
stage: '/project/:name/stage/:stageName',
freight: '/project/:name/freight/:freightName',

login: '/login',
tokenRenew: '/token-renew'
Expand Down
19 changes: 19 additions & 0 deletions ui/src/features/common/manifest-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import yaml from 'yaml';

import { Freight, Stage } from '@ui/gen/v1alpha1/generated_pb';

import YamlEditor from './code-editor/yaml-editor-lazy';

export const ManifestPreview = ({ object }: { object: Stage | Freight }) => {
const encodedObject = yaml.stringify(object.toJson(), (_, v) => {
if (typeof v === 'string' && v === '') {
return;
}
if (Array.isArray(v) && v.length === 0) {
return;
}
return v;
});

return <YamlEditor value={encodedObject} height='100%' disabled />;
};
77 changes: 77 additions & 0 deletions ui/src/features/freight/freight-details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { faFile, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Drawer, Tabs, Typography } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { generatePath, useNavigate, useParams } from 'react-router-dom';

import { paths } from '@ui/config/paths';
import { Freight } from '@ui/gen/v1alpha1/generated_pb';

import { ManifestPreview } from '../common/manifest-preview';
import { getAlias } from '../common/utils';

import { FreightStatusList } from './freight-status-list';

const CopyValue = (props: { value: string; label: string; className?: string }) => (
<div className={classNames('flex items-center text-gray-500 font-mono', props.className)}>
<span className='text-gray-400 mr-2 text-xs'>{props.label}</span>
<Typography.Text copyable>{props.value}</Typography.Text>
</div>
);
export const FreightDetails = ({ freight }: { freight?: Freight }) => {
const navigate = useNavigate();
const { name: projectName } = useParams();
const [alias, setAlias] = useState<string | undefined>();

useEffect(() => {
if (freight) {
setAlias(getAlias(freight));
}
}, [freight]);

const onClose = () => navigate(generatePath(paths.project, { name: projectName }));

return (
<Drawer open={!!freight} onClose={onClose} width={'80%'} closable={false}>
{freight && (
<div className='flex flex-col h-full'>
<div className='flex items-center justify-between mb-4'>
<div>
<Typography.Title level={1} style={{ margin: 0, marginBottom: '0.5em' }}>
{alias || freight.metadata?.name}
</Typography.Title>
{alias && freight?.metadata?.name && (
<CopyValue label='NAME:' value={freight.metadata?.name} />
)}
</div>

{freight?.metadata?.uid && <CopyValue label='UID:' value={freight?.metadata?.uid} />}
</div>
<div className='flex flex-col flex-1'>
<Tabs
className='flex-1'
defaultActiveKey='1'
style={{ minHeight: '500px' }}
items={[
{
key: '1',
label: 'Details',
icon: <FontAwesomeIcon icon={faInfoCircle} />,
children: <FreightStatusList freight={freight} />
},
{
key: '2',
label: 'Live Manifest',
icon: <FontAwesomeIcon icon={faFile} />,
className: 'h-full pb-2',
children: <ManifestPreview object={freight} />
}
]}
/>
</div>
</div>
)}
</Drawer>
);
};
61 changes: 61 additions & 0 deletions ui/src/features/freight/freight-status-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { IconDefinition, faCertificate, faCircleCheck } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useContext } from 'react';

import { ColorContext } from '@ui/context/colors';
import { Freight } from '@ui/gen/v1alpha1/generated_pb';

const StageIndicator = ({ stageName, icon }: { stageName: string; icon?: IconDefinition }) => {
const stageColorMap = useContext(ColorContext);
return (
<div
className='rounded-md px-3 py-1 mr-2 text-white font-medium text-base'
style={{ backgroundColor: stageColorMap[stageName] }}
>
{icon && <FontAwesomeIcon icon={icon} className='mr-2' />}
{stageName}
</div>
);
};

const StageStatusList = ({
title,
stageNames,
icon
}: {
title: string;
stageNames: string[];
icon?: IconDefinition;
}) => {
return (
<div className='mb-6'>
<div className='text-xs font-semibold mb-2 uppercase'>{title}</div>
{stageNames.length > 0 ? (
<div className='flex items-center gap-2'>
{stageNames.map((stageName) => (
<StageIndicator stageName={stageName} key={stageName} icon={icon} />
))}
</div>
) : (
<div className='w-full bg-gray-100 px-3 py-2 font-medium text-gray-600 rounded'>
This freight has not been {title} any stages yet.
</div>
)}
</div>
);
};

export const FreightStatusList = ({ freight }: { freight?: Freight }) => (
<div>
<StageStatusList
title='verified in'
stageNames={Object.keys(freight?.status?.verifiedIn || {}) || []}
icon={faCertificate}
/>
<StageStatusList
title='approved for'
stageNames={Object.keys(freight?.status?.approvedFor || {}) || []}
icon={faCircleCheck}
/>
</div>
);
2 changes: 1 addition & 1 deletion ui/src/features/freightline/freight-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const FreightItem = ({
}) => {
return (
<div
className={classNames('relative h-full', styles.freightItem, {
className={classNames('relative h-full cursor-pointer', styles.freightItem, {
['w-32']: !empty && mode !== FreightMode.Confirming,
['border-gray-500']: mode === FreightMode.Default && !empty,
[styles.promotable]: mode === FreightMode.Promotable,
Expand Down
2 changes: 1 addition & 1 deletion ui/src/features/freightline/freightline-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const FreightlineHeader = ({
<div
className='px-2 py-1 rounded text-white ml-2 font-semibold'
style={{
backgroundColor: stageColorMap[promotingStage?.metadata?.uid || '']
backgroundColor: stageColorMap[promotingStage?.metadata?.name || '']
}}
>
{promotingStage?.metadata?.name?.toUpperCase()}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/features/freightline/stage-indicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const StageIndicators = (props: { stages: Stage[]; faded?: boolean }) =>
{(props.stages || []).map((s) => (
<StageIndicator
stage={s}
backgroundColor={stageColorMap[s?.metadata?.uid || '']}
backgroundColor={stageColorMap[s?.metadata?.name || '']}
key={s?.metadata?.uid}
faded={props.faded}
/>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/features/project/project-details/images.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const Images = ({ projectName, stages }: { projectName: string; stages: S
}
stages[stage.metadata?.name as string] = {
opacity: 1 - i / len,
backgroundColor: colors[stage.metadata?.uid as string]
backgroundColor: colors[stage.metadata?.name as string]
};
});
});
Expand Down
16 changes: 13 additions & 3 deletions ui/src/features/project/project-details/project-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import { useQueryClient } from '@tanstack/react-query';
import { Button, Dropdown, Space, Tooltip, message } from 'antd';
import { graphlib, layout } from 'dagre';
import React from 'react';
import { useParams } from 'react-router-dom';
import { generatePath, useNavigate, useParams } from 'react-router-dom';

import { paths } from '@ui/config/paths';
import { transportWithAuth } from '@ui/config/transport';
import { ColorContext } from '@ui/context/colors';
import { LoadingState } from '@ui/features/common';
import { useModal } from '@ui/features/common/modal/use-modal';
import { getAlias } from '@ui/features/common/utils';
import { FreightDetails } from '@ui/features/freight/freight-details';
import { ConfirmPromotionDialogue } from '@ui/features/freightline/confirm-promotion-dialogue';
import { FreightContents } from '@ui/features/freightline/freight-contents';
import { FreightItem } from '@ui/features/freightline/freight-item';
Expand Down Expand Up @@ -80,7 +82,7 @@ const warehouseNodeHeight = 110;
const getSeconds = (ts?: Time): number => Number(ts?.seconds) || 0;

export const ProjectDetails = () => {
const { name, stageName } = useParams();
const { name, stageName, freightName } = useParams();
const { data, isLoading } = useQuery(listStages, { project: name });
const {
data: freightData,
Expand All @@ -92,6 +94,8 @@ export const ProjectDetails = () => {
project: name
});

const navigate = useNavigate();

const client = useQueryClient();

const { show: showCreateStage } = useModal(
Expand Down Expand Up @@ -417,7 +421,7 @@ export const ProjectDetails = () => {
setStageColorMap(scm);
nodes.forEach((node) => {
if (node.type === NodeType.STAGE) {
const color = scm[node.data?.metadata?.uid || ''];
const color = scm[node.data?.metadata?.name || ''];
if (color) {
node.color = color;
}
Expand Down Expand Up @@ -544,6 +548,9 @@ export const ProjectDetails = () => {
if (isLoading || isLoadingFreight) return <LoadingState />;

const stage = stageName && (data?.stages || []).find((item) => item.metadata?.name === stageName);
const freight =
freightName &&
(freightData?.groups['']?.freight || []).find((item) => item.metadata?.name === freightName);

const isFaded = (stage: Stage): boolean => {
if (!promotingStage || !confirmingPromotion) {
Expand Down Expand Up @@ -606,6 +613,8 @@ export const ProjectDetails = () => {
onClick={() => {
if (promotingStage && promotionEligible[id]) {
setConfirmingPromotion(confirmingPromotion ? undefined : f?.metadata?.name);
} else {
navigate(generatePath(paths.freight, { name, freightName: id }));
}
}}
mode={freightModeFor(id)}
Expand Down Expand Up @@ -920,6 +929,7 @@ export const ProjectDetails = () => {
</div>
</div>
{stage && <StageDetails stage={stage} />}
{freight && <FreightDetails freight={freight} />}
</ColorContext.Provider>
</div>
);
Expand Down
19 changes: 0 additions & 19 deletions ui/src/features/stage/manifest-preview.tsx

This file was deleted.

5 changes: 3 additions & 2 deletions ui/src/features/stage/stage-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { HealthStatusIcon } from '@ui/features/common/health-status/health-statu
import { Subscriptions } from '@ui/features/stage/subscriptions';
import { Stage } from '@ui/gen/v1alpha1/generated_pb';

import { ManifestPreview } from './manifest-preview';
import { ManifestPreview } from '../common/manifest-preview';

import { Promotions } from './promotions';
import { StageActions } from './stage-actions';
import { Verifications } from './verifications';
Expand Down Expand Up @@ -59,7 +60,7 @@ export const StageDetails = ({ stage }: { stage: Stage }) => {
key: '3',
label: 'Live Manifest',
className: 'h-full pb-2',
children: <ManifestPreview stage={stage} />
children: <ManifestPreview object={stage} />
}
]}
/>
Expand Down
6 changes: 3 additions & 3 deletions ui/src/features/stage/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const getStageColors = (project: string, stages: Stage[]): ColorMap => {
for (const stage of stages) {
const color = parseColorAnnotation(stage);
if (color) {
m[stage?.metadata?.uid || ''] = ColorMapHex[color];
m[stage?.metadata?.name || ''] = ColorMapHex[color];
}
}
return m;
Expand Down Expand Up @@ -96,7 +96,7 @@ export const generateStageColors = (sortedStages: Stage[], prevMap?: ColorMap) =
const color = parseColorAnnotation(stage);
if (color) {
delete curColors[color];
finalMap[stage?.metadata?.uid || ''] = ColorMapHex[color];
finalMap[stage?.metadata?.name || ''] = ColorMapHex[color];
}
}
const colors = Object.values(curColors);
Expand All @@ -106,7 +106,7 @@ export const generateStageColors = (sortedStages: Stage[], prevMap?: ColorMap) =
}
let i = 0;
for (const stage of sortedStages) {
const id = stage?.metadata?.uid;
const id = stage?.metadata?.name;
if (!id || finalMap[id]) {
continue;
}
Expand Down
Loading