-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathAttachFloatingIpModal.tsx
More file actions
128 lines (121 loc) · 3.4 KB
/
AttachFloatingIpModal.tsx
File metadata and controls
128 lines (121 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright Oxide Computer Company
*/
import { useQuery } from '@tanstack/react-query'
import { useForm } from 'react-hook-form'
import {
api,
qErrorsAllowed,
queryClient,
useApiMutation,
type FloatingIp,
type Instance,
} from '~/api'
import { ListboxField } from '~/components/form/fields/ListboxField'
import { HL } from '~/components/HL'
import { addToast } from '~/stores/toast'
import { Message } from '~/ui/lib/Message'
import { Slash } from '~/ui/lib/Slash'
import { ModalForm } from './form/ModalForm'
function IpPoolName({ ipPoolId }: { ipPoolId: string }) {
const { data: result } = useQuery(
qErrorsAllowed(
api.ipPoolView,
{ path: { pool: ipPoolId } },
{
errorsExpected: {
explanation: 'the referenced IP pool may have been deleted.',
statusCode: 404,
},
}
)
)
// As with IpPoolCell, this should never happen, but to be safe …
if (!result || result.type === 'error') return null
return (
<>
<Slash />
<span>{result.data.name}</span>
</>
)
}
function FloatingIpLabel({ fip }: { fip: FloatingIp }) {
return (
<div className="text-secondary selected:text-accent-secondary">
<div>{fip.name}</div>
<div className="flex gap-0.5">
<div>{fip.ip}</div>
<IpPoolName ipPoolId={fip.ipPoolId} />
{fip.description && (
<>
<Slash />
<div className="grow overflow-hidden text-left text-ellipsis whitespace-pre">
{fip.description}
</div>
</>
)}
</div>
</div>
)
}
export const AttachFloatingIpModal = ({
floatingIps,
instance,
onDismiss,
}: {
floatingIps: Array<FloatingIp>
instance: Instance
onDismiss: () => void
}) => {
const floatingIpAttach = useApiMutation(api.floatingIpAttach, {
onSuccess(floatingIp) {
queryClient.invalidateEndpoint('floatingIpList')
queryClient.invalidateEndpoint('instanceExternalIpList')
// prettier-ignore
addToast(<>IP <HL>{floatingIp.name}</HL> attached</>)
onDismiss()
},
})
const form = useForm({ defaultValues: { floatingIp: '' } })
const floatingIp = form.watch('floatingIp')
return (
<ModalForm
form={form}
onDismiss={onDismiss}
submitLabel="Attach floating IP"
submitError={floatingIpAttach.error}
loading={floatingIpAttach.isPending}
title="Attach floating IP"
onSubmit={() =>
floatingIpAttach.mutate({
path: { floatingIp }, // note that this is an ID!
body: { kind: 'instance', parent: instance.id },
})
}
submitDisabled={!floatingIp ? 'Select a floating IP' : undefined}
>
<Message
variant="info"
content={`Instance ‘${instance.name}’ will be reachable at the selected IP address`}
/>
<form>
<ListboxField
control={form.control}
name="floatingIp"
label="Floating IP"
placeholder="Select a floating IP"
items={floatingIps.map((ip) => ({
value: ip.id,
label: <FloatingIpLabel fip={ip} />,
selectedLabel: ip.name,
}))}
required
/>
</form>
</ModalForm>
)
}