Skip to content

Commit 69a171c

Browse files
authored
chore: convert more route modules (#2716)
* convert image routes * convert more routes * convert more * convert ip pool forms, vpc, and disk create * convert image edit things (all claude code) * fix ip pool edit crumb * Convert InstancePage and all its tabs (all claude) * remove extra crumbs
1 parent e9dafb3 commit 69a171c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+348
-244
lines changed

.oxlintrc.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@
3535
{
3636
// default exports are needed in the route modules and the config files,
3737
// but we want to avoid them anywhere else
38-
"files": ["app/pages/**/*", "app/layouts/**/*", "app/forms/**/*", "*.config.ts", "*.config.mjs"],
38+
"files": [
39+
"app/pages/**/*",
40+
"app/layouts/**/*",
41+
"app/forms/**/*",
42+
"*.config.ts",
43+
"*.config.mjs"
44+
],
3945
"rules": {
4046
"import/no-default-export": "off"
4147
}

app/components/form/fields/SshKeysField.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { usePrefetchedApiQuery } from '@oxide/api'
1212
import { Key16Icon } from '@oxide/design-system/icons/react'
1313

1414
import type { InstanceCreateInput } from '~/forms/instance-create'
15-
import { Component as CreateSSHKeySideModalForm } from '~/forms/ssh-key-create'
15+
import { SSHKeyCreate } from '~/forms/ssh-key-create'
1616
import { Button } from '~/ui/lib/Button'
1717
import { Checkbox } from '~/ui/lib/Checkbox'
1818
import { Divider } from '~/ui/lib/Divider'
@@ -138,7 +138,7 @@ export function SshKeysField({
138138
</div>
139139
)}
140140
{showAddSshKey && (
141-
<CreateSSHKeySideModalForm
141+
<SSHKeyCreate
142142
onDismiss={() => setShowAddSshKey(false)}
143143
message={
144144
<Message

app/forms/disk-create.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import { filesize } from 'filesize'
99
import { useMemo } from 'react'
1010
import { useController, useForm, type Control } from 'react-hook-form'
11-
import { useNavigate, type NavigateFunction } from 'react-router'
1211

1312
import {
1413
useApiMutation,
@@ -59,11 +58,7 @@ type CreateSideModalFormProps = {
5958
* the RQ `onSuccess` defined for the mutation.
6059
*/
6160
onSubmit?: (diskCreate: DiskCreate) => void
62-
/**
63-
* Passing navigate is a bit of a hack to be able to do a nav from the routes
64-
* file. The callers that don't need the arg can ignore it.
65-
*/
66-
onDismiss: (navigate: NavigateFunction) => void
61+
onDismiss: () => void
6762
onSuccess?: (disk: Disk) => void
6863
unavailableDiskNames?: string[]
6964
}
@@ -75,14 +70,13 @@ export function CreateDiskSideModalForm({
7570
unavailableDiskNames = [],
7671
}: CreateSideModalFormProps) {
7772
const queryClient = useApiQueryClient()
78-
const navigate = useNavigate()
7973

8074
const createDisk = useApiMutation('diskCreate', {
8175
onSuccess(data) {
8276
queryClient.invalidateQueries('diskList')
8377
addToast(<>Disk <HL>{data.name}</HL> created</>) // prettier-ignore
8478
onSuccess?.(data)
85-
onDismiss(navigate)
79+
onDismiss()
8680
},
8781
})
8882

@@ -123,7 +117,7 @@ export function CreateDiskSideModalForm({
123117
form={form}
124118
formType="create"
125119
resourceName="disk"
126-
onDismiss={() => onDismiss(navigate)}
120+
onDismiss={onDismiss}
127121
onSubmit={({ size, ...rest }) => {
128122
const body = { size: size * GiB, ...rest }
129123
if (onSubmit) {

app/forms/image-edit.tsx

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,64 +6,21 @@
66
* Copyright Oxide Computer Company
77
*/
88
import { useForm } from 'react-hook-form'
9-
import { useNavigate, type LoaderFunctionArgs } from 'react-router'
9+
import { useNavigate } from 'react-router'
1010

11-
import { apiQueryClient, usePrefetchedApiQuery, type Image } from '@oxide/api'
11+
import { type Image } from '@oxide/api'
1212
import { Images16Icon } from '@oxide/design-system/icons/react'
1313

1414
import { DescriptionField } from '~/components/form/fields/DescriptionField'
1515
import { NameField } from '~/components/form/fields/NameField'
1616
import { TextField } from '~/components/form/fields/TextField'
1717
import { SideModalForm } from '~/components/form/SideModalForm'
18-
import {
19-
getProjectImageSelector,
20-
getSiloImageSelector,
21-
useProjectImageSelector,
22-
useSiloImageSelector,
23-
} from '~/hooks/use-params'
2418
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
2519
import { ResourceLabel } from '~/ui/lib/SideModal'
26-
import { pb } from '~/util/path-builder'
2720
import { capitalize } from '~/util/str'
2821
import { bytesToGiB } from '~/util/units'
2922

30-
export const ProjectImageEdit = {
31-
loader: async ({ params }: LoaderFunctionArgs) => {
32-
const { project, image } = getProjectImageSelector(params)
33-
await apiQueryClient.prefetchQuery('imageView', { path: { image }, query: { project } })
34-
return null
35-
},
36-
Component: EditProjectImageSideModalForm,
37-
}
38-
39-
export const SiloImageEdit = {
40-
loader: async ({ params }: LoaderFunctionArgs) => {
41-
const { image } = getSiloImageSelector(params)
42-
await apiQueryClient.prefetchQuery('imageView', { path: { image } })
43-
return null
44-
},
45-
Component: EditSiloImageSideModalForm,
46-
}
47-
48-
function EditProjectImageSideModalForm() {
49-
const { project, image } = useProjectImageSelector()
50-
const { data } = usePrefetchedApiQuery('imageView', {
51-
path: { image },
52-
query: { project },
53-
})
54-
55-
const dismissLink = pb.projectImages({ project })
56-
return <EditImageSideModalForm image={data} dismissLink={dismissLink} type="Project" />
57-
}
58-
59-
function EditSiloImageSideModalForm() {
60-
const { image } = useSiloImageSelector()
61-
const { data } = usePrefetchedApiQuery('imageView', { path: { image } })
62-
63-
return <EditImageSideModalForm image={data} dismissLink={pb.siloImages()} type="Silo" />
64-
}
65-
66-
function EditImageSideModalForm({
23+
export function EditImageSideModalForm({
6724
image,
6825
dismissLink,
6926
type,

app/forms/image-from-snapshot.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { NameField } from '~/components/form/fields/NameField'
2222
import { TextField } from '~/components/form/fields/TextField'
2323
import { SideModalForm } from '~/components/form/SideModalForm'
2424
import { HL } from '~/components/HL'
25+
import { titleCrumb } from '~/hooks/use-crumbs'
2526
import { getProjectSnapshotSelector, useProjectSnapshotSelector } from '~/hooks/use-params'
2627
import { addToast } from '~/stores/toast'
2728
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
@@ -38,13 +39,15 @@ const defaultValues: Omit<ImageCreate, 'source'> = {
3839
const snapshotView = ({ project, snapshot }: PP.Snapshot) =>
3940
apiq('snapshotView', { path: { snapshot }, query: { project } })
4041

41-
CreateImageFromSnapshotSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
42+
export async function clientLoader({ params }: LoaderFunctionArgs) {
4243
const { project, snapshot } = getProjectSnapshotSelector(params)
4344
await queryClient.prefetchQuery(snapshotView({ project, snapshot }))
4445
return null
4546
}
4647

47-
export function CreateImageFromSnapshotSideModalForm() {
48+
export const handle = titleCrumb('Create image from snapshot')
49+
50+
export default function CreateImageFromSnapshotSideModalForm() {
4851
const { snapshot, project } = useProjectSnapshotSelector()
4952
const { data } = usePrefetchedQuery(snapshotView({ project, snapshot }))
5053
const navigate = useNavigate()

app/forms/image-upload.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { NameField } from '~/components/form/fields/NameField'
3535
import { RadioField } from '~/components/form/fields/RadioField'
3636
import { TextField } from '~/components/form/fields/TextField'
3737
import { SideModalForm } from '~/components/form/SideModalForm'
38+
import { titleCrumb } from '~/hooks/use-crumbs'
3839
import { useProjectSelector } from '~/hooks/use-params'
3940
import { Message } from '~/ui/lib/Message'
4041
import { Modal } from '~/ui/lib/Modal'
@@ -175,11 +176,12 @@ const CHUNK_SIZE_BYTES = 512 * KiB
175176
// TODO: make sure cleanup, cancelEverything, and resetMainFlow are called in
176177
// the right places
177178

178-
Component.displayName = 'ImageCreate'
179+
export const handle = titleCrumb('Upload image')
180+
179181
/**
180182
* Upload an image. Opens a second modal to show upload progress.
181183
*/
182-
export function Component() {
184+
export default function ImageCreate() {
183185
const navigate = useNavigate()
184186
const queryClient = useApiQueryClient()
185187
const { project } = useProjectSelector()

app/forms/ip-pool-create.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
1414
import { NameField } from '~/components/form/fields/NameField'
1515
import { SideModalForm } from '~/components/form/SideModalForm'
1616
import { HL } from '~/components/HL'
17+
import { titleCrumb } from '~/hooks/use-crumbs'
1718
import { addToast } from '~/stores/toast'
1819
import { Message } from '~/ui/lib/Message'
1920
import { pb } from '~/util/path-builder'
@@ -23,7 +24,9 @@ const defaultValues: IpPoolCreate = {
2324
description: '',
2425
}
2526

26-
export function CreateIpPoolSideModalForm() {
27+
export const handle = titleCrumb('New IP pool')
28+
29+
export default function CreateIpPoolSideModalForm() {
2730
const navigate = useNavigate()
2831
const queryClient = useApiQueryClient()
2932

app/forms/ip-pool-edit.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,22 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
1919
import { NameField } from '~/components/form/fields/NameField'
2020
import { SideModalForm } from '~/components/form/SideModalForm'
2121
import { HL } from '~/components/HL'
22+
import { makeCrumb } from '~/hooks/use-crumbs'
2223
import { getIpPoolSelector, useIpPoolSelector } from '~/hooks/use-params'
2324
import { addToast } from '~/stores/toast'
2425
import { pb } from '~/util/path-builder'
2526

2627
import { IpPoolVisibilityMessage } from './ip-pool-create'
2728

28-
export async function loader({ params }: LoaderFunctionArgs) {
29+
export async function clientLoader({ params }: LoaderFunctionArgs) {
2930
const { pool } = getIpPoolSelector(params)
3031
await apiQueryClient.prefetchQuery('ipPoolView', { path: { pool } })
3132
return null
3233
}
3334

34-
Component.displayName = 'EditIpPoolSideModalForm'
35-
export function Component() {
35+
export const handle = makeCrumb('Edit IP pool')
36+
37+
export default function EditIpPoolSideModalForm() {
3638
const queryClient = useApiQueryClient()
3739
const navigate = useNavigate()
3840
const poolSelector = useIpPoolSelector()

app/forms/ip-pool-range-add.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useApiMutation, useApiQueryClient, type IpRange } from '@oxide/api'
1212

1313
import { TextField } from '~/components/form/fields/TextField'
1414
import { SideModalForm } from '~/components/form/SideModalForm'
15+
import { titleCrumb } from '~/hooks/use-crumbs'
1516
import { useIpPoolSelector } from '~/hooks/use-params'
1617
import { addToast } from '~/stores/toast'
1718
import { Message } from '~/ui/lib/Message'
@@ -59,8 +60,9 @@ function resolver(values: IpRange) {
5960
return Object.keys(errors).length > 0 ? { values: {}, errors } : { values, errors: {} }
6061
}
6162

62-
Component.displayName = 'IpPoolAddRange'
63-
export function Component() {
63+
export const handle = titleCrumb('Add Range')
64+
65+
export default function IpPoolAddRange() {
6466
const { pool } = useIpPoolSelector()
6567
const navigate = useNavigate()
6668
const queryClient = useApiQueryClient()

app/forms/project-create.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { DescriptionField } from '~/components/form/fields/DescriptionField'
1414
import { NameField } from '~/components/form/fields/NameField'
1515
import { SideModalForm } from '~/components/form/SideModalForm'
1616
import { HL } from '~/components/HL'
17+
import { titleCrumb } from '~/hooks/use-crumbs'
1718
import { addToast } from '~/stores/toast'
1819
import { pb } from '~/util/path-builder'
1920

@@ -22,8 +23,9 @@ const defaultValues: ProjectCreate = {
2223
description: '',
2324
}
2425

25-
Component.displayName = 'ProjectCreateSideModalForm'
26-
export function Component() {
26+
export const handle = titleCrumb('New project')
27+
28+
export default function ProjectCreateSideModalForm() {
2729
const navigate = useNavigate()
2830
const queryClient = useApiQueryClient()
2931

0 commit comments

Comments
 (0)