Skip to content

Commit

Permalink
add project and pipelines card, add svgr (#3780)
Browse files Browse the repository at this point in the history
  • Loading branch information
bklimov-web committed Nov 27, 2024
1 parent e826594 commit f744ece
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 91 deletions.
3 changes: 2 additions & 1 deletion portals-ui/sites/ngs-portal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"tailwindcss": "^3.4.15",
"typescript": "~5.6.2",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10"
"vite": "^5.4.10",
"vite-plugin-svgr": "^4.3.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { createHashRouter } from 'react-router-dom';
import { Layout } from '../../../pages/layout/index.tsx';
import { Home } from '../../../pages/home/home.tsx';
import Pipelines from '../../../pages/pipelines/index.tsx';
import Projects from '../../../pages/projects/index.tsx';
import { ProjectsPage } from '../../../pages/projects';
import Runs from '../../../pages/runs/index.tsx';
import { AppRoutes, RoutePath } from '../../../shared/constants/routes.ts';

const routerConfig: Record<AppRoutes, RouteObject> = {
[AppRoutes.HOME]: { path: RoutePath[AppRoutes.HOME], element: <Home /> },
[AppRoutes.PROJECTS]: {
path: RoutePath[AppRoutes.PROJECTS],
element: <Projects />,
element: <ProjectsPage />,
},
[AppRoutes.PIPELINES]: {
path: RoutePath[AppRoutes.PIPELINES],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { PipelinesList } from './pipelines-list';
export { ProjectsList } from './projects-list';
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Tag, Badge, FlexRow, RichTextView } from '@epam/uui';
import ContentPersonFillIcon from '@epam/assets/icons/content-person-fill.svg?react';
import cn from 'classnames';
import { Link } from 'react-router-dom';
import type { ReactNode } from 'react';

type Props = {
id: number;
name: ReactNode;
owner: string;
hasDivider?: boolean;
description?: string;
tags?: string[];
};

export const PipelineCard = ({
id,
name,
owner,
description,
tags,
hasDivider = false,
}: Props) => {
return (
<div
className={cn('px-4 py-4 bg-white w-full space-y-2', {
'border-t-2 border-[var(--uui-neutral-30)]': hasDivider,
})}>
{tags?.length && (
<FlexRow columnGap="6" size="24">
{tags.map((tag) => (
<Tag caption={tag} size="24" />
))}
</FlexRow>
)}

<FlexRow columnGap="12" size="24">
<Link
className="text-lg text-[var(--uui-link)] hover:text-[var(--uui-link-hover)] no-underline"
to={`/pipeline/${id}`}>
{name}
</Link>
<Badge
icon={ContentPersonFillIcon}
caption={owner}
color="neutral"
size="18"
cx="shrink-0"
/>
</FlexRow>

{description && <RichTextView>{description}</RichTextView>}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Pipeline } from '@cloud-pipeline/core';
import HighlightedText from '../../../shared/highlight-text';
import { ItemsPanel } from '../../../widgets/items-panel/items-panel';
import { PipelineCard } from './pipeline-card';

type Props = {
pipelines: Pipeline[];
};

export const PipelinesList = ({ pipelines }: Props) => {
const renderItem = (item: Pipeline, search: string, i: number) => {
const { id, name, owner, description } = item;

return (
<PipelineCard
key={id}
id={id}
name={<HighlightedText search={search}>{name}</HighlightedText>}
owner={owner}
hasDivider={i !== 0}
description={description}
/>
);
};

return (
<ItemsPanel
className="max-h-full list-container overflow-auto"
title="Pipelines"
items={pipelines}
renderItem={renderItem}
sliced
search
itemKey="id"
viewAll={{ title: 'View all pipelines', link: '/pipelines' }}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Tag, Badge, FlexRow, RichTextView } from '@epam/uui';
import ContentPersonFillIcon from '@epam/assets/icons/content-person-fill.svg?react';
import cn from 'classnames';
import { Link } from 'react-router-dom';
import type { ReactNode } from 'react';

type Props = {
id: number;
name: ReactNode;
owner: string;
accessRights: {
read: boolean;
write: boolean;
execute: boolean;
};
hasDivider?: boolean;
description?: string;
tags?: string[];
};

export const ProjectCard = ({
id,
name,
owner,
description,
accessRights,
tags,
hasDivider = false,
}: Props) => {
const hasSomeRights =
accessRights && Object.values(accessRights).some((right) => Boolean(right));

return (
<div
className={cn('px-4 py-4 bg-white w-full space-y-2', {
'border-t-2 border-[var(--uui-neutral-30)]': hasDivider,
})}>
{tags?.length && (
<FlexRow columnGap="6" size="24">
{tags.map((tag) => (
<Tag caption={tag} size="24" />
))}
</FlexRow>
)}

<FlexRow columnGap="12" size="24">
<Link
className="text-lg text-[var(--uui-link)] hover:text-[var(--uui-link-hover)] no-underline"
to={`/project/${id}`}>
{name}
</Link>
<Badge
icon={ContentPersonFillIcon}
caption={owner}
color="neutral"
size="18"
cx="shrink-0"
/>
</FlexRow>

{hasSomeRights && (
<FlexRow columnGap="6" size="30">
{accessRights.read && (
<Badge size="24" fill="outline" caption="Read" color="info" />
)}
{accessRights.write && (
<Badge size="24" fill="outline" caption="Write" color="warning" />
)}
{accessRights.execute && (
<Badge size="24" fill="outline" caption="Execute" color="success" />
)}
</FlexRow>
)}

{description && <RichTextView>{description}</RichTextView>}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
executeAllowed,
readAllowed,
writeAllowed,
type Project,
} from '@cloud-pipeline/core';
import { Button } from '@epam/uui';
import { ProjectCard } from './project-card';
import HighlightedText from '../../../shared/highlight-text';
import { ItemsPanel } from '../../../widgets/items-panel/items-panel';

type Props = {
projects: Project[];
};

export const ProjectsList = ({ projects }: Props) => {
const renderItem = (item: Project, search: string, i: number) => {
const { mask, id, name, owner } = item;

const accessRights = {
read: readAllowed(mask),
write: writeAllowed(mask),
execute: executeAllowed(mask),
};

return (
<ProjectCard
key={id}
id={id}
name={<HighlightedText search={search}>{name}</HighlightedText>}
owner={owner}
hasDivider={i !== 0}
accessRights={accessRights}
/>
);
};

return (
<ItemsPanel
className="max-h-full list-container overflow-auto"
title="Projects"
actions={
<Button caption="Create project" size="24" onClick={() => null} />
}
items={projects}
renderItem={renderItem}
sliced
search
itemKey="id"
viewAll={{ title: 'View all projects', link: '/projects' }}
/>
);
};
53 changes: 16 additions & 37 deletions portals-ui/sites/ngs-portal/src/pages/home/home.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,43 @@
import { useEffect } from 'react';
import type { Run } from '@cloud-pipeline/core';
import { Button } from '@epam/uui';
import { useProjectsState } from '../../state/projects/hooks';
import { loadProjects } from '../../state/projects/load-projects';
import HighlightedText from '../../shared/highlight-text';
import { ItemsPanel } from '../../widgets/items-panel/items-panel.tsx';
import { usePipelinesState } from '../../state/pipelines/hooks.ts';
import { loadPipelines } from '../../state/pipelines/load-pipelines.ts';
import './style.css';
import { ProjectsList, PipelinesList } from './components';

export const Home = () => {
const { projects } = useProjectsState();
const { pipelines } = usePipelinesState();

useEffect(() => {
loadProjects()
.then(() => {})
.catch(() => {});
}, []);

useEffect(() => {
loadPipelines()
.then(() => {})
.catch(() => {});
}, []);

return (
<div className="flex h-full w-full gap-1 overflow-hidden flex-nowrap p-1">
<div className="flex-1 h-full overflow-auto p-2">
<ItemsPanel
className="max-h-full list-container overflow-auto"
title="Projects"
actions={
<Button caption="Create project" size="24" onClick={() => null} />
}
items={projects}
renderItem={(item, search) => (
<div className="p-2 border-b">
<HighlightedText search={search}>{item.name}</HighlightedText>
</div>
)}
sliced
search
itemKey="id"
viewAll={{ title: 'View all projects', link: '/projects' }}
/>
</div>
<div className="flex-1 h-full overflow-auto p-2">
<ItemsPanel
className="max-h-full list-container overflow-auto"
title="Pipelines"
items={pipelines}
renderItem={(item, search) => (
<div className="p-2 border-b">
<HighlightedText search={search}>{item.name}</HighlightedText>
</div>
)}
sliced
search
itemKey="id"
viewAll={{ title: 'View all pipelines', link: '/pipelines' }}
/>
</div>
{projects && (
<div className="flex-1 h-full overflow-auto p-2">
<ProjectsList projects={projects} />
</div>
)}

{pipelines && (
<div className="flex-1 h-full overflow-auto p-2">
<PipelinesList pipelines={pipelines} />
</div>
)}

<div className="flex-1 h-full overflow-auto p-2">
<ItemsPanel
className="max-h-full list-container overflow-auto"
Expand Down
48 changes: 1 addition & 47 deletions portals-ui/sites/ngs-portal/src/pages/projects/index.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1 @@
import { useEffect } from 'react';
import { Spinner } from '@epam/uui';
import { loadProjects } from '../../state/projects/load-projects';
import { useProjectsState } from '../../state/projects/hooks';
import { List, ListHeader } from '@cloud-pipeline/components';
import { useSearch } from '../../shared/hooks/use-search.ts';
import HighlightedText from '../../shared/highlight-text';

export default function Projects() {
useEffect(() => {
loadProjects()
.then(() => {})
.catch(() => {});
}, []);
const { projects, error, pending } = useProjectsState();
const { search, onSearchChange, filtered } = useSearch({
items: projects ?? [],
});
if (error) {
return <div>{error}</div>;
}
if (pending) {
return <Spinner />;
}
if (!projects) {
return <div>No data</div>;
}
return (
<div className="flex flex-col overflow-auto">
<ListHeader
title="Projects"
className="shrink-0 border"
search={search}
onSearch={onSearchChange}
/>
<List
className="overflow-auto border-b border-l border-r"
data={filtered}
renderItem={(project) => (
<HighlightedText search={search}>{project.name}</HighlightedText>
)}
itemKey="id"
sliced={20}
/>
</div>
);
}
export { ProjectsPage } from './projects';
Loading

0 comments on commit f744ece

Please sign in to comment.