Skip to content

Commit 8962cfc

Browse files
authored
4564 - pipeline list components (#4586)
1 parent 89a6c05 commit 8962cfc

File tree

11 files changed

+291
-1
lines changed

11 files changed

+291
-1
lines changed
40.9 KB
Loading

ui/src/features/dataset/gallery/media-item.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.floatingContainer {
22
opacity: 0;
33
position: absolute;
4+
line-height: 0px;
45
padding: var(--spectrum-global-dimension-size-125);
56
background: var(--spectrum-global-color-gray-50);
67
border-radius: var(--spectrum-global-dimension-size-50);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { ActionButton, Item, Menu, MenuTrigger } from '@geti/ui';
5+
import { MoreMenu } from '@geti/ui/icons';
6+
7+
export const MenuActions = () => {
8+
return (
9+
<MenuTrigger>
10+
<ActionButton isQuiet UNSAFE_style={{ fill: 'var(--spectrum-gray-900)' }}>
11+
<MoreMenu />
12+
</ActionButton>
13+
<Menu>
14+
<Item>Export</Item>
15+
<Item>Duplicate</Item>
16+
<Item>Delete</Item>
17+
</Menu>
18+
</MenuTrigger>
19+
);
20+
};
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
export const mockedProjects = [
5+
{
6+
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
7+
model_id: 'b0feaabc-da2b-442e-9b3e-55c11c2c2ff2',
8+
name: 'Production Pipeline',
9+
sink_id: 'b5787c06-964b-4097-8eca-238b8cf79fc8',
10+
source_id: 'd2cbd8d0-17b8-463e-85a2-4aaed031674d',
11+
status: 'running',
12+
labels: ['Box', 'Paper', 'Blocks'],
13+
type: 'Object Detection',
14+
images: 5000,
15+
date: '04 March 2021, 3:34 PM',
16+
},
17+
{
18+
id: 'e2f3g4h5-i6j7-8901-bcde-fg2345678901',
19+
model_id: 'c1feaabc-da2b-442e-9b3e-55c11c2c2ff3',
20+
name: 'Testing Pipeline',
21+
sink_id: 'c6787c06-964b-4097-8eca-238b8cf79fc9',
22+
source_id: 'e3cbd8d0-17b8-463e-85a2-4aaed031674e',
23+
status: 'stopped',
24+
labels: ['Box', 'Paper', 'Blocks'],
25+
type: 'Object Detection',
26+
images: 5000,
27+
date: '04 March 2021, 3:34 PM',
28+
},
29+
{
30+
id: 'i3j4k5l6-m7n8-9012-cdef-hi3456789012',
31+
model_id: 'd2feaabc-da2b-442e-9b3e-55c11c2c2ff4',
32+
name: 'Development Pipeline',
33+
sink_id: 'd7897c06-964b-4097-8eca-238b8cf79fca',
34+
source_id: 'f4cbd8d0-17b8-463e-85a2-4aaed031674f',
35+
status: 'paused',
36+
labels: ['Box', 'Paper', 'Blocks'],
37+
type: 'Object Detection',
38+
images: 5000,
39+
date: '04 March 2021, 3:34 PM',
40+
},
41+
{
42+
id: 'm4n5o6p7-q8r9-0123-defg-jk4567890123',
43+
model_id: 'e3feaabc-da2b-442e-9b3e-55c11c2c2ff5',
44+
name: 'Analytics Pipeline',
45+
sink_id: 'e8907c06-964b-4097-8eca-238b8cf79fcb',
46+
source_id: 'g5cbd8d0-17b8-463e-85a2-4aaed0316750',
47+
status: 'running',
48+
labels: ['Box', 'Paper', 'Blocks'],
49+
type: 'Object Detection',
50+
images: 5000,
51+
date: '04 March 2021, 3:34 PM',
52+
},
53+
{
54+
id: 's5t6u7v8-w9x0-1234-efgh-lm5678901234',
55+
model_id: 'f4feaabc-da2b-442e-9b3e-55c11c2c2ff6',
56+
name: 'Backup Pipeline',
57+
sink_id: 'f9017c06-964b-4097-8eca-238b8cf79fcc',
58+
source_id: 'h6cbd8d0-17b8-463e-85a2-4aaed0316751',
59+
status: 'error',
60+
labels: ['Box', 'Paper', 'Blocks'],
61+
type: 'Object Detection',
62+
images: 5000,
63+
date: '04 March 2021, 3:34 PM',
64+
},
65+
];
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Text } from '@geti/ui';
5+
import { AddCircle } from '@geti/ui/icons';
6+
import { Link } from 'react-router-dom';
7+
8+
import { paths } from '../../../router';
9+
10+
import classes from './new-project-link.module.scss';
11+
12+
export const NewProjectLink = () => {
13+
return (
14+
<Link to={paths.pipeline.new.pattern} className={classes.link}>
15+
<AddCircle />
16+
<Text>Add another project</Text>
17+
</Link>
18+
);
19+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.link {
2+
cursor: pointer;
3+
border: 1px dashed var(--spectrum-gray-700) !important;
4+
display: flex;
5+
align-items: center;
6+
flex-direction: column;
7+
justify-content: center;
8+
border-radius: regular;
9+
background-color: var(--spectrum-global-color-gray-300);
10+
gap: var(--spectrum-global-dimension-size-200);
11+
padding: var(--spectrum-global-dimension-size-275);
12+
min-height: var(--spectrum-global-dimension-size-1000);
13+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Flex, Heading, Tag, Text, View } from '@geti/ui';
5+
import { clsx } from 'clsx';
6+
7+
import thumbnailUrl from '../../../assets/mocked-project-thumbnail.png';
8+
import { MenuActions } from './menu-actions.component';
9+
import { mockedProjects } from './mocked-projects';
10+
11+
import classes from './project-list.module.scss';
12+
13+
type ProjectCardProps = {
14+
item: (typeof mockedProjects)[0];
15+
isActive: boolean;
16+
};
17+
18+
export const ProjectCard = ({ item, isActive }: ProjectCardProps) => {
19+
return (
20+
<Flex UNSAFE_className={clsx({ [classes.card]: true, [classes.activeCard]: isActive })}>
21+
<View aria-label={'project thumbnail'}>
22+
<img src={thumbnailUrl} alt={item.name} />
23+
</View>
24+
25+
<View width={'100%'} padding={'size-200'}>
26+
<Flex alignItems={'center'} justifyContent={'space-between'}>
27+
<Heading level={3}>{item.name}</Heading>
28+
<MenuActions />
29+
</Flex>
30+
31+
<Flex marginBottom={'size-200'} gap={'size-50'}>
32+
{isActive && <Tag withDot={false} text='Active' className={clsx(classes.tag, classes.activeTag)} />}
33+
<Tag withDot={false} text={item.type} className={classes.tag} />
34+
</Flex>
35+
36+
<Flex alignItems={'center'} gap={'size-100'} direction={'row'} wrap='wrap'>
37+
<Text>• Edited: 2025-08-07 06:05 AM</Text>
38+
<Text>• Images: {item.images}</Text>
39+
<Text>• Labels: {item.labels.join(', ')}</Text>
40+
</Flex>
41+
</View>
42+
</Flex>
43+
);
44+
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Button, ButtonGroup, Content, Dialog, DialogTrigger, Grid, Heading, Text } from '@geti/ui';
5+
import { CloseSmall } from '@geti/ui/icons';
6+
import { isEmpty } from 'lodash-es';
7+
8+
import { mockedProjects } from './mocked-projects';
9+
import { NewProjectLink } from './new-project-link.component';
10+
import { ProjectCard } from './project-card';
11+
12+
import classes from './project-list.module.scss';
13+
14+
export const ProjectList = () => {
15+
return (
16+
<DialogTrigger type='fullscreenTakeover'>
17+
<Button variant='secondary'>New Project</Button>
18+
{(close) => (
19+
<Dialog UNSAFE_className={classes.dialog}>
20+
<Content>
21+
<Heading
22+
level={1}
23+
marginBottom={'size-250'}
24+
UNSAFE_style={{
25+
textAlign: 'center',
26+
fontSize: 'var(--spectrum-global-dimension-font-size-700)',
27+
}}
28+
>
29+
Projects
30+
</Heading>
31+
32+
<Text UNSAFE_className={classes.description}>
33+
To create a project, start by defining your objectives. Then, design the data flow to ensure
34+
proper processing at each stage. Implement the required tools and technologies for
35+
automation, and finally, test the project to confirm it runs smoothly and meets your goals.
36+
</Text>
37+
38+
<Grid
39+
gap={'size-300'}
40+
marginX={'auto'}
41+
justifyContent={'center'}
42+
columns={isEmpty(mockedProjects) ? ['size-3600'] : ['1fr', '1fr']}
43+
>
44+
<NewProjectLink />
45+
46+
{mockedProjects?.map((item, index) => (
47+
<ProjectCard key={item.id} item={item} isActive={index === 0} />
48+
))}
49+
</Grid>
50+
</Content>
51+
<ButtonGroup>
52+
<Button variant='secondary' onPress={close} UNSAFE_className={classes.button}>
53+
<CloseSmall /> Close
54+
</Button>
55+
</ButtonGroup>
56+
</Dialog>
57+
)}
58+
</DialogTrigger>
59+
);
60+
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
.dialog {
2+
background-color: var(--spectrum-gray-100);
3+
background-image: url('./../../../assets/background.png');
4+
background-blend-mode: luminosity;
5+
background-position: center;
6+
background-repeat: no-repeat;
7+
background-size: cover;
8+
}
9+
10+
.button {
11+
path {
12+
fill: currentColor;
13+
}
14+
}
15+
16+
.description {
17+
display: block;
18+
text-align: center;
19+
margin: auto;
20+
max-width: 50rem;
21+
font-size: var(--spectrum-global-dimension-font-size-200);
22+
line-height: var(--spectrum-global-dimension-font-size-500);
23+
margin-bottom: var(--spectrum-global-dimension-font-size-1000);
24+
}
25+
26+
.card {
27+
border-width: var(--spectrum-global-dimension-size-thin);
28+
border-color: var(--spectrum-global-color-gray-200);
29+
border-radius: var(--spectrum-global-dimension-size-50);
30+
background-color: var(--spectrum-global-color-gray-50);
31+
32+
img {
33+
height: 100%;
34+
object-fit: cover;
35+
}
36+
}
37+
38+
.activeCard {
39+
border: var(--spectrum-global-dimension-size-25) solid var(--energy-blue);
40+
41+
h3 {
42+
color: var(--energy-blue);
43+
}
44+
}
45+
46+
.tag {
47+
width: fit-content;
48+
color: var(--spectrum-gray-50);
49+
background: rgba(255, 255, 255, 0.06);
50+
color: var(--spectrum-global-color-gray-800);
51+
padding: var(--spectrum-global-dimension-size-25);
52+
border-radius: var(--spectrum-global-dimension-size-50);
53+
}
54+
55+
.activeTag {
56+
color: var(--spectrum-gray-50);
57+
58+
background-color: var(--energy-blue) !important;
59+
}

ui/src/features/pipelines/view-pipeline.component.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { Button, ButtonGroup, Divider, Flex, Grid, Heading, repeat, Text, View }
55
import { capitalize, isArray, startsWith } from 'lodash-es';
66

77
import { $api } from '../../api/client';
8+
import { paths } from '../../router';
89
import Background from './../../assets/background.png';
10+
import { ProjectList } from './modal/project-list.component';
911

1012
type FieldProps = {
1113
field: string;
@@ -90,7 +92,12 @@ export const ViewPipeline = () => {
9092
</Grid>
9193
<Divider size='S' />
9294
<ButtonGroup>
93-
<Button variant='secondary' marginStart='auto'>
95+
<ProjectList />
96+
<Button
97+
href={paths.pipeline.edit({ pipelineId: '' })}
98+
variant='secondary'
99+
marginStart='auto'
100+
>
94101
Edit
95102
</Button>
96103
</ButtonGroup>

0 commit comments

Comments
 (0)