refactor(ui): type the form values the CLI panels read (#860)

This commit is contained in:
Julian Sommer
2026-08-24 10:59:33 +10:00
committed by GitHub
parent ae112a5a49
commit 52ae70ca33
18 changed files with 215 additions and 267 deletions
@@ -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<CreateInstanceFormData>,
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<string, unknown> = {}
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([
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateKeyPairData) => {
const response = await createMutation.mutateAsync(data)
@@ -127,7 +130,7 @@ export function CreateKeyPairPage() {
<FieldError errors={[errors.keyType]} />
</Field>
<CliCommandPanel commands={buildCreateKeyPairCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateKeyPairCommands(values)} />
{/* Actions */}
<FormActions
@@ -154,11 +157,10 @@ export function CreateKeyPairPage() {
}
function buildCreateKeyPairCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateKeyPairData>,
): 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 [
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: ImportKeyPairData) => {
await importMutation.mutateAsync(data)
@@ -98,7 +100,7 @@ function ImportKeyPair() {
<FieldError errors={[errors.publicKeyMaterial]} />
</Field>
<CliCommandPanel commands={buildImportKeyPairCommands(cliWatch)} />
<CliCommandPanel commands={buildImportKeyPairCommands(values)} />
{/* Actions */}
<FormActions
@@ -116,10 +118,9 @@ function ImportKeyPair() {
}
function buildImportKeyPairCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<ImportKeyPairData>,
): CliCommand[] {
const rawKeyName = watch("keyName")
const keyName = typeof rawKeyName === "string" ? rawKeyName : ""
const keyName = values.keyName ?? ""
return [
{
@@ -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<CreateLoadBalancerFormData>,
tgWatch: UseFormWatch<CreateTargetGroupFormData>,
): 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") || "<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") || "<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") || "<TG-Name>"
const tgPort = tgAsNumber("port") ?? 80
const tgVpc = tgAsString("vpcId")
const tgProtocol = tgAsString("protocol") || "HTTP"
const tgName = tgWatch("name") || "<TG-Name>"
const tgPort = tgWatch("port") ?? 80
const tgVpc = tgWatch("vpcId")
const tgProtocol = tgWatch("protocol") || "HTTP"
commands.push({
label: "Create Target Group",
parts: [
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateNatGatewayFormData) => {
const result = await createMutation.mutateAsync({
@@ -207,7 +210,7 @@ function CreateNatGateway() {
<FieldError errors={[errors.allocationId]} />
</Field>
<CliCommandPanel commands={buildCreateNatGatewayCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateNatGatewayCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -224,15 +227,13 @@ function CreateNatGateway() {
}
function buildCreateNatGatewayCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateNatGatewayFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreatePlacementGroupFormData) => {
const result = await createMutation.mutateAsync(data)
@@ -125,9 +128,7 @@ function CreatePlacementGroup() {
<FieldError errors={[errors.strategy]} />
</Field>
<CliCommandPanel
commands={buildCreatePlacementGroupCommands(cliWatch)}
/>
<CliCommandPanel commands={buildCreatePlacementGroupCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -148,12 +149,10 @@ function shellSingleQuote(value: string): string {
}
function buildCreatePlacementGroupCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreatePlacementGroupFormData>,
): 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) : "<GroupName>"
return [
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateSecurityGroupFormData) => {
const result = await createMutation.mutateAsync(data)
@@ -161,9 +164,7 @@ function CreateSecurityGroup() {
<FieldError errors={[errors.vpcId]} />
</Field>
<CliCommandPanel
commands={buildCreateSecurityGroupCommands(cliWatch)}
/>
<CliCommandPanel commands={buildCreateSecurityGroupCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -180,14 +181,11 @@ function CreateSecurityGroup() {
}
function buildCreateSecurityGroupCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateSecurityGroupFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateSnapshotFormData) => {
await createMutation.mutateAsync(data)
@@ -137,7 +140,7 @@ function CreateSnapshot() {
/>
</Field>
<CliCommandPanel commands={buildCreateSnapshotCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateSnapshotCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -154,12 +157,10 @@ function CreateSnapshot() {
}
function buildCreateSnapshotCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateSnapshotFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateSubnetFormData) => {
const result = await createMutation.mutateAsync(data)
@@ -201,7 +204,7 @@ function CreateSubnet() {
/>
</Field>
<CliCommandPanel commands={buildCreateSubnetCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateSubnetCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -216,14 +219,11 @@ function CreateSubnet() {
}
function buildCreateSubnetCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateSubnetFormData>,
): 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: [
@@ -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<CreateTargetGroupFormData>,
): 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[] = [
{
@@ -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<CreateVolumeFormData>,
): 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 [
{
@@ -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<CreateVpcWizardFormData>,
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[] = [
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateGroupFormData) => {
await createMutation.mutateAsync(data)
@@ -78,7 +80,7 @@ function CreateGroup() {
<FieldError errors={[errors.path]} />
</Field>
<CliCommandPanel commands={buildCreateGroupCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateGroupCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -93,12 +95,10 @@ function CreateGroup() {
}
function buildCreateGroupCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateGroupFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateInstanceProfileFormData) => {
await createMutation.mutateAsync(data)
@@ -86,7 +88,7 @@ function CreateInstanceProfile() {
</Field>
<CliCommandPanel
commands={buildCreateInstanceProfileCommands(cliWatch)}
commands={buildCreateInstanceProfileCommands(values)}
/>
<FormActions
@@ -104,12 +106,10 @@ function CreateInstanceProfile() {
}
function buildCreateInstanceProfileCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateInstanceProfileFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreatePolicyFormData) => {
await createMutation.mutateAsync(data)
@@ -123,7 +126,7 @@ function CreatePolicy() {
<FieldError errors={[errors.policyDocument]} />
</Field>
<CliCommandPanel commands={buildCreatePolicyCommands(cliWatch)} />
<CliCommandPanel commands={buildCreatePolicyCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -138,14 +141,11 @@ function CreatePolicy() {
}
function buildCreatePolicyCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreatePolicyFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateRoleFormData) => {
await createMutation.mutateAsync(data)
@@ -111,7 +114,7 @@ function CreateRole() {
<FieldError errors={[errors.assumeRolePolicyDocument]} />
</Field>
<CliCommandPanel commands={buildCreateRoleCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateRoleCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -126,14 +129,11 @@ function CreateRole() {
}
function buildCreateRoleCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateRoleFormData>,
): 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 = [
{
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateUserFormData) => {
await createMutation.mutateAsync(data)
@@ -75,7 +77,7 @@ function CreateUser() {
<FieldError errors={[errors.path]} />
</Field>
<CliCommandPanel commands={buildCreateUserCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateUserCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -90,12 +92,10 @@ function CreateUser() {
}
function buildCreateUserCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateUserFormData>,
): 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" },
@@ -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<string, unknown>)[name] : undefined
const onSubmit = async (data: CreateBucketFormData) => {
await createMutation.mutateAsync(data)
@@ -76,7 +78,7 @@ function CreateBucket() {
<FieldError errors={[errors.bucketName]} />
</Field>
<CliCommandPanel commands={buildCreateBucketCommands(cliWatch)} />
<CliCommandPanel commands={buildCreateBucketCommands(values)} />
<FormActions
isPending={createMutation.isPending}
@@ -91,10 +93,9 @@ function CreateBucket() {
}
function buildCreateBucketCommands(
watch: (name?: string) => unknown,
values: DeepPartialSkipArrayKey<CreateBucketFormData>,
): CliCommand[] {
const rawBucket = watch("bucketName")
const bucket = typeof rawBucket === "string" ? rawBucket : ""
const bucket = values.bucketName ?? ""
return [
{