Skip to content

Commit aec681b

Browse files
authored
Instance create form: automatically select SSH key after creation (#3173)
After you create an SSH key in the instance create form, it should automatically be checked to be injected into the instance. Otherwise why would you be adding it? Actual fix is 9285551, second commit is a tiny bit of code cleanup. Did my favorite Claude thing and asked it whether we originally had a reason to not auto-select the SSH key. Definitively, no: > Ideally the new keys are pre-selected I suppose, though it might not be worth the work. #1867 (comment) https://github.com/user-attachments/assets/899bb513-d809-41a1-8e9d-7cc79ff09bbf
1 parent 0fe1729 commit aec681b

File tree

3 files changed

+25
-12
lines changed

3 files changed

+25
-12
lines changed

app/components/form/fields/SshKeysField.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ export function SshKeysField({
5555
control: Control<InstanceCreateInput>
5656
isSubmitting: boolean
5757
}) {
58-
const keys = usePrefetchedQuery(q(api.currentUserSshKeyList, {})).data?.items || []
58+
const allKeys = usePrefetchedQuery(q(api.currentUserSshKeyList, {})).data.items
5959
const [showAddSshKey, setShowAddSshKey] = useState(false)
6060

6161
const {
62-
field: { value, onChange },
62+
field: { value: selectedKeys, onChange },
6363
fieldState: { error },
6464
} = useController({
6565
control,
@@ -73,6 +73,8 @@ export function SshKeysField({
7373
},
7474
})
7575

76+
const allAreSelected = allKeys.length === selectedKeys.length
77+
7678
return (
7779
<div className="max-w-lg">
7880
<div className="mb-2">
@@ -81,11 +83,11 @@ export function SshKeysField({
8183
SSH keys can be added and removed in your user settings
8284
</TextInputHint>
8385
</div>
84-
{keys.length > 0 ? (
86+
{allKeys.length > 0 ? (
8587
<>
8688
<div className="space-y-2">
8789
<div className="flex flex-col space-y-2">
88-
{keys.map((key) => (
90+
{allKeys.map((key) => (
8991
<CheckboxField
9092
name="sshPublicKeys"
9193
control={control}
@@ -102,12 +104,10 @@ export function SshKeysField({
102104

103105
<Divider />
104106
<Checkbox
105-
checked={value.length === keys.length}
106-
indeterminate={value.length > 0 && value.length < keys.length}
107+
checked={allAreSelected}
108+
indeterminate={selectedKeys.length > 0 && !allAreSelected}
107109
// if fewer than all are checked, check all. if all are checked, check none
108-
onChange={() =>
109-
onChange(value.length < keys.length ? keys.map((key) => key.id) : [])
110-
}
110+
onChange={() => onChange(allAreSelected ? [] : allKeys.map((key) => key.id))}
111111
disabled={isSubmitting}
112112
>
113113
<span className="select-none">Select all</span>
@@ -140,6 +140,7 @@ export function SshKeysField({
140140
{showAddSshKey && (
141141
<SSHKeyCreate
142142
onDismiss={() => setShowAddSshKey(false)}
143+
onSuccess={(sshKey) => onChange([...selectedKeys, sshKey.id])}
143144
message={
144145
<Message
145146
variant="info"

app/forms/ssh-key-create.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
import { useForm } from 'react-hook-form'
99
import { useNavigate } from 'react-router'
1010

11-
import { api, queryClient, useApiMutation, type SshKeyCreate } from '@oxide/api'
11+
import {
12+
api,
13+
queryClient,
14+
useApiMutation,
15+
type SshKey,
16+
type SshKeyCreate,
17+
} from '@oxide/api'
1218

1319
import { DescriptionField } from '~/components/form/fields/DescriptionField'
1420
import { NameField } from '~/components/form/fields/NameField'
@@ -28,17 +34,19 @@ const defaultValues: SshKeyCreate = {
2834

2935
type Props = {
3036
onDismiss?: () => void
37+
onSuccess?: (sshKey: SshKey) => void
3138
message?: React.ReactNode
3239
}
3340

34-
export function SSHKeyCreate({ onDismiss, message }: Props) {
41+
export function SSHKeyCreate({ onDismiss, onSuccess, message }: Props) {
3542
const navigate = useNavigate()
3643

3744
const handleDismiss = onDismiss ? onDismiss : () => navigate(pb.sshKeys())
3845

3946
const createSshKey = useApiMutation(api.currentUserSshKeyCreate, {
4047
onSuccess(sshKey) {
4148
queryClient.invalidateEndpoint('currentUserSshKeyList')
49+
onSuccess?.(sshKey)
4250
handleDismiss()
4351
// prettier-ignore
4452
addToast(<>SSH key <HL>{sshKey.name}</HL> created</>)

test/e2e/instance-create.e2e.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,15 @@ test('add ssh key from instance create form', async ({ page }) => {
338338
await dialog.getByRole('button', { name: 'Add SSH Key' }).click()
339339

340340
await expect(newCheckbox).toBeVisible()
341-
await expect(newCheckbox).not.toBeChecked()
341+
await expect(newCheckbox).toBeChecked()
342+
343+
await closeToast(page)
342344

343345
// pop over to the real SSH keys page and see it there, why not
344346
await page.getByLabel('User menu').click()
345347
await page.getByRole('menuitem', { name: 'Settings' }).click()
348+
// the new key being auto-checked makes the form dirty, which triggers confirm leave
349+
await page.getByRole('button', { name: 'Leave this page' }).click()
346350
await page.getByRole('link', { name: 'SSH Keys' }).click()
347351
await expectRowVisible(page.getByRole('table'), { name: newKey, description: 'hi' })
348352
})

0 commit comments

Comments
 (0)