feat: enhance AssignNode functionality with zone redundancy options and update API integration

This commit is contained in:
Adekabang 2025-07-31 02:16:04 -04:00
parent d6847884e0
commit 4de96071d4
4 changed files with 89 additions and 14 deletions

View File

@ -23,6 +23,8 @@ const defaultValues: AssignNodeSchema = {
capacityUnit: "GB", capacityUnit: "GB",
isGateway: false, isGateway: false,
tags: [], tags: [],
zoneRedundancyType: "atLeast",
zoneRedundancyAtLeast: 1,
}; };
const AssignNodeDialog = () => { const AssignNodeDialog = () => {
@ -36,6 +38,10 @@ const AssignNodeDialog = () => {
defaultValues, defaultValues,
}); });
const isGateway = useWatch({ control: form.control, name: "isGateway" }); const isGateway = useWatch({ control: form.control, name: "isGateway" });
const zoneRedundancyType = useWatch({
control: form.control,
name: "zoneRedundancyType",
});
const assignNode = useAssignNode({ const assignNode = useAssignNode({
onSuccess() { onSuccess() {
@ -106,10 +112,20 @@ const AssignNodeDialog = () => {
? calculateCapacity(values.capacity, values.capacityUnit) ? calculateCapacity(values.capacity, values.capacityUnit)
: null; : null;
const data = { const data = {
parameters: {
zoneRedundancy:
values.zoneRedundancyType === "maximum"
? ("maximum" as const)
: { atLeast: Number(values.zoneRedundancyAtLeast) },
},
roles: [
{
id: values.nodeId, id: values.nodeId,
zone: values.zone, zone: values.zone,
capacity, capacity,
tags: values.tags, tags: values.tags,
},
],
}; };
assignNode.mutate(data); assignNode.mutate(data);
}); });
@ -228,6 +244,39 @@ const AssignNodeDialog = () => {
/> />
)} )}
/> />
<FormControl
form={form}
name="zoneRedundancyType"
title="Zone Redundancy"
render={(field) => (
<Select
name={field.name}
value={String(field.value || "")}
onChange={field.onChange}
>
<option value="atLeast">At Least</option>
<option value="maximum">Maximum</option>
</Select>
)}
/>
{zoneRedundancyType === "atLeast" && (
<FormControl
form={form}
name="zoneRedundancyAtLeast"
title="Minimum Zones"
className="mt-2"
render={(field) => (
<Input
type="number"
name={field.name}
value={String(field.value || "")}
onChange={field.onChange}
/>
)}
/>
)}
</Modal.Body> </Modal.Body>
<Modal.Actions> <Modal.Actions>
<Button type="button" onClick={assignNodeDialog.close}> <Button type="button" onClick={assignNodeDialog.close}>

View File

@ -43,7 +43,7 @@ export const useConnectNode = (options?: Partial<UseMutationOptions<ConnectNodeR
export const useAssignNode = (options?: Partial<UseMutationOptions<void, Error, AssignNodeBody>>) => { export const useAssignNode = (options?: Partial<UseMutationOptions<void, Error, AssignNodeBody>>) => {
return useMutation<void, Error, AssignNodeBody>({ return useMutation<void, Error, AssignNodeBody>({
mutationFn: (data) => api.post("/v2/AddClusterLayout", { body: [data] }), mutationFn: (data) => api.post("/v2/UpdateClusterLayout", { body: { parameters: data.parameters, roles: data.roles } }),
...options, ...options,
}); });
}; };
@ -51,7 +51,7 @@ export const useAssignNode = (options?: Partial<UseMutationOptions<void, Error,
export const useUnassignNode = (options?: Partial<UseMutationOptions<void, Error, string>>) => { export const useUnassignNode = (options?: Partial<UseMutationOptions<void, Error, string>>) => {
return useMutation<void, Error, string>({ return useMutation<void, Error, string>({
mutationFn: (nodeId) => mutationFn: (nodeId) =>
api.post("/v2/AddClusterLayout", { body: [{ id: nodeId, remove: true }] }), api.post("/v2/UpdateClusterLayout", { body: { parameters: null, roles: [{ id: nodeId, remove: true }] } }),
...options, ...options,
}); });
}; };

View File

@ -16,6 +16,8 @@ export const assignNodeSchema = z
capacityUnit: z.enum(capacityUnits), capacityUnit: z.enum(capacityUnits),
isGateway: z.boolean(), isGateway: z.boolean(),
tags: z.string().min(1).array(), tags: z.string().min(1).array(),
zoneRedundancyType: z.enum(["atLeast", "maximum"]),
zoneRedundancyAtLeast: z.coerce.number(),
}) })
.refine( .refine(
(values) => values.isGateway || (values.capacity && values.capacity > 0), (values) => values.isGateway || (values.capacity && values.capacity > 0),
@ -23,6 +25,19 @@ export const assignNodeSchema = z
message: "Capacity required", message: "Capacity required",
path: ["capacity"], path: ["capacity"],
} }
)
.refine(
(data) => {
if (data.zoneRedundancyType === "atLeast" && !data.zoneRedundancyAtLeast) {
return false;
}
return true;
},
{
message:
'Zone Redundancy At Least is required when Zone Redundancy Type is "atLeast"',
path: ["zoneRedundancyAtLeast"],
}
); );
export type AssignNodeSchema = z.infer<typeof assignNodeSchema>; export type AssignNodeSchema = z.infer<typeof assignNodeSchema>;

View File

@ -32,7 +32,7 @@ export type DataPartition = {
export type Role = { export type Role = {
id: string; id: string;
zone: string; zone: string;
capacity: number; capacity: number | null;
tags: string[]; tags: string[];
}; };
@ -46,11 +46,22 @@ export type GetClusterLayoutResult = {
stagedRoleChanges: StagedRole[]; stagedRoleChanges: StagedRole[];
}; };
export type AssignNodeBody = { export type PartitionNumber = {
atLeast: number;
};
export type LayoutParameters = {
zoneRedundancy: "maximum" | PartitionNumber;
}
export type NodeRoleChange = {
remove: boolean;
id: string; id: string;
zone: string; }
capacity: number | null;
tags: string[]; export type AssignNodeBody = {
parameters: null | LayoutParameters,
roles: Role[] | NodeRoleChange[];
}; };
export type ApplyLayoutResult = { export type ApplyLayoutResult = {