77 */
88import * as Accordion from '@radix-ui/react-accordion'
99import cn from 'classnames'
10- import { useEffect , useRef , useState } from 'react'
10+ import { useEffect , useMemo , useRef , useState } from 'react'
1111import { useWatch , type Control } from 'react-hook-form'
1212import { useNavigate , type LoaderFunctionArgs } from 'react-router-dom'
1313import type { SetRequired } from 'type-fest'
@@ -25,19 +25,15 @@ import {
2525import {
2626 DirectionRightIcon ,
2727 EmptyMessage ,
28- FieldLabel ,
2928 FormDivider ,
3029 Images16Icon ,
3130 Instances24Icon ,
32- Key16Icon ,
3331 Message ,
3432 RadioCard ,
35- Table ,
3633 Tabs ,
3734 TextInputHint ,
38- Truncate ,
3935} from '@oxide/ui'
40- import { formatDateTime , GiB , invariant } from '@oxide/util'
36+ import { GiB , invariant } from '@oxide/util'
4137
4238import {
4339 CheckboxField ,
@@ -51,6 +47,7 @@ import {
5147 NameField ,
5248 NetworkInterfaceField ,
5349 RadioFieldDyn ,
50+ SshKeysField ,
5451 TextField ,
5552 type DiskTableItem ,
5653} from 'app/components/form'
@@ -69,6 +66,8 @@ export type InstanceCreateInput = Assign<
6966 bootDiskSize : number
7067 image : string
7168 userData : File | null
69+ // ssh keys are always specified. we do not need the undefined case
70+ sshPublicKeys : NonNullable < InstanceCreate [ 'sshPublicKeys' ] >
7271 }
7372>
7473
@@ -91,6 +90,8 @@ const baseDefaultValues: InstanceCreateInput = {
9190 disks : [ ] ,
9291 networkInterfaces : { type : 'default' } ,
9392
93+ sshPublicKeys : [ ] ,
94+
9495 start : true ,
9596
9697 userData : null ,
@@ -135,9 +136,13 @@ export function CreateInstanceForm() {
135136
136137 const defaultImage = allImages [ 0 ]
137138
139+ const { data : sshKeys } = usePrefetchedApiQuery ( 'currentUserSshKeyList' , { } )
140+ const allKeys = useMemo ( ( ) => sshKeys . items . map ( ( key ) => key . id ) , [ sshKeys ] )
141+
138142 const defaultValues : InstanceCreateInput = {
139143 ...baseDefaultValues ,
140144 image : defaultImage ?. id || '' ,
145+ sshPublicKeys : allKeys ,
141146 // Use 2x the image size as the default boot disk size
142147 bootDiskSize : Math . ceil ( defaultImage ?. size / GiB ) * 2 || 10 ,
143148 }
@@ -210,6 +215,7 @@ export function CreateInstanceForm() {
210215 externalIps : [ { type : 'ephemeral' } ] ,
211216 start : values . start ,
212217 networkInterfaces : values . networkInterfaces ,
218+ sshPublicKeys : values . sshPublicKeys ,
213219 userData,
214220 } ,
215221 } )
@@ -417,7 +423,7 @@ export function CreateInstanceForm() {
417423 < FormDivider />
418424 < Form . Heading id = "authentication" > Authentication</ Form . Heading >
419425
420- < SshKeysTable />
426+ < SshKeysField control = { control } />
421427
422428 < FormDivider />
423429 < Form . Heading id = "advanced" > Advanced</ Form . Heading >
@@ -517,70 +523,6 @@ function AccordionItem({ value, label, children, isOpen }: AccordionItemProps) {
517523 )
518524}
519525
520- const SshKeysTable = ( ) => {
521- const keys = usePrefetchedApiQuery ( 'currentUserSshKeyList' , { } ) . data ?. items || [ ]
522-
523- return (
524- < div className = "max-w-lg" >
525- < div className = "mb-2" >
526- < FieldLabel id = "ssh-keys-label" > SSH keys</ FieldLabel >
527- < TextInputHint id = "ssh-keys-label-help-text" >
528- SSH keys can be added and removed in your user settings
529- </ TextInputHint >
530- </ div >
531-
532- { keys . length > 0 ? (
533- < Table className = "w-full" >
534- < Table . Header >
535- < Table . HeaderRow >
536- < Table . HeadCell > Name</ Table . HeadCell >
537- < Table . HeadCell > Created</ Table . HeadCell >
538- </ Table . HeaderRow >
539- </ Table . Header >
540- < Table . Body >
541- { keys . map ( ( key ) => (
542- < Table . Row key = { key . id } >
543- < Table . Cell height = "auto" >
544- < Truncate text = { key . name } maxLength = { 28 } />
545- </ Table . Cell >
546- < Table . Cell height = "auto" className = "text-secondary" >
547- { formatDateTime ( key . timeCreated ) }
548- </ Table . Cell >
549- </ Table . Row >
550- ) ) }
551- </ Table . Body >
552- </ Table >
553- ) : (
554- < div className = "mb-4 flex max-w-lg items-center justify-center rounded-lg border p-6 border-default" >
555- < EmptyMessage
556- icon = { < Key16Icon /> }
557- title = "No SSH keys"
558- body = "You need to add a SSH key to be able to see it here"
559- />
560- </ div >
561- ) }
562-
563- < Message
564- variant = "notice"
565- content = {
566- < >
567- If your image supports the cidata volume and{ ' ' }
568- < a
569- target = "_blank"
570- href = "https://cloudinit.readthedocs.io/en/latest/"
571- rel = "noreferrer"
572- >
573- cloud-init
574- </ a >
575- , the keys above will be added to your instance. Keys are added when the
576- instance is created and are not updated after instance launch.
577- </ >
578- }
579- />
580- </ div >
581- )
582- }
583-
584526const renderLargeRadioCards = ( category : string ) => {
585527 return PRESETS . filter ( ( option ) => option . category === category ) . map ( ( option ) => (
586528 < RadioCard key = { option . id } value = { option . id } >
0 commit comments