From a03d24f52d4380cb2180ad2e9925ec30bfa5e2ce Mon Sep 17 00:00:00 2001 From: CHEYNE Date: Fri, 24 Nov 2023 16:41:22 +0800 Subject: [PATCH] [#793] feat(UI): Replace mock data to real data in Metalakes page (#803) ### What changes were proposed in this pull request? This PR replaces the original mock data with the data obtained from real API requests, and shows gravitino version in page header. When preparing to perform a delete action, a secondary confirmation popup is added. image All APIs are include `headers: { Accept: "application/vnd.gravitino.v1+json" }`. Associated API addresses: ``` GET http://localhost:8090/api/metalakes ``` ``` GET http://localhost:8090/api/metalakes/{metalake_name} ``` ``` GET http://localhost:8090/api/version ``` ``` POST http://localhost:8090/api/metalakes Example body: { "name": "metalake_name", "comment": "test comment", "properties": { "test_key": "test_value" } } ``` ``` PUT http://localhost:8090/api/metalakes/{metalake_name} Example body: { "updates": [ { "@type": "rename", "newName": "newName" }, { "@type": "updateComment", "newComment": "newComment" }, { "@type": "removeProperty", "property": "key" }, { "@type": "setProperty", "property": "key", "value": "value" } ] } ``` ``` DELETE http://localhost:8090/api/metalakes/{metalake_name} ``` ### Why are the changes needed? Fix: #793 ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? No Co-authored-by: Jerry Shao --- web/.vscode/settings.json | 3 +- web/next.config.js | 18 ++ web/package.json | 3 +- web/src/@core/api/index.js | 22 ++ web/src/@core/api/metalakes/index.js | 54 +++++ .../custom-dialog/ConfirmDeleteDialog.js | 63 ++++++ web/src/@core/enums/apiEnum.js | 15 ++ web/src/@core/enums/httpEnum.js | 38 ++++ web/src/@core/utils/func.js | 96 ++++++++ web/src/@core/utils/is.js | 105 +++++++++ web/src/iconify-bundle/bundle-icons-react.js | 5 +- web/src/iconify-bundle/icons-bundle-react.js | 14 ++ web/src/layouts/components/VersionView.js | 32 +++ .../components/horizontal/AppBarContent.js | 7 +- web/src/pages/index.js | 4 +- web/src/store/index.js | 6 +- web/src/store/metalake/index.js | 52 ----- web/src/store/metalakes/index.js | 64 ++++++ web/src/store/version/index.js | 44 ++++ web/src/views/home/CreateMetalakeDialog.js | 55 ++++- web/src/views/home/DetailsDrawer.js | 27 ++- .../views/home/{Table.js => MetalakeList.js} | 145 +++++++++--- web/yarn.lock | 208 +++++++++++++++++- 23 files changed, 978 insertions(+), 102 deletions(-) create mode 100644 web/src/@core/api/index.js create mode 100644 web/src/@core/api/metalakes/index.js create mode 100644 web/src/@core/components/custom-dialog/ConfirmDeleteDialog.js create mode 100644 web/src/@core/enums/apiEnum.js create mode 100644 web/src/@core/enums/httpEnum.js create mode 100644 web/src/@core/utils/func.js create mode 100644 web/src/@core/utils/is.js create mode 100644 web/src/layouts/components/VersionView.js delete mode 100644 web/src/store/metalake/index.js create mode 100644 web/src/store/metalakes/index.js create mode 100644 web/src/store/version/index.js rename web/src/views/home/{Table.js => MetalakeList.js} (50%) diff --git a/web/.vscode/settings.json b/web/.vscode/settings.json index 439eb7e1a79..aa950c05b73 100644 --- a/web/.vscode/settings.json +++ b/web/.vscode/settings.json @@ -49,7 +49,8 @@ "nprogress", "reduxjs", "Roboto", - "Segoe" + "Segoe", + "tabler" ], "search-node-modules.path": "node_modules", "markdown.extension.toc.updateOnSave": false diff --git a/web/next.config.js b/web/next.config.js index 809bb5f9059..c22b0e13a4f 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -7,7 +7,25 @@ const path = require('path') /** @type {import('next').NextConfig} */ +const isProdEnv = process.env.NODE_ENV === 'production' + +const devConfig = isProdEnv + ? {} + : { + async rewrites() { + return { + fallback: [ + { + source: '/api/:path*', + destination: 'http://localhost:8090/api/:path*' + } + ] + } + } + } + module.exports = { + ...devConfig, distDir: 'dist', output: process.env.OUTPUT_MODE || 'standalone', trailingSlash: true, diff --git a/web/package.json b/web/package.json index 032597a48b6..5155ef79617 100644 --- a/web/package.json +++ b/web/package.json @@ -40,6 +40,7 @@ "react-perfect-scrollbar": "1.5.8", "react-popper": "2.3.0", "react-redux": "8.1.3", + "react-use": "^17.4.0", "yup": "1.3.2" }, "devDependencies": { @@ -65,4 +66,4 @@ "@emotion/react/@emotion/babel-plugin/@babel/core": "7.0.0", "@emotion/react/@emotion/babel-plugin/@babel/plugin-syntax-jsx/@babel/core": "7.0.0" } -} \ No newline at end of file +} diff --git a/web/src/@core/api/index.js b/web/src/@core/api/index.js new file mode 100644 index 00000000000..b9206ecf615 --- /dev/null +++ b/web/src/@core/api/index.js @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +import axios from 'axios' + +import { VersionApiEnum } from 'src/@core/enums/apiEnum' + +const ApiEnum = { + ...VersionApiEnum +} + +export const getVersionApi = () => { + return axios({ + url: `${ApiEnum.GET}`, + method: 'get', + headers: { + Accept: 'application/vnd.gravitino.v1+json' + } + }) +} diff --git a/web/src/@core/api/metalakes/index.js b/web/src/@core/api/metalakes/index.js new file mode 100644 index 00000000000..524c67acc35 --- /dev/null +++ b/web/src/@core/api/metalakes/index.js @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +import axios from 'axios' + +import { MetalakesApiEnum } from 'src/@core/enums/apiEnum' + +const ApiEnum = { + ...MetalakesApiEnum +} + +export const getMetalakesApi = () => { + return axios({ + url: `${ApiEnum.GET}`, + method: 'get', + headers: { + Accept: 'application/vnd.gravitino.v1+json' + } + }) +} + +export const createMetalakeApi = data => { + return axios({ + url: `${ApiEnum.CREATE}`, + method: 'post', + data, + headers: { + Accept: 'application/vnd.gravitino.v1+json' + } + }) +} + +export const deleteMetalakeApi = name => { + return axios({ + url: `${ApiEnum.DELETE}/${name}`, + method: 'delete', + headers: { + Accept: 'application/vnd.gravitino.v1+json' + } + }) +} + +export const updateMetalakeApi = ({ name, data }) => { + return axios({ + url: `${ApiEnum.UPDATE}/${name}`, + method: 'put', + headers: { + Accept: 'application/vnd.gravitino.v1+json' + }, + data + }) +} diff --git a/web/src/@core/components/custom-dialog/ConfirmDeleteDialog.js b/web/src/@core/components/custom-dialog/ConfirmDeleteDialog.js new file mode 100644 index 00000000000..5104d71a4a3 --- /dev/null +++ b/web/src/@core/components/custom-dialog/ConfirmDeleteDialog.js @@ -0,0 +1,63 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +import Box from '@mui/material/Box' +import Button from '@mui/material/Button' +import Dialog from '@mui/material/Dialog' +import Typography from '@mui/material/Typography' +import DialogContent from '@mui/material/DialogContent' +import DialogActions from '@mui/material/DialogActions' + +import Icon from 'src/@core/components/icon' + +const ConfirmDeleteDialog = props => { + const { open, setOpen, handleConfirmDeleteSubmit } = props + + const handleClose = () => setOpen(false) + + return ( + + [`${theme.spacing(5)} !important`, `${theme.spacing(15)} !important`], + pt: theme => [`${theme.spacing(8)} !important`, `${theme.spacing(12.5)} !important`] + }} + > + + + + Confirm Delete ? + + This action can not reserve! + + + [`${theme.spacing(5)} !important`, `${theme.spacing(15)} !important`], + pb: theme => [`${theme.spacing(8)} !important`, `${theme.spacing(12.5)} !important`] + }} + > + + + + + ) +} + +export default ConfirmDeleteDialog diff --git a/web/src/@core/enums/apiEnum.js b/web/src/@core/enums/apiEnum.js new file mode 100644 index 00000000000..2cb3b50b4f1 --- /dev/null +++ b/web/src/@core/enums/apiEnum.js @@ -0,0 +1,15 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +export const MetalakesApiEnum = { + GET: '/api/metalakes', + CREATE: '/api/metalakes', + DELETE: '/api/metalakes', + UPDATE: '/api/metalakes' +} + +export const VersionApiEnum = { + GET: '/api/version' +} diff --git a/web/src/@core/enums/httpEnum.js b/web/src/@core/enums/httpEnum.js new file mode 100644 index 00000000000..2620860f5bd --- /dev/null +++ b/web/src/@core/enums/httpEnum.js @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +/** + * @description: Request result set + */ +export const ResultEnum = { + SUCCESS: 0, + ERROR: 1, + TIMEOUT: 401, + TYPE: 'success' +} + +/** + * @description: request method + */ +export const RequestEnum = { + GET: 'GET', + POST: 'POST', + PUT: 'PUT', + DELETE: 'DELETE' +} + +/** + * @description: contentType + */ +export const ContentTypeEnum = { + // ** json + JSON: 'application/json;charset=UTF-8', + + // ** form-data qs + FORM_URLENCODED: 'application/x-www-form-urlencoded;charset=UTF-8', + + // ** form-data upload + FORM_DATA: 'multipart/form-data;charset=UTF-8' +} diff --git a/web/src/@core/utils/func.js b/web/src/@core/utils/func.js new file mode 100644 index 00000000000..7b1c02d12ff --- /dev/null +++ b/web/src/@core/utils/func.js @@ -0,0 +1,96 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +export const randomColor = () => { + let maxVal = 0xffffff + let randomNumber = Math.random() * maxVal + randomNumber = Math.floor(randomNumber) + randomNumber = randomNumber.toString(16) + let randColor = randomNumber.padStart(6, 0) + + return `#${randColor.toUpperCase()}` +} + +export function setObjToUrlParams(baseUrl, obj) { + let parameters = '' + for (const key in obj) { + parameters += key + '=' + encodeURIComponent(obj[key]) + '&' + } + parameters = parameters.replace(/&$/, '') + + return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters +} + +export function deepMerge(src = {}, target = {}) { + let key + for (key in target) { + src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]) + } + + return src +} + +export const parseNum = (number, m = 0) => { + return Math.round(Math.pow(10, m) * number) / Math.pow(10, m) +} + +/** + * Calculate the page number based on the total number of items. + * @param {number} count + * @param {number} pageSize + * @returns + */ +export function pagerCount(count, pageSize) { + if (typeof count == 'number') { + if (count > 0) { + try { + let _pagerCount = count % pageSize == 0 ? count / pageSize : count / pageSize + 1 + let c = _pagerCount.toFixed(0) + _pagerCount = c > _pagerCount ? c - 1 : c + + return _pagerCount + } catch (error) { + return 0 + } + } else { + return 0 + } + } else { + return 0 + } +} + +export const genUpdateMetalakeUpdates = (originalData, newData) => { + const updates = [] + + if (originalData.name !== newData.name) { + updates.push({ '@type': 'rename', newName: newData.name }) + } + + if (originalData.comment !== newData.comment) { + updates.push({ '@type': 'updateComment', newComment: newData.comment }) + } + + const originalProperties = originalData.properties || {} + const newProperties = newData.properties || {} + + for (const key in originalProperties) { + if (!(key in newProperties)) { + updates.push({ '@type': 'removeProperty', property: key }) + } + } + + for (const key in newProperties) { + if (originalProperties[key] !== newProperties[key]) { + if (originalProperties[key] === undefined) { + updates.push({ '@type': 'setProperty', property: key, value: newProperties[key] }) + } else { + updates.push({ '@type': 'setProperty', property: key, value: newProperties[key] }) + } + } + } + + return updates +} diff --git a/web/src/@core/utils/is.js b/web/src/@core/utils/is.js new file mode 100644 index 00000000000..f6237fc5dc5 --- /dev/null +++ b/web/src/@core/utils/is.js @@ -0,0 +1,105 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +const toString = Object.prototype.toString + +export function is(val, type) { + return toString.call(val) === `[object ${type}]` +} + +export function isDef(val) { + return typeof val !== 'undefined' +} + +export function isUnDef(val) { + return !isDef(val) +} + +export function isObject(val) { + return val !== null && is(val, 'Object') +} + +export function isEmpty(val) { + if (isArray(val) || isString(val)) { + return val.length === 0 + } + + if (val instanceof Map || val instanceof Set) { + return val.size === 0 + } + + if (isObject(val)) { + return Object.keys(val).length === 0 + } + + return false +} + +export function isDate(val) { + return is(val, 'Date') +} + +export function isNull(val) { + return val === null +} + +export function isNullAndUnDef(val) { + return isUnDef(val) && isNull(val) +} + +export function isNullOrUnDef(val) { + return isUnDef(val) || isNull(val) +} + +export function isNumber(val) { + return is(val, 'Number') +} + +export function isPromise(val) { + return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch) +} + +export function isString(val) { + return is(val, 'String') +} + +export function isFunction(val) { + return typeof val === 'function' +} + +export function isBoolean(val) { + return is(val, 'Boolean') +} + +export function isRegExp(val) { + return is(val, 'RegExp') +} + +export function isArray(val) { + return val && Array.isArray(val) +} + +export function isWindow(val) { + return typeof window !== 'undefined' && is(val, 'Window') +} + +export function isElement(val) { + return isObject(val) && !!val.tagName +} + +export function isMap(val) { + return is(val, 'Map') +} + +export const isServer = typeof window === 'undefined' + +export const isClient = !isServer + +export function isUrl(path) { + const reg = + /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/ + + return reg.test(path) +} diff --git a/web/src/iconify-bundle/bundle-icons-react.js b/web/src/iconify-bundle/bundle-icons-react.js index ee825230ddd..694a1848285 100644 --- a/web/src/iconify-bundle/bundle-icons-react.js +++ b/web/src/iconify-bundle/bundle-icons-react.js @@ -34,7 +34,10 @@ const sources = { 'mdi:minus-circle-outline', 'mdi:plus-circle-outline', 'bx:show-alt', - 'bx:bxs-plus-square' + 'bx:bxs-plus-square', + 'tabler:alert-circle', + 'mdi:square-edit-outline', + 'mdi:delete-outline' ], svg: [ // ** Example code: { dir: 'src/iconify-bundle/svg', monotone: false, prefix: 'custom' } diff --git a/web/src/iconify-bundle/icons-bundle-react.js b/web/src/iconify-bundle/icons-bundle-react.js index 9532a307b2b..c26a6e74565 100644 --- a/web/src/iconify-bundle/icons-bundle-react.js +++ b/web/src/iconify-bundle/icons-bundle-react.js @@ -32,9 +32,23 @@ addCollection({ }, 'plus-circle-outline': { body: '' + }, + 'delete-outline': { + body: '' } }, lastModified: 1698215149, width: 24, height: 24 }) +addCollection({ + prefix: 'tabler', + icons: { + 'alert-circle': { + body: '' + } + }, + lastModified: 1698388788, + width: 24, + height: 24 +}) diff --git a/web/src/layouts/components/VersionView.js b/web/src/layouts/components/VersionView.js new file mode 100644 index 00000000000..59d08235215 --- /dev/null +++ b/web/src/layouts/components/VersionView.js @@ -0,0 +1,32 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +import { useEffect } from 'react' + +import Typography from '@mui/material/Typography' + +import { useDispatch, useSelector } from 'react-redux' +import { fetchVersion, setVersion } from 'src/store/version' + +const VersionView = () => { + const dispatch = useDispatch() + const store = useSelector(state => state.version) + + useEffect(() => { + if (typeof window !== 'undefined') { + const version = window.sessionStorage.getItem('version') + + if (!version || version === 'undefined') { + dispatch(fetchVersion()) + } else { + dispatch(setVersion(version)) + } + } + }, [dispatch]) + + return {store.version} +} + +export default VersionView diff --git a/web/src/layouts/components/horizontal/AppBarContent.js b/web/src/layouts/components/horizontal/AppBarContent.js index a22f8c4d002..514b8e4b2ce 100644 --- a/web/src/layouts/components/horizontal/AppBarContent.js +++ b/web/src/layouts/components/horizontal/AppBarContent.js @@ -4,9 +4,14 @@ */ import Box from '@mui/material/Box' +import VersionView from '../VersionView' const AppBarContent = props => { - return + return ( + + + + ) } export default AppBarContent diff --git a/web/src/pages/index.js b/web/src/pages/index.js index 5e590af504a..6223a9b797a 100644 --- a/web/src/pages/index.js +++ b/web/src/pages/index.js @@ -6,7 +6,7 @@ import Grid from '@mui/material/Grid' import Typography from '@mui/material/Typography' import PageHeader from 'src/@core/components/page-header' -import Table from 'src/views/home/Table' +import MetalakeList from 'src/views/home/MetalakeList' const Home = () => { return ( @@ -21,7 +21,7 @@ const Home = () => { } /> - + ) diff --git a/web/src/store/index.js b/web/src/store/index.js index 3d5c7bb30bf..3bc056d5578 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -5,11 +5,13 @@ import { configureStore } from '@reduxjs/toolkit' -import metalake from 'src/store/metalake' +import version from './version' +import metalakes from './metalakes' export const store = configureStore({ reducer: { - metalake + version, + metalakes }, middleware: getDefaultMiddleware => getDefaultMiddleware({ diff --git a/web/src/store/metalake/index.js b/web/src/store/metalake/index.js deleted file mode 100644 index 5267e694b00..00000000000 --- a/web/src/store/metalake/index.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2023 Datastrato. - * This software is licensed under the Apache License version 2. - */ - -import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' - -import axios from 'axios' - -export const fetchMetalakes = createAsyncThunk('appMetalakes/fetchMetalakes', async params => { - const response = await axios.get('/apis/metalakes', { - params - }) - - return response.data -}) - -export const createMetalake = createAsyncThunk( - 'appMetalakes/createMetalake', - async (metalake, { getState, dispatch }) => { - const response = await axios.post('/apis/metalakes', { - data: { - metalake - } - }) - - dispatch(fetchMetalakes(getState().metalake.params)) - - return response.data.metalake - } -) - -export const appMetalakesSlice = createSlice({ - name: 'appMetalakes', - initialState: { - data: [], - total: 1, - params: {}, - allData: [] - }, - reducers: {}, - extraReducers: builder => { - builder.addCase(fetchMetalakes.fulfilled, (state, action) => { - state.data = action.payload.metalakes - state.total = action.payload.total - state.params = action.payload.params - state.allData = action.payload.allData - }) - } -}) - -export default appMetalakesSlice.reducer diff --git a/web/src/store/metalakes/index.js b/web/src/store/metalakes/index.js new file mode 100644 index 00000000000..e14771836c0 --- /dev/null +++ b/web/src/store/metalakes/index.js @@ -0,0 +1,64 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' + +import { createMetalakeApi, getMetalakesApi, deleteMetalakeApi, updateMetalakeApi } from 'src/@core/api/metalakes' + +export const fetchMetalakes = createAsyncThunk('appMetalakes/fetchMetalakes', async (params, { getState }) => { + const response = await getMetalakesApi() + + const { metalakes } = response.data + + return { + metalakes + } +}) + +export const createMetalake = createAsyncThunk('appMetalakes/createMetalake', async (data, { getState, dispatch }) => { + const response = await createMetalakeApi(data) + + dispatch(fetchMetalakes()) + + return response.metalake +}) + +export const deleteMetalake = createAsyncThunk('appMetalakes/deleteMetalake', async (name, { dispatch }) => { + const response = await deleteMetalakeApi(name) + + dispatch(fetchMetalakes()) + + return response.data +}) + +export const updateMetalake = createAsyncThunk('appMetalakes/updateMetalake', async ({ name, data }, { dispatch }) => { + const response = await updateMetalakeApi({ name, data }) + + dispatch(fetchMetalakes()) + + return response.data +}) + +export const appMetalakesSlice = createSlice({ + name: 'appMetalakes', + initialState: { + metalakes: [], + filteredMetalakes: [] + }, + reducers: { + setFilteredMetalakes(state, action) { + state.filteredMetalakes = action.payload + } + }, + extraReducers: builder => { + builder.addCase(fetchMetalakes.fulfilled, (state, action) => { + state.metalakes = action.payload.metalakes + }) + } +}) + +export const { setFilteredMetalakes } = appMetalakesSlice.actions + +export default appMetalakesSlice.reducer diff --git a/web/src/store/version/index.js b/web/src/store/version/index.js new file mode 100644 index 00000000000..286ba547992 --- /dev/null +++ b/web/src/store/version/index.js @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Datastrato. + * This software is licensed under the Apache License version 2. + */ + +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' + +import { getVersionApi } from 'src/@core/api' + +export const fetchVersion = createAsyncThunk('appVersion/fetchVersion', async (params, { getState }) => { + const response = await getVersionApi() + + const { version } = response.data + + sessionStorage.setItem('version', version.version) + + console.log( + `Gravitino Version: %c${version.version}`, + `color: white; background-color: #6062E0; padding: 2px; border-radius: 4px;` + ) + + return version +}) + +export const appVersionSlice = createSlice({ + name: 'appVersion', + initialState: { + version: '' + }, + reducers: { + setVersion(state, action) { + state.version = action.payload + } + }, + extraReducers: builder => { + builder.addCase(fetchVersion.fulfilled, (state, action) => { + state.version = action.payload.version + }) + } +}) + +export const { setVersion } = appVersionSlice.actions + +export default appVersionSlice.reducer diff --git a/web/src/views/home/CreateMetalakeDialog.js b/web/src/views/home/CreateMetalakeDialog.js index f6287632492..50a6ece511e 100644 --- a/web/src/views/home/CreateMetalakeDialog.js +++ b/web/src/views/home/CreateMetalakeDialog.js @@ -3,7 +3,7 @@ * This software is licensed under the Apache License version 2. */ -import { useState, forwardRef } from 'react' +import { useState, forwardRef, useEffect } from 'react' import Box from '@mui/material/Box' import Button from '@mui/material/Button' @@ -24,6 +24,8 @@ import * as yup from 'yup' import { useForm, Controller } from 'react-hook-form' import { yupResolver } from '@hookform/resolvers/yup' +import { genUpdateMetalakeUpdates } from 'src/@core/utils/func' + const defaultValues = { name: '', comment: '' @@ -38,13 +40,18 @@ const Transition = forwardRef(function Transition(props, ref) { }) const CreateMetalakeDialog = props => { - const { open, setOpen, store, dispatch, createMetalake } = props + const { open, setOpen, type = 'create', data = {}, store, dispatch, createMetalake, updateMetalake } = props + + const typeText = type === 'create' ? 'Create' : 'Update' const [innerProps, setInnerProps] = useState([]) + const [cacheData, setCacheData] = useState() + const { control, reset, + setValue, handleSubmit, formState: { errors } } = useForm({ @@ -78,16 +85,50 @@ const CreateMetalakeDialog = props => { } const onSubmit = data => { - const reqData = { + const properties = innerProps.reduce((acc, item) => { + acc[item.key] = item.value + + return acc + }, {}) + + const metalakeData = { ...data, - properties: innerProps + properties + } + + if (type === 'create') { + dispatch(createMetalake({ ...metalakeData })) + } else { + const reqData = { updates: genUpdateMetalakeUpdates(cacheData, metalakeData) } + + if (reqData.updates.length !== 0) { + dispatch(updateMetalake({ name: cacheData.name, data: reqData })) + } } - dispatch(createMetalake({ ...reqData })) handleClose() reset() } + useEffect(() => { + if (open && JSON.stringify(data) !== '{}') { + setCacheData(data) + const { properties } = data + + const propsArr = Object.keys(properties).map(item => { + return { + key: item, + value: properties[item] + } + }) + + setInnerProps(propsArr) + + setValue('name', data.name) + setValue('comment', data.comment) + } + }, [open, data, setValue]) + return (
@@ -108,7 +149,7 @@ const CreateMetalakeDialog = props => { - Create Metalake + {typeText} Metalake @@ -209,7 +250,7 @@ const CreateMetalakeDialog = props => { }} >