2020import React from 'react' ;
2121
2222import type { StoragePool } from '../../types' ;
23- import type { DialogValues , ValidationFailed } from './storageVolumeCreateBody' ;
24- import type { Dialogs } from 'dialogs' ;
23+ import { useDialogs } from 'dialogs' ;
2524
2625import { Button } from "@patternfly/react-core/dist/esm/components/Button" ;
2726import { Form } from "@patternfly/react-core/dist/esm/components/Form" ;
@@ -31,165 +30,138 @@ import {
3130import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip" ;
3231import cockpit from 'cockpit' ;
3332
34- import { ModalError } from 'cockpit-components-inline-notification.jsx' ;
35- import { DialogsContext } from 'dialogs.jsx' ;
36- import { units , getDefaultVolumeFormat , convertToUnit , isEmpty } from '../../helpers.js' ;
33+ import { convertToUnit } from '../../helpers.js' ;
3734import { storageVolumeCreate } from '../../libvirtApi/storageVolume.js' ;
38- import { VolumeCreateBody } from './storageVolumeCreateBody.jsx' ;
35+
36+ import {
37+ type VolumeCreateValue , VolumeCreate , init_VolumeCreate , validate_VolumeCreate ,
38+ } from './storageVolumeCreateBody.jsx' ;
39+ import {
40+ useDialogState ,
41+ DialogError , DialogErrorMessage ,
42+ DialogActionButton , DialogCancelButton ,
43+ } from '../common/dialog' ;
3944
4045const _ = cockpit . gettext ;
4146
42- interface CreateStorageVolumeModalProps {
43- idPrefix : string ;
44- storagePool : StoragePool ;
47+ interface CreateStorageVolumeValues {
48+ volume : VolumeCreateValue ,
4549}
4650
47- interface CreateStorageVolumeModalState extends DialogValues {
48- createInProgress : boolean ,
49- validate ?: boolean ;
50- dialogError : string | undefined ,
51- dialogErrorDetail ?: string | undefined ;
52- }
51+ const CreateStorageVolumeModal = ( {
52+ idPrefix,
53+ storagePool,
54+ } : {
55+ idPrefix : string ;
56+ storagePool : StoragePool ;
57+ } ) => {
58+ const Dialogs = useDialogs ( ) ;
5359
54- class CreateStorageVolumeModal extends React . Component < CreateStorageVolumeModalProps , CreateStorageVolumeModalState > {
55- static contextType = DialogsContext ;
56- declare context : Dialogs ;
57-
58- constructor ( props : CreateStorageVolumeModalProps ) {
59- super ( props ) ;
60- this . state = {
61- createInProgress : false ,
62- dialogError : undefined ,
63- volumeName : '' ,
64- size : 1 ,
65- unit : units . GiB . name ,
66- format : getDefaultVolumeFormat ( props . storagePool ) ,
60+ function init ( ) {
61+ return {
62+ volume : init_VolumeCreate ( storagePool ) ,
6763 } ;
68- this . dialogErrorSet = this . dialogErrorSet . bind ( this ) ;
69- this . onCreateClicked = this . onCreateClicked . bind ( this ) ;
70- this . onValueChanged = this . onValueChanged . bind ( this ) ;
71- this . validateParams = this . validateParams . bind ( this ) ;
7264 }
7365
74- dialogErrorSet ( text : string , detail : string ) {
75- this . setState ( { dialogError : text , dialogErrorDetail : detail } ) ;
66+ function validate ( ) {
67+ validate_VolumeCreate ( dlg . value ( "volume" ) ) ;
7668 }
7769
78- onValueChanged < K extends keyof DialogValues > ( key : K , value : DialogValues [ K ] ) {
79- this . setState ( { [ key ] : value } as Pick < CreateStorageVolumeModalState , K > ) ;
80- }
81-
82- validateParams ( ) {
83- const validationFailed : ValidationFailed = { } ;
84-
85- if ( isEmpty ( this . state . volumeName . trim ( ) ) )
86- validationFailed . volumeName = _ ( "Name must not be empty" ) ;
87- const poolCapacity = convertToUnit ( this . props . storagePool . capacity , units . B , this . state . unit ) ;
88- if ( this . state . size > poolCapacity )
89- validationFailed . size = cockpit . format ( _ ( "Storage volume size must not exceed the storage pool's capacity ($0 $1)" ) , poolCapacity . toFixed ( 2 ) , this . state . unit ) ;
90-
91- return validationFailed ;
92- }
93-
94- onCreateClicked ( ) {
95- const Dialogs = this . context ;
96- const validation = this . validateParams ( ) ;
97- if ( Object . getOwnPropertyNames ( validation ) . length > 0 ) {
98- this . setState ( { createInProgress : false , validate : true } ) ;
99- } else {
100- this . setState ( { createInProgress : true , validate : false } ) ;
101-
102- const { volumeName, format } = this . state ;
103- const { name, connectionName } = this . props . storagePool ;
104- const size = convertToUnit ( this . state . size , this . state . unit , 'MiB' ) ;
105-
106- storageVolumeCreate ( { connectionName, poolName : name , volName : volumeName , size, format } )
107- . then ( ( ) => Dialogs . close ( ) )
108- . catch ( exc => {
109- this . setState ( { createInProgress : false } ) ;
110- this . dialogErrorSet ( _ ( "Volume failed to be created" ) , exc . message ) ;
111- } ) ;
70+ const dlg = useDialogState < CreateStorageVolumeValues > ( init , validate ) ;
71+
72+ async function create ( values : CreateStorageVolumeValues ) {
73+ const { name : volName , format, size : volSize , unit } = values . volume ;
74+ const { name : poolName , connectionName } = storagePool ;
75+ const size = convertToUnit ( volSize , unit , 'MiB' ) ;
76+
77+ try {
78+ await storageVolumeCreate ( {
79+ connectionName,
80+ poolName,
81+ volName,
82+ size,
83+ format
84+ } ) ;
85+ } catch ( ex ) {
86+ throw DialogError . fromError ( _ ( "Volume failed to be created" ) , ex ) ;
11287 }
11388 }
11489
115- render ( ) {
116- const Dialogs = this . context ;
117- const idPrefix = `${ this . props . idPrefix } -dialog` ;
118- const validationFailed = this . state . validate ? this . validateParams ( ) : { } ;
119-
120- return (
121- < Modal position = "top" variant = "medium" id = { `${ idPrefix } -modal` } className = 'volume-create' isOpen onClose = { Dialogs . close } >
122- < ModalHeader title = { _ ( "Create storage volume" ) } />
123- < ModalBody >
124- < Form isHorizontal >
125- { this . state . dialogError &&
126- < ModalError
127- dialogError = { this . state . dialogError }
128- { ...this . state . dialogErrorDetail && { dialogErrorDetail : this . state . dialogErrorDetail } }
129- />
130- }
131- < VolumeCreateBody format = { this . state . format }
132- idPrefix = { idPrefix }
133- onValueChanged = { this . onValueChanged }
134- size = { this . state . size }
135- storagePool = { this . props . storagePool }
136- unit = { this . state . unit }
137- validationFailed = { validationFailed }
138- volumeName = { this . state . volumeName } />
139- </ Form >
140- </ ModalBody >
141- < ModalFooter >
142- < Button variant = "primary" onClick = { this . onCreateClicked } isLoading = { this . state . createInProgress } isDisabled = { this . state . createInProgress } >
143- { _ ( "Create" ) }
144- </ Button >
145- < Button variant = 'link' onClick = { Dialogs . close } >
146- { _ ( "Cancel" ) }
147- </ Button >
148- </ ModalFooter >
149- </ Modal >
150- ) ;
151- }
152- }
153-
154- interface StorageVolumeCreateProps {
90+ return (
91+ < Modal
92+ position = "top"
93+ variant = "medium"
94+ id = { `${ idPrefix } -dialog-modal` }
95+ className = 'volume-create'
96+ isOpen
97+ onClose = { Dialogs . close }
98+ >
99+ < ModalHeader title = { _ ( "Create storage volume" ) } />
100+ < ModalBody >
101+ < DialogErrorMessage dialog = { dlg } />
102+ < Form isHorizontal >
103+ < VolumeCreate value = { dlg . value ( "volume" ) } />
104+ </ Form >
105+ </ ModalBody >
106+ < ModalFooter >
107+ < DialogActionButton dialog = { dlg } onClose = { Dialogs . close } action = { create } >
108+ { _ ( "Create" ) }
109+ </ DialogActionButton >
110+ < DialogCancelButton dialog = { dlg } onClose = { Dialogs . close } />
111+ </ ModalFooter >
112+ </ Modal >
113+ ) ;
114+ } ;
115+
116+ export const StorageVolumeCreate = ( {
117+ storagePool,
118+ } : {
155119 storagePool : StoragePool ,
156- }
157-
158- export class StorageVolumeCreate extends React . Component < StorageVolumeCreateProps > {
159- static contextType = DialogsContext ;
160- declare context : Dialogs ;
161-
162- render ( ) {
163- const Dialogs = this . context ;
164- const idPrefix = `${ this . props . storagePool . name } -${ this . props . storagePool . connectionName } -create-volume` ;
165- const poolTypesNotSupportingVolumeCreation = [ 'iscsi' , 'iscsi-direct' , 'gluster' , 'mpath' ] ;
166-
167- const createButton = ( ) => {
168- if ( ! poolTypesNotSupportingVolumeCreation . includes ( this . props . storagePool . type ) && this . props . storagePool . active ) {
169- return (
170- < Button id = { `${ idPrefix } -button` }
120+ } ) => {
121+ const Dialogs = useDialogs ( ) ;
122+ const idPrefix = `${ storagePool . name } -${ storagePool . connectionName } -create-volume` ;
123+ const poolTypesNotSupportingVolumeCreation = [ 'iscsi' , 'iscsi-direct' , 'gluster' , 'mpath' ] ;
124+
125+ const createButton = ( ) => {
126+ if ( ! poolTypesNotSupportingVolumeCreation . includes ( storagePool . type ) && storagePool . active ) {
127+ return (
128+ < Button id = { `${ idPrefix } -button` }
129+ variant = 'secondary'
130+ onClick = {
131+ ( ) => Dialogs . show (
132+ < CreateStorageVolumeModal
133+ idPrefix = "create-volume"
134+ storagePool = { storagePool }
135+ />
136+ )
137+ }
138+ >
139+ { _ ( "Create volume" ) }
140+ </ Button >
141+ ) ;
142+ } else {
143+ return (
144+ < Tooltip
145+ id = 'create-tooltip'
146+ content = {
147+ storagePool . active
148+ ? _ ( "Pool type doesn't support volume creation" )
149+ : _ ( "Pool needs to be active to create volume" )
150+ }
151+ >
152+ < span >
153+ < Button
154+ id = { `${ idPrefix } -button` }
171155 variant = 'secondary'
172- onClick = { ( ) => Dialogs . show ( < CreateStorageVolumeModal idPrefix = "create-volume"
173- storagePool = { this . props . storagePool } /> ) } >
174- { _ ( "Create volume" ) }
175- </ Button >
176- ) ;
177- } else {
178- return (
179- < Tooltip id = 'create-tooltip'
180- content = { this . props . storagePool . active ? _ ( "Pool type doesn't support volume creation" ) : _ ( "Pool needs to be active to create volume" ) } >
181- < span >
182- < Button id = { `${ idPrefix } -button` }
183- variant = 'secondary'
184- isDisabled >
185- { _ ( "Create volume" ) }
186- </ Button >
187- </ span >
188- </ Tooltip >
189- ) ;
190- }
191- } ;
156+ isDisabled
157+ >
158+ { _ ( "Create volume" ) }
159+ </ Button >
160+ </ span >
161+ </ Tooltip >
162+ ) ;
163+ }
164+ } ;
192165
193- return createButton ( ) ;
194- }
195- }
166+ return createButton ( ) ;
167+ } ;
0 commit comments