feat(ws): Notebooks 2.0 // Frontend // Workspaces details // Live mockup (#174)
Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com>
This commit is contained in:
parent
249a45677d
commit
d84621aac1
|
@ -43,6 +43,7 @@ const App: React.FC = () => {
|
||||||
<Page
|
<Page
|
||||||
mainContainerId="primary-app-container"
|
mainContainerId="primary-app-container"
|
||||||
masthead={masthead}
|
masthead={masthead}
|
||||||
|
isContentFilled
|
||||||
isManagedSidebar
|
isManagedSidebar
|
||||||
sidebar={<NavSidebar />}
|
sidebar={<NavSidebar />}
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
DrawerActions,
|
||||||
|
DrawerCloseButton,
|
||||||
|
DrawerHead,
|
||||||
|
DrawerPanelBody,
|
||||||
|
DrawerPanelContent,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
TabTitleText,
|
||||||
|
Title,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import { Workspace } from '~/shared/types';
|
||||||
|
import { WorkspaceDetailsOverview } from '~/app/pages/Workspaces/Details/WorkspaceDetailsOverview';
|
||||||
|
import { WorkspaceDetailsActions } from '~/app/pages/Workspaces/Details/WorkspaceDetailsActions';
|
||||||
|
|
||||||
|
type WorkspaceDetailsProps = {
|
||||||
|
workspace: Workspace;
|
||||||
|
onCloseClick: React.MouseEventHandler;
|
||||||
|
onEditClick: React.MouseEventHandler;
|
||||||
|
onDeleteClick: React.MouseEventHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkspaceDetails: React.FunctionComponent<WorkspaceDetailsProps> = ({
|
||||||
|
workspace,
|
||||||
|
onCloseClick,
|
||||||
|
onEditClick,
|
||||||
|
onDeleteClick,
|
||||||
|
}) => {
|
||||||
|
const [activeTabKey, setActiveTabKey] = React.useState<string | number>(0);
|
||||||
|
|
||||||
|
const handleTabClick = (
|
||||||
|
event: React.MouseEvent | React.KeyboardEvent | MouseEvent,
|
||||||
|
tabIndex: string | number,
|
||||||
|
) => {
|
||||||
|
setActiveTabKey(tabIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DrawerPanelContent isResizable defaultSize="50%">
|
||||||
|
<DrawerHead>
|
||||||
|
<Title headingLevel="h6">{workspace.name}</Title>
|
||||||
|
<WorkspaceDetailsActions onEditClick={onEditClick} onDeleteClick={onDeleteClick} />
|
||||||
|
<DrawerActions>
|
||||||
|
<DrawerCloseButton onClick={onCloseClick} />
|
||||||
|
</DrawerActions>
|
||||||
|
</DrawerHead>
|
||||||
|
<DrawerPanelBody>
|
||||||
|
<Tabs activeKey={activeTabKey} onSelect={handleTabClick}>
|
||||||
|
<Tab eventKey={0} title={<TabTitleText>Overview</TabTitleText>} aria-label="Overview">
|
||||||
|
<WorkspaceDetailsOverview workspace={workspace} />
|
||||||
|
</Tab>
|
||||||
|
<Tab eventKey={1} title={<TabTitleText>Activity</TabTitleText>} aria-label="Activity">
|
||||||
|
Activity
|
||||||
|
</Tab>
|
||||||
|
<Tab eventKey={2} title={<TabTitleText>Logs</TabTitleText>} aria-label="Logs">
|
||||||
|
Logs
|
||||||
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
eventKey={3}
|
||||||
|
title={<TabTitleText>Pod template</TabTitleText>}
|
||||||
|
aria-label="Pod template"
|
||||||
|
>
|
||||||
|
Pod template
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</DrawerPanelBody>
|
||||||
|
</DrawerPanelContent>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,65 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
Dropdown,
|
||||||
|
DropdownList,
|
||||||
|
MenuToggle,
|
||||||
|
DropdownItem,
|
||||||
|
Flex,
|
||||||
|
FlexItem,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
|
interface WorkspaceDetailsActionsProps {
|
||||||
|
onEditClick: React.MouseEventHandler;
|
||||||
|
onDeleteClick: React.MouseEventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WorkspaceDetailsActions: React.FC<WorkspaceDetailsActionsProps> = ({
|
||||||
|
onEditClick,
|
||||||
|
onDeleteClick,
|
||||||
|
}) => {
|
||||||
|
const [isOpen, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex>
|
||||||
|
<FlexItem>
|
||||||
|
<Dropdown
|
||||||
|
isOpen={isOpen}
|
||||||
|
onSelect={() => setOpen(false)}
|
||||||
|
onOpenChange={(open) => setOpen(open)}
|
||||||
|
popperProps={{ position: 'end' }}
|
||||||
|
toggle={(toggleRef) => (
|
||||||
|
<MenuToggle
|
||||||
|
variant="primary"
|
||||||
|
ref={toggleRef}
|
||||||
|
onClick={() => setOpen(!isOpen)}
|
||||||
|
isExpanded={isOpen}
|
||||||
|
aria-label="Workspace details action toggle"
|
||||||
|
data-testid="workspace-details-action-toggle"
|
||||||
|
>
|
||||||
|
Actions
|
||||||
|
</MenuToggle>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<DropdownList>
|
||||||
|
<DropdownItem
|
||||||
|
id="workspace-details-action-edit-button"
|
||||||
|
aria-label="Edit workspace"
|
||||||
|
key="edit-workspace-button"
|
||||||
|
onClick={onEditClick}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem
|
||||||
|
id="workspace-details-action-delete-button"
|
||||||
|
aria-label="Delete workspace"
|
||||||
|
key="delete-workspace-button"
|
||||||
|
onClick={onDeleteClick}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</DropdownItem>
|
||||||
|
</DropdownList>
|
||||||
|
</Dropdown>
|
||||||
|
</FlexItem>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
DescriptionList,
|
||||||
|
DescriptionListTerm,
|
||||||
|
DescriptionListGroup,
|
||||||
|
DescriptionListDescription,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
import { Workspace } from '~/shared/types';
|
||||||
|
|
||||||
|
type WorkspaceDetailsOverviewProps = {
|
||||||
|
workspace: Workspace;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WorkspaceDetailsOverview: React.FunctionComponent<WorkspaceDetailsOverviewProps> = ({
|
||||||
|
workspace,
|
||||||
|
}) => (
|
||||||
|
<DescriptionList>
|
||||||
|
<DescriptionListGroup>
|
||||||
|
<DescriptionListTerm>Name</DescriptionListTerm>
|
||||||
|
<DescriptionListDescription>{workspace.name}</DescriptionListDescription>
|
||||||
|
</DescriptionListGroup>
|
||||||
|
<DescriptionListGroup>
|
||||||
|
<DescriptionListTerm>Kind</DescriptionListTerm>
|
||||||
|
<DescriptionListDescription>{workspace.kind}</DescriptionListDescription>
|
||||||
|
</DescriptionListGroup>
|
||||||
|
<DescriptionListGroup>
|
||||||
|
<DescriptionListTerm>Labels</DescriptionListTerm>
|
||||||
|
<DescriptionListDescription>
|
||||||
|
{workspace.podTemplate.podMetadata.labels.join(', ')}
|
||||||
|
</DescriptionListDescription>
|
||||||
|
</DescriptionListGroup>
|
||||||
|
<DescriptionListGroup>
|
||||||
|
<DescriptionListTerm>Pod config</DescriptionListTerm>
|
||||||
|
<DescriptionListDescription>{workspace.options.podConfig}</DescriptionListDescription>
|
||||||
|
</DescriptionListGroup>
|
||||||
|
</DescriptionList>
|
||||||
|
);
|
|
@ -1,5 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerContentBody,
|
||||||
PageSection,
|
PageSection,
|
||||||
MenuToggle,
|
MenuToggle,
|
||||||
TimestampTooltipVariant,
|
TimestampTooltipVariant,
|
||||||
|
@ -36,7 +39,7 @@ import {
|
||||||
} from '@patternfly/react-table';
|
} from '@patternfly/react-table';
|
||||||
import { FilterIcon } from '@patternfly/react-icons';
|
import { FilterIcon } from '@patternfly/react-icons';
|
||||||
import { Workspace, WorkspacesColumnNames, WorkspaceState } from '~/shared/types';
|
import { Workspace, WorkspacesColumnNames, WorkspaceState } from '~/shared/types';
|
||||||
|
import { WorkspaceDetails } from '~/app/pages/Workspaces/Details/WorkspaceDetails';
|
||||||
import { ExpandedWorkspaceRow } from '~/app/pages/Workspaces/ExpandedWorkspaceRow';
|
import { ExpandedWorkspaceRow } from '~/app/pages/Workspaces/ExpandedWorkspaceRow';
|
||||||
import { formatRam } from 'shared/utilities/WorkspaceResources';
|
import { formatRam } from 'shared/utilities/WorkspaceResources';
|
||||||
|
|
||||||
|
@ -52,6 +55,10 @@ export const Workspaces: React.FunctionComponent = () => {
|
||||||
cpu: 3,
|
cpu: 3,
|
||||||
ram: 500,
|
ram: 500,
|
||||||
podTemplate: {
|
podTemplate: {
|
||||||
|
podMetadata: {
|
||||||
|
labels: ['label1', 'label2'],
|
||||||
|
annotations: ['annotation1', 'annotation2'],
|
||||||
|
},
|
||||||
volumes: {
|
volumes: {
|
||||||
home: '/home',
|
home: '/home',
|
||||||
data: [
|
data: [
|
||||||
|
@ -98,6 +105,10 @@ export const Workspaces: React.FunctionComponent = () => {
|
||||||
cpu: 1,
|
cpu: 1,
|
||||||
ram: 12540,
|
ram: 12540,
|
||||||
podTemplate: {
|
podTemplate: {
|
||||||
|
podMetadata: {
|
||||||
|
labels: ['label1', 'label2'],
|
||||||
|
annotations: ['annotation1', 'annotation2'],
|
||||||
|
},
|
||||||
volumes: {
|
volumes: {
|
||||||
home: '/home',
|
home: '/home',
|
||||||
data: [
|
data: [
|
||||||
|
@ -145,6 +156,20 @@ export const Workspaces: React.FunctionComponent = () => {
|
||||||
lastActivity: 'Last Activity',
|
lastActivity: 'Last Activity',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Selected workspace
|
||||||
|
const [selectedWorkspace, setSelectedWorkspace] = React.useState<Workspace | null>(null);
|
||||||
|
|
||||||
|
const selectWorkspace = React.useCallback(
|
||||||
|
(newSelectedWorkspace) => {
|
||||||
|
if (selectedWorkspace?.name === newSelectedWorkspace?.name) {
|
||||||
|
setSelectedWorkspace(null);
|
||||||
|
} else {
|
||||||
|
setSelectedWorkspace(newSelectedWorkspace);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[selectedWorkspace],
|
||||||
|
);
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
const [activeAttributeMenu, setActiveAttributeMenu] = React.useState<string>(columnNames.name);
|
const [activeAttributeMenu, setActiveAttributeMenu] = React.useState<string>(columnNames.name);
|
||||||
const [isAttributeMenuOpen, setIsAttributeMenuOpen] = React.useState(false);
|
const [isAttributeMenuOpen, setIsAttributeMenuOpen] = React.useState(false);
|
||||||
|
@ -389,28 +414,51 @@ export const Workspaces: React.FunctionComponent = () => {
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
const defaultActions = (workspace: Workspace): IActions =>
|
const editAction = React.useCallback((workspace: Workspace) => {
|
||||||
[
|
console.log(`Clicked on edit, on row ${workspace.name}`);
|
||||||
{
|
}, []);
|
||||||
title: 'Edit',
|
|
||||||
onClick: () => console.log(`Clicked on edit, on row ${workspace.name}`),
|
const deleteAction = React.useCallback((workspace: Workspace) => {
|
||||||
},
|
console.log(`Clicked on delete, on row ${workspace.name}`);
|
||||||
{
|
}, []);
|
||||||
title: 'Delete',
|
|
||||||
onClick: () => console.log(`Clicked on delete, on row ${workspace.name}`),
|
const startRestartAction = React.useCallback((workspace: Workspace) => {
|
||||||
},
|
console.log(`Clicked on start/restart, on row ${workspace.name}`);
|
||||||
{
|
}, []);
|
||||||
isSeparator: true,
|
|
||||||
},
|
const stopAction = React.useCallback((workspace: Workspace) => {
|
||||||
{
|
console.log(`Clicked on stop, on row ${workspace.name}`);
|
||||||
title: 'Start/restart',
|
}, []);
|
||||||
onClick: () => console.log(`Clicked on start/restart, on row ${workspace.name}`),
|
|
||||||
},
|
const defaultActions = React.useCallback(
|
||||||
{
|
(workspace: Workspace): IActions =>
|
||||||
title: 'Stop',
|
[
|
||||||
onClick: () => console.log(`Clicked on stop, on row ${workspace.name}`),
|
{
|
||||||
},
|
title: 'View Details',
|
||||||
] as IActions;
|
onClick: () => selectWorkspace(workspace),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Edit',
|
||||||
|
onClick: () => editAction(workspace),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Delete',
|
||||||
|
onClick: () => deleteAction(workspace),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isSeparator: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Start/restart',
|
||||||
|
onClick: () => startRestartAction(workspace),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Stop',
|
||||||
|
onClick: () => stopAction(workspace),
|
||||||
|
},
|
||||||
|
] as IActions,
|
||||||
|
[selectWorkspace, editAction, deleteAction, startRestartAction, stopAction],
|
||||||
|
);
|
||||||
|
|
||||||
// States
|
// States
|
||||||
|
|
||||||
|
@ -447,80 +495,100 @@ export const Workspaces: React.FunctionComponent = () => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const workspaceDetailsContent = (
|
||||||
|
<>
|
||||||
|
{selectedWorkspace && (
|
||||||
|
<WorkspaceDetails
|
||||||
|
workspace={selectedWorkspace}
|
||||||
|
onCloseClick={() => selectWorkspace(null)}
|
||||||
|
onEditClick={() => editAction(selectedWorkspace)}
|
||||||
|
onDeleteClick={() => deleteAction(selectedWorkspace)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<Drawer isExpanded={selectedWorkspace != null}>
|
||||||
<Title headingLevel="h1">Kubeflow Workspaces</Title>
|
<DrawerContent panelContent={workspaceDetailsContent}>
|
||||||
<p>View your existing workspaces or create new workspaces.</p>
|
<DrawerContentBody>
|
||||||
{toolbar}
|
<PageSection isFilled>
|
||||||
<Table aria-label="Sortable table" ouiaId="SortableTable">
|
<Title headingLevel="h1">Kubeflow Workspaces</Title>
|
||||||
<Thead>
|
<p>View your existing workspaces or create new workspaces.</p>
|
||||||
<Tr>
|
{toolbar}
|
||||||
<Th />
|
<Table aria-label="Sortable table" ouiaId="SortableTable">
|
||||||
<Th sort={getSortParams(0)}>{columnNames.name}</Th>
|
<Thead>
|
||||||
<Th sort={getSortParams(1)}>{columnNames.kind}</Th>
|
<Tr>
|
||||||
<Th sort={getSortParams(2)}>{columnNames.image}</Th>
|
<Th />
|
||||||
<Th sort={getSortParams(3)}>{columnNames.podConfig}</Th>
|
<Th sort={getSortParams(0)}>{columnNames.name}</Th>
|
||||||
<Th sort={getSortParams(4)}>{columnNames.state}</Th>
|
<Th sort={getSortParams(1)}>{columnNames.kind}</Th>
|
||||||
<Th sort={getSortParams(5)}>{columnNames.homeVol}</Th>
|
<Th sort={getSortParams(2)}>{columnNames.image}</Th>
|
||||||
<Th sort={getSortParams(6)} info={{ tooltip: 'Workspace CPU usage' }}>
|
<Th sort={getSortParams(3)}>{columnNames.podConfig}</Th>
|
||||||
{columnNames.cpu}
|
<Th sort={getSortParams(4)}>{columnNames.state}</Th>
|
||||||
</Th>
|
<Th sort={getSortParams(5)}>{columnNames.homeVol}</Th>
|
||||||
<Th sort={getSortParams(7)} info={{ tooltip: 'Workspace memory usage' }}>
|
<Th sort={getSortParams(6)} info={{ tooltip: 'Workspace CPU usage' }}>
|
||||||
{columnNames.ram}
|
{columnNames.cpu}
|
||||||
</Th>
|
</Th>
|
||||||
<Th sort={getSortParams(8)}>{columnNames.lastActivity}</Th>
|
<Th sort={getSortParams(7)} info={{ tooltip: 'Workspace memory usage' }}>
|
||||||
<Th screenReaderText="Primary action" />
|
{columnNames.ram}
|
||||||
</Tr>
|
</Th>
|
||||||
</Thead>
|
<Th sort={getSortParams(8)}>{columnNames.lastActivity}</Th>
|
||||||
{sortedWorkspaces.map((workspace, rowIndex) => (
|
<Th screenReaderText="Primary action" />
|
||||||
<Tbody key={rowIndex} isExpanded={isWorkspaceExpanded(workspace)}>
|
</Tr>
|
||||||
<Tr>
|
</Thead>
|
||||||
<Td
|
{sortedWorkspaces.map((workspace, rowIndex) => (
|
||||||
expand={{
|
<Tbody key={rowIndex} isExpanded={isWorkspaceExpanded(workspace)}>
|
||||||
rowIndex,
|
<Tr>
|
||||||
isExpanded: isWorkspaceExpanded(workspace),
|
<Td
|
||||||
onToggle: () => setWorkspaceExpanded(workspace, !isWorkspaceExpanded(workspace)),
|
expand={{
|
||||||
}}
|
rowIndex,
|
||||||
/>
|
isExpanded: isWorkspaceExpanded(workspace),
|
||||||
<Td dataLabel={columnNames.name}>{workspace.name}</Td>
|
onToggle: () =>
|
||||||
<Td dataLabel={columnNames.kind}>{workspace.kind}</Td>
|
setWorkspaceExpanded(workspace, !isWorkspaceExpanded(workspace)),
|
||||||
<Td dataLabel={columnNames.image}>{workspace.options.imageConfig}</Td>
|
}}
|
||||||
<Td dataLabel={columnNames.podConfig}>{workspace.options.podConfig}</Td>
|
/>
|
||||||
<Td dataLabel={columnNames.state}>
|
<Td dataLabel={columnNames.name}>{workspace.name}</Td>
|
||||||
<Label color={stateColors[workspace.status.state]}>
|
<Td dataLabel={columnNames.kind}>{workspace.kind}</Td>
|
||||||
{WorkspaceState[workspace.status.state]}
|
<Td dataLabel={columnNames.image}>{workspace.options.imageConfig}</Td>
|
||||||
</Label>
|
<Td dataLabel={columnNames.podConfig}>{workspace.options.podConfig}</Td>
|
||||||
</Td>
|
<Td dataLabel={columnNames.state}>
|
||||||
<Td dataLabel={columnNames.homeVol}>{workspace.podTemplate.volumes.home}</Td>
|
<Label color={stateColors[workspace.status.state]}>
|
||||||
<Td dataLabel={columnNames.cpu}>{`${workspace.cpu}%`}</Td>
|
{WorkspaceState[workspace.status.state]}
|
||||||
<Td dataLabel={columnNames.ram}>{formatRam(workspace.ram)}</Td>
|
</Label>
|
||||||
<Td dataLabel={columnNames.lastActivity}>
|
</Td>
|
||||||
<Timestamp
|
<Td dataLabel={columnNames.homeVol}>{workspace.podTemplate.volumes.home}</Td>
|
||||||
date={new Date(workspace.status.activity.lastActivity)}
|
<Td dataLabel={columnNames.cpu}>{`${workspace.cpu}%`}</Td>
|
||||||
tooltip={{ variant: TimestampTooltipVariant.default }}
|
<Td dataLabel={columnNames.ram}>{formatRam(workspace.ram)}</Td>
|
||||||
>
|
<Td dataLabel={columnNames.lastActivity}>
|
||||||
1 hour ago
|
<Timestamp
|
||||||
</Timestamp>
|
date={new Date(workspace.status.activity.lastActivity)}
|
||||||
</Td>
|
tooltip={{ variant: TimestampTooltipVariant.default }}
|
||||||
<Td isActionCell>
|
>
|
||||||
<ActionsColumn items={defaultActions(workspace)} />
|
1 hour ago
|
||||||
</Td>
|
</Timestamp>
|
||||||
</Tr>
|
</Td>
|
||||||
{isWorkspaceExpanded(workspace) && (
|
<Td isActionCell>
|
||||||
<ExpandedWorkspaceRow workspace={workspace} columnNames={columnNames} />
|
<ActionsColumn items={defaultActions(workspace)} />
|
||||||
)}
|
</Td>
|
||||||
</Tbody>
|
</Tr>
|
||||||
))}
|
{isWorkspaceExpanded(workspace) && (
|
||||||
</Table>
|
<ExpandedWorkspaceRow workspace={workspace} columnNames={columnNames} />
|
||||||
<Pagination
|
)}
|
||||||
itemCount={333}
|
</Tbody>
|
||||||
widgetId="bottom-example"
|
))}
|
||||||
perPage={perPage}
|
</Table>
|
||||||
page={page}
|
<Pagination
|
||||||
variant={PaginationVariant.bottom}
|
itemCount={333}
|
||||||
onSetPage={onSetPage}
|
widgetId="bottom-example"
|
||||||
onPerPageSelect={onPerPageSelect}
|
perPage={perPage}
|
||||||
/>
|
page={page}
|
||||||
</PageSection>
|
variant={PaginationVariant.bottom}
|
||||||
|
onSetPage={onSetPage}
|
||||||
|
onPerPageSelect={onPerPageSelect}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
|
</DrawerContentBody>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,6 +55,10 @@ export interface Workspace {
|
||||||
cpu: number;
|
cpu: number;
|
||||||
ram: number;
|
ram: number;
|
||||||
podTemplate: {
|
podTemplate: {
|
||||||
|
podMetadata: {
|
||||||
|
labels: string[];
|
||||||
|
annotations: string[];
|
||||||
|
};
|
||||||
volumes: {
|
volumes: {
|
||||||
home: string;
|
home: string;
|
||||||
data: {
|
data: {
|
||||||
|
|
Loading…
Reference in New Issue