diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js
new file mode 100644
index 0000000000..afc5b0e2d4
--- /dev/null
+++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js
@@ -0,0 +1,78 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import { useMemo } from 'react'
+// import { useHistory } from 'react-router-dom'
+import { RefreshDouble } from 'iconoir-react'
+
+import { useAuth } from 'client/features/Auth'
+import { useMarketplaceAppApi } from 'client/features/One'
+import { Translate } from 'client/components/HOC'
+
+import { createActions } from 'client/components/Tables/Enhanced/Utils'
+// import { PATH } from 'client/apps/sunstone/routesOne'
+import { T, MARKETPLACE_APP_ACTIONS } from 'client/constants'
+
+const MessageToConfirmAction = rows => {
+ const names = rows?.map?.(({ original }) => original?.NAME)
+
+ return (
+ <>
+
+
+ {`: ${names.join(', ')}`}
+
+
+
+
+ >
+ )
+}
+
+MessageToConfirmAction.displayName = 'MessageToConfirmAction'
+
+const Actions = () => {
+ const { view, getResourceView } = useAuth()
+ const { getMarketplaceApps } = useMarketplaceAppApi()
+
+ const marketplaceAppActions = useMemo(() => createActions({
+ filters: getResourceView('MARKETPLACE-APP')?.actions,
+ actions: [
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.REFRESH,
+ tooltip: T.Refresh,
+ icon: RefreshDouble,
+ action: async () => {
+ await getMarketplaceApps()
+ }
+ }
+ /* {
+ accessor: MARKETPLACE_APP_ACTIONS.CREATE_DIALOG,
+ tooltip: T.CreateMarketApp,
+ icon: AddSquare,
+ action: () => {
+ const path = PATH.STORAGE.MARKETPLACE_APPS.CREATE
+
+ history.push(path)
+ }
+ } */
+ ]
+ }), [view])
+
+ return marketplaceAppActions
+}
+
+export default Actions
diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js
index 9a0831ce34..74331ba757 100644
--- a/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js
+++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/index.js
@@ -21,15 +21,20 @@ import { useFetch } from 'client/hooks'
import { useMarketplaceApp, useMarketplaceAppApi } from 'client/features/One'
import { SkeletonTable, EnhancedTable } from 'client/components/Tables'
+import { createColumns } from 'client/components/Tables/Enhanced/Utils'
import MarketplaceAppColumns from 'client/components/Tables/MarketplaceApps/columns'
import MarketplaceAppRow from 'client/components/Tables/MarketplaceApps/row'
-const MarketplaceAppsTable = () => {
- const columns = useMemo(() => MarketplaceAppColumns, [])
+const MarketplaceAppsTable = props => {
+ const { view, getResourceView, filterPool } = useAuth()
+
+ const columns = useMemo(() => createColumns({
+ filters: getResourceView('MARKETPLACE-APP')?.filters,
+ columns: MarketplaceAppColumns
+ }), [view])
const marketplaceApps = useMarketplaceApp()
const { getMarketplaceApps } = useMarketplaceAppApi()
- const { filterPool } = useAuth()
const { status, fetchRequest, loading, reloading, STATUS } = useFetch(getMarketplaceApps)
const { INIT, PENDING } = STATUS
@@ -47,6 +52,7 @@ const MarketplaceAppsTable = () => {
isLoading={loading || reloading}
getRowId={row => String(row.ID)}
RowComponent={MarketplaceAppRow}
+ {...props}
/>
)
}
diff --git a/src/fireedge/src/client/components/Tabs/MarketplaceApp/Info/index.js b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Info/index.js
new file mode 100644
index 0000000000..cdd70bdb64
--- /dev/null
+++ b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Info/index.js
@@ -0,0 +1,134 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import { useContext } from 'react'
+import PropTypes from 'prop-types'
+
+import { useMarketplaceAppApi } from 'client/features/One'
+import { TabContext } from 'client/components/Tabs/TabProvider'
+import { Permissions, Ownership, AttributePanel } from 'client/components/Tabs/Common'
+import Information from 'client/components/Tabs/MarketplaceApp/Info/information'
+
+import { Tr } from 'client/components/HOC'
+import { getActionsAvailable, filterAttributes, jsonToXml } from 'client/models/Helper'
+import { cloneObject, set } from 'client/utils'
+import { T } from 'client/constants'
+
+const HIDDEN_ATTRIBUTES_REG = /^(VMTEMPLATE64|APPTEMPLATE64)$/
+
+const MarketplaceAppInfoTab = ({ tabProps = {} }) => {
+ const {
+ information_panel: informationPanel,
+ permissions_panel: permissionsPanel,
+ ownership_panel: ownershipPanel,
+ attributes_panel: attributesPanel
+ } = tabProps
+
+ const { rename, changeOwnership, changePermissions, updateTemplate } = useMarketplaceAppApi()
+ const { handleRefetch, data: marketplaceApp = {} } = useContext(TabContext)
+ const { ID, UNAME, UID, GNAME, GID, PERMISSIONS, TEMPLATE } = marketplaceApp
+
+ const handleChangeOwnership = async newOwnership => {
+ const response = await changeOwnership(ID, newOwnership)
+ String(response) === String(ID) && (await handleRefetch?.())
+ }
+
+ const handleChangePermission = async newPermission => {
+ const response = await changePermissions(ID, newPermission)
+ String(response) === String(ID) && (await handleRefetch?.())
+ }
+
+ const handleRename = async (_, newName) => {
+ const response = await rename(ID, newName)
+ String(response) === String(ID) && (await handleRefetch?.())
+ }
+
+ const handleAttributeInXml = async (path, newValue) => {
+ const newTemplate = cloneObject(TEMPLATE)
+
+ set(newTemplate, path, newValue)
+
+ const xml = jsonToXml(newTemplate)
+
+ // 0: Replace the whole template
+ const response = await updateTemplate(ID, xml, 0)
+ String(response) === String(ID) && (await handleRefetch?.())
+ }
+
+ const getActions = actions => getActionsAvailable(actions)
+
+ const { attributes } = filterAttributes(TEMPLATE, { hidden: HIDDEN_ATTRIBUTES_REG })
+
+ return (
+
+ {informationPanel?.enabled && (
+
+ )}
+ {permissionsPanel?.enabled && (
+
+ )}
+ {ownershipPanel?.enabled && (
+
+ )}
+ {attributesPanel?.enabled && attributes && (
+
+ )}
+
+ )
+}
+
+MarketplaceAppInfoTab.propTypes = {
+ tabProps: PropTypes.object
+}
+
+MarketplaceAppInfoTab.displayName = 'MarketplaceAppInfoTab'
+
+export default MarketplaceAppInfoTab
diff --git a/src/fireedge/src/client/components/Tabs/MarketplaceApp/Info/information.js b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Info/information.js
new file mode 100644
index 0000000000..8c84be600b
--- /dev/null
+++ b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Info/information.js
@@ -0,0 +1,80 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import PropTypes from 'prop-types'
+import { generatePath } from 'react-router'
+
+import { StatusChip } from 'client/components/Status'
+import { List } from 'client/components/Tabs/Common'
+
+import { getType, getState } from 'client/models/MarketplaceApp'
+import { timeToString, levelLockToString } from 'client/models/Helper'
+import { prettyBytes } from 'client/utils'
+import { T, MARKETPLACE_APP_ACTIONS } from 'client/constants'
+import { PATH } from 'client/apps/sunstone/routesOne'
+
+const InformationPanel = ({ marketplaceApp = {}, handleRename, actions }) => {
+ const { ID, NAME, REGTIME, LOCK, MARKETPLACE, MARKETPLACE_ID, SIZE, FORMAT, VERSION } = marketplaceApp
+ const typeName = getType(marketplaceApp)
+ const { name: stateName, color: stateColor } = getState(marketplaceApp)
+
+ const info = [
+ { name: T.ID, value: ID },
+ {
+ name: T.Name,
+ value: NAME,
+ canEdit: actions?.includes?.(MARKETPLACE_APP_ACTIONS.RENAME),
+ handleEdit: handleRename
+ },
+ {
+ name: T.Marketplace,
+ value: `#${MARKETPLACE_ID} ${MARKETPLACE}`,
+ link: !Number.isNaN(+MARKETPLACE_ID) &&
+ generatePath(PATH.STORAGE.MARKETPLACES.DETAIL, { id: MARKETPLACE_ID })
+ },
+ {
+ name: T.StartTime,
+ value: timeToString(REGTIME)
+ },
+ { name: T.Type, value: typeName },
+ { name: T.Size, value: prettyBytes(SIZE, 'MB') },
+ {
+ name: T.State,
+ value:
+ },
+ { name: T.Locked, value: levelLockToString(LOCK?.LOCKED) },
+ { name: T.Format, value: FORMAT },
+ { name: T.Version, value: VERSION }
+ ]
+
+ return (
+
+ )
+}
+
+InformationPanel.displayName = 'InformationPanel'
+
+InformationPanel.propTypes = {
+ actions: PropTypes.arrayOf(PropTypes.string),
+ handleRename: PropTypes.func,
+ marketplaceApp: PropTypes.object
+}
+
+export default InformationPanel
diff --git a/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js
new file mode 100644
index 0000000000..c78d329c2d
--- /dev/null
+++ b/src/fireedge/src/client/components/Tabs/MarketplaceApp/Template.js
@@ -0,0 +1,70 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import { useContext, useMemo } from 'react'
+import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material'
+import { NavArrowDown as ExpandMoreIcon } from 'iconoir-react'
+
+import { TabContext } from 'client/components/Tabs/TabProvider'
+import { Translate } from 'client/components/HOC'
+import { T } from 'client/constants'
+
+const parseTemplateInB64 = template => {
+ try {
+ return decodeURIComponent(escape(atob(template)))
+ } catch (e) { return {} }
+}
+
+const AppTemplateTab = () => {
+ const { data: marketplaceApp = {} } = useContext(TabContext)
+ const { TEMPLATE: { APPTEMPLATE64, VMTEMPLATE64 } } = marketplaceApp
+
+ const appTemplate = useMemo(() => parseTemplateInB64(APPTEMPLATE64), [APPTEMPLATE64])
+ const vmTemplate = useMemo(() => parseTemplateInB64(VMTEMPLATE64), [VMTEMPLATE64])
+
+ return (
+ <>
+
+ }>
+
+
+
+
+
+ {appTemplate}
+
+
+
+
+
+ }>
+
+
+
+
+
+ {vmTemplate}
+
+
+
+
+ >
+ )
+}
+
+AppTemplateTab.displayName = 'AppTemplateTab'
+
+export default AppTemplateTab
diff --git a/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js b/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js
new file mode 100644
index 0000000000..aa74d67455
--- /dev/null
+++ b/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js
@@ -0,0 +1,84 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable jsdoc/require-jsdoc */
+import { memo, useEffect, useState } from 'react'
+import PropTypes from 'prop-types'
+import { LinearProgress } from '@mui/material'
+
+import { useFetch } from 'client/hooks'
+import { useAuth } from 'client/features/Auth'
+import { useMarketplaceAppApi } from 'client/features/One'
+
+import Tabs from 'client/components/Tabs'
+import { camelCase } from 'client/utils'
+
+import TabProvider from 'client/components/Tabs/TabProvider'
+import Info from 'client/components/Tabs/MarketplaceApp/Info'
+import Template from 'client/components/Tabs/MarketplaceApp/Template'
+
+const getTabComponent = tabName => ({
+ info: Info,
+ template: Template
+}[tabName])
+
+const MarketplaceAppTabs = memo(({ id }) => {
+ const { getMarketplaceApp } = useMarketplaceAppApi()
+ const { data, fetchRequest, loading, error } = useFetch(getMarketplaceApp)
+
+ const handleRefetch = () => fetchRequest(id, { reload: true })
+
+ const [tabsAvailable, setTabs] = useState(() => [])
+ const { view, getResourceView } = useAuth()
+
+ useEffect(() => {
+ fetchRequest(id)
+ }, [id])
+
+ useEffect(() => {
+ const infoTabs = getResourceView('MARKETPLACE-APP')?.['info-tabs'] ?? {}
+
+ setTabs(() => Object.entries(infoTabs)
+ ?.filter(([_, { enabled } = {}]) => !!enabled)
+ ?.map(([tabName, tabProps]) => {
+ const camelName = camelCase(tabName)
+ const TabContent = getTabComponent(camelName)
+
+ return TabContent && {
+ name: camelName,
+ renderContent: props => TabContent({ ...props, tabProps })
+ }
+ })
+ ?.filter(Boolean))
+ }, [view])
+
+ if ((!data && !error) || loading) {
+ return
+ }
+
+ return (
+
+
+
+ )
+})
+
+MarketplaceAppTabs.propTypes = {
+ id: PropTypes.string.isRequired
+}
+
+MarketplaceAppTabs.displayName = 'MarketplaceAppTabs'
+
+export default MarketplaceAppTabs
diff --git a/src/fireedge/src/client/constants/marketplaceApp.js b/src/fireedge/src/client/constants/marketplaceApp.js
index 5515fc8516..658d8c5331 100644
--- a/src/fireedge/src/client/constants/marketplaceApp.js
+++ b/src/fireedge/src/client/constants/marketplaceApp.js
@@ -15,5 +15,6 @@
* ------------------------------------------------------------------------- */
export const MARKETPLACE_APP_ACTIONS = {
REFRESH: 'refresh',
- CREATE_DIALOG: 'create_dialog'
+ CREATE_DIALOG: 'create_dialog',
+ RENAME: 'rename'
}
diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js
index 676009572f..2245893bbe 100644
--- a/src/fireedge/src/client/constants/translates.js
+++ b/src/fireedge/src/client/constants/translates.js
@@ -286,6 +286,7 @@ module.exports = {
ID: 'ID',
Name: 'Name',
State: 'State',
+ Size: 'Size',
Description: 'Description',
RegistrationTime: 'Registration time',
StartTime: 'Start time',
@@ -295,6 +296,7 @@ module.exports = {
Type: 'Type',
Data: 'Data',
Validate: 'Validate',
+ Format: 'Format',
/* permissions */
Permissions: 'Permissions',
@@ -578,6 +580,11 @@ module.exports = {
ReservedMemory: 'Allocated Memory',
ReservedCpu: 'Allocated CPU',
+ /* Marketplace App schema */
+ /* Marketplace App - general */
+ Version: 'Version',
+ AppTemplate: 'App Template',
+
/* User inputs */
UserInputs: 'User Inputs',
UserInputsConcept: `
diff --git a/src/fireedge/src/client/containers/MarketplaceApps/index.js b/src/fireedge/src/client/containers/MarketplaceApps/index.js
index 67d02e7fd4..c7d1c2e30e 100644
--- a/src/fireedge/src/client/containers/MarketplaceApps/index.js
+++ b/src/fireedge/src/client/containers/MarketplaceApps/index.js
@@ -13,24 +13,53 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
-/* eslint-disable jsdoc/require-jsdoc */
-
-import { Container, Box } from '@mui/material'
+import { useState, JSXElementConstructor } from 'react'
+import { Container, Stack, Chip } from '@mui/material'
import { MarketplaceAppsTable } from 'client/components/Tables'
+import MarketplaceAppActions from 'client/components/Tables/MarketplaceApps/actions'
+import MarketplaceAppsTabs from 'client/components/Tabs/MarketplaceApp'
+import SplitPane from 'client/components/SplitPane'
+import MultipleTags from 'client/components/MultipleTags'
+/**
+ * Displays a Marketplace Apps list.
+ *
+ * @returns {JSXElementConstructor} List of Marketplace Apps
+ */
function MarketplaceApps () {
+ const [selectedRows, onSelectedRowsChange] = useState(() => [])
+ const actions = MarketplaceAppActions()
+
return (
-
-
-
+
+
+
+
+ {selectedRows?.length > 0 && (
+
+ {selectedRows?.length === 1
+ ?
+ :
+ (
+ toggleRowSelected(false)}
+ />
+ ))}
+ />
+
+ }
+
+ )}
+
+
)
}
diff --git a/src/fireedge/src/client/features/One/marketplaceApp/services.js b/src/fireedge/src/client/features/One/marketplaceApp/services.js
index b5b8a91c37..37ef4d5567 100644
--- a/src/fireedge/src/client/features/One/marketplaceApp/services.js
+++ b/src/fireedge/src/client/features/One/marketplaceApp/services.js
@@ -35,7 +35,7 @@ export const marketplaceAppService = ({
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
- return res?.data?.MARKETAPP ?? {}
+ return res?.data?.MARKETPLACEAPP ?? {}
},
/**