Move form view to edit mode only
Signed-off-by: Charles Thao <cthao@redhat.com>
This commit is contained in:
parent
df265d4888
commit
51a7fbec69
|
|
@ -68,6 +68,7 @@ const AppRoutes: React.FC = () => {
|
||||||
<Route path={AppRoutePaths.workspaceKindSummary} element={<WorkspaceKindSummaryWrapper />} />
|
<Route path={AppRoutePaths.workspaceKindSummary} element={<WorkspaceKindSummaryWrapper />} />
|
||||||
<Route path={AppRoutePaths.workspaceKinds} element={<WorkspaceKinds />} />
|
<Route path={AppRoutePaths.workspaceKinds} element={<WorkspaceKinds />} />
|
||||||
<Route path={AppRoutePaths.workspaceKindCreate} element={<WorkspaceKindForm />} />
|
<Route path={AppRoutePaths.workspaceKindCreate} element={<WorkspaceKindForm />} />
|
||||||
|
<Route path={AppRoutePaths.workspaceKindEdit} element={<WorkspaceKindForm />} />
|
||||||
<Route path="/" element={<Navigate to={AppRoutePaths.workspaces} replace />} />
|
<Route path="/" element={<Navigate to={AppRoutePaths.workspaces} replace />} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,17 @@ import { useCurrentRouteKey } from '~/app/hooks/useCurrentRouteKey';
|
||||||
import { useTypedLocation } from '~/app/routerHelper';
|
import { useTypedLocation } from '~/app/routerHelper';
|
||||||
import { AppRouteKey, RouteStateMap } from '~/app/routes';
|
import { AppRouteKey, RouteStateMap } from '~/app/routes';
|
||||||
|
|
||||||
type WorkspaceFormLocationState = RouteStateMap['workspaceEdit'] | RouteStateMap['workspaceCreate'];
|
type WorkspaceFormLocationState =
|
||||||
|
| RouteStateMap['workspaceEdit']
|
||||||
|
| RouteStateMap['workspaceCreate']
|
||||||
|
| RouteStateMap['workspaceKindEdit']
|
||||||
|
| RouteStateMap['workspaceKindCreate'];
|
||||||
|
|
||||||
interface WorkspaceFormLocationData {
|
interface WorkspaceFormLocationData {
|
||||||
mode: 'edit' | 'create';
|
mode: 'edit' | 'create';
|
||||||
namespace: string;
|
namespace: string;
|
||||||
workspaceName?: string;
|
workspaceName?: string;
|
||||||
|
workspaceKindName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouteStateIfMatch<K extends AppRouteKey>(
|
function getRouteStateIfMatch<K extends AppRouteKey>(
|
||||||
|
|
@ -25,10 +30,13 @@ function getRouteStateIfMatch<K extends AppRouteKey>(
|
||||||
|
|
||||||
export function useWorkspaceFormLocationData(): WorkspaceFormLocationData {
|
export function useWorkspaceFormLocationData(): WorkspaceFormLocationData {
|
||||||
const { selectedNamespace } = useNamespaceContext();
|
const { selectedNamespace } = useNamespaceContext();
|
||||||
const location = useTypedLocation<'workspaceEdit' | 'workspaceCreate'>();
|
const location = useTypedLocation<
|
||||||
|
'workspaceEdit' | 'workspaceCreate' | 'workspaceKindEdit' | 'workspaceKindCreate'
|
||||||
|
>();
|
||||||
const routeKey = useCurrentRouteKey();
|
const routeKey = useCurrentRouteKey();
|
||||||
const rawState = location.state as WorkspaceFormLocationState | undefined;
|
const rawState = location.state as WorkspaceFormLocationState | undefined;
|
||||||
|
|
||||||
|
// Workspace Edit Mode
|
||||||
if (routeKey === 'workspaceEdit') {
|
if (routeKey === 'workspaceEdit') {
|
||||||
const editState = getRouteStateIfMatch('workspaceEdit', routeKey, rawState);
|
const editState = getRouteStateIfMatch('workspaceEdit', routeKey, rawState);
|
||||||
const namespace = editState?.namespace ?? selectedNamespace;
|
const namespace = editState?.namespace ?? selectedNamespace;
|
||||||
|
|
@ -45,6 +53,7 @@ export function useWorkspaceFormLocationData(): WorkspaceFormLocationData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workspace Create Mode
|
||||||
if (routeKey === 'workspaceCreate') {
|
if (routeKey === 'workspaceCreate') {
|
||||||
const createState = getRouteStateIfMatch('workspaceCreate', routeKey, rawState);
|
const createState = getRouteStateIfMatch('workspaceCreate', routeKey, rawState);
|
||||||
const namespace = createState?.namespace ?? selectedNamespace;
|
const namespace = createState?.namespace ?? selectedNamespace;
|
||||||
|
|
@ -55,5 +64,35 @@ export function useWorkspaceFormLocationData(): WorkspaceFormLocationData {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workspace Kind Edit Mode
|
||||||
|
if (routeKey === 'workspaceKindEdit') {
|
||||||
|
const editState = getRouteStateIfMatch('workspaceKindEdit', routeKey, rawState);
|
||||||
|
const namespace = editState?.namespace ?? selectedNamespace;
|
||||||
|
// TODO: remove default jupyterlab from workspace
|
||||||
|
const workspaceKindName = editState?.workspaceKindName || 'jupyterlab';
|
||||||
|
|
||||||
|
if (!workspaceKindName) {
|
||||||
|
throw new Error('Workspace kind name is required for edit mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
mode: 'edit',
|
||||||
|
namespace,
|
||||||
|
workspaceKindName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspace Kind Create Mode
|
||||||
|
if (routeKey === 'workspaceKindCreate') {
|
||||||
|
const createState = getRouteStateIfMatch('workspaceKindCreate', routeKey, rawState);
|
||||||
|
const namespace = createState?.namespace ?? selectedNamespace;
|
||||||
|
|
||||||
|
return {
|
||||||
|
mode: 'create',
|
||||||
|
namespace,
|
||||||
|
// formType: 'workspaceKind',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error('Unknown workspace form route');
|
throw new Error('Unknown workspace form route');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,12 @@ import {
|
||||||
PageGroup,
|
PageGroup,
|
||||||
PageSection,
|
PageSection,
|
||||||
Stack,
|
Stack,
|
||||||
ToggleGroup,
|
|
||||||
ToggleGroupItem,
|
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { useTypedNavigate } from '~/app/routerHelper';
|
import { useTypedNavigate } from '~/app/routerHelper';
|
||||||
import useGenericObjectState from '~/app/hooks/useGenericObjectState';
|
import useGenericObjectState from '~/app/hooks/useGenericObjectState';
|
||||||
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
||||||
import { WorkspaceKindFormData } from '~/app/types';
|
import { WorkspaceKindFormData } from '~/app/types';
|
||||||
|
import { useWorkspaceFormLocationData } from '~/app/hooks/useWorkspaceFormLocationData';
|
||||||
import { WorkspaceKindFileUpload } from './fileUpload/WorkspaceKindFileUpload';
|
import { WorkspaceKindFileUpload } from './fileUpload/WorkspaceKindFileUpload';
|
||||||
import { WorkspaceKindFormProperties } from './properties/WorkspaceKindFormProperties';
|
import { WorkspaceKindFormProperties } from './properties/WorkspaceKindFormProperties';
|
||||||
import { WorkspaceKindFormImage } from './image/WorkspaceKindFormImage';
|
import { WorkspaceKindFormImage } from './image/WorkspaceKindFormImage';
|
||||||
|
|
@ -31,12 +30,10 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
const navigate = useTypedNavigate();
|
const navigate = useTypedNavigate();
|
||||||
const { api } = useNotebookAPI();
|
const { api } = useNotebookAPI();
|
||||||
// TODO: Detect mode by route
|
// TODO: Detect mode by route
|
||||||
const [mode] = useState('create');
|
const { mode } = useWorkspaceFormLocationData();
|
||||||
const [yamlValue, setYamlValue] = useState('');
|
const [yamlValue, setYamlValue] = useState('');
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [view, setView] = useState<WorkspaceKindFormView>(WorkspaceKindFormView.FileUpload);
|
|
||||||
const [validated, setValidated] = useState<ValidationStatus>('default');
|
const [validated, setValidated] = useState<ValidationStatus>('default');
|
||||||
const workspaceKindFileUploadId = 'workspace-kind-form-fileupload-view';
|
|
||||||
|
|
||||||
const [data, setData, resetData] = useGenericObjectState<WorkspaceKindFormData>({
|
const [data, setData, resetData] = useGenericObjectState<WorkspaceKindFormData>({
|
||||||
properties: {
|
properties: {
|
||||||
|
|
@ -58,18 +55,6 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleViewClick = useCallback(
|
|
||||||
(event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent) => {
|
|
||||||
const { id } = event.currentTarget as HTMLElement;
|
|
||||||
setView(
|
|
||||||
id === workspaceKindFileUploadId
|
|
||||||
? WorkspaceKindFormView.FileUpload
|
|
||||||
: WorkspaceKindFormView.Form,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSubmit = useCallback(async () => {
|
const handleSubmit = useCallback(async () => {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
// TODO: Complete handleCreate with API call to create a new WS kind
|
// TODO: Complete handleCreate with API call to create a new WS kind
|
||||||
|
|
@ -106,37 +91,18 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
{`${mode === 'create' ? 'Create' : 'Edit'} workspace kind`}
|
{`${mode === 'create' ? 'Create' : 'Edit'} workspace kind`}
|
||||||
</Content>
|
</Content>
|
||||||
<Content component={ContentVariants.p}>
|
<Content component={ContentVariants.p}>
|
||||||
{view === WorkspaceKindFormView.FileUpload
|
{mode === 'create'
|
||||||
? `Please upload or drag and drop a Workspace Kind YAML file.`
|
? `Please upload or drag and drop a Workspace Kind YAML file.`
|
||||||
: `View and edit the Workspace Kind's information. Some fields may not be
|
: `View and edit the Workspace Kind's information. Some fields may not be
|
||||||
represented in this form`}
|
represented in this form`}
|
||||||
</Content>
|
</Content>
|
||||||
</FlexItem>
|
</FlexItem>
|
||||||
{mode === 'edit' && (
|
|
||||||
<FlexItem>
|
|
||||||
<ToggleGroup className="workspace-kind-form-header" aria-label="Toggle form view">
|
|
||||||
<ToggleGroupItem
|
|
||||||
text="YAML Upload"
|
|
||||||
buttonId={workspaceKindFileUploadId}
|
|
||||||
isSelected={view === WorkspaceKindFormView.FileUpload}
|
|
||||||
onChange={handleViewClick}
|
|
||||||
/>
|
|
||||||
<ToggleGroupItem
|
|
||||||
text="Form View"
|
|
||||||
buttonId="workspace-kind-form-form-view"
|
|
||||||
isSelected={view === WorkspaceKindFormView.Form}
|
|
||||||
onChange={handleViewClick}
|
|
||||||
isDisabled={yamlValue === '' || validated === 'error'}
|
|
||||||
/>
|
|
||||||
</ToggleGroup>
|
|
||||||
</FlexItem>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
</PageGroup>
|
</PageGroup>
|
||||||
<PageSection isFilled>
|
<PageSection isFilled>
|
||||||
{view === WorkspaceKindFormView.FileUpload && (
|
{mode === 'create' && (
|
||||||
<WorkspaceKindFileUpload
|
<WorkspaceKindFileUpload
|
||||||
setData={setData}
|
setData={setData}
|
||||||
resetData={resetData}
|
resetData={resetData}
|
||||||
|
|
@ -146,7 +112,7 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
setValidated={setValidated}
|
setValidated={setValidated}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{view === WorkspaceKindFormView.Form && (
|
{mode === 'edit' && (
|
||||||
<>
|
<>
|
||||||
<WorkspaceKindFormProperties
|
<WorkspaceKindFormProperties
|
||||||
mode={mode}
|
mode={mode}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
SplitItem,
|
SplitItem,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { CPU_UNITS, MEMORY_UNITS_FOR_SELECTION, UnitOption } from '~/shared/utilities/valueUnits';
|
import { CPU_UNITS, MEMORY_UNITS_FOR_SELECTION, UnitOption } from '~/shared/utilities/valueUnits';
|
||||||
|
import { extractNumericValue, extractUnit } from '~/shared/utilities/WorkspaceUtils';
|
||||||
|
|
||||||
interface ResourceInputWrapperProps {
|
interface ResourceInputWrapperProps {
|
||||||
value: string;
|
value: string;
|
||||||
|
|
@ -29,10 +30,15 @@ const unitMap: {
|
||||||
|
|
||||||
const DEFAULT_STEP = 1;
|
const DEFAULT_STEP = 1;
|
||||||
|
|
||||||
|
const DEFAULT_UNITS = {
|
||||||
|
memory: 'Mi',
|
||||||
|
cpu: '',
|
||||||
|
};
|
||||||
|
|
||||||
export const ResourceInputWrapper: React.FC<ResourceInputWrapperProps> = ({
|
export const ResourceInputWrapper: React.FC<ResourceInputWrapperProps> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
min = 0,
|
min = 1,
|
||||||
max,
|
max,
|
||||||
step = DEFAULT_STEP,
|
step = DEFAULT_STEP,
|
||||||
type,
|
type,
|
||||||
|
|
@ -44,37 +50,23 @@ export const ResourceInputWrapper: React.FC<ResourceInputWrapperProps> = ({
|
||||||
const [unit, setUnit] = useState<string>('');
|
const [unit, setUnit] = useState<string>('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (type === 'memory') {
|
if (type === 'custom') {
|
||||||
// Extract numeric value and unit from memory string (e.g., "512Mi" -> "512" and "Mi")
|
|
||||||
const match = value.match(/^(\d+)([MGTP]i)?$/i);
|
|
||||||
if (match) {
|
|
||||||
setInputValue(match[1]);
|
|
||||||
setUnit(match[2] || 'Mi');
|
|
||||||
} else {
|
|
||||||
setInputValue('');
|
|
||||||
setUnit('Mi');
|
|
||||||
}
|
|
||||||
} else if (type === 'cpu') {
|
|
||||||
const match = value.match(/^(\d+)([m])?$/i);
|
|
||||||
if (match) {
|
|
||||||
setInputValue(match[1]);
|
|
||||||
setUnit(match[2] || '');
|
|
||||||
} else {
|
|
||||||
setInputValue('');
|
|
||||||
setUnit('');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setInputValue(value);
|
setInputValue(value);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
const numericValue = extractNumericValue(value, type);
|
||||||
|
const extractedUnit = extractUnit(value, type);
|
||||||
|
setInputValue(numericValue);
|
||||||
|
setUnit(extractedUnit || DEFAULT_UNITS[type]);
|
||||||
}, [value, type]);
|
}, [value, type]);
|
||||||
|
|
||||||
const handleInputChange = useCallback(
|
const handleInputChange = useCallback(
|
||||||
(newValue: string) => {
|
(newValue: string) => {
|
||||||
setInputValue(newValue);
|
setInputValue(newValue);
|
||||||
if (type === 'memory' || type === 'cpu') {
|
if (type === 'custom') {
|
||||||
onChange(newValue ? `${newValue}${unit}` : '');
|
|
||||||
} else {
|
|
||||||
onChange(newValue);
|
onChange(newValue);
|
||||||
|
} else {
|
||||||
|
onChange(newValue ? `${newValue}${unit}` : '');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onChange, type, unit],
|
[onChange, type, unit],
|
||||||
|
|
@ -110,7 +102,6 @@ export const ResourceInputWrapper: React.FC<ResourceInputWrapperProps> = ({
|
||||||
[handleInputChange],
|
[handleInputChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Memoize the unit options to prevent unnecessary re-renders
|
|
||||||
const unitOptions = useMemo(
|
const unitOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
type !== 'custom'
|
type !== 'custom'
|
||||||
|
|
@ -123,7 +114,7 @@ export const ResourceInputWrapper: React.FC<ResourceInputWrapperProps> = ({
|
||||||
<Split className="workspacekind-form-resource-input">
|
<Split className="workspacekind-form-resource-input">
|
||||||
<SplitItem>
|
<SplitItem>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
value={parseFloat(inputValue) || 0}
|
value={parseFloat(inputValue) || 1}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onMinus={handleDecrement}
|
onMinus={handleDecrement}
|
||||||
onChange={handleNumberInputChange}
|
onChange={handleNumberInputChange}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import {
|
||||||
FormFieldGroupHeader,
|
FormFieldGroupHeader,
|
||||||
TextInput,
|
TextInput,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
Content,
|
||||||
|
ContentVariants,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { PlusCircleIcon, TrashAltIcon } from '@patternfly/react-icons';
|
import { PlusCircleIcon, TrashAltIcon } from '@patternfly/react-icons';
|
||||||
import { ResourceInputWrapper } from './ResourceInputWrapper';
|
import { ResourceInputWrapper } from './ResourceInputWrapper';
|
||||||
|
|
@ -32,12 +34,10 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
custom,
|
custom,
|
||||||
}) => {
|
}) => {
|
||||||
// State for tracking limit toggles
|
// State for tracking limit toggles
|
||||||
const [cpuRequestEnabled, setCpuRequestEnabled] = useState<boolean>(cpu.request.length > 0);
|
const [cpuRequestEnabled, setCpuRequestEnabled] = useState(cpu.request.length > 0);
|
||||||
const [memoryRequestEnabled, setMemoryRequestEnabled] = useState<boolean>(
|
const [memoryRequestEnabled, setMemoryRequestEnabled] = useState(memory.request.length > 0);
|
||||||
memory.request.length > 0,
|
const [cpuLimitEnabled, setCpuLimitEnabled] = useState(cpu.limit.length > 0);
|
||||||
);
|
const [memoryLimitEnabled, setMemoryLimitEnabled] = useState(memory.limit.length > 0);
|
||||||
const [cpuLimitEnabled, setCpuLimitEnabled] = useState<boolean>(cpu.limit.length > 0);
|
|
||||||
const [memoryLimitEnabled, setMemoryLimitEnabled] = useState<boolean>(memory.limit.length > 0);
|
|
||||||
const [customLimitsEnabled, setCustomLimitsEnabled] = useState<Record<number, boolean>>(() => {
|
const [customLimitsEnabled, setCustomLimitsEnabled] = useState<Record<number, boolean>>(() => {
|
||||||
const customToggles: Record<number, boolean> = {};
|
const customToggles: Record<number, boolean> = {};
|
||||||
custom.forEach((res, idx) => {
|
custom.forEach((res, idx) => {
|
||||||
|
|
@ -67,7 +67,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
const handleAddCustom = useCallback(() => {
|
const handleAddCustom = useCallback(() => {
|
||||||
setResources((resources: PodResourceEntry[]) => [
|
setResources((resources: PodResourceEntry[]) => [
|
||||||
...resources,
|
...resources,
|
||||||
{ type: '', request: '', limit: '' },
|
{ type: '', request: '1', limit: '' },
|
||||||
]);
|
]);
|
||||||
}, [setResources]);
|
}, [setResources]);
|
||||||
|
|
||||||
|
|
@ -97,16 +97,18 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
const handleCpuLimitToggle = useCallback(
|
const handleCpuLimitToggle = useCallback(
|
||||||
(enabled: boolean) => {
|
(enabled: boolean) => {
|
||||||
setCpuLimitEnabled(enabled);
|
setCpuLimitEnabled(enabled);
|
||||||
|
handleChange('cpu', 'limit', cpu.request);
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
handleChange('cpu', 'limit', '');
|
handleChange('cpu', 'limit', '');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleChange],
|
[cpu.request, handleChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCpuRequestToggle = useCallback(
|
const handleCpuRequestToggle = useCallback(
|
||||||
(enabled: boolean) => {
|
(enabled: boolean) => {
|
||||||
setCpuRequestEnabled(enabled);
|
setCpuRequestEnabled(enabled);
|
||||||
|
handleChange('cpu', 'request', '1');
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
handleChange('cpu', 'request', '');
|
handleChange('cpu', 'request', '');
|
||||||
handleCpuLimitToggle(enabled);
|
handleCpuLimitToggle(enabled);
|
||||||
|
|
@ -118,16 +120,18 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
const handleMemoryLimitToggle = useCallback(
|
const handleMemoryLimitToggle = useCallback(
|
||||||
(enabled: boolean) => {
|
(enabled: boolean) => {
|
||||||
setMemoryLimitEnabled(enabled);
|
setMemoryLimitEnabled(enabled);
|
||||||
|
handleChange('memory', 'limit', memory.request);
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
handleChange('memory', 'limit', '');
|
handleChange('memory', 'limit', '');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleChange],
|
[handleChange, memory.request],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMemoryRequestToggle = useCallback(
|
const handleMemoryRequestToggle = useCallback(
|
||||||
(enabled: boolean) => {
|
(enabled: boolean) => {
|
||||||
setMemoryRequestEnabled(enabled);
|
setMemoryRequestEnabled(enabled);
|
||||||
|
handleChange('memory', 'request', '1Mi');
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
handleChange('memory', 'request', '');
|
handleChange('memory', 'request', '');
|
||||||
handleMemoryLimitToggle(enabled);
|
handleMemoryLimitToggle(enabled);
|
||||||
|
|
@ -159,9 +163,9 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
id: 'workspace-kind-podconfig-resource',
|
id: 'workspace-kind-podconfig-resource',
|
||||||
}}
|
}}
|
||||||
titleDescription={
|
titleDescription={
|
||||||
<p style={{ fontSize: '12px' }}>
|
<Content component={ContentVariants.p} style={{ fontSize: '12px' }}>
|
||||||
Optional: Configure k8s Pod Resource Requests & Limits
|
Optional: Configure k8s Pod Resource Requests & Limits.
|
||||||
</p>
|
</Content>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
@ -190,7 +194,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
value={cpu.request}
|
value={cpu.request}
|
||||||
onChange={(value) => handleChange('cpu', 'request', value)}
|
onChange={(value) => handleChange('cpu', 'request', value)}
|
||||||
placeholder="e.g. 1"
|
placeholder="e.g. 1"
|
||||||
min={0}
|
min={1}
|
||||||
aria-label="CPU request"
|
aria-label="CPU request"
|
||||||
isDisabled={!cpuRequestEnabled}
|
isDisabled={!cpuRequestEnabled}
|
||||||
/>
|
/>
|
||||||
|
|
@ -201,7 +205,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
value={memory.request}
|
value={memory.request}
|
||||||
onChange={(value) => handleChange('memory', 'request', value)}
|
onChange={(value) => handleChange('memory', 'request', value)}
|
||||||
placeholder="e.g. 512Mi"
|
placeholder="e.g. 512Mi"
|
||||||
min={0}
|
min={1}
|
||||||
aria-label="Memory request"
|
aria-label="Memory request"
|
||||||
isDisabled={!memoryRequestEnabled}
|
isDisabled={!memoryRequestEnabled}
|
||||||
/>
|
/>
|
||||||
|
|
@ -232,7 +236,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
value={cpu.limit}
|
value={cpu.limit}
|
||||||
onChange={(value) => handleChange('cpu', 'limit', value)}
|
onChange={(value) => handleChange('cpu', 'limit', value)}
|
||||||
placeholder="e.g. 2"
|
placeholder="e.g. 2"
|
||||||
min={0}
|
min={parseFloat(cpu.request)}
|
||||||
step={1}
|
step={1}
|
||||||
aria-label="CPU limit"
|
aria-label="CPU limit"
|
||||||
isDisabled={!cpuRequestEnabled || !cpuLimitEnabled}
|
isDisabled={!cpuRequestEnabled || !cpuLimitEnabled}
|
||||||
|
|
@ -244,7 +248,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
value={memory.limit}
|
value={memory.limit}
|
||||||
onChange={(value) => handleChange('memory', 'limit', value)}
|
onChange={(value) => handleChange('memory', 'limit', value)}
|
||||||
placeholder="e.g. 1Gi"
|
placeholder="e.g. 1Gi"
|
||||||
min={0}
|
min={parseFloat(memory.request)}
|
||||||
aria-label="Memory limit"
|
aria-label="Memory limit"
|
||||||
isDisabled={!memoryRequestEnabled || !memoryLimitEnabled}
|
isDisabled={!memoryRequestEnabled || !memoryLimitEnabled}
|
||||||
/>
|
/>
|
||||||
|
|
@ -278,7 +282,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
value={res.request}
|
value={res.request}
|
||||||
onChange={(value) => handleChange(res.type, 'request', value)}
|
onChange={(value) => handleChange(res.type, 'request', value)}
|
||||||
placeholder="Request"
|
placeholder="Request"
|
||||||
min={0}
|
min={1}
|
||||||
aria-label="Custom resource request"
|
aria-label="Custom resource request"
|
||||||
/>
|
/>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
|
|
@ -287,7 +291,10 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
id={`custom-limit-switch-${idx}`}
|
id={`custom-limit-switch-${idx}`}
|
||||||
label="Set Limit"
|
label="Set Limit"
|
||||||
isChecked={customLimitsEnabled[idx] || false}
|
isChecked={customLimitsEnabled[idx] || false}
|
||||||
onChange={(_event, checked) => handleCustomLimitToggle(idx, checked)}
|
onChange={(_event, checked) => {
|
||||||
|
handleChange(res.type, 'limit', res.request);
|
||||||
|
handleCustomLimitToggle(idx, checked);
|
||||||
|
}}
|
||||||
aria-label={`Enable limit for ${res.type || 'custom resource'}`}
|
aria-label={`Enable limit for ${res.type || 'custom resource'}`}
|
||||||
/>
|
/>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
|
|
@ -297,7 +304,7 @@ export const WorkspaceKindFormPodConfigResource: React.FC<Props> = ({
|
||||||
value={res.limit}
|
value={res.limit}
|
||||||
onChange={(value) => handleChange(res.type, 'limit', value)}
|
onChange={(value) => handleChange(res.type, 'limit', value)}
|
||||||
placeholder="Limit"
|
placeholder="Limit"
|
||||||
min={0}
|
min={parseFloat(res.request)}
|
||||||
isDisabled={!customLimitsEnabled[idx]}
|
isDisabled={!customLimitsEnabled[idx]}
|
||||||
aria-label={`${res.type || 'Custom resource'} limit`}
|
aria-label={`${res.type || 'Custom resource'} limit`}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export const AppRoutePaths = {
|
||||||
workspaceKinds: '/workspacekinds',
|
workspaceKinds: '/workspacekinds',
|
||||||
workspaceKindSummary: '/workspacekinds/:kind/summary',
|
workspaceKindSummary: '/workspacekinds/:kind/summary',
|
||||||
workspaceKindCreate: '/workspacekinds/create',
|
workspaceKindCreate: '/workspacekinds/create',
|
||||||
|
workspaceKindEdit: '/workspacekinds/edit',
|
||||||
} satisfies Record<string, `/${string}`>;
|
} satisfies Record<string, `/${string}`>;
|
||||||
|
|
||||||
export type AppRoute = (typeof AppRoutePaths)[keyof typeof AppRoutePaths];
|
export type AppRoute = (typeof AppRoutePaths)[keyof typeof AppRoutePaths];
|
||||||
|
|
@ -31,6 +32,7 @@ export type RouteParamsMap = {
|
||||||
kind: string;
|
kind: string;
|
||||||
};
|
};
|
||||||
workspaceKindCreate: undefined;
|
workspaceKindCreate: undefined;
|
||||||
|
workspaceKindEdit: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -62,6 +64,10 @@ export type RouteStateMap = {
|
||||||
workspaceKindCreate: {
|
workspaceKindCreate: {
|
||||||
namespace: string;
|
namespace: string;
|
||||||
};
|
};
|
||||||
|
workspaceKindEdit: {
|
||||||
|
namespace: string;
|
||||||
|
workspaceKindName: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -82,4 +88,5 @@ export type RouteSearchParamsMap = {
|
||||||
workspaceKinds: undefined;
|
workspaceKinds: undefined;
|
||||||
workspaceKindSummary: undefined;
|
workspaceKindSummary: undefined;
|
||||||
workspaceKindCreate: undefined;
|
workspaceKindCreate: undefined;
|
||||||
|
workspaceKindEdit: undefined;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,30 @@ export enum YesNoValue {
|
||||||
No = 'No',
|
No = 'No',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RESOURCE_UNIT_CONFIG = {
|
||||||
|
cpu: CPU_UNITS,
|
||||||
|
memory: MEMORY_UNITS_FOR_PARSING,
|
||||||
|
gpu: OTHER,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseResourceValue = (
|
||||||
|
value: string,
|
||||||
|
resourceType: ResourceType,
|
||||||
|
): [number | undefined, { name: string; unit: string } | undefined] => {
|
||||||
|
const units = RESOURCE_UNIT_CONFIG[resourceType];
|
||||||
|
return splitValueUnit(value, units);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extractNumericValue = (value: string, resourceType: ResourceType): string => {
|
||||||
|
const [numericValue] = parseResourceValue(value, resourceType);
|
||||||
|
return String(numericValue || '');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extractUnit = (value: string, resourceType: ResourceType): string => {
|
||||||
|
const [, unit] = parseResourceValue(value, resourceType);
|
||||||
|
return unit?.unit || '';
|
||||||
|
};
|
||||||
|
|
||||||
export const extractResourceValue = (
|
export const extractResourceValue = (
|
||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
resourceType: ResourceType,
|
resourceType: ResourceType,
|
||||||
|
|
@ -24,18 +48,13 @@ export const formatResourceValue = (v: string | undefined, resourceType?: Resour
|
||||||
if (v === undefined) {
|
if (v === undefined) {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
switch (resourceType) {
|
|
||||||
case 'cpu': {
|
if (!resourceType) {
|
||||||
const [cpuValue, cpuUnit] = splitValueUnit(v, CPU_UNITS);
|
|
||||||
return `${cpuValue ?? ''} ${cpuUnit.name}`;
|
|
||||||
}
|
|
||||||
case 'memory': {
|
|
||||||
const [memoryValue, memoryUnit] = splitValueUnit(v, MEMORY_UNITS_FOR_PARSING);
|
|
||||||
return `${memoryValue ?? ''} ${memoryUnit.name}`;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [value, unit] = parseResourceValue(v, resourceType);
|
||||||
|
return `${value || ''} ${unit?.name || ''}`.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatResourceFromWorkspace = (
|
export const formatResourceFromWorkspace = (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue