Skip to content

Commit 698405f

Browse files
vichanssontinova
authored andcommitted
F #7138: Update vrouter download modal (#3728)
Signed-off-by: Victor Hansson <vhansson@opennebula.io> (cherry picked from commit ac8c7bd)
1 parent 65792ce commit 698405f

3 files changed

Lines changed: 57 additions & 202 deletions

File tree

src/fireedge/src/modules/components/Forms/MarketplaceApp/ExportForm/Steps/DatastoresTable/schema.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
* See the License for the specific language governing permissions and *
1414
* limitations under the License. *
1515
* ------------------------------------------------------------------------- */
16+
import { T } from '@ConstantsModule'
1617
import { array, object, ArraySchema } from 'yup'
1718

1819
/** @type {ArraySchema} Datastore table schema */
1920
export const SCHEMA = array(object())
20-
.min(1)
21+
.min(1, T.SelectADatastore)
2122
.max(1)
2223
.required()
2324
.ensure()

src/fireedge/src/modules/components/Tables/VrTemplates/table.js

Lines changed: 54 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,29 @@
1313
* See the License for the specific language governing permissions and *
1414
* limitations under the License. *
1515
* ------------------------------------------------------------------------- */
16-
import { Box, Grid, useTheme } from '@mui/material'
1716
import MultipleTags from '@modules/components/MultipleTags'
18-
import { SubmitButton } from '@modules/components/FormControl'
19-
import { Component, useEffect, useMemo, useState } from 'react'
20-
import { DatastoreDialog } from '@modules/components/Tables/VrTemplates/DatastoreSelectionDialog'
17+
import { Component, useMemo, useCallback } from 'react'
18+
import { CloudDownload } from 'iconoir-react'
2119

2220
import {
2321
useAuth,
2422
useViews,
25-
useGeneralApi,
26-
MarketplaceAPI,
23+
useModalsApi,
2724
VrTemplateAPI,
2825
MarketplaceAppAPI,
29-
DatastoreAPI,
3026
} from '@FeaturesModule'
3127
import { getResourceLabels } from '@UtilsModule'
3228
import { getColorFromString, timeToString } from '@ModelsModule'
3329
import { RESOURCE_NAMES, T, STYLE_BUTTONS } from '@ConstantsModule'
34-
import { BoxIso as DownloadIcon } from 'iconoir-react'
35-
36-
import { Tr, Translate } from '@modules/components/HOC'
30+
import { Box } from '@mui/material'
3731
import EnhancedTable, {
3832
createColumns,
3933
} from '@modules/components/Tables/Enhanced'
4034
import WrapperRow from '@modules/components/Tables/Enhanced/WrapperRow'
4135
import VrTemplateColumns from '@modules/components/Tables/VrTemplates/columns'
4236
import VrTemplateRow from '@modules/components/Tables/VrTemplates/row'
43-
import { useStyles } from '@modules/components/Tabs/EmptyTab/styles'
44-
import InfoEmpty from 'iconoir-react/dist/InfoEmpty'
45-
import { debounce } from 'lodash'
37+
import SubmitButton from '@modules/components/FormControl/SubmitButton'
38+
import { ExportForm } from '@modules/components/Forms/MarketplaceApp'
4639

4740
const DEFAULT_DATA_CY = 'vrouter-templates'
4841

@@ -51,35 +44,32 @@ const DEFAULT_DATA_CY = 'vrouter-templates'
5144
* @returns {Component} - Vr Templates table
5245
*/
5346
const VrTemplatesTable = (props) => {
47+
const { showModal } = useModalsApi()
48+
const [exportApp] = MarketplaceAppAPI.useExportAppMutation()
49+
const { data: defaultVrApp = [], isFetching: loadingApps } =
50+
MarketplaceAppAPI.useGetMarketplaceAppsQuery(undefined, {
51+
selectFromResult: ({ data: result = [] }) => ({
52+
data: result?.find(
53+
(app) =>
54+
app?.MARKETPLACE === 'OpenNebula Public' &&
55+
app?.NAME === 'Service Virtual Router'
56+
),
57+
}),
58+
})
5459
const { rootProps = {}, searchProps = {}, ...rest } = props ?? {}
5560
const { labels = {} } = useAuth()
5661

57-
const theme = useTheme()
58-
const classes = useMemo(() => useStyles(theme), [theme])
59-
6062
rootProps['data-cy'] ??= DEFAULT_DATA_CY
6163
searchProps['data-cy'] ??= `search-${DEFAULT_DATA_CY}`
6264

63-
const { enqueueError, enqueueSuccess, enqueueInfo } = useGeneralApi()
6465
const { view, getResourceView } = useViews()
65-
const [loading, setLoading] = useState(false)
66-
const [oneMarket, setOneMarket] = useState(null)
67-
const [defaultImage, setDefaultImage] = useState(null)
68-
const [disableDownload, setDisableDownload] = useState(false)
69-
const [dialogOpen, setDialogOpen] = useState(false)
70-
const [enableMarketplace] = MarketplaceAPI.useEnableMarketplaceMutation()
71-
const [exportApp] = MarketplaceAppAPI.useExportAppMutation()
7266
const {
7367
data = [],
74-
isFetching,
68+
isFetching: loadingTemplates,
7569
refetch,
7670
} = VrTemplateAPI.useGetVrTemplatesQuery()
77-
const [
78-
fetchMarketplaces,
79-
{ data: marketplaces = [], isFetching: fetchingMarketplaces },
80-
] = MarketplaceAPI.useLazyGetMarketplacesQuery(undefined, {
81-
skip: true, // Does not run on initial render
82-
})
71+
72+
const loading = loadingTemplates || loadingApps
8373

8474
const fmtData = useMemo(
8575
() =>
@@ -98,24 +88,6 @@ const VrTemplatesTable = (props) => {
9888
[data, labels]
9989
)
10090

101-
const [
102-
fetchDatastores,
103-
{ data: datastores = [], isFetching: fetchingDatastores },
104-
] = DatastoreAPI.useLazyGetDatastoresQuery(undefined, {
105-
skip: true, // Does not run on initial render
106-
})
107-
108-
const [
109-
fetchMarketplaceApps,
110-
{ data: marketplaceApps = [], isFetching: fetchingMarketplaceApps },
111-
] = MarketplaceAppAPI.useLazyGetMarketplaceAppsQuery(undefined, {
112-
skip: true,
113-
}) // Does not run on initial render
114-
115-
useEffect(() => {
116-
setLoading(isFetching || fetchingMarketplaces)
117-
}, [isFetching, fetchingMarketplaces])
118-
11991
const columns = useMemo(
12092
() =>
12193
createColumns({
@@ -125,166 +97,47 @@ const VrTemplatesTable = (props) => {
12597
[view]
12698
)
12799

128-
const submitDisabled = useMemo(
129-
() =>
130-
!(
131-
!fetchingMarketplaces &&
132-
!fetchingMarketplaceApps &&
133-
!fetchingDatastores
134-
),
135-
[fetchingMarketplaces, fetchingMarketplaceApps, fetchingDatastores]
136-
)
137-
138-
const handleDownloadDefaultImage = debounce(
139-
async () => {
140-
// Use cache if exists
141-
setDisableDownload(true)
142-
await fetchMarketplaces(undefined, true)
143-
await fetchDatastores(undefined, true)
144-
if (defaultImage !== null) {
145-
setDialogOpen(true)
146-
}
100+
const handleSubmit = useCallback(
101+
async (formData) => {
102+
await exportApp({ id: defaultVrApp.ID, ...formData })
103+
refetch()
147104
},
148-
4000,
149-
{ leading: true }
105+
[defaultVrApp]
150106
)
151107

152-
useEffect(() => {
153-
if (marketplaces?.length) {
154-
const mid =
155-
marketplaces?.find(
156-
(marketplace) => marketplace?.NAME === 'OpenNebula Public'
157-
) ?? null
158-
mid !== null &&
159-
setOneMarket({
160-
ID: mid?.ID ?? -1,
161-
STATE: mid?.STATE === '0' ? 'ENABLED' : 'DISABLED',
162-
})
163-
}
164-
}, [marketplaces])
165-
166-
useEffect(() => {
167-
// Internal ASYNC funcs -->
168-
169-
const fetchApps = async () => {
170-
await fetchMarketplaceApps(undefined, true) // Prefer cache
171-
}
172-
173-
const enableMarket = async (marketID) => {
174-
const response = await enableMarketplace({ id: marketID })
175-
if (response?.data === parseInt(marketID, 10)) {
176-
enqueueInfo(T.InfoEnableOpenNebulaMarketplace)
177-
setOneMarket({ ...oneMarket, STATE: 'ENABLED' })
178-
fetchApps()
179-
}
180-
181-
return response
182-
}
183-
184-
// <--Internal ASYNC funcs
185-
186-
try {
187-
if (
188-
oneMarket !== null &&
189-
oneMarket?.ID !== -1 &&
190-
oneMarket?.STATE !== 'ENABLED'
191-
) {
192-
enableMarket(oneMarket?.ID).catch(console.error)
193-
194-
// Prefer cache
195-
} else if (
196-
oneMarket !== null &&
197-
oneMarket?.ID !== -1 &&
198-
oneMarket?.STATE === 'ENABLED'
199-
) {
200-
fetchApps()
201-
}
202-
} catch {}
203-
}, [oneMarket])
204-
205-
useEffect(() => {
206-
if (marketplaceApps?.length && !fetchingMarketplaceApps) {
207-
const vrTemplate =
208-
marketplaceApps?.find(
209-
(app) => app?.NAME === 'Service Virtual Router'
210-
) ?? null
211-
setDefaultImage(vrTemplate)
212-
}
213-
}, [marketplaceApps])
214-
215-
useEffect(() => {
216-
if (defaultImage !== null && !fetchingDatastores) {
217-
setDisableDownload(true)
218-
setDialogOpen(true)
219-
}
220-
}, [defaultImage, datastores])
221-
222-
const handleCloseDialog = () => {
223-
setDialogOpen(false)
224-
setDisableDownload(false)
225-
}
226-
227-
const handleDownloadApp = async (datastoreId) => {
228-
await exportApp({
229-
id: defaultImage?.ID,
230-
name: 'Service Virtual Router',
231-
vmname: 'Service Virtual Router',
232-
datastore: datastoreId,
108+
const handleExportApp = () => {
109+
showModal({
110+
id: 'export-default-vr',
111+
dialogProps: {
112+
title: T.DownloadDefaultImage,
113+
dataCy: 'modal-download-vr-app',
114+
},
115+
form: ExportForm({
116+
initialValues: defaultVrApp,
117+
stepProps: defaultVrApp,
118+
}),
119+
onSubmit: handleSubmit,
233120
})
234-
.then((res) => {
235-
if (res?.data) {
236-
enqueueSuccess(T.SuccessDownloadDefaultImage) && refetch()
237-
} else if (res?.error) {
238-
enqueueError(res.error)
239-
} else {
240-
throw new Error('Unexpected problem occured')
241-
}
242-
})
243-
.catch((error) => {
244-
console.error(error)
245-
})
246121
}
247122

248-
const filterDatastores = useMemo(() =>
249-
datastores?.filter((ds) => ds?.TYPE === '0')
250-
)
251-
252123
const NoDataAvailable = () => (
253-
<>
254-
<Grid
255-
container
256-
sx={{
257-
alignItems: 'start',
258-
justifyContent: 'start',
259-
width: '100%',
260-
height: '100%',
261-
}}
262-
>
263-
<Grid item xs={12}>
264-
<Box className={classes.noDataMessage}>
265-
<InfoEmpty />
266-
<Translate word={T.NoDataAvailable} />
267-
<SubmitButton
268-
importance={STYLE_BUTTONS.IMPORTANCE.MAIN}
269-
size={STYLE_BUTTONS.SIZE.MEDIUM}
270-
type={STYLE_BUTTONS.TYPE.FILLED}
271-
disabled={disableDownload}
272-
icon={<DownloadIcon />}
273-
onClick={() => handleDownloadDefaultImage()}
274-
label={Tr(T.DownloadDefaultImage)}
275-
/>
276-
</Box>
277-
</Grid>
278-
</Grid>
279-
280-
<DatastoreDialog
281-
open={dialogOpen}
282-
onClose={handleCloseDialog}
283-
datastores={filterDatastores}
284-
submitDisabled={submitDisabled}
285-
onSelect={(ds) => handleDownloadApp(ds?.ID ?? -1)}
124+
<Box
125+
sx={{
126+
width: 'fit-content',
127+
height: 'fit-content',
128+
}}
129+
>
130+
<SubmitButton
131+
dataCy="download-vr-app"
132+
icon=<CloudDownload />
133+
label={T.DownloadDefaultImage}
134+
importance={STYLE_BUTTONS.IMPORTANCE.MAIN}
135+
size={STYLE_BUTTONS.SIZE.MEDIUM}
136+
type={STYLE_BUTTONS.TYPE.FILLED}
137+
disabled={loading}
138+
onClick={handleExportApp}
286139
/>
287-
</>
140+
</Box>
288141
)
289142

290143
const listHeader = [

src/fireedge/src/modules/constants/translates.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ module.exports = {
5555
NumberOfResourcesSelected: 'All %s resources are selected',
5656
SelectAllResources: 'Select all %s resources',
5757
SelectAImage: 'Select a image',
58+
SelectADatastore: 'Select a datastore',
5859
ClearSelection: 'Clear selection',
5960
ResetFilters: 'Clear current search query, filters, and sorts',
6061
VmsClearErrors:

0 commit comments

Comments
 (0)