feat(ws): Make Workspace Kind drawer resizable and add table view to WS kind details (#483)
Signed-off-by: Charles Thao <cthao@redhat.com>
This commit is contained in:
parent
13a66aef2b
commit
de0e5c4356
|
@ -45,7 +45,7 @@ export const WorkspaceKindDetails: React.FunctionComponent<WorkspaceKindDetailsP
|
|||
};
|
||||
|
||||
return (
|
||||
<DrawerPanelContent data-testid="workspaceDetails">
|
||||
<DrawerPanelContent minSize="45%" isResizable data-testid="workspaceDetails">
|
||||
<DrawerHead>
|
||||
<Title headingLevel="h6">{workspaceKind.name}</Title>
|
||||
<DrawerActions>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Button, List, ListItem } from '@patternfly/react-core';
|
||||
import { WorkspaceKind } from '~/shared/api/backendApiTypes';
|
||||
import { WorkspaceCountPerKind } from '~/app/hooks/useWorkspaceCountPerKind';
|
||||
import { useTypedNavigate } from '~/app/routerHelper';
|
||||
import { WorkspaceKindDetailsTable } from './WorkspaceKindDetailsTable';
|
||||
|
||||
type WorkspaceDetailsImagesProps = {
|
||||
workspaceKind: WorkspaceKind;
|
||||
|
@ -12,37 +11,21 @@ type WorkspaceDetailsImagesProps = {
|
|||
export const WorkspaceKindDetailsImages: React.FunctionComponent<WorkspaceDetailsImagesProps> = ({
|
||||
workspaceKind,
|
||||
workspaceCountPerKind,
|
||||
}) => {
|
||||
const navigate = useTypedNavigate();
|
||||
|
||||
return (
|
||||
<List isPlain>
|
||||
{workspaceKind.podTemplate.options.imageConfig.values.map((image, rowIndex) => (
|
||||
<ListItem key={rowIndex}>
|
||||
{image.displayName}:{' '}
|
||||
<Button
|
||||
variant="link"
|
||||
isInline
|
||||
className="workspace-kind-summary-button"
|
||||
onClick={() =>
|
||||
navigate('workspaceKindSummary', {
|
||||
params: { kind: workspaceKind.name },
|
||||
state: {
|
||||
}) => (
|
||||
<WorkspaceKindDetailsTable
|
||||
rows={workspaceKind.podTemplate.options.imageConfig.values.map((image) => ({
|
||||
id: image.id,
|
||||
displayName: image.displayName,
|
||||
kindName: workspaceKind.name,
|
||||
workspaceCountRouteState: {
|
||||
imageId: image.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
{
|
||||
workspaceCount:
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workspaceCountPerKind[workspaceKind.name]
|
||||
? workspaceCountPerKind[workspaceKind.name].countByImage[image.id] ?? 0
|
||||
: 0
|
||||
}
|
||||
{' Workspaces'}
|
||||
</Button>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
: 0,
|
||||
}))}
|
||||
tableKind="image"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Button, List, ListItem } from '@patternfly/react-core';
|
||||
import { WorkspaceKind } from '~/shared/api/backendApiTypes';
|
||||
import { WorkspaceCountPerKind } from '~/app/hooks/useWorkspaceCountPerKind';
|
||||
import { useTypedNavigate } from '~/app/routerHelper';
|
||||
import { WorkspaceKindDetailsTable } from './WorkspaceKindDetailsTable';
|
||||
|
||||
type WorkspaceDetailsNamespacesProps = {
|
||||
workspaceKind: WorkspaceKind;
|
||||
|
@ -11,42 +10,25 @@ type WorkspaceDetailsNamespacesProps = {
|
|||
|
||||
export const WorkspaceKindDetailsNamespaces: React.FunctionComponent<
|
||||
WorkspaceDetailsNamespacesProps
|
||||
> = ({ workspaceKind, workspaceCountPerKind }) => {
|
||||
const navigate = useTypedNavigate();
|
||||
|
||||
return (
|
||||
<List isPlain>
|
||||
{Object.keys(
|
||||
> = ({ workspaceKind, workspaceCountPerKind }) => (
|
||||
<WorkspaceKindDetailsTable
|
||||
rows={Object.keys(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workspaceCountPerKind[workspaceKind.name]
|
||||
? workspaceCountPerKind[workspaceKind.name].countByNamespace
|
||||
: [],
|
||||
).map((namespace, rowIndex) => (
|
||||
<ListItem key={rowIndex}>
|
||||
{namespace}:{' '}
|
||||
<Button
|
||||
variant="link"
|
||||
className="workspace-kind-summary-button"
|
||||
isInline
|
||||
onClick={() =>
|
||||
navigate('workspaceKindSummary', {
|
||||
params: { kind: workspaceKind.name },
|
||||
state: {
|
||||
).map((namespace, rowIndex) => ({
|
||||
id: String(rowIndex),
|
||||
displayName: namespace,
|
||||
kindName: workspaceKind.name,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workspaceCount: workspaceCountPerKind[workspaceKind.name]
|
||||
? workspaceCountPerKind[workspaceKind.name].countByNamespace[namespace]
|
||||
: 0,
|
||||
workspaceCountRouteState: {
|
||||
namespace,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workspaceCountPerKind[workspaceKind.name]
|
||||
? workspaceCountPerKind[workspaceKind.name].countByNamespace[namespace]
|
||||
: 0
|
||||
}
|
||||
{' Workspaces'}
|
||||
</Button>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
}))}
|
||||
tableKind="namespace"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Button, List, ListItem } from '@patternfly/react-core';
|
||||
import { WorkspaceKind } from '~/shared/api/backendApiTypes';
|
||||
import { WorkspaceCountPerKind } from '~/app/hooks/useWorkspaceCountPerKind';
|
||||
import { useTypedNavigate } from '~/app/routerHelper';
|
||||
import { WorkspaceKindDetailsTable } from './WorkspaceKindDetailsTable';
|
||||
|
||||
type WorkspaceDetailsPodConfigsProps = {
|
||||
workspaceKind: WorkspaceKind;
|
||||
|
@ -11,37 +10,20 @@ type WorkspaceDetailsPodConfigsProps = {
|
|||
|
||||
export const WorkspaceKindDetailsPodConfigs: React.FunctionComponent<
|
||||
WorkspaceDetailsPodConfigsProps
|
||||
> = ({ workspaceKind, workspaceCountPerKind }) => {
|
||||
const navigate = useTypedNavigate();
|
||||
|
||||
return (
|
||||
<List isPlain>
|
||||
{workspaceKind.podTemplate.options.podConfig.values.map((podConfig, rowIndex) => (
|
||||
<ListItem key={rowIndex}>
|
||||
{podConfig.displayName}:{' '}
|
||||
<Button
|
||||
variant="link"
|
||||
className="workspace-kind-summary-button"
|
||||
isInline
|
||||
onClick={() =>
|
||||
navigate('workspaceKindSummary', {
|
||||
params: { kind: workspaceKind.name },
|
||||
state: {
|
||||
> = ({ workspaceKind, workspaceCountPerKind }) => (
|
||||
<WorkspaceKindDetailsTable
|
||||
rows={workspaceKind.podTemplate.options.podConfig.values.map((podConfig) => ({
|
||||
id: podConfig.id,
|
||||
displayName: podConfig.displayName,
|
||||
kindName: workspaceKind.name,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workspaceCount: workspaceCountPerKind[workspaceKind.name]
|
||||
? workspaceCountPerKind[workspaceKind.name].countByPodConfig[podConfig.id] ?? 0
|
||||
: 0,
|
||||
workspaceCountRouteState: {
|
||||
podConfigId: podConfig.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
workspaceCountPerKind[workspaceKind.name]
|
||||
? workspaceCountPerKind[workspaceKind.name].countByPodConfig[podConfig.id] ?? 0
|
||||
: 0
|
||||
}
|
||||
{' Workspaces'}
|
||||
</Button>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
}))}
|
||||
tableKind="podConfig"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import { Table, Thead, Tr, Td, Tbody, Th } from '@patternfly/react-table';
|
||||
import { Button, Content, Pagination, PaginationVariant } from '@patternfly/react-core';
|
||||
import { useTypedNavigate } from '~/app/routerHelper';
|
||||
import { RouteStateMap } from '~/app/routes';
|
||||
|
||||
export interface WorkspaceKindDetailsTableRow {
|
||||
id: string;
|
||||
displayName: string;
|
||||
kindName: string;
|
||||
workspaceCount: number;
|
||||
workspaceCountRouteState: RouteStateMap['workspaceKindSummary'];
|
||||
}
|
||||
|
||||
interface WorkspaceKindDetailsTableProps {
|
||||
rows: WorkspaceKindDetailsTableRow[];
|
||||
tableKind: 'image' | 'podConfig' | 'namespace';
|
||||
}
|
||||
|
||||
export const WorkspaceKindDetailsTable: React.FC<WorkspaceKindDetailsTableProps> = ({
|
||||
rows,
|
||||
tableKind,
|
||||
}) => {
|
||||
const navigate = useTypedNavigate();
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage, setPerPage] = useState(10);
|
||||
const rowPages = useMemo(() => {
|
||||
const pages = [];
|
||||
for (let i = 0; i < rows.length; i += perPage) {
|
||||
pages.push(rows.slice(i, i + perPage));
|
||||
}
|
||||
return pages;
|
||||
}, [perPage, rows]);
|
||||
|
||||
const onSetPage = (
|
||||
_event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
||||
newPage: number,
|
||||
) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const onPerPageSelect = (
|
||||
_event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
||||
newPerPage: number,
|
||||
newPage: number,
|
||||
) => {
|
||||
setPerPage(newPerPage);
|
||||
setPage(newPage);
|
||||
};
|
||||
return (
|
||||
<Content>
|
||||
<Table aria-label={`workspace-kind-details-${tableKind}`}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Name</Th>
|
||||
<Th>Workspaces</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{rowPages[page - 1].map((row) => (
|
||||
<Tr key={row.id}>
|
||||
<Td>{row.displayName}</Td>
|
||||
<Td>
|
||||
<Button
|
||||
variant="link"
|
||||
isInline
|
||||
className="workspace-kind-summary-button"
|
||||
onClick={() =>
|
||||
navigate('workspaceKindSummary', {
|
||||
params: { kind: row.kindName },
|
||||
state: row.workspaceCountRouteState,
|
||||
})
|
||||
}
|
||||
>
|
||||
{row.workspaceCount} Workspaces
|
||||
</Button>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
<Pagination
|
||||
itemCount={rows.length}
|
||||
widgetId="pagination-bottom"
|
||||
perPage={perPage}
|
||||
page={page}
|
||||
variant={PaginationVariant.bottom}
|
||||
isCompact
|
||||
onSetPage={onSetPage}
|
||||
onPerPageSelect={onPerPageSelect}
|
||||
/>
|
||||
</Content>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue