Skip to content

Commit

Permalink
Merge branch 'main' into andrea/early-access
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo-cavazzi authored Jan 14, 2025
2 parents f00fbd0 + 88d44e7 commit 5d96fb0
Show file tree
Hide file tree
Showing 14 changed files with 538 additions and 176 deletions.
4 changes: 3 additions & 1 deletion client/src/features/session/components/SessionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ export function SessionRowResourceRequests({
{entries.map(([key, value], index) => (
<span key={key}>
<span className="text-nowrap">
<span className="fw-bold">{value} </span>
<span className="fw-bold">
{value} {(key === "memory" || key === "storage") && "GB "}
</span>
{key !== "name" && key}
</span>
{entries.length - 1 === index ? " " : " | "}
Expand Down
76 changes: 47 additions & 29 deletions client/src/features/sessionsV2/SessionView/SessionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@ export function SessionView({
resourceRequests={{
name: launcherResourceClass.name,
cpu: launcherResourceClass.cpu,
memory: `${launcherResourceClass.memory}G`,
storage: `${launcherResourceClass.default_storage}G`,
memory: launcherResourceClass.memory,
storage:
launcher?.disk_storage ?? launcherResourceClass.default_storage,
gpu: launcherResourceClass.gpu,
}}
/>
Expand Down Expand Up @@ -374,27 +375,29 @@ export function SessionView({
<div>
<div className={cx("d-flex", "justify-content-between", "mb-2")}>
<h4 className="my-auto">Default Resource Class</h4>
<PermissionsGuard
disabled={null}
enabled={
<>
<Button
color="outline-primary"
id="modify-resource-class-button"
onClick={toggleModifyResources}
size="sm"
tabIndex={0}
>
<Pencil className="bi" />
</Button>
<UncontrolledTooltip target="modify-resource-class-button">
Set resource class
</UncontrolledTooltip>
</>
}
requestedPermission="write"
userPermissions={permissions}
/>
{launcher && (
<PermissionsGuard
disabled={null}
enabled={
<>
<Button
color="outline-primary"
id="modify-resource-class-button"
onClick={toggleModifyResources}
size="sm"
tabIndex={0}
>
<Pencil className="bi" />
</Button>
<UncontrolledTooltip target="modify-resource-class-button">
Set resource class
</UncontrolledTooltip>
</>
}
requestedPermission="write"
userPermissions={permissions}
/>
)}
</div>
{resourceDetails}
{launcherResourceClass && !userLauncherResourceClass && (
Expand All @@ -403,12 +406,27 @@ export function SessionView({
You do not have access to this resource class.
</p>
)}
<ModifyResourcesLauncherModal
isOpen={isModifyResourcesOpen}
toggleModal={toggleModifyResources}
resourceClassId={userLauncherResourceClass?.id}
sessionLauncherId={launcher?.id}
/>
{launcher &&
launcherResourceClass &&
launcher.disk_storage &&
launcher.disk_storage > launcherResourceClass.max_storage && (
<p>
<ExclamationTriangleFill
className={cx("bi", "text-warning", "me-1")}
/>
The selected disk storage exceeds the maximum value allowed (
{launcherResourceClass.max_storage} GB).
</p>
)}
{launcher && (
<ModifyResourcesLauncherModal
isOpen={isModifyResourcesOpen}
toggleModal={toggleModifyResources}
resourceClassId={userLauncherResourceClass?.id}
diskStorage={launcher.disk_storage}
sessionLauncherId={launcher.id}
/>
)}
</div>

<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import cx from "classnames";
import { useMemo } from "react";
import { Control, Controller, useWatch } from "react-hook-form";
import {
Control,
Controller,
FieldErrors,
UseFormSetValue,
} from "react-hook-form";
import { SingleValue } from "react-select";
import { Input, Label } from "reactstrap";
FormText,
Input,
InputGroup,
InputGroupText,
Label,
UncontrolledTooltip,
} from "reactstrap";
import { WarnAlert } from "../../../../components/Alert";
import { RtkErrorAlert } from "../../../../components/errors/RtkErrorAlert";
import { Loader } from "../../../../components/Loader";
import { useGetResourcePoolsQuery } from "../../../dataServices/computeResources.api";
import { ResourceClass } from "../../../dataServices/dataServices.types";
import { SessionClassSelectorV2 } from "../../../session/components/options/SessionClassOption";
import { SessionLauncherForm } from "../../sessionsV2.types";
import {
MIN_SESSION_STORAGE_GB,
STEP_SESSION_STORAGE_GB,
} from "../../../session/startSessionOptions.constants";

interface LauncherDetailsFieldsProps {
control: Control<SessionLauncherForm, unknown>;
errors: FieldErrors<SessionLauncherForm>;
setValue: UseFormSetValue<SessionLauncherForm>;
control: Control<SessionLauncherForm>;
}
export function LauncherDetailsFields({
setValue,
control,
errors,
}: LauncherDetailsFieldsProps) {
export function LauncherDetailsFields({ control }: LauncherDetailsFieldsProps) {
const {
data: resourcePools,
isLoading: isLoadingResourcesPools,
error: resourcePoolsError,
} = useGetResourcePoolsQuery({});

const onChangeResourceClass = (resourceClass: SingleValue<ResourceClass>) => {
if (resourceClass) setValue("resourceClass", resourceClass);
};

const defaultSessionClass = useMemo(
() =>
resourcePools
Expand All @@ -64,6 +59,13 @@ export function LauncherDetailsFields({
[resourcePools]
);

const watchCurrentSessionClass = useWatch({
control,
name: "resourceClass",
defaultValue: defaultSessionClass,
});
const watchCurrentDiskStorage = useWatch({ control, name: "diskStorage" });

return (
<div className={cx("d-flex", "flex-column", "gap-3")}>
<div className={cx("form-label", "mb-0")}>
Expand All @@ -76,9 +78,9 @@ export function LauncherDetailsFields({
<Controller
control={control}
name="name"
render={({ field }) => (
render={({ field, fieldState: { error } }) => (
<Input
className={cx(errors.name && "is-invalid")}
className={cx(error && "is-invalid")}
id="addSessionLauncherName"
placeholder="session name"
type="text"
Expand Down Expand Up @@ -107,32 +109,113 @@ export function LauncherDetailsFields({
{!isLoadingResourcesPools &&
resourcePools &&
resourcePools?.length > 0 ? (
<Controller
control={control}
name="resourceClass"
defaultValue={defaultSessionClass}
render={() => (
<>
<SessionClassSelectorV2
id="addSessionResourceClass"
resourcePools={resourcePools}
onChange={onChangeResourceClass}
defaultSessionClass={defaultSessionClass}
/>
{errors?.resourceClass && (
<div className={cx("small", "text-danger")}>
Please provide a resource class
</div>
)}
</>
)}
rules={{ required: true }}
/>
<>
<Controller
control={control}
name="resourceClass"
defaultValue={defaultSessionClass}
render={({
field: { onChange, value },
fieldState: { error },
}) => (
<>
<SessionClassSelectorV2
id="addSessionResourceClass"
currentSessionClass={value}
resourcePools={resourcePools}
onChange={onChange}
defaultSessionClass={defaultSessionClass}
/>
{error && (
<div className={cx("small", "text-danger")}>
Please provide a resource class
</div>
)}
</>
)}
rules={{ required: true }}
/>
</>
) : (
<WarnAlert>
There are no one resource pool available to create a session
</WarnAlert>
)}

{watchCurrentSessionClass && (
<div className={cx("field-group", "mt-3")}>
<div>
Disk Storage:{" "}
<span className="fw-bold">
{watchCurrentDiskStorage &&
watchCurrentDiskStorage !=
watchCurrentSessionClass.default_storage ? (
<>{watchCurrentDiskStorage} GB</>
) : (
<>{watchCurrentSessionClass?.default_storage} GB (default)</>
)}
</span>
</div>
<Controller
control={control}
name="diskStorage"
render={({ field, fieldState: { error } }) => (
<>
<InputGroup className={cx(error && "is-invalid")}>
<Input
className={cx(error && "is-invalid")}
type="number"
min={MIN_SESSION_STORAGE_GB}
max={watchCurrentSessionClass.max_storage}
step={STEP_SESSION_STORAGE_GB}
{...field}
value={field.value ?? ""}
onChange={(event) => {
if (isNaN(event.target.valueAsNumber)) {
field.onChange(event.target.value);
} else {
field.onChange(event.target.valueAsNumber);
}
}}
/>
<InputGroupText id="configure-disk-storage-addon">
GB
</InputGroupText>
<UncontrolledTooltip target="configure-disk-storage-addon">
Gigabytes
</UncontrolledTooltip>
</InputGroup>
<FormText>
Default: {watchCurrentSessionClass.default_storage} GB, max:{" "}
{watchCurrentSessionClass.max_storage} GB
</FormText>
<div className="invalid-feedback">
{error?.message ||
"Please provide a valid value for disk storage."}
</div>
</>
)}
rules={{
min: {
value: MIN_SESSION_STORAGE_GB,
message: `Please select a value greater than or equal to ${MIN_SESSION_STORAGE_GB}.`,
},
max: {
value: watchCurrentSessionClass.max_storage,
message: `Selected disk storage exceeds maximum allowed value (${watchCurrentSessionClass.max_storage} GB).`,
},
validate: {
integer: (value: unknown) =>
value == null ||
value === "" ||
(!isNaN(parseInt(`${value}`, 10)) &&
parseInt(`${value}`, 10) == parseFloat(`${value}`)),
},
deps: ["resourceClass"],
}}
/>
</div>
)}
</div>
</div>
);
Expand Down
Loading

0 comments on commit 5d96fb0

Please sign in to comment.