From 52ae70ca3337822ccf6449704ddc047af2dd3c05 Mon Sep 17 00:00:00 2001 From: Julian Sommer <115516918+juliansommer@users.noreply.github.com> Date: Mon, 24 Aug 2026 10:59:33 +1000 Subject: [PATCH] refactor(ui): type the form values the CLI panels read (#860) --- .../-components/run-instances-page.tsx | 94 +++++++------------ .../-components/create-key-pair-page.tsx | 18 ++-- .../_auth/ec2/(key)/import-key-pair.tsx | 15 +-- .../-components/create-load-balancer-page.tsx | 65 +++++-------- .../ec2/(nat-gateways)/create-nat-gateway.tsx | 21 +++-- .../create-placement-group.tsx | 21 ++--- .../create-security-group.tsx | 24 +++-- .../_auth/ec2/(snapshots)/create-snapshot.tsx | 19 ++-- .../_auth/ec2/(subnet)/create-subnet.tsx | 24 ++--- .../-components/create-target-group-page.tsx | 37 +++----- .../_auth/ec2/(volumes)/create-volume.tsx | 10 +- .../src/routes/_auth/ec2/(vpc)/create-vpc.tsx | 21 +++-- .../_auth/iam/(groups)/create-group.tsx | 18 ++-- .../create-instance-profile.tsx | 18 ++-- .../_auth/iam/(policies)/create-policy.tsx | 22 ++--- .../routes/_auth/iam/(roles)/create-role.tsx | 22 ++--- .../routes/_auth/iam/(users)/create-user.tsx | 18 ++-- .../_auth/s3/(buckets)/create-bucket.tsx | 15 +-- 18 files changed, 215 insertions(+), 267 deletions(-) diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(instances)/-components/run-instances-page.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(instances)/-components/run-instances-page.tsx index cf80a49c8..a1dc38bda 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(instances)/-components/run-instances-page.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(instances)/-components/run-instances-page.tsx @@ -4,7 +4,12 @@ import { useQuery, useSuspenseQuery } from "@tanstack/react-query" import { useNavigate } from "@tanstack/react-router" import { ChevronDown } from "lucide-react" import { useEffect } from "react" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + useForm, + type UseFormWatch, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -935,21 +940,13 @@ function resolveTemplateVersion( } function buildRunInstancesCommands( - watch: (name?: string) => unknown, + watch: UseFormWatch, vpcId: string | undefined, ): CliCommand[] { - const rawLaunchTemplateId = watch("launchTemplateId") - const launchTemplateId = - typeof rawLaunchTemplateId === "string" ? rawLaunchTemplateId : "" + const launchTemplateId = watch("launchTemplateId") ?? "" if (launchTemplateId) { - const rawTemplateVersion = watch("launchTemplateVersion") - const templateVersion = - typeof rawTemplateVersion === "string" && rawTemplateVersion - ? rawTemplateVersion - : "$Default" - const rawTemplateCount = watch("count") - const templateCount = - typeof rawTemplateCount === "number" ? rawTemplateCount : 0 + const templateVersion = watch("launchTemplateVersion") || "$Default" + const templateCount = watch("count") ?? 0 return [ { label: "Run Instances", @@ -970,34 +967,16 @@ function buildRunInstancesCommands( ] } - const rawImageId = watch("imageId") - const imageId = typeof rawImageId === "string" ? rawImageId : "" - const rawInstanceType = watch("instanceType") - const instanceType = - typeof rawInstanceType === "string" ? rawInstanceType : "" - const rawKeyName = watch("keyName") - const keyName = typeof rawKeyName === "string" ? rawKeyName : "" - const rawSubnetId = watch("subnetId") - const subnetId = typeof rawSubnetId === "string" ? rawSubnetId : "" - const rawPlacementGroupName = watch("placementGroupName") - const placementGroupName = - typeof rawPlacementGroupName === "string" ? rawPlacementGroupName : "" - const rawCount = watch("count") - const count = typeof rawCount === "number" ? rawCount : 0 - const rawRootDeviceName = watch("rootDeviceName") - const rootDeviceName = - typeof rawRootDeviceName === "string" ? rawRootDeviceName : "" - const rawRootVolumeSize = watch("rootVolumeSize") - const rootVolumeSize = - typeof rawRootVolumeSize === "number" ? rawRootVolumeSize : undefined - const rawRootVolumeType = watch("rootVolumeType") - const rootVolumeType = - typeof rawRootVolumeType === "string" ? rawRootVolumeType : "" - const rawRootDeleteOnTermination = watch("rootDeleteOnTermination") - const rootDeleteOnTermination = - typeof rawRootDeleteOnTermination === "boolean" - ? rawRootDeleteOnTermination - : true + const imageId = watch("imageId") ?? "" + const instanceType = watch("instanceType") ?? "" + const keyName = watch("keyName") ?? "" + const subnetId = watch("subnetId") ?? "" + const placementGroupName = watch("placementGroupName") ?? "" + const count = watch("count") ?? 0 + const rootDeviceName = watch("rootDeviceName") ?? "" + const rootVolumeSize = watch("rootVolumeSize") + const rootVolumeType = watch("rootVolumeType") + const rootDeleteOnTermination = watch("rootDeleteOnTermination") const parts = [ { @@ -1033,17 +1012,11 @@ function buildRunInstancesCommands( // Security groups — create-new emits create + authorize commands and feeds // $SG_ID into run-instances; select-existing passes the chosen IDs. - const rawSgMode = watch("securityGroupMode") - const sgMode = typeof rawSgMode === "string" ? rawSgMode : "create" - const rawNewSgName = watch("newSgName") - const sgName = typeof rawNewSgName === "string" ? rawNewSgName : "" - const rawNewSgDesc = watch("newSgDescription") - const sgDesc = typeof rawNewSgDesc === "string" ? rawNewSgDesc : "" - const rawRuleSource = watch("ruleSource") - const ruleSource = - typeof rawRuleSource === "string" ? rawRuleSource : "anywhere" - const rawCustomCidr = watch("customCidr") - const customCidr = typeof rawCustomCidr === "string" ? rawCustomCidr : "" + const sgMode = watch("securityGroupMode") || "create" + const sgName = watch("newSgName") ?? "" + const sgDesc = watch("newSgDescription") ?? "" + const ruleSource = watch("ruleSource") || "anywhere" + const customCidr = watch("customCidr") ?? "" const sgCommands: CliCommand[] = [] if (sgMode === "create") { sgCommands.push({ @@ -1103,10 +1076,7 @@ function buildRunInstancesCommands( { type: "value" as const, value: " $SG_ID" }, ) } else { - const rawSgIds = watch("securityGroupIds") - const sgIds = Array.isArray(rawSgIds) - ? rawSgIds.filter((id): id is string => typeof id === "string") - : [] + const sgIds = watch("securityGroupIds") ?? [] if (sgIds.length > 0) { parts.push( { type: "flag" as const, value: " \\\n --security-group-ids" }, @@ -1117,17 +1087,21 @@ function buildRunInstancesCommands( const hasStorageOverride = rootVolumeSize !== undefined || - rawRootVolumeType !== undefined || - rawRootDeleteOnTermination !== undefined + rootVolumeType !== undefined || + rootDeleteOnTermination !== undefined if (hasStorageOverride) { - const ebs: Record = {} + const ebs: { + VolumeSize?: number + VolumeType?: string + DeleteOnTermination?: boolean + } = {} if (rootVolumeSize !== undefined) { ebs.VolumeSize = rootVolumeSize } if (rootVolumeType) { ebs.VolumeType = rootVolumeType } - if (rawRootDeleteOnTermination !== undefined) { + if (rootDeleteOnTermination !== undefined) { ebs.DeleteOnTermination = rootDeleteOnTermination } const bdm = JSON.stringify([ diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/-components/create-key-pair-page.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/-components/create-key-pair-page.tsx index 31f6b5786..7e8a90c29 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/-components/create-key-pair-page.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/-components/create-key-pair-page.tsx @@ -1,7 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useNavigate } from "@tanstack/react-router" import { useState } from "react" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -51,8 +56,6 @@ export function CreateKeyPairPage() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateKeyPairData) => { const response = await createMutation.mutateAsync(data) @@ -127,7 +130,7 @@ export function CreateKeyPairPage() { - + {/* Actions */} unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawKeyName = watch("keyName") - const keyName = typeof rawKeyName === "string" ? rawKeyName : "" - const rawKeyType = watch("keyType") + const keyName = values.keyName ?? "" + const rawKeyType = values.keyType const keyType = typeof rawKeyType === "string" ? rawKeyType : "rsa" return [ diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/import-key-pair.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/import-key-pair.tsx index a122858fc..a79da663f 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/import-key-pair.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(key)/import-key-pair.tsx @@ -1,6 +1,10 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { useForm, useWatch } from "react-hook-form" +import { + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -41,8 +45,6 @@ function ImportKeyPair() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: ImportKeyPairData) => { await importMutation.mutateAsync(data) @@ -98,7 +100,7 @@ function ImportKeyPair() { - + {/* Actions */} unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawKeyName = watch("keyName") - const keyName = typeof rawKeyName === "string" ? rawKeyName : "" + const keyName = values.keyName ?? "" return [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(load-balancers)/-components/create-load-balancer-page.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(load-balancers)/-components/create-load-balancer-page.tsx index c0d57390d..6b3da0cdc 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(load-balancers)/-components/create-load-balancer-page.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(load-balancers)/-components/create-load-balancer-page.tsx @@ -3,7 +3,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { useNavigate } from "@tanstack/react-router" import { useState } from "react" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + useForm, + type UseFormWatch, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -790,35 +795,20 @@ export function CreateLoadBalancerPage() { } function buildCreateLbCommands( - watch: (name?: string) => unknown, - tgWatch: (name?: string) => unknown, + watch: UseFormWatch, + tgWatch: UseFormWatch, ): CliCommand[] { - const asString = (key: string): string => { - const raw = watch(key) - return typeof raw === "string" ? raw : "" - } - const asStringArray = (key: string): string[] => { - const raw = watch(key) - return Array.isArray(raw) - ? raw.filter((v): v is string => typeof v === "string") - : [] - } - const asNumber = (key: string): number | undefined => { - const raw = watch(key) - return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined - } - - const name = asString("name") || "" - const lbTypeValue = asString("type") || "application" - const scheme = asString("scheme") || "internet-facing" - const subnets = asStringArray("subnetIds") - const sgs = asStringArray("securityGroupIds") - const listenerProtocol = asString("listener.protocol") || "HTTP" - const listenerPort = asNumber("listener.port") ?? 80 - const certificateArn = asString("listener.certificateArn") - const sslPolicy = asString("listener.sslPolicy") - const tgMode = asString("listener.targetGroupMode") - const existingTgArn = asString("listener.existingTargetGroupArn") + const name = watch("name") || "" + const lbTypeValue = watch("type") || "application" + const scheme = watch("scheme") || "internet-facing" + const subnets = watch("subnetIds") + const sgs = watch("securityGroupIds") + const listenerProtocol = watch("listener.protocol") || "HTTP" + const listenerPort = watch("listener.port") ?? 80 + const certificateArn = watch("listener.certificateArn") + const sslPolicy = watch("listener.sslPolicy") + const tgMode = watch("listener.targetGroupMode") + const existingTgArn = watch("listener.existingTargetGroupArn") const commands: CliCommand[] = [] const comment: CommandPart = { @@ -829,21 +819,12 @@ function buildCreateLbCommands( : "# Create ALB with listener and default target group\n\n", } - const tgAsString = (key: string): string => { - const raw = tgWatch(key) - return typeof raw === "string" ? raw : "" - } - const tgAsNumber = (key: string): number | undefined => { - const raw = tgWatch(key) - return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined - } - // Either a create-target-group step (mode=new) or just a TG_ARN= assignment if (tgMode === "new") { - const tgName = tgAsString("name") || "" - const tgPort = tgAsNumber("port") ?? 80 - const tgVpc = tgAsString("vpcId") - const tgProtocol = tgAsString("protocol") || "HTTP" + const tgName = tgWatch("name") || "" + const tgPort = tgWatch("port") ?? 80 + const tgVpc = tgWatch("vpcId") + const tgProtocol = tgWatch("protocol") || "HTTP" commands.push({ label: "Create Target Group", parts: [ diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(nat-gateways)/create-nat-gateway.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(nat-gateways)/create-nat-gateway.tsx index b718594ea..4a846954e 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(nat-gateways)/create-nat-gateway.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(nat-gateways)/create-nat-gateway.tsx @@ -1,7 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { createFileRoute, Link, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -78,8 +83,6 @@ function CreateNatGateway() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateNatGatewayFormData) => { const result = await createMutation.mutateAsync({ @@ -207,7 +210,7 @@ function CreateNatGateway() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawSubnetId = watch("subnetId") - const subnetId = typeof rawSubnetId === "string" ? rawSubnetId : "" - const rawAllocationId = watch("allocationId") + const subnetId = values.subnetId ?? "" + const rawAllocationId = values.allocationId const allocationId = typeof rawAllocationId === "string" ? rawAllocationId : "" - const rawName = watch("name") - const name = typeof rawName === "string" ? rawName : "" + const name = values.name ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(placement-groups)/create-placement-group.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(placement-groups)/create-placement-group.tsx index d45aba8f1..38edcb408 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(placement-groups)/create-placement-group.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(placement-groups)/create-placement-group.tsx @@ -1,6 +1,11 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -56,8 +61,6 @@ function CreatePlacementGroup() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreatePlacementGroupFormData) => { const result = await createMutation.mutateAsync(data) @@ -125,9 +128,7 @@ function CreatePlacementGroup() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawName = watch("groupName") - const name = typeof rawName === "string" ? rawName : "" - const rawStrategy = watch("strategy") - const strategy = typeof rawStrategy === "string" ? rawStrategy : "" + const name = values.groupName ?? "" + const strategy = values.strategy ?? "" const nameValue = name ? shellSingleQuote(name) : "" return [ diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(security-groups)/create-security-group.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(security-groups)/create-security-group.tsx index f9e605a10..e68f74fc3 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(security-groups)/create-security-group.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(security-groups)/create-security-group.tsx @@ -1,7 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -66,8 +71,6 @@ function CreateSecurityGroup() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateSecurityGroupFormData) => { const result = await createMutation.mutateAsync(data) @@ -161,9 +164,7 @@ function CreateSecurityGroup() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawName = watch("groupName") - const name = typeof rawName === "string" ? rawName : "" - const rawDesc = watch("description") - const desc = typeof rawDesc === "string" ? rawDesc : "" - const rawVpcId = watch("vpcId") - const vpcId = typeof rawVpcId === "string" ? rawVpcId : "" + const name = values.groupName ?? "" + const desc = values.description ?? "" + const vpcId = values.vpcId ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(snapshots)/create-snapshot.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(snapshots)/create-snapshot.tsx index 156a25e2c..25e615c58 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(snapshots)/create-snapshot.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(snapshots)/create-snapshot.tsx @@ -1,7 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { z } from "zod" import { BackLink } from "@/components/back-link" @@ -69,8 +74,6 @@ function CreateSnapshot() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateSnapshotFormData) => { await createMutation.mutateAsync(data) @@ -137,7 +140,7 @@ function CreateSnapshot() { /> - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawVolumeId = watch("volumeId") - const volumeId = typeof rawVolumeId === "string" ? rawVolumeId : "" - const rawDescription = watch("description") - const description = typeof rawDescription === "string" ? rawDescription : "" + const volumeId = values.volumeId ?? "" + const description = values.description ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(subnet)/create-subnet.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(subnet)/create-subnet.tsx index 2804dac2a..bc44170b7 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(subnet)/create-subnet.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(subnet)/create-subnet.tsx @@ -1,7 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -70,8 +75,6 @@ function CreateSubnet() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateSubnetFormData) => { const result = await createMutation.mutateAsync(data) @@ -201,7 +204,7 @@ function CreateSubnet() { /> - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawVpcId = watch("vpcId") - const vpcId = typeof rawVpcId === "string" ? rawVpcId : "" - const rawCidr = watch("cidrBlock") - const cidr = typeof rawCidr === "string" ? rawCidr : "" - const rawAz = watch("availabilityZone") - const az = typeof rawAz === "string" ? rawAz : "" + const vpcId = values.vpcId ?? "" + const cidr = values.cidrBlock ?? "" + const az = values.availabilityZone ?? "" const parts = [ { @@ -245,7 +245,7 @@ function buildCreateSubnetCommands( const commands: CliCommand[] = [{ label: "Create Subnet", parts }] - if (watch("mapPublicIpOnLaunch") === true) { + if (values.mapPublicIpOnLaunch === true) { commands.push({ label: "Enable auto-assign public IPv4", parts: [ diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(target-groups)/-components/create-target-group-page.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(target-groups)/-components/create-target-group-page.tsx index 2db20c36d..4a105c4ff 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(target-groups)/-components/create-target-group-page.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(target-groups)/-components/create-target-group-page.tsx @@ -1,7 +1,7 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { useNavigate } from "@tanstack/react-router" -import { useForm } from "react-hook-form" +import { useForm, type UseFormWatch } from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -106,29 +106,20 @@ export function CreateTargetGroupPage() { } function buildCreateTargetGroupCommands( - watch: (name?: string) => unknown, + watch: UseFormWatch, ): CliCommand[] { - const asString = (key: string): string => { - const raw = watch(key) - return typeof raw === "string" ? raw : "" - } - const asNumber = (key: string): number | undefined => { - const raw = watch(key) - return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined - } - - const name = asString("name") - const protocol = asString("protocol") || "HTTP" - const port = asNumber("port") - const vpcId = asString("vpcId") - const hcProtocol = asString("healthCheck.protocol") || "HTTP" - const hcPath = asString("healthCheck.path") || "/" - const hcPort = asString("healthCheck.port") || "traffic-port" - const hcInterval = asNumber("healthCheck.intervalSeconds") - const hcTimeout = asNumber("healthCheck.timeoutSeconds") - const hcHealthy = asNumber("healthCheck.healthyThresholdCount") - const hcUnhealthy = asNumber("healthCheck.unhealthyThresholdCount") - const hcMatcher = asString("healthCheck.matcher") || "200" + const name = watch("name") + const protocol = watch("protocol") || "HTTP" + const port = watch("port") + const vpcId = watch("vpcId") + const hcProtocol = watch("healthCheck.protocol") || "HTTP" + const hcPath = watch("healthCheck.path") || "/" + const hcPort = watch("healthCheck.port") || "traffic-port" + const hcInterval = watch("healthCheck.intervalSeconds") + const hcTimeout = watch("healthCheck.timeoutSeconds") + const hcHealthy = watch("healthCheck.healthyThresholdCount") + const hcUnhealthy = watch("healthCheck.unhealthyThresholdCount") + const hcMatcher = watch("healthCheck.matcher") || "200" const parts: CommandPart[] = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(volumes)/create-volume.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(volumes)/create-volume.tsx index e8611aa3b..4af706192 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(volumes)/create-volume.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(volumes)/create-volume.tsx @@ -1,7 +1,7 @@ import { zodResolver } from "@hookform/resolvers/zod" import { useSuspenseQuery } from "@tanstack/react-query" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm } from "react-hook-form" +import { Controller, useForm, type UseFormWatch } from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -141,12 +141,10 @@ function CreateVolume() { } function buildCreateVolumeCommands( - watch: (name?: string) => unknown, + watch: UseFormWatch, ): CliCommand[] { - const rawSize = watch("size") - const size = typeof rawSize === "number" ? rawSize : 0 - const rawAz = watch("availabilityZone") - const az = typeof rawAz === "string" ? rawAz : "" + const size = watch("size") + const az = watch("availabilityZone") return [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(vpc)/create-vpc.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(vpc)/create-vpc.tsx index bc862d3e8..c2ea1edf0 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(vpc)/create-vpc.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/ec2/(vpc)/create-vpc.tsx @@ -1,7 +1,12 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" import { useState } from "react" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + useForm, + type UseFormWatch, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -398,20 +403,16 @@ function CreateVpc() { } function buildCreateVpcCommands( - watch: (name?: string) => unknown, + watch: UseFormWatch, subnetCidrs: { publicSubnets: { cidr: string }[] privateSubnets: { cidr: string }[] }, ): CliCommand[] { - const rawMode = watch("mode") - const mode = typeof rawMode === "string" ? rawMode : "" - const rawCidr = watch("cidrBlock") - const cidr = typeof rawCidr === "string" ? rawCidr : "" - const rawTenancy = watch("tenancy") - const tenancy = typeof rawTenancy === "string" ? rawTenancy : "" - const rawNat = watch("natGateway") - const natGateway = typeof rawNat === "string" ? rawNat : "" + const mode = watch("mode") + const cidr = watch("cidrBlock") + const tenancy = watch("tenancy") + const natGateway = watch("natGateway") if (mode === "vpc-only") { const parts: CommandPart[] = [ diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(groups)/create-group.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(groups)/create-group.tsx index a64d3d76c..17a112286 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(groups)/create-group.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(groups)/create-group.tsx @@ -1,6 +1,10 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { useForm, useWatch } from "react-hook-form" +import { + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -36,8 +40,6 @@ function CreateGroup() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateGroupFormData) => { await createMutation.mutateAsync(data) @@ -78,7 +80,7 @@ function CreateGroup() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawGroupName = watch("groupName") - const groupName = typeof rawGroupName === "string" ? rawGroupName : "" - const rawPath = watch("path") - const path = typeof rawPath === "string" ? rawPath : "" + const groupName = values.groupName ?? "" + const path = values.path ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(instance-profiles)/create-instance-profile.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(instance-profiles)/create-instance-profile.tsx index 98adda8d7..583b4c95b 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(instance-profiles)/create-instance-profile.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(instance-profiles)/create-instance-profile.tsx @@ -1,6 +1,10 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { useForm, useWatch } from "react-hook-form" +import { + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -41,8 +45,6 @@ function CreateInstanceProfile() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateInstanceProfileFormData) => { await createMutation.mutateAsync(data) @@ -86,7 +88,7 @@ function CreateInstanceProfile() { unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawName = watch("instanceProfileName") - const name = typeof rawName === "string" ? rawName : "" - const rawPath = watch("path") - const path = typeof rawPath === "string" ? rawPath : "" + const name = values.instanceProfileName ?? "" + const path = values.path ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(policies)/create-policy.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(policies)/create-policy.tsx index b8871ae18..8a8143839 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(policies)/create-policy.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(policies)/create-policy.tsx @@ -1,6 +1,11 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -55,8 +60,6 @@ function CreatePolicy() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreatePolicyFormData) => { await createMutation.mutateAsync(data) @@ -123,7 +126,7 @@ function CreatePolicy() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawName = watch("policyName") - const name = typeof rawName === "string" ? rawName : "" - const rawDesc = watch("description") - const desc = typeof rawDesc === "string" ? rawDesc : "" - const rawDoc = watch("policyDocument") - const doc = typeof rawDoc === "string" ? rawDoc : "" + const name = values.policyName ?? "" + const desc = values.description ?? "" + const doc = values.policyDocument ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(roles)/create-role.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(roles)/create-role.tsx index feede3dcd..1482e6193 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(roles)/create-role.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(roles)/create-role.tsx @@ -1,6 +1,11 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { Controller, useForm, useWatch } from "react-hook-form" +import { + Controller, + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -44,8 +49,6 @@ function CreateRole() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateRoleFormData) => { await createMutation.mutateAsync(data) @@ -111,7 +114,7 @@ function CreateRole() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawName = watch("roleName") - const name = typeof rawName === "string" ? rawName : "" - const rawDesc = watch("description") - const desc = typeof rawDesc === "string" ? rawDesc : "" - const rawDoc = watch("assumeRolePolicyDocument") - const doc = typeof rawDoc === "string" ? rawDoc : "" + const name = values.roleName ?? "" + const desc = values.description ?? "" + const doc = values.assumeRolePolicyDocument ?? "" const parts = [ { diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(users)/create-user.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(users)/create-user.tsx index 0d6531e01..9643a0752 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(users)/create-user.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/iam/(users)/create-user.tsx @@ -1,6 +1,10 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { useForm, useWatch } from "react-hook-form" +import { + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -36,8 +40,6 @@ function CreateUser() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateUserFormData) => { await createMutation.mutateAsync(data) @@ -75,7 +77,7 @@ function CreateUser() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawUserName = watch("userName") - const userName = typeof rawUserName === "string" ? rawUserName : "" - const rawPath = watch("path") - const path = typeof rawPath === "string" ? rawPath : "" + const userName = values.userName ?? "" + const path = values.path ?? "" const parts = [ { type: "bin" as const, value: "AWS_PROFILE=spinifex aws iam create-user" }, diff --git a/spinifex/services/spinifexui/frontend/src/routes/_auth/s3/(buckets)/create-bucket.tsx b/spinifex/services/spinifexui/frontend/src/routes/_auth/s3/(buckets)/create-bucket.tsx index 3a0bcb775..16cddb1b6 100644 --- a/spinifex/services/spinifexui/frontend/src/routes/_auth/s3/(buckets)/create-bucket.tsx +++ b/spinifex/services/spinifexui/frontend/src/routes/_auth/s3/(buckets)/create-bucket.tsx @@ -1,6 +1,10 @@ import { zodResolver } from "@hookform/resolvers/zod" import { createFileRoute, useNavigate } from "@tanstack/react-router" -import { useForm, useWatch } from "react-hook-form" +import { + type DeepPartialSkipArrayKey, + useForm, + useWatch, +} from "react-hook-form" import { BackLink } from "@/components/back-link" import { @@ -40,8 +44,6 @@ function CreateBucket() { }) const values = useWatch({ control }) - const cliWatch = (name?: string): unknown => - name ? (values as Record)[name] : undefined const onSubmit = async (data: CreateBucketFormData) => { await createMutation.mutateAsync(data) @@ -76,7 +78,7 @@ function CreateBucket() { - + unknown, + values: DeepPartialSkipArrayKey, ): CliCommand[] { - const rawBucket = watch("bucketName") - const bucket = typeof rawBucket === "string" ? rawBucket : "" + const bucket = values.bucketName ?? "" return [ {