Skip to content

Commit f0cf2be

Browse files
authored
Add floating IP edit form (#2033)
1 parent 3ad46af commit f0cf2be

File tree

12 files changed

+246
-7
lines changed

12 files changed

+246
-7
lines changed

OMICRON_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
d514878f417a94247791bd5564fbaafa9b4170a0
1+
1f26c66921b9215bfe11d750514939bcdc11ae12

app/api/__generated__/Api.ts

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/api/__generated__/OMICRON_VERSION

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/api/__generated__/msw-handlers.ts

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/api/__generated__/validate.ts

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/forms/floating-ip-edit.tsx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright Oxide Computer Company
7+
*/
8+
import { useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
9+
10+
import {
11+
apiQueryClient,
12+
useApiMutation,
13+
useApiQueryClient,
14+
usePrefetchedApiQuery,
15+
} from '@oxide/api'
16+
17+
import { DescriptionField } from '~/components/form/fields/DescriptionField'
18+
import { NameField } from '~/components/form/fields/NameField'
19+
import { SideModalForm } from '~/components/form/SideModalForm'
20+
import { getFloatingIpSelector, useFloatingIpSelector, useForm, useToast } from 'app/hooks'
21+
import { pb } from 'app/util/path-builder'
22+
23+
EditFloatingIpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
24+
const { floatingIp, project } = getFloatingIpSelector(params)
25+
await apiQueryClient.prefetchQuery('floatingIpView', {
26+
path: { floatingIp },
27+
query: { project },
28+
})
29+
return null
30+
}
31+
32+
export function EditFloatingIpSideModalForm() {
33+
const queryClient = useApiQueryClient()
34+
const addToast = useToast()
35+
const navigate = useNavigate()
36+
37+
const floatingIpSelector = useFloatingIpSelector()
38+
39+
const onDismiss = () => navigate(pb.floatingIps({ project: floatingIpSelector.project }))
40+
41+
const { data: floatingIp } = usePrefetchedApiQuery('floatingIpView', {
42+
path: { floatingIp: floatingIpSelector.floatingIp },
43+
query: { project: floatingIpSelector.project },
44+
})
45+
46+
const editFloatingIp = useApiMutation('floatingIpUpdate', {
47+
onSuccess(_floatingIp) {
48+
queryClient.invalidateQueries('floatingIpList')
49+
addToast({ content: 'Your floating IP has been updated' })
50+
onDismiss()
51+
},
52+
})
53+
54+
const form = useForm({ defaultValues: floatingIp })
55+
56+
return (
57+
<SideModalForm
58+
id="edit-floating-ip-form"
59+
form={form}
60+
title="Edit floating IP"
61+
onDismiss={onDismiss}
62+
onSubmit={({ name, description }) => {
63+
editFloatingIp.mutate({
64+
path: { floatingIp: floatingIpSelector.floatingIp },
65+
query: { project: floatingIpSelector.project },
66+
body: { name, description },
67+
})
68+
}}
69+
loading={editFloatingIp.isPending}
70+
submitError={editFloatingIp.error}
71+
submitLabel="Save changes"
72+
>
73+
<NameField name="name" control={form.control} />
74+
<DescriptionField name="description" control={form.control} />
75+
</SideModalForm>
76+
)
77+
}

app/pages/project/floating-ips/FloatingIpsPage.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
import { useState } from 'react'
99
import { useForm } from 'react-hook-form'
10-
import { Link, Outlet, type LoaderFunctionArgs } from 'react-router-dom'
10+
import { Link, Outlet, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
1111

1212
import {
1313
apiQueryClient,
@@ -66,6 +66,7 @@ export function FloatingIpsPage() {
6666
const { data: instances } = usePrefetchedApiQuery('instanceList', {
6767
query: { project },
6868
})
69+
const navigate = useNavigate()
6970
const getInstanceName = (instanceId: string) =>
7071
instances.items.find((i) => i.id === instanceId)?.name
7172

@@ -122,6 +123,20 @@ export function FloatingIpsPage() {
122123
},
123124
}
124125
return [
126+
{
127+
label: 'Edit',
128+
onActivate: () => {
129+
apiQueryClient.setQueryData(
130+
'floatingIpView',
131+
{
132+
path: { floatingIp: floatingIp.name },
133+
query: { project },
134+
},
135+
floatingIp
136+
)
137+
navigate(pb.floatingIpEdit({ project, floatingIp: floatingIp.name }))
138+
},
139+
},
125140
attachOrDetachAction,
126141
{
127142
label: 'Delete',

app/routes.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { RouterDataErrorBoundary } from './components/ErrorBoundary'
1111
import { NotFound } from './components/ErrorPage'
1212
import { CreateDiskSideModalForm } from './forms/disk-create'
1313
import { CreateFloatingIpSideModalForm } from './forms/floating-ip-create'
14+
import { EditFloatingIpSideModalForm } from './forms/floating-ip-edit'
1415
import { CreateIdpSideModalForm } from './forms/idp/create'
1516
import { EditIdpSideModalForm } from './forms/idp/edit'
1617
import {
@@ -350,13 +351,19 @@ export const routes = createRoutesFromElements(
350351
/>
351352
</Route>
352353

353-
<Route loader={FloatingIpsPage.loader} element={<FloatingIpsPage />}>
354+
<Route element={<FloatingIpsPage />} loader={FloatingIpsPage.loader}>
354355
<Route path="floating-ips" handle={{ crumb: 'Floating IPs' }} element={null} />
355356
<Route
356357
path="floating-ips-new"
357358
element={<CreateFloatingIpSideModalForm />}
358359
handle={{ crumb: 'New Floating IP' }}
359360
/>
361+
<Route
362+
path="floating-ips/:floatingIp/edit"
363+
element={<EditFloatingIpSideModalForm />}
364+
loader={EditFloatingIpSideModalForm.loader}
365+
handle={{ crumb: 'Edit Floating IP' }}
366+
/>
360367
</Route>
361368

362369
<Route element={<DisksPage />} loader={DisksPage.loader}>

app/util/path-builder.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { pb } from './path-builder'
1111

1212
// params can be the same for all of them because they only use what they need
1313
const params = {
14+
floatingIp: 'f',
1415
project: 'p',
1516
instance: 'i',
1617
vpc: 'v',
@@ -31,6 +32,8 @@ test('path builder', () => {
3132
"diskInventory": "/system/inventory/disks",
3233
"disks": "/projects/p/disks",
3334
"disksNew": "/projects/p/disks-new",
35+
"floatingIp": "/projects/p/floating-ips/f",
36+
"floatingIpEdit": "/projects/p/floating-ips/f/edit",
3437
"floatingIps": "/projects/p/floating-ips",
3538
"floatingIpsNew": "/projects/p/floating-ips-new",
3639
"instance": "/projects/p/instances/i",

app/util/path-builder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Image = Required<PP.Image>
2020
type Snapshot = Required<PP.Snapshot>
2121
type SiloImage = Required<PP.SiloImage>
2222
type IpPool = Required<PP.IpPool>
23+
type FloatingIp = Required<PP.FloatingIp>
2324

2425
export const pb = {
2526
projects: () => `/projects`,
@@ -68,6 +69,8 @@ export const pb = {
6869
vpcEdit: (params: Vpc) => `${pb.vpc(params)}/edit`,
6970
floatingIps: (params: Project) => `${pb.project(params)}/floating-ips`,
7071
floatingIpsNew: (params: Project) => `${pb.project(params)}/floating-ips-new`,
72+
floatingIp: (params: FloatingIp) => `${pb.floatingIps(params)}/${params.floatingIp}`,
73+
floatingIpEdit: (params: FloatingIp) => `${pb.floatingIp(params)}/edit`,
7174

7275
siloUtilization: () => '/utilization',
7376
siloAccess: () => '/access',

0 commit comments

Comments
 (0)