From 7ecc1022b29996f13d86336399ea1ab3d9552536 Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Wed, 15 Jun 2022 18:35:47 -0500 Subject: [PATCH] Added decimal support to bucket quota selectors (#2126) - Fixed an issue with calculateBytes function - Fixed add bucket validation form Signed-off-by: Benjamin Perez --- portal-ui/src/common/utils.ts | 2 +- portal-ui/src/history.ts | 2 - .../Buckets/BucketDetails/EnableQuota.tsx | 28 +++- .../ListBuckets/AddBucket/AddBucket.tsx | 10 +- .../ListBuckets/AddBucket/addBucketsSlice.ts | 36 +++-- .../Buckets/ListBuckets/BucketListItem.tsx | 2 +- .../AddTierConfiguration.tsx | 4 +- .../Console/Policies/PolicySelectors.tsx | 11 +- .../screens/Console/Policies/SetPolicy.tsx | 10 +- .../screens/Console/Users/AddUsersSlice.tsx | 149 +++++++++--------- .../Console/Users/PasswordSelector.tsx | 66 ++++---- .../screens/Console/Users/SetUserPolicies.tsx | 8 +- .../screens/Console/Users/UserSelector.tsx | 48 +++--- .../Console/Users/thunk/AddUsersThunk.tsx | 78 ++++----- 14 files changed, 234 insertions(+), 220 deletions(-) diff --git a/portal-ui/src/common/utils.ts b/portal-ui/src/common/utils.ts index 650a9b8a7e..b57d52912b 100644 --- a/portal-ui/src/common/utils.ts +++ b/portal-ui/src/common/utils.ts @@ -596,7 +596,7 @@ export const calculateBytes = ( // Get unit for measure const i = Math.floor(Math.log(bytes) / Math.log(k)); - const fractionDigits = showDecimals ? 0 : 1; + const fractionDigits = showDecimals ? 1 : 0; const bytesUnit = bytes / Math.pow(k, i); diff --git a/portal-ui/src/history.ts b/portal-ui/src/history.ts index 9847946162..3d89583745 100644 --- a/portal-ui/src/history.ts +++ b/portal-ui/src/history.ts @@ -1,5 +1,3 @@ // check if we are using base path, if not this always is `/` const baseLocation = new URL(document.baseURI); export const baseUrl = baseLocation.pathname; - - diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableQuota.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableQuota.tsx index cff1c85c3d..3ece57d9df 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableQuota.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableQuota.tsx @@ -70,21 +70,34 @@ const EnableQuota = ({ const [quotaEnabled, setQuotaEnabled] = useState(false); const [quotaSize, setQuotaSize] = useState("1"); const [quotaUnit, setQuotaUnit] = useState("Ti"); + const [validInput, setValidInput] = useState(false); useEffect(() => { if (enabled) { setQuotaEnabled(true); if (cfg) { - const unitCalc = calculateBytes(cfg.quota, false, false, true); + const unitCalc = calculateBytes(cfg.quota, true, false, true); setQuotaSize(unitCalc.total.toString()); setQuotaUnit(unitCalc.unit); + setValidInput(true); } } }, [enabled, cfg]); + useEffect(() => { + const valRegExp = /^\d*(?:\.\d{1,2})?$/; + + if (!quotaEnabled) { + setValidInput(true); + return; + } + + setValidInput(valRegExp.test(quotaSize)); + }, [quotaEnabled, quotaSize]); + const enableBucketEncryption = () => { - if (loading) { + if (loading || !validInput) { return; } let req = { @@ -145,11 +158,13 @@ const EnableQuota = ({ id="quota_size" name="quota_size" onChange={(e: React.ChangeEvent) => { - if (e.target.validity.valid) { - setQuotaSize(e.target.value); + setQuotaSize(e.target.value); + if (!e.target.validity.valid) { + setValidInput(false); + } else { + setValidInput(true); } }} - pattern={"[0-9]*"} label="Quota" value={quotaSize} required @@ -165,6 +180,7 @@ const EnableQuota = ({ disabled={false} /> } + error={!validInput ? "Please enter a valid quota" : ""} /> @@ -189,7 +205,7 @@ const EnableQuota = ({ type="submit" variant="contained" color="primary" - disabled={loading} + disabled={loading || !validInput} > Save diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx index 5cd641c04e..671772fd33 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx @@ -130,7 +130,7 @@ const AddBucket = ({ classes }: IsetProps) => { (state: AppState) => state.addBucket.retentionValidity ); const addLoading = useSelector((state: AppState) => state.addBucket.loading); - const valid = useSelector((state: AppState) => state.addBucket.valid); + const invalidFields = useSelector((state: AppState) => state.addBucket.invalidFields); const lockingFieldDisabled = useSelector( (state: AppState) => state.addBucket.lockingFieldDisabled ); @@ -285,19 +285,16 @@ const AddBucket = ({ classes }: IsetProps) => { ) => { - if (e.target.validity.valid) { dispatch(setQuotaSize(e.target.value)); - } }} label="Capacity" value={quotaSize} required min="1" - pattern={"[0-9]*"} overlayObject={ { disabled={false} /> } + error={invalidFields.includes("quotaSize") ? "Please enter a valid quota" : ""} /> @@ -387,7 +385,7 @@ const AddBucket = ({ classes }: IsetProps) => { type="submit" variant="contained" color="primary" - disabled={addLoading || valid} + disabled={addLoading || invalidFields.length > 0} > Create Bucket diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts index 598531af13..a3063aab40 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts @@ -19,7 +19,7 @@ import { addBucketAsync } from "./addBucketThunks"; export interface AddBucketState { loading: boolean; - valid: boolean; + invalidFields: string[]; name: string; versioningEnabled: boolean; lockingEnabled: boolean; @@ -36,7 +36,7 @@ export interface AddBucketState { const initialState: AddBucketState = { loading: false, - valid: false, + invalidFields: ["name"], name: "", versioningEnabled: false, lockingEnabled: false, @@ -57,8 +57,11 @@ export const addBucketsSlice = createSlice({ reducers: { setName: (state, action: PayloadAction) => { state.name = action.payload; + if (state.name.trim() === "") { - state.valid = false; + state.invalidFields = [...state.invalidFields, "name"]; + } else { + state.invalidFields = state.invalidFields.filter((field) => field !== "name"); } }, setVersioning: (state, action: PayloadAction) => { @@ -75,13 +78,22 @@ export const addBucketsSlice = createSlice({ }, setQuota: (state, action: PayloadAction) => { state.quotaEnabled = action.payload; + + if(!action.payload) { + state.quotaSize = "1"; + state.quotaUnit = "Ti"; + + state.invalidFields = state.invalidFields.filter((field) => field !== "quotaSize"); + } }, setQuotaSize: (state, action: PayloadAction) => { state.quotaSize = action.payload; - if (state.quotaEnabled && state.valid) { - if (state.quotaSize.trim() === "" || parseInt(state.quotaSize) === 0) { - state.valid = false; + if (state.quotaEnabled) { + if (state.quotaSize.trim() === "" || parseInt(state.quotaSize) === 0 || !(/^\d*(?:\.\d{1,2})?$/.test(state.quotaSize))) { + state.invalidFields = [...state.invalidFields, "quotaSize"]; + } else { + state.invalidFields = state.invalidFields.filter((field) => field !== "quotaSize"); } } }, @@ -109,7 +121,9 @@ export const addBucketsSlice = createSlice({ state.retentionEnabled && (Number.isNaN(state.retentionValidity) || state.retentionValidity < 1) ) { - state.valid = false; + state.invalidFields = [...state.invalidFields, "retentionValidity"]; + } else { + state.invalidFields = state.invalidFields.filter((field) => field !== "retentionValidity"); } }, setRetentionMode: (state, action: PayloadAction) => { @@ -121,10 +135,12 @@ export const addBucketsSlice = createSlice({ setRetentionValidity: (state, action: PayloadAction) => { state.retentionValidity = action.payload; if ( - state.retentionEnabled && - (Number.isNaN(state.retentionValidity) || state.retentionValidity < 1) + state.retentionEnabled && + (Number.isNaN(state.retentionValidity) || state.retentionValidity < 1) ) { - state.valid = false; + state.invalidFields = [...state.invalidFields, "retentionValidity"]; + } else { + state.invalidFields = state.invalidFields.filter((field) => field !== "retentionValidity"); } }, diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/BucketListItem.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/BucketListItem.tsx index f98bc8b448..5665e810dc 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/BucketListItem.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/BucketListItem.tsx @@ -177,7 +177,7 @@ const BucketListItem = ({ const usageUnit = usage.split(" ")[1]; const quota = get(bucket, "details.quota.quota", "0"); - const quotaForString = calculateBytes(quota); + const quotaForString = calculateBytes(quota, true, false); const accessToStr = (bucket: Bucket): string => { if (bucket.rw_access?.read && !bucket.rw_access?.write) { diff --git a/portal-ui/src/screens/Console/Configurations/TiersConfiguration/AddTierConfiguration.tsx b/portal-ui/src/screens/Console/Configurations/TiersConfiguration/AddTierConfiguration.tsx index 2b549b0520..b4769db935 100644 --- a/portal-ui/src/screens/Console/Configurations/TiersConfiguration/AddTierConfiguration.tsx +++ b/portal-ui/src/screens/Console/Configurations/TiersConfiguration/AddTierConfiguration.tsx @@ -78,9 +78,7 @@ interface IAddNotificationEndpointProps { classes: any; } -const AddTierConfiguration = ({ - classes, -}: IAddNotificationEndpointProps) => { +const AddTierConfiguration = ({ classes }: IAddNotificationEndpointProps) => { const dispatch = useAppDispatch(); const navigate = useNavigate(); const params = useParams(); diff --git a/portal-ui/src/screens/Console/Policies/PolicySelectors.tsx b/portal-ui/src/screens/Console/Policies/PolicySelectors.tsx index 8be1cfbebc..cd8b097072 100644 --- a/portal-ui/src/screens/Console/Policies/PolicySelectors.tsx +++ b/portal-ui/src/screens/Console/Policies/PolicySelectors.tsx @@ -85,11 +85,13 @@ const PolicySelectors = ({ const [loading, isLoading] = useState(false); const [filter, setFilter] = useState(""); - const currentPolicies = useSelector((state: AppState) => state.createUser.selectedPolicies); + const currentPolicies = useSelector( + (state: AppState) => state.createUser.selectedPolicies + ); const fetchPolicies = useCallback(() => { isLoading(true); - + api .invoke("GET", `/api/v1/policies?limit=1000`) .then((res: PolicyList) => { @@ -115,11 +117,10 @@ const PolicySelectors = ({ }, [loading, fetchPolicies]); const selectionChanged = (e: React.ChangeEvent) => { - const targetD = e.target; const value = targetD.value; const checked = targetD.checked; - + let elements: string[] = [...currentPolicies]; // We clone the checkedUsers array if (checked) { @@ -131,7 +132,7 @@ const PolicySelectors = ({ } // remove empty values elements = elements.filter((element) => element !== ""); - + dispatch(setSelectedPolicies(elements)); }; diff --git a/portal-ui/src/screens/Console/Policies/SetPolicy.tsx b/portal-ui/src/screens/Console/Policies/SetPolicy.tsx index 87823299fe..c18af36687 100644 --- a/portal-ui/src/screens/Console/Policies/SetPolicy.tsx +++ b/portal-ui/src/screens/Console/Policies/SetPolicy.tsx @@ -48,8 +48,6 @@ interface ISetPolicyProps { open: boolean; } - - const styles = (theme: Theme) => createStyles({ ...modalBasic, @@ -76,7 +74,9 @@ const SetPolicy = ({ const [loading, setLoading] = useState(false); const [actualPolicy, setActualPolicy] = useState([]); const [selectedPolicy, setSelectedPolicy] = useState([]); - const currentPolicies = useSelector((state: AppState) => state.createUser.selectedPolicies); + const currentPolicies = useSelector( + (state: AppState) => state.createUser.selectedPolicies + ); const setPolicyAction = () => { let users = null; let groups = null; @@ -175,9 +175,7 @@ const SetPolicy = ({ )}
- +
diff --git a/portal-ui/src/screens/Console/Users/AddUsersSlice.tsx b/portal-ui/src/screens/Console/Users/AddUsersSlice.tsx index 0f73aa42e5..9a9780f71a 100644 --- a/portal-ui/src/screens/Console/Users/AddUsersSlice.tsx +++ b/portal-ui/src/screens/Console/Users/AddUsersSlice.tsx @@ -14,95 +14,92 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { - createUserAsync, - resetFormAsync, -} from "./thunk/AddUsersThunk"; +import { createUserAsync, resetFormAsync } from "./thunk/AddUsersThunk"; export interface ICreateUser { - userName: string; - secretKey: string; - selectedGroups: string[]; - selectedPolicies: string[]; - showPassword: boolean; - sendEnabled: boolean; - addLoading: boolean; - apinoerror: boolean; - secretKeylength: number; + userName: string; + secretKey: string; + selectedGroups: string[]; + selectedPolicies: string[]; + showPassword: boolean; + sendEnabled: boolean; + addLoading: boolean; + apinoerror: boolean; + secretKeylength: number; } const initialState: ICreateUser = { - addLoading: false, - showPassword: false, - sendEnabled: false, - apinoerror: false, - userName: "", - secretKey: "", - selectedGroups: [], - selectedPolicies: [], - secretKeylength: 0, + addLoading: false, + showPassword: false, + sendEnabled: false, + apinoerror: false, + userName: "", + secretKey: "", + selectedGroups: [], + selectedPolicies: [], + secretKeylength: 0, }; export const createUserSlice = createSlice({ - name: "createUser", - initialState, - reducers: { - setAddLoading: (state, action: PayloadAction) => { - state.addLoading = action.payload; - }, - setUserName: (state, action: PayloadAction) => { - state.userName = action.payload; - }, - setSelectedGroups: (state, action: PayloadAction) => { - state.selectedGroups = action.payload; - }, - setSecretKey: (state, action: PayloadAction) => { - state.secretKey = action.payload; - state.secretKeylength = state.secretKey.length; - }, - setSelectedPolicies: (state, action: PayloadAction) => { - state.selectedPolicies = action.payload; - }, - setShowPassword: (state, action: PayloadAction) => { - state.showPassword = action.payload; - }, - setSendEnabled: (state) => { - state.sendEnabled = state.userName.trim() !== ""; - }, - setApinoerror: (state, action: PayloadAction) => { - state.apinoerror = action.payload; - }, + name: "createUser", + initialState, + reducers: { + setAddLoading: (state, action: PayloadAction) => { + state.addLoading = action.payload; }, - extraReducers: (builder) => { - builder - .addCase(resetFormAsync.fulfilled, (state, action) => { - state.userName = ""; - state.selectedGroups = []; - state.secretKey = ""; - state.selectedPolicies = []; - state.showPassword = false; - }) - .addCase(createUserAsync.pending, (state, action) => { - state.addLoading = true; - }) - .addCase(createUserAsync.rejected, (state, action) => { - state.addLoading = false; - }) - .addCase(createUserAsync.fulfilled, (state, action) => { - state.apinoerror = true; - }); + setUserName: (state, action: PayloadAction) => { + state.userName = action.payload; }, + setSelectedGroups: (state, action: PayloadAction) => { + state.selectedGroups = action.payload; + }, + setSecretKey: (state, action: PayloadAction) => { + state.secretKey = action.payload; + state.secretKeylength = state.secretKey.length; + }, + setSelectedPolicies: (state, action: PayloadAction) => { + state.selectedPolicies = action.payload; + }, + setShowPassword: (state, action: PayloadAction) => { + state.showPassword = action.payload; + }, + setSendEnabled: (state) => { + state.sendEnabled = state.userName.trim() !== ""; + }, + setApinoerror: (state, action: PayloadAction) => { + state.apinoerror = action.payload; + }, + }, + extraReducers: (builder) => { + builder + .addCase(resetFormAsync.fulfilled, (state, action) => { + state.userName = ""; + state.selectedGroups = []; + state.secretKey = ""; + state.selectedPolicies = []; + state.showPassword = false; + }) + .addCase(createUserAsync.pending, (state, action) => { + state.addLoading = true; + }) + .addCase(createUserAsync.rejected, (state, action) => { + state.addLoading = false; + }) + .addCase(createUserAsync.fulfilled, (state, action) => { + state.apinoerror = true; + }); + }, }); export const { - setUserName, - setSelectedGroups, - setSecretKey, - setSelectedPolicies, - setShowPassword, - setAddLoading, - setSendEnabled, - setApinoerror, + setUserName, + setSelectedGroups, + setSecretKey, + setSelectedPolicies, + setShowPassword, + setAddLoading, + setSendEnabled, + setApinoerror, } = createUserSlice.actions; export default createUserSlice.reducer; diff --git a/portal-ui/src/screens/Console/Users/PasswordSelector.tsx b/portal-ui/src/screens/Console/Users/PasswordSelector.tsx index 9f3f9a68c7..baf52e1769 100644 --- a/portal-ui/src/screens/Console/Users/PasswordSelector.tsx +++ b/portal-ui/src/screens/Console/Users/PasswordSelector.tsx @@ -16,48 +16,42 @@ import React from "react"; import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; -import {setSecretKey, setShowPassword} from "./AddUsersSlice"; +import { setSecretKey, setShowPassword } from "./AddUsersSlice"; import { useSelector } from "react-redux"; -import {AppState, useAppDispatch} from "../../../store"; +import { AppState, useAppDispatch } from "../../../store"; import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye"; interface IAddUserProps2 { - classes: any; + classes: any; } -const PasswordSelector = ({ classes }: IAddUserProps2 ) => { - const dispatch = useAppDispatch(); - const showPassword = useSelector( - (state: AppState) => state.createUser.showPassword - ) - const secretKey = useSelector( - (state: AppState) => state.createUser.secretKey - ) - return ( - ) => { - dispatch(setSecretKey(e.target.value)); - }} - autoComplete="current-password" - overlayIcon={ - showPassword ? ( - - ) : ( - - ) - } - overlayAction={() => dispatch(setShowPassword(!showPassword))} - /> - ); +const PasswordSelector = ({ classes }: IAddUserProps2) => { + const dispatch = useAppDispatch(); + const showPassword = useSelector( + (state: AppState) => state.createUser.showPassword + ); + const secretKey = useSelector( + (state: AppState) => state.createUser.secretKey + ); + return ( + ) => { + dispatch(setSecretKey(e.target.value)); + }} + autoComplete="current-password" + overlayIcon={showPassword ? : } + overlayAction={() => dispatch(setShowPassword(!showPassword))} + /> + ); }; export default PasswordSelector; diff --git a/portal-ui/src/screens/Console/Users/SetUserPolicies.tsx b/portal-ui/src/screens/Console/Users/SetUserPolicies.tsx index 86e2d0eccd..2ea03d9749 100644 --- a/portal-ui/src/screens/Console/Users/SetUserPolicies.tsx +++ b/portal-ui/src/screens/Console/Users/SetUserPolicies.tsx @@ -61,7 +61,9 @@ const SetUserPolicies = ({ const [actualPolicy, setActualPolicy] = useState([]); const [selectedPolicy, setSelectedPolicy] = useState([]); - const statePolicies = useSelector((state: AppState) => state.createUser.selectedPolicies); + const statePolicies = useSelector( + (state: AppState) => state.createUser.selectedPolicies + ); const SetUserPoliciesAction = () => { let entity = "user"; @@ -111,9 +113,7 @@ const SetUserPolicies = ({ > - + diff --git a/portal-ui/src/screens/Console/Users/UserSelector.tsx b/portal-ui/src/screens/Console/Users/UserSelector.tsx index 76f2ef9294..3889e562b4 100644 --- a/portal-ui/src/screens/Console/Users/UserSelector.tsx +++ b/portal-ui/src/screens/Console/Users/UserSelector.tsx @@ -18,34 +18,32 @@ import React, { Fragment } from "react"; import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import { setUserName } from "./AddUsersSlice"; import { useSelector } from "react-redux"; -import {AppState, useAppDispatch} from "../../../store"; +import { AppState, useAppDispatch } from "../../../store"; interface IAddUserProps2 { - classes: any; + classes: any; } -const UserSelector = ({ classes }: IAddUserProps2 ) => { - const dispatch = useAppDispatch(); - const userName = useSelector( - (state: AppState) => state.createUser.userName - ) - return ( - - ) => { - dispatch(setUserName(e.target.value)); - }} - /> - - ); +const UserSelector = ({ classes }: IAddUserProps2) => { + const dispatch = useAppDispatch(); + const userName = useSelector((state: AppState) => state.createUser.userName); + return ( + + ) => { + dispatch(setUserName(e.target.value)); + }} + /> + + ); }; export default UserSelector; diff --git a/portal-ui/src/screens/Console/Users/thunk/AddUsersThunk.tsx b/portal-ui/src/screens/Console/Users/thunk/AddUsersThunk.tsx index 1c7394c5bb..03e85f77f2 100644 --- a/portal-ui/src/screens/Console/Users/thunk/AddUsersThunk.tsx +++ b/portal-ui/src/screens/Console/Users/thunk/AddUsersThunk.tsx @@ -16,50 +16,50 @@ import { createAsyncThunk } from "@reduxjs/toolkit"; import { - setSelectedGroups, - setUserName, - setSecretKey, - setSelectedPolicies, - setShowPassword, - setAddLoading, + setSelectedGroups, + setUserName, + setSecretKey, + setSelectedPolicies, + setShowPassword, + setAddLoading, } from "../AddUsersSlice"; -import {AppState} from "../../../../store"; +import { AppState } from "../../../../store"; import api from "../../../../common/api"; -import {ErrorResponseHandler} from "../../../../common/types"; -import {setErrorSnackMessage} from "../../../../systemSlice"; +import { ErrorResponseHandler } from "../../../../common/types"; +import { setErrorSnackMessage } from "../../../../systemSlice"; export const resetFormAsync = createAsyncThunk( - "resetForm/resetFormAsync", - async (_, { dispatch }) => { - dispatch(setSelectedGroups([])); - dispatch(setUserName("")); - dispatch(setSecretKey("")); - dispatch(setSelectedPolicies([])); - dispatch(setShowPassword(false)); - } + "resetForm/resetFormAsync", + async (_, { dispatch }) => { + dispatch(setSelectedGroups([])); + dispatch(setUserName("")); + dispatch(setSecretKey("")); + dispatch(setSelectedPolicies([])); + dispatch(setShowPassword(false)); + } ); export const createUserAsync = createAsyncThunk( - "createTenant/createNamespaceAsync", - async (_, { getState, rejectWithValue, dispatch }) => { - const state = getState() as AppState; - const accessKey = state.createUser.userName - const secretKey = state.createUser.secretKey - const selectedGroups = state.createUser.selectedGroups - const selectedPolicies = state.createUser.selectedPolicies - return api - .invoke("POST", "/api/v1/users", { - accessKey, - secretKey, - groups: selectedGroups, - policies: selectedPolicies, - }) - .then((res) => { - dispatch(setAddLoading(false)); - }) - .catch((err: ErrorResponseHandler) => { - dispatch(setAddLoading(false)); - dispatch(setErrorSnackMessage(err)); - }); - } + "createTenant/createNamespaceAsync", + async (_, { getState, rejectWithValue, dispatch }) => { + const state = getState() as AppState; + const accessKey = state.createUser.userName; + const secretKey = state.createUser.secretKey; + const selectedGroups = state.createUser.selectedGroups; + const selectedPolicies = state.createUser.selectedPolicies; + return api + .invoke("POST", "/api/v1/users", { + accessKey, + secretKey, + groups: selectedGroups, + policies: selectedPolicies, + }) + .then((res) => { + dispatch(setAddLoading(false)); + }) + .catch((err: ErrorResponseHandler) => { + dispatch(setAddLoading(false)); + dispatch(setErrorSnackMessage(err)); + }); + } );