feat(ws): add initial workspace creation wizard frontend (#227)
* feat(ws): add initial workspace creation wizard frontend Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com> * feat(ws): add initial workspace creation wizard frontend Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com> * card view style fixes (#2) Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> * fix(ws): fix scroll behavior with PageGroup Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> * feat(ws): add initial workspace creation wizard frontend Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com> * fix(ws): Apply flex-grow: 0 to page section Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> --------- Signed-off-by: paulovmr <832830+paulovmr@users.noreply.github.com> Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> Co-authored-by: Jenny <32821331+jenny-s51@users.noreply.github.com>
This commit is contained in:
parent
19eca50561
commit
2bc10ecc20
|
@ -138,6 +138,16 @@ module.exports = (env) => {
|
|||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
],
|
||||
include: [
|
||||
path.resolve(relativeDir, 'node_modules/@patternfly/react-catalog-view-extension/dist/css/react-catalog-view-extension.css'),
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
output: {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"version": "0.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@patternfly/react-catalog-view-extension": "^6.0.0",
|
||||
"@patternfly/react-code-editor": "^6.0.0",
|
||||
"@patternfly/react-core": "^6.0.0",
|
||||
"@patternfly/react-icons": "^6.0.0",
|
||||
|
@ -4039,6 +4040,20 @@
|
|||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-catalog-view-extension": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@patternfly/react-catalog-view-extension/-/react-catalog-view-extension-6.0.0.tgz",
|
||||
"integrity": "sha512-AfvfJXjADXX1YZnWNZLe0mLgBepSnn4Xn9SQsvTeS+PLFFGgJQEsbhRyFXdGVJLVU/19+a/YOpHgZZeI2dus0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@patternfly/react-core": "^6.0.0",
|
||||
"@patternfly/react-styles": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17 || ^18",
|
||||
"react-dom": "^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-code-editor": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.1.0.tgz",
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@patternfly/react-code-editor": "^6.0.0",
|
||||
"@patternfly/react-catalog-view-extension": "^6.0.0",
|
||||
"@patternfly/react-core": "^6.0.0",
|
||||
"@patternfly/react-icons": "^6.0.0",
|
||||
"@patternfly/react-styles": "^6.0.0",
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Route, Routes } from 'react-router-dom';
|
|||
import { NotFound } from './pages/notFound/NotFound';
|
||||
import { Debug } from './pages/Debug/Debug';
|
||||
import { Workspaces } from './pages/Workspaces/Workspaces';
|
||||
import { WorkspaceCreation } from './pages/Workspaces/Creation/WorkspaceCreation';
|
||||
import '~/shared/style/MUI-theme.scss';
|
||||
|
||||
export const isNavDataGroup = (navItem: NavDataItem): navItem is NavDataGroup =>
|
||||
|
@ -43,7 +44,7 @@ export const useAdminDebugSettings = (): NavDataItem[] => {
|
|||
export const useNavData = (): NavDataItem[] => [
|
||||
{
|
||||
label: 'Notebooks',
|
||||
path: '/',
|
||||
path: '/workspaces',
|
||||
},
|
||||
...useAdminDebugSettings(),
|
||||
];
|
||||
|
@ -53,6 +54,8 @@ const AppRoutes: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/workspaces/create" element={<WorkspaceCreation />} />
|
||||
<Route path="/workspaces" element={<Workspaces />} />
|
||||
<Route path="/" element={<Workspaces />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
{
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Content,
|
||||
Flex,
|
||||
FlexItem,
|
||||
PageGroup,
|
||||
PageSection,
|
||||
ProgressStep,
|
||||
ProgressStepper,
|
||||
} from '@patternfly/react-core';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { WorkspaceCreationImageSelection } from '~/app/pages/Workspaces/Creation/WorkspaceCreationImageSelection';
|
||||
import { WorkspaceCreationKindSelection } from '~/app/pages/Workspaces/Creation/WorkspaceCreationKindSelection';
|
||||
import { WorkspaceCreationPropertiesSelection } from '~/app/pages/Workspaces/Creation/WorkspaceCreationPropertiesSelection';
|
||||
|
||||
enum WorkspaceCreationSteps {
|
||||
KindSelection,
|
||||
ImageSelection,
|
||||
Properties,
|
||||
}
|
||||
|
||||
const WorkspaceCreation: React.FunctionComponent = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [currentStep, setCurrentStep] = useState(WorkspaceCreationSteps.KindSelection);
|
||||
|
||||
const getStepVariant = useCallback(
|
||||
(step: WorkspaceCreationSteps) => {
|
||||
if (step > currentStep) {
|
||||
return 'pending';
|
||||
}
|
||||
if (step < currentStep) {
|
||||
return 'success';
|
||||
}
|
||||
return 'info';
|
||||
},
|
||||
[currentStep],
|
||||
);
|
||||
|
||||
const previousStep = useCallback(() => {
|
||||
setCurrentStep(currentStep - 1);
|
||||
}, [currentStep]);
|
||||
|
||||
const nextStep = useCallback(() => {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}, [currentStep]);
|
||||
|
||||
const cancel = useCallback(() => {
|
||||
navigate('/workspaces');
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageGroup stickyOnBreakpoint={{ default: 'top' }}>
|
||||
<PageSection isFilled={false}>
|
||||
<Content>
|
||||
<h1>Create workspace</h1>
|
||||
</Content>
|
||||
<ProgressStepper aria-label="Workspace creation stepper">
|
||||
<ProgressStep
|
||||
variant={getStepVariant(WorkspaceCreationSteps.KindSelection)}
|
||||
id="kind-selection-step"
|
||||
titleId="kind-selection-step-title"
|
||||
aria-label="Kind selection step"
|
||||
>
|
||||
Kind selection
|
||||
</ProgressStep>
|
||||
<ProgressStep
|
||||
variant={getStepVariant(WorkspaceCreationSteps.ImageSelection)}
|
||||
isCurrent
|
||||
id="image-selection-step"
|
||||
titleId="image-selection-step-title"
|
||||
aria-label="Image selection step"
|
||||
>
|
||||
Image selection
|
||||
</ProgressStep>
|
||||
<ProgressStep
|
||||
variant={getStepVariant(WorkspaceCreationSteps.Properties)}
|
||||
id="properties-step"
|
||||
titleId="properties-step-title"
|
||||
aria-label="Properties step"
|
||||
>
|
||||
Properties
|
||||
</ProgressStep>
|
||||
</ProgressStepper>
|
||||
</PageSection>
|
||||
</PageGroup>
|
||||
<PageSection isFilled>
|
||||
{currentStep === WorkspaceCreationSteps.KindSelection && <WorkspaceCreationKindSelection />}
|
||||
{currentStep === WorkspaceCreationSteps.ImageSelection && (
|
||||
<WorkspaceCreationImageSelection />
|
||||
)}
|
||||
{currentStep === WorkspaceCreationSteps.Properties && (
|
||||
<WorkspaceCreationPropertiesSelection />
|
||||
)}
|
||||
</PageSection>
|
||||
<PageSection isFilled={false} stickyOnBreakpoint={{ default: 'bottom' }}>
|
||||
<Flex>
|
||||
<FlexItem>
|
||||
<Button
|
||||
variant="primary"
|
||||
ouiaId="Primary"
|
||||
onClick={previousStep}
|
||||
isDisabled={currentStep === 0}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
</FlexItem>
|
||||
<FlexItem>
|
||||
<Button
|
||||
variant="primary"
|
||||
ouiaId="Primary"
|
||||
onClick={nextStep}
|
||||
isDisabled={currentStep === Object.keys(WorkspaceCreationSteps).length / 2 - 1}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</FlexItem>
|
||||
<FlexItem>
|
||||
<Button variant="link" isInline onClick={cancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</FlexItem>
|
||||
</Flex>
|
||||
</PageSection>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { WorkspaceCreation };
|
|
@ -0,0 +1,10 @@
|
|||
import * as React from 'react';
|
||||
import { Content } from '@patternfly/react-core';
|
||||
|
||||
const WorkspaceCreationImageSelection: React.FunctionComponent = () => (
|
||||
<Content>
|
||||
<p>Select a workspace image and image version to use for the workspace.</p>
|
||||
</Content>
|
||||
);
|
||||
|
||||
export { WorkspaceCreationImageSelection };
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import { Title } from '@patternfly/react-core';
|
||||
import { WorkspaceKind } from '~/shared/types';
|
||||
|
||||
type WorkspaceCreationKindDetailsProps = {
|
||||
workspaceKind?: WorkspaceKind;
|
||||
};
|
||||
|
||||
export const WorkspaceCreationKindDetails: React.FunctionComponent<
|
||||
WorkspaceCreationKindDetailsProps
|
||||
> = ({ workspaceKind }) => (
|
||||
<>
|
||||
{!workspaceKind && <p>Select a workspace kind to view its details here.</p>}
|
||||
|
||||
{workspaceKind && (
|
||||
<>
|
||||
<Title headingLevel="h6">Workspace kind</Title>
|
||||
<Title headingLevel="h3">{workspaceKind.name}</Title>
|
||||
<p>{workspaceKind.description}</p>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
|
@ -0,0 +1,131 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
CardBody,
|
||||
CardTitle,
|
||||
Gallery,
|
||||
PageSection,
|
||||
Toolbar,
|
||||
ToolbarContent,
|
||||
Card,
|
||||
CardHeader,
|
||||
EmptyState,
|
||||
EmptyStateBody,
|
||||
} from '@patternfly/react-core';
|
||||
import { SearchIcon } from '@patternfly/react-icons/dist/esm/icons/search-icon';
|
||||
import { WorkspaceKind } from '~/shared/types';
|
||||
import Filter, { FilteredColumn } from '~/shared/components/Filter';
|
||||
|
||||
type WorkspaceCreationKindListProps = {
|
||||
allWorkspaceKinds: WorkspaceKind[];
|
||||
onSelect: (workspaceKind: WorkspaceKind) => void;
|
||||
};
|
||||
|
||||
export const WorkspaceCreationKindList: React.FunctionComponent<WorkspaceCreationKindListProps> = ({
|
||||
allWorkspaceKinds,
|
||||
onSelect,
|
||||
}) => {
|
||||
const [workspaceKinds, setWorkspaceKinds] = useState<WorkspaceKind[]>(allWorkspaceKinds);
|
||||
const [selectedWorkspaceKind, setSelectedWorkspaceKind] = useState<WorkspaceKind>();
|
||||
|
||||
const filterableColumns = useMemo(
|
||||
() => ({
|
||||
name: 'Name',
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const onFilter = useCallback(
|
||||
(filters: FilteredColumn[]) => {
|
||||
// Search name with search value
|
||||
let filteredWorkspaceKinds = allWorkspaceKinds;
|
||||
filters.forEach((filter) => {
|
||||
let searchValueInput: RegExp;
|
||||
try {
|
||||
searchValueInput = new RegExp(filter.value, 'i');
|
||||
} catch {
|
||||
searchValueInput = new RegExp(filter.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
|
||||
}
|
||||
|
||||
filteredWorkspaceKinds = filteredWorkspaceKinds.filter((kind) => {
|
||||
if (filter.value === '') {
|
||||
return true;
|
||||
}
|
||||
switch (filter.columnName) {
|
||||
case filterableColumns.name:
|
||||
return (
|
||||
kind.name.search(searchValueInput) >= 0 ||
|
||||
kind.displayName.search(searchValueInput) >= 0
|
||||
);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
setWorkspaceKinds(filteredWorkspaceKinds);
|
||||
},
|
||||
[filterableColumns, allWorkspaceKinds],
|
||||
);
|
||||
|
||||
const onChange = useCallback(
|
||||
(event: React.FormEvent<HTMLInputElement>) => {
|
||||
const newSelectedWorkspaceKind = workspaceKinds.find(
|
||||
(kind) => kind.name === event.currentTarget.name,
|
||||
);
|
||||
setSelectedWorkspaceKind(newSelectedWorkspaceKind);
|
||||
onSelect(newSelectedWorkspaceKind);
|
||||
},
|
||||
[workspaceKinds, onSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageSection>
|
||||
<Toolbar id="toolbar-group-types">
|
||||
<ToolbarContent>
|
||||
<Filter
|
||||
id="filter-workspace-kinds"
|
||||
onFilter={onFilter}
|
||||
columnNames={filterableColumns}
|
||||
/>
|
||||
</ToolbarContent>
|
||||
</Toolbar>
|
||||
</PageSection>
|
||||
<PageSection isFilled>
|
||||
{workspaceKinds.length === 0 && (
|
||||
<EmptyState titleText="No results found" headingLevel="h4" icon={SearchIcon}>
|
||||
<EmptyStateBody>
|
||||
No results match the filter criteria. Clear all filters and try again.
|
||||
</EmptyStateBody>
|
||||
</EmptyState>
|
||||
)}
|
||||
{workspaceKinds.length > 0 && (
|
||||
<Gallery hasGutter aria-label="Selectable card container">
|
||||
{workspaceKinds.map((kind) => (
|
||||
<Card
|
||||
isCompact
|
||||
isSelectable
|
||||
key={kind.name}
|
||||
id={kind.name.replace(/ /g, '-')}
|
||||
isSelected={kind.name === selectedWorkspaceKind?.name}
|
||||
>
|
||||
<CardHeader
|
||||
selectableActions={{
|
||||
selectableActionId: `selectable-actions-item-${kind.name}`,
|
||||
selectableActionAriaLabelledby: kind.name.replace(/ /g, '-'),
|
||||
name: kind.name,
|
||||
variant: 'single',
|
||||
onChange,
|
||||
}}
|
||||
>
|
||||
<img src={kind.icon.url} alt={`${kind.name} icon`} style={{ maxWidth: '60px' }} />
|
||||
<CardTitle>{kind.displayName}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>{kind.description}</CardBody>
|
||||
</Card>
|
||||
))}
|
||||
</Gallery>
|
||||
)}
|
||||
</PageSection>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,131 @@
|
|||
import * as React from 'react';
|
||||
import { Content, Divider, Split, SplitItem } from '@patternfly/react-core';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { WorkspaceKind } from '~/shared/types';
|
||||
import { WorkspaceCreationKindDetails } from '~/app/pages/Workspaces/Creation/WorkspaceCreationKindDetails';
|
||||
import { WorkspaceCreationKindList } from '~/app/pages/Workspaces/Creation/WorkspaceCreationKindList';
|
||||
|
||||
const WorkspaceCreationKindSelection: React.FunctionComponent = () => {
|
||||
/* Replace mocks below for BFF call */
|
||||
const mockedWorkspaceKind: WorkspaceKind = useMemo(
|
||||
() => ({
|
||||
name: 'jupyter-lab1',
|
||||
displayName: 'JupyterLab Notebook',
|
||||
description: 'A Workspace which runs JupyterLab in a Pod',
|
||||
deprecated: false,
|
||||
deprecationMessage: '',
|
||||
hidden: false,
|
||||
icon: {
|
||||
url: 'https://jupyter.org/assets/favicons/apple-touch-icon-152x152.png',
|
||||
},
|
||||
logo: {
|
||||
url: 'https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg',
|
||||
},
|
||||
podTemplate: {
|
||||
podMetadata: {
|
||||
labels: { myWorkspaceKindLabel: 'my-value' },
|
||||
annotations: { myWorkspaceKindAnnotation: 'my-value' },
|
||||
},
|
||||
volumeMounts: { home: '/home/jovyan' },
|
||||
options: {
|
||||
imageConfig: {
|
||||
default: 'jupyterlab_scipy_190',
|
||||
values: [
|
||||
{
|
||||
id: 'jupyterlab_scipy_180',
|
||||
displayName: 'jupyter-scipy:v1.8.0',
|
||||
labels: { pythonVersion: '3.11' },
|
||||
hidden: true,
|
||||
redirect: {
|
||||
to: 'jupyterlab_scipy_190',
|
||||
message: {
|
||||
text: 'This update will change...',
|
||||
level: 'Info',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'jupyterlab_scipy_190',
|
||||
displayName: 'jupyter-scipy:v1.9.0',
|
||||
labels: { pythonVersion: '3.11' },
|
||||
hidden: true,
|
||||
redirect: {
|
||||
to: 'jupyterlab_scipy_200',
|
||||
message: {
|
||||
text: 'This update will change...',
|
||||
level: 'Warning',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
podConfig: {
|
||||
default: 'tiny_cpu',
|
||||
values: [
|
||||
{
|
||||
id: 'tiny_cpu',
|
||||
displayName: 'Tiny CPU',
|
||||
description: 'Pod with 0.1 CPU, 128 Mb RAM',
|
||||
labels: { cpu: '100m', memory: '128Mi' },
|
||||
redirect: {
|
||||
to: 'small_cpu',
|
||||
message: {
|
||||
text: 'This update will change...',
|
||||
level: 'Danger',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
/* Replace mocks below for BFF call */
|
||||
const allWorkspaceKinds = useMemo(() => {
|
||||
const kinds: WorkspaceKind[] = [];
|
||||
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
const kind = { ...mockedWorkspaceKind };
|
||||
kind.name += i;
|
||||
kind.displayName += ` ${i}`;
|
||||
kind.podTemplate = { ...mockedWorkspaceKind.podTemplate };
|
||||
kind.podTemplate.podMetadata = { ...mockedWorkspaceKind.podTemplate.podMetadata };
|
||||
kind.podTemplate.podMetadata.labels = {
|
||||
...mockedWorkspaceKind.podTemplate.podMetadata.labels,
|
||||
};
|
||||
kind.podTemplate.podMetadata.labels[`my-label-key-${Math.ceil(i / 4)}`] =
|
||||
`my-label-value-${Math.ceil(i)}`;
|
||||
kinds.push(kind);
|
||||
}
|
||||
|
||||
return kinds;
|
||||
}, [mockedWorkspaceKind]);
|
||||
|
||||
const [selectedKind, setSelectedKind] = useState<WorkspaceKind>();
|
||||
|
||||
const kindDetailsContent = useMemo(
|
||||
() => <WorkspaceCreationKindDetails workspaceKind={selectedKind} />,
|
||||
[selectedKind],
|
||||
);
|
||||
|
||||
return (
|
||||
<Content style={{ height: '100%' }}>
|
||||
<p>Select a workspace kind to use for the workspace.</p>
|
||||
<Divider />
|
||||
<Split hasGutter>
|
||||
<SplitItem isFilled>
|
||||
<WorkspaceCreationKindList
|
||||
allWorkspaceKinds={allWorkspaceKinds}
|
||||
onSelect={(workspaceKind) => setSelectedKind(workspaceKind)}
|
||||
/>
|
||||
</SplitItem>
|
||||
<SplitItem style={{ minWidth: '200px' }}>{kindDetailsContent}</SplitItem>
|
||||
</Split>
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
|
||||
export { WorkspaceCreationKindSelection };
|
|
@ -0,0 +1,10 @@
|
|||
import * as React from 'react';
|
||||
import { Content } from '@patternfly/react-core';
|
||||
|
||||
const WorkspaceCreationPropertiesSelection: React.FunctionComponent = () => (
|
||||
<Content>
|
||||
<p>Configure properties for your workspace.</p>
|
||||
</Content>
|
||||
);
|
||||
|
||||
export { WorkspaceCreationPropertiesSelection };
|
|
@ -32,7 +32,8 @@ import {
|
|||
QuestionCircleIcon,
|
||||
CodeIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Workspace, WorkspacesColumnNames, WorkspaceState } from '~/shared/types';
|
||||
import { WorkspaceDetails } from '~/app/pages/Workspaces/Details/WorkspaceDetails';
|
||||
import { ExpandedWorkspaceRow } from '~/app/pages/Workspaces/ExpandedWorkspaceRow';
|
||||
|
@ -182,6 +183,11 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
},
|
||||
];
|
||||
|
||||
const navigate = useNavigate();
|
||||
const createWorkspace = useCallback(() => {
|
||||
navigate('/workspaces/create');
|
||||
}, [navigate]);
|
||||
|
||||
const [workspaceKinds] = useWorkspaceKinds();
|
||||
let kindLogoDict: Record<string, string> = {};
|
||||
kindLogoDict = buildKindLogoDictionary(workspaceKinds);
|
||||
|
@ -554,7 +560,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
<br />
|
||||
<Content style={{ display: 'flex', alignItems: 'flex-start', columnGap: '20px' }}>
|
||||
<Filter id="filter-workspaces" onFilter={onFilter} columnNames={filterableColumns} />
|
||||
<Button variant="primary" ouiaId="Primary">
|
||||
<Button variant="primary" ouiaId="Primary" onClick={createWorkspace}>
|
||||
Create Workspace
|
||||
</Button>
|
||||
</Content>
|
||||
|
|
|
@ -104,7 +104,6 @@
|
|||
|
||||
--pf-t--global--border--width--box--status--default: 1px;
|
||||
--pf-t--global--border--radius--pill: var(--mui-shape-borderRadius);
|
||||
--pf-t--global--border--radius--medium: var(--mui-shape-borderRadius);
|
||||
--pf-t--global--text--color--brand--default: var(--mui-palette-primary-main);
|
||||
|
||||
--pf-t--global--color--nonstatus--blue--default: var(--mui-palette-primary-main);
|
||||
|
@ -545,48 +544,6 @@
|
|||
row-gap: none;
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-radio {
|
||||
--pf-v6-c-radio--AccentColor: var(--mui-palette-primary-main);
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-radio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: var(--mui-radio--margin);
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-radio__input {
|
||||
/* Hide default radio button */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-radio__label {
|
||||
--pf-v6-c-radio__label--FontSize: 16px;
|
||||
padding-left: var(--mui-radio-PaddingLeft);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Custom radio circle */
|
||||
.mui-theme .pf-v6-c-radio__label::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: var(--mui-radio__label--Width);
|
||||
height: var(--mui-radio__label--Height);
|
||||
border: 2px solid var(--mui-palette-primary-main);
|
||||
border-radius: 50%;
|
||||
background: var(--mui-palette-common-white);
|
||||
}
|
||||
|
||||
/* When radio is checked */
|
||||
.mui-theme .pf-v6-c-radio__input:checked+.pf-v6-c-radio__label::before {
|
||||
background: var(--mui-palette-common-white);
|
||||
border-color: var(--mui-palette-primary-main);
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-page__sidebar {
|
||||
--pf-v6-c-page__sidebar--BackgroundColor: var(--kf-central-primary-background-color);
|
||||
|
@ -598,20 +555,6 @@
|
|||
|
||||
}
|
||||
|
||||
/* Inner dot for checked state */
|
||||
.mui-theme .pf-v6-c-radio__input:checked+.pf-v6-c-radio__label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
/* Center the dot */
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: var(--mui-radio__input--Width);
|
||||
/* Size of inner dot */
|
||||
height: var(--mui-radio__input--Height);
|
||||
border-radius: 50%;
|
||||
background: var(--mui-palette-primary-main);
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-table {
|
||||
--pf-v6-c-table__sort--m-selected__button--Color: var(--mui-palette-text-primary);
|
||||
|
@ -789,6 +732,10 @@
|
|||
box-shadow: var(--mui-shadows-1);
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-page__main-group.pf-m-sticky-top {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.mui-theme .pf-v6-c-pagination {
|
||||
--pf-v6-c-pagination__total-items--Display: block;
|
||||
}
|
||||
|
|
|
@ -30,37 +30,40 @@ export interface WorkspaceKind {
|
|||
options: {
|
||||
imageConfig: {
|
||||
default: string;
|
||||
values: [
|
||||
{
|
||||
id: string;
|
||||
displayName: string;
|
||||
labels: {
|
||||
pythonVersion: string;
|
||||
values: {
|
||||
id: string;
|
||||
displayName: string;
|
||||
labels: {
|
||||
pythonVersion: string;
|
||||
};
|
||||
hidden: boolean;
|
||||
redirect?: {
|
||||
to: string;
|
||||
message: {
|
||||
text: string;
|
||||
level: string;
|
||||
};
|
||||
hidden: true;
|
||||
redirect?: {
|
||||
to: string;
|
||||
message: {
|
||||
text: string;
|
||||
level: string;
|
||||
};
|
||||
};
|
||||
},
|
||||
];
|
||||
};
|
||||
}[];
|
||||
};
|
||||
podConfig: {
|
||||
default: string;
|
||||
values: [
|
||||
{
|
||||
id: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
labels: {
|
||||
cpu: string;
|
||||
memory: string;
|
||||
values: {
|
||||
id: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
labels: {
|
||||
cpu: string;
|
||||
memory: string;
|
||||
};
|
||||
redirect?: {
|
||||
to: string;
|
||||
message: {
|
||||
text: string;
|
||||
level: string;
|
||||
};
|
||||
},
|
||||
];
|
||||
};
|
||||
}[];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue