From 41e1b4a5d58ed3dc10c4bc845a13d0a5820e48c8 Mon Sep 17 00:00:00 2001 From: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> Date: Fri, 3 Jun 2022 21:39:12 -0700 Subject: [PATCH] Add Bucket slice refactor to reduce re-renders (#2087) * Add Bucket slice refactor to reduce re-renders * Fix Button on object browser * Update Logo Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> --- portal-ui/src/icons/ConsoleAgpl.tsx | 43 ++- .../src/screens/Console/Buckets/Buckets.tsx | 2 +- .../ListBuckets/{ => AddBucket}/AddBucket.tsx | 272 ++++-------------- .../ListBuckets/AddBucket/AddBucketName.tsx | 41 +++ .../ListBuckets/AddBucket/addBucketThunks.ts | 85 ++++++ .../ListBuckets/AddBucket/addBucketsSlice.ts | 159 ++++++++++ .../screens/Console/Buckets/bucketsSlice.ts | 122 -------- .../Common/Components/LicensedConsoleLogo.tsx | 2 +- .../ObjectBrowser/BrowserBreadcrumbs.tsx | 15 +- portal-ui/src/store.ts | 4 +- 10 files changed, 380 insertions(+), 365 deletions(-) rename portal-ui/src/screens/Console/Buckets/ListBuckets/{ => AddBucket}/AddBucket.tsx (60%) create mode 100644 portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucketName.tsx create mode 100644 portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketThunks.ts create mode 100644 portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts delete mode 100644 portal-ui/src/screens/Console/Buckets/bucketsSlice.ts diff --git a/portal-ui/src/icons/ConsoleAgpl.tsx b/portal-ui/src/icons/ConsoleAgpl.tsx index c58daeaa9..8cb096fb9 100644 --- a/portal-ui/src/icons/ConsoleAgpl.tsx +++ b/portal-ui/src/icons/ConsoleAgpl.tsx @@ -22,30 +22,29 @@ const ConsoleAgpl = (props: SVGProps) => { {...props} className={`min-icon`} fill={"currentcolor"} + viewBox="0 0 61.059 25.5334" xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 61.059 29.822" > - - - - - - - - - - + + + + + ); diff --git a/portal-ui/src/screens/Console/Buckets/Buckets.tsx b/portal-ui/src/screens/Console/Buckets/Buckets.tsx index dbd713b5d..c5259a1eb 100644 --- a/portal-ui/src/screens/Console/Buckets/Buckets.tsx +++ b/portal-ui/src/screens/Console/Buckets/Buckets.tsx @@ -27,7 +27,7 @@ const BucketDetails = React.lazy(() => import("./BucketDetails/BucketDetails")); const BrowserHandler = React.lazy( () => import("./BucketDetails/BrowserHandler") ); -const AddBucket = React.lazy(() => import("./ListBuckets/AddBucket")); +const AddBucket = React.lazy(() => import("./ListBuckets/AddBucket/AddBucket")); const Buckets = () => { return ( diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx similarity index 60% rename from portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket.tsx rename to portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx index a1cc9f3c7..98cc096f6 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucket.tsx @@ -1,5 +1,5 @@ // This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. +// Copyright (c) 2022 MinIO, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by @@ -14,51 +14,43 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { Fragment, useEffect, useState } from "react"; +import React, { Fragment } from "react"; import Grid from "@mui/material/Grid"; import { Button, LinearProgress } from "@mui/material"; import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; -import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary"; -import api from "../../../../common/api"; -import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; -import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector"; -import { getBytes, k8sScalarUnitsExcluding } from "../../../../common/utils"; -import { AppState } from "../../../../store"; -import history from "../../../../history"; +import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary"; +import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector"; +import { k8sScalarUnitsExcluding } from "../../../../../common/utils"; +import { AppState } from "../../../../../store"; import { useDispatch, useSelector } from "react-redux"; -import { useDebounce } from "use-debounce"; -import { MakeBucketRequest } from "../types"; -import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; -import { ErrorResponseHandler } from "../../../../common/types"; -import PageHeader from "../../Common/PageHeader/PageHeader"; -import BackLink from "../../../../common/BackLink"; -import { BucketsIcon, InfoIcon } from "../../../../icons"; +import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; +import PageHeader from "../../../Common/PageHeader/PageHeader"; +import BackLink from "../../../../../common/BackLink"; +import { BucketsIcon, InfoIcon } from "../../../../../icons"; -import PageLayout from "../../Common/Layout/PageLayout"; -import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu"; -import FormLayout from "../../Common/FormLayout"; -import HelpBox from "../../../../common/HelpBox"; -import SectionTitle from "../../Common/SectionTitle"; +import PageLayout from "../../../Common/Layout/PageLayout"; +import InputUnitMenu from "../../../Common/FormComponents/InputUnitMenu/InputUnitMenu"; +import FormLayout from "../../../Common/FormLayout"; +import HelpBox from "../../../../../common/HelpBox"; +import SectionTitle from "../../../Common/SectionTitle"; +import { selDistSet, selSiteRep } from "../../../../../systemSlice"; import { - selDistSet, - selSiteRep, - setErrorSnackMessage, -} from "../../../../systemSlice"; -import { - addBucketEnableObjectLocking, - addBucketName, - addBucketQuota, - addBucketQuotaSize, - addBucketQuotaType, - addBucketQuotaUnit, - addBucketRetention, - addBucketRetentionMode, - addBucketRetentionUnit, - addBucketRetentionValidity, - addBucketVersioning, -} from "../bucketsSlice"; + resetForm, + setEnableObjectLocking, + setQuota, + setQuotaSize, + setQuotaUnit, + setRetention, + setRetentionMode, + setRetentionUnit, + setRetentionValidity, + setVersioning, +} from "./addBucketsSlice"; +import { addBucketAsync } from "./addBucketThunks"; +import AddBucketName from "./AddBucketName"; const styles = (theme: Theme) => createStyles({ @@ -105,172 +97,48 @@ const styles = (theme: Theme) => ...containerForHeader(theme.spacing(4)), }); -interface IAddBucketProps { +interface IsetProps { classes: any; } -const AddBucket = ({ classes }: IAddBucketProps) => { +const AddBucket = ({ classes }: IsetProps) => { const dispatch = useDispatch(); - const bucketName = useSelector( - (state: AppState) => state.buckets.addBucketName - ); const versioningEnabled = useSelector( - (state: AppState) => state.buckets.addBucketVersioningEnabled + (state: AppState) => state.addBucket.versioningEnabled ); const lockingEnabled = useSelector( - (state: AppState) => state.buckets.addBucketLockingEnabled + (state: AppState) => state.addBucket.lockingEnabled ); const quotaEnabled = useSelector( - (state: AppState) => state.buckets.addBucketQuotaEnabled - ); - const quotaType = useSelector( - (state: AppState) => state.buckets.addBucketQuotaType - ); - const quotaSize = useSelector( - (state: AppState) => state.buckets.addBucketQuotaSize - ); - const quotaUnit = useSelector( - (state: AppState) => state.buckets.addBucketQuotaUnit + (state: AppState) => state.addBucket.quotaEnabled ); + const quotaSize = useSelector((state: AppState) => state.addBucket.quotaSize); + const quotaUnit = useSelector((state: AppState) => state.addBucket.quotaUnit); const retentionEnabled = useSelector( - (state: AppState) => state.buckets.addBucketRetentionEnabled + (state: AppState) => state.addBucket.retentionEnabled ); const retentionMode = useSelector( - (state: AppState) => state.buckets.addBucketRetentionMode + (state: AppState) => state.addBucket.retentionMode ); const retentionUnit = useSelector( - (state: AppState) => state.buckets.addBucketRetentionUnit + (state: AppState) => state.addBucket.retentionUnit ); const retentionValidity = useSelector( - (state: AppState) => state.buckets.addBucketRetentionValidity + (state: AppState) => state.addBucket.retentionValidity + ); + const addLoading = useSelector((state: AppState) => state.addBucket.loading); + const valid = useSelector((state: AppState) => state.addBucket.valid); + const lockingFieldDisabled = useSelector( + (state: AppState) => state.addBucket.lockingFieldDisabled ); const distributedSetup = useSelector(selDistSet); const siteReplicationInfo = useSelector(selSiteRep); - const [addLoading, setAddLoading] = useState(false); - const [sendEnabled, setSendEnabled] = useState(false); - const [lockingFieldDisabled, setLockingFieldDisabled] = - useState(false); - - const addRecord = (event: React.FormEvent) => { - event.preventDefault(); - if (addLoading) { - return; - } - setAddLoading(true); - - let request: MakeBucketRequest = { - name: bucketName, - versioning: - distributedSetup && !siteReplicationInfo.enabled - ? versioningEnabled - : false, - locking: distributedSetup ? lockingEnabled : false, - }; - - if (distributedSetup) { - if (quotaEnabled) { - const amount = getBytes(quotaSize, quotaUnit, true); - request.quota = { - enabled: true, - quota_type: quotaType, - amount: parseInt(amount), - }; - } - - if (retentionEnabled) { - request.retention = { - mode: retentionMode, - unit: retentionUnit, - validity: retentionValidity, - }; - } - } - - api - .invoke("POST", "/api/v1/buckets", request) - .then((res) => { - setAddLoading(false); - const newBucketName = `${bucketName}`; - resetForm(); - history.push(`/buckets/${newBucketName}/browse`); - }) - .catch((err: ErrorResponseHandler) => { - setAddLoading(false); - dispatch(setErrorSnackMessage(err)); - }); + const resForm = () => { + dispatch(resetForm()); }; - const [value] = useDebounce(bucketName, 1000); - - useEffect(() => { - dispatch(addBucketName(value)); - }, [value, dispatch]); - - const resetForm = () => { - dispatch(addBucketName("")); - dispatch(addBucketVersioning(false)); - dispatch(addBucketEnableObjectLocking(false)); - dispatch(addBucketQuota(false)); - dispatch(addBucketQuotaType("hard")); - dispatch(addBucketQuotaSize("1")); - dispatch(addBucketQuotaUnit("Ti")); - dispatch(addBucketRetention(false)); - dispatch(addBucketRetentionMode("compliance")); - dispatch(addBucketRetentionUnit("days")); - dispatch(addBucketRetentionValidity(180)); - }; - - useEffect(() => { - let valid = false; - - if (bucketName.trim() !== "") { - valid = true; - } - - if (quotaEnabled && valid) { - if (quotaSize.trim() === "" || parseInt(quotaSize) === 0) { - valid = false; - } - } - - if (!versioningEnabled || !retentionEnabled) { - dispatch(addBucketRetention(false)); - dispatch(addBucketRetentionMode("compliance")); - dispatch(addBucketRetentionUnit("days")); - dispatch(addBucketRetentionValidity(180)); - } - - if (retentionEnabled) { - // if retention is enabled, then objec locking should be enabled as well - dispatch(addBucketEnableObjectLocking(true)); - setLockingFieldDisabled(true); - } else { - setLockingFieldDisabled(false); - } - - if ( - retentionEnabled && - (Number.isNaN(retentionValidity) || retentionValidity < 1) - ) { - valid = false; - } - - setSendEnabled(valid); - }, [ - bucketName, - retentionEnabled, - lockingEnabled, - quotaType, - quotaSize, - quotaUnit, - quotaEnabled, - dispatch, - retentionValidity, - versioningEnabled, - ]); - return ( } /> @@ -312,21 +180,13 @@ const AddBucket = ({ classes }: IAddBucketProps) => { noValidate autoComplete="off" onSubmit={(e: React.FormEvent) => { - addRecord(e); + e.preventDefault(); + dispatch(addBucketAsync()); }} > - ) => { - dispatch(addBucketName(event.target.value)); - }} - label="Bucket Name" - value={bucketName} - /> + Features @@ -368,7 +228,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { name="versioned" checked={versioningEnabled} onChange={(event: React.ChangeEvent) => { - dispatch(addBucketVersioning(event.target.checked)); + dispatch(setVersioning(event.target.checked)); }} label={"Versioning"} disabled={ @@ -386,11 +246,9 @@ const AddBucket = ({ classes }: IAddBucketProps) => { disabled={lockingFieldDisabled || !distributedSetup} checked={lockingEnabled} onChange={(event: React.ChangeEvent) => { - dispatch( - addBucketEnableObjectLocking(event.target.checked) - ); + dispatch(setEnableObjectLocking(event.target.checked)); if (event.target.checked && !siteReplicationInfo.enabled) { - dispatch(addBucketVersioning(true)); + dispatch(setVersioning(true)); } }} label={"Object Locking"} @@ -404,7 +262,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { name="bucket_quota" checked={quotaEnabled} onChange={(event: React.ChangeEvent) => { - dispatch(addBucketQuota(event.target.checked)); + dispatch(setQuota(event.target.checked)); }} label={"Quota"} disabled={!distributedSetup} @@ -419,7 +277,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { name="quota_size" onChange={(e: React.ChangeEvent) => { if (e.target.validity.valid) { - dispatch(addBucketQuotaSize(e.target.value)); + dispatch(setQuotaSize(e.target.value)); } }} label="Capacity" @@ -431,7 +289,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { { - dispatch(addBucketQuotaUnit(newValue)); + dispatch(setQuotaUnit(newValue)); }} unitSelected={quotaUnit} unitsList={k8sScalarUnitsExcluding(["Ki"])} @@ -450,7 +308,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { name="bucket_retention" checked={retentionEnabled} onChange={(event: React.ChangeEvent) => { - dispatch(addBucketRetention(event.target.checked)); + dispatch(setRetention(event.target.checked)); }} label={"Retention"} /> @@ -465,9 +323,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { name="retention_mode" label="Mode" onChange={(e: React.ChangeEvent<{ value: unknown }>) => { - dispatch( - addBucketRetentionMode(e.target.value as string) - ); + dispatch(setRetentionMode(e.target.value as string)); }} selectorOptions={[ { value: "compliance", label: "Compliance" }, @@ -481,9 +337,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { id="retention_validity" name="retention_validity" onChange={(e: React.ChangeEvent) => { - dispatch( - addBucketRetentionValidity(e.target.valueAsNumber) - ); + dispatch(setRetentionValidity(e.target.valueAsNumber)); }} label="Validity" value={String(retentionValidity)} @@ -492,7 +346,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { { - dispatch(addBucketRetentionUnit(newValue)); + dispatch(setRetentionUnit(newValue)); }} unitSelected={retentionUnit} unitsList={[ @@ -512,7 +366,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { type="button" variant={"outlined"} className={classes.clearButton} - onClick={resetForm} + onClick={resForm} > Clear @@ -520,7 +374,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => { type="submit" variant="contained" color="primary" - disabled={addLoading || !sendEnabled} + disabled={addLoading || valid} > Create Bucket diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucketName.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucketName.tsx new file mode 100644 index 000000000..05e104c4f --- /dev/null +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/AddBucketName.tsx @@ -0,0 +1,41 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React from "react"; +import { setName } from "./addBucketsSlice"; +import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import { useDispatch, useSelector } from "react-redux"; +import { AppState } from "../../../../../store"; + +const AddBucketName = () => { + const dispatch = useDispatch(); + + const bucketName = useSelector((state: AppState) => state.addBucket.name); + return ( + ) => { + dispatch(setName(event.target.value)); + }} + label="Bucket Name" + value={bucketName} + /> + ); +}; + +export default AddBucketName; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketThunks.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketThunks.ts new file mode 100644 index 000000000..0ee746b6e --- /dev/null +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketThunks.ts @@ -0,0 +1,85 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { MakeBucketRequest } from "../../types"; +import { getBytes } from "../../../../../common/utils"; +import api from "../../../../../common/api"; +import history from "../../../../../history"; +import { ErrorResponseHandler } from "../../../../../common/types"; +import { setErrorSnackMessage } from "../../../../../systemSlice"; +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { AppState } from "../../../../../store"; +import { resetForm } from "./addBucketsSlice"; + +export const addBucketAsync = createAsyncThunk( + "buckets/addBucketAsync", + async (_, { getState, rejectWithValue, dispatch }) => { + const state = getState() as AppState; + + const bucketName = state.addBucket.name; + const versioningEnabled = state.addBucket.versioningEnabled; + const lockingEnabled = state.addBucket.lockingEnabled; + const quotaEnabled = state.addBucket.quotaEnabled; + const quotaSize = state.addBucket.quotaSize; + const quotaUnit = state.addBucket.quotaUnit; + const retentionEnabled = state.addBucket.retentionEnabled; + const retentionMode = state.addBucket.retentionMode; + const retentionUnit = state.addBucket.retentionUnit; + const retentionValidity = state.addBucket.retentionValidity; + const distributedSetup = state.system.distributedSetup; + const siteReplicationInfo = state.system.siteReplicationInfo; + + let request: MakeBucketRequest = { + name: bucketName, + versioning: + distributedSetup && !siteReplicationInfo.enabled + ? versioningEnabled + : false, + locking: distributedSetup ? lockingEnabled : false, + }; + + if (distributedSetup) { + if (quotaEnabled) { + const amount = getBytes(quotaSize, quotaUnit, true); + request.quota = { + enabled: true, + quota_type: "hard", + amount: parseInt(amount), + }; + } + + if (retentionEnabled) { + request.retention = { + mode: retentionMode, + unit: retentionUnit, + validity: retentionValidity, + }; + } + } + + return api + .invoke("POST", "/api/v1/buckets", request) + .then((res) => { + const newBucketName = `${bucketName}`; + dispatch(resetForm()); + history.push(`/buckets/${newBucketName}/browse`); + }) + .catch((err: ErrorResponseHandler) => { + dispatch(setErrorSnackMessage(err)); + return rejectWithValue(err); + }); + } +); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts new file mode 100644 index 000000000..68f1b38f8 --- /dev/null +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice.ts @@ -0,0 +1,159 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// 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 { addBucketAsync } from "./addBucketThunks"; + +export interface AddBucketState { + loading: boolean; + valid: boolean; + name: string; + versioningEnabled: boolean; + lockingEnabled: boolean; + lockingFieldDisabled: boolean; + quotaEnabled: boolean; + quotaSize: string; + quotaUnit: string; + retentionEnabled: boolean; + retentionMode: string; + retentionUnit: string; + retentionValidity: number; +} + +const initialState: AddBucketState = { + loading: false, + valid: false, + name: "", + versioningEnabled: false, + lockingEnabled: false, + lockingFieldDisabled: false, + quotaEnabled: false, + quotaSize: "1", + quotaUnit: "Ti", + retentionEnabled: false, + retentionMode: "compliance", + retentionUnit: "days", + retentionValidity: 180, +}; + +export const addBucketsSlice = createSlice({ + name: "addBuckets", + initialState, + reducers: { + setName: (state, action: PayloadAction) => { + state.name = action.payload; + if (state.name.trim() === "") { + state.valid = false; + } + }, + setVersioning: (state, action: PayloadAction) => { + state.versioningEnabled = action.payload; + if (!state.versioningEnabled || !state.retentionEnabled) { + state.retentionEnabled = false; + state.retentionMode = "compliance"; + state.retentionUnit = "days"; + state.retentionValidity = 180; + } + }, + setEnableObjectLocking: (state, action: PayloadAction) => { + state.lockingEnabled = action.payload; + }, + setQuota: (state, action: PayloadAction) => { + state.quotaEnabled = action.payload; + }, + setQuotaSize: (state, action: PayloadAction) => { + state.quotaSize = action.payload; + + if (state.quotaEnabled && state.valid) { + if (state.quotaSize.trim() === "" || parseInt(state.quotaSize) === 0) { + state.valid = false; + } + } + }, + setQuotaUnit: (state, action: PayloadAction) => { + state.quotaUnit = action.payload; + }, + setRetention: (state, action: PayloadAction) => { + state.retentionEnabled = action.payload; + if (!state.versioningEnabled || !state.retentionEnabled) { + state.retentionEnabled = false; + state.retentionMode = "compliance"; + state.retentionUnit = "days"; + state.retentionValidity = 180; + } + + if (state.retentionEnabled) { + // if retention is enabled, then object locking should be enabled as well + state.lockingEnabled = true; + state.lockingFieldDisabled = true; + } else { + state.lockingFieldDisabled = false; + } + + if ( + state.retentionEnabled && + (Number.isNaN(state.retentionValidity) || state.retentionValidity < 1) + ) { + state.valid = false; + } + }, + setRetentionMode: (state, action: PayloadAction) => { + state.retentionMode = action.payload; + }, + setRetentionUnit: (state, action: PayloadAction) => { + state.retentionUnit = action.payload; + }, + setRetentionValidity: (state, action: PayloadAction) => { + state.retentionValidity = action.payload; + if ( + state.retentionEnabled && + (Number.isNaN(state.retentionValidity) || state.retentionValidity < 1) + ) { + state.valid = false; + } + }, + + resetForm: (state) => initialState, + }, + extraReducers: (builder) => { + builder + .addCase(addBucketAsync.pending, (state) => { + state.loading = true; + }) + .addCase(addBucketAsync.rejected, (state) => { + state.loading = false; + }) + .addCase(addBucketAsync.fulfilled, (state, action) => { + state.loading = false; + }); + }, +}); + +export const { + setName, + setVersioning, + setEnableObjectLocking, + setQuota, + setQuotaSize, + setQuotaUnit, + resetForm, + setRetention, + setRetentionMode, + setRetentionUnit, + setRetentionValidity, +} = addBucketsSlice.actions; + +export default addBucketsSlice.reducer; diff --git a/portal-ui/src/screens/Console/Buckets/bucketsSlice.ts b/portal-ui/src/screens/Console/Buckets/bucketsSlice.ts deleted file mode 100644 index 13757a22e..000000000 --- a/portal-ui/src/screens/Console/Buckets/bucketsSlice.ts +++ /dev/null @@ -1,122 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// 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"; - -export interface BucketsState { - open: boolean; - addBucketName: string; - addBucketVersioningEnabled: boolean; - addBucketLockingEnabled: boolean; - addBucketQuotaEnabled: boolean; - addBucketQuotaType: string; - addBucketQuotaSize: string; - addBucketQuotaUnit: string; - addBucketRetentionEnabled: boolean; - addBucketRetentionMode: string; - addBucketRetentionUnit: string; - addBucketRetentionValidity: number; -} - -const initialState: BucketsState = { - open: false, - addBucketName: "", - addBucketVersioningEnabled: false, - addBucketLockingEnabled: false, - addBucketQuotaEnabled: false, - addBucketQuotaType: "hard", - addBucketQuotaSize: "1", - addBucketQuotaUnit: "Ti", - addBucketRetentionEnabled: false, - addBucketRetentionMode: "compliance", - addBucketRetentionUnit: "days", - addBucketRetentionValidity: 180, -}; - -export const bucketsSlice = createSlice({ - name: "buckets", - initialState, - reducers: { - addBucketOpen: (state, action: PayloadAction) => { - state.open = action.payload; - }, - addBucketName: (state, action: PayloadAction) => { - state.addBucketName = action.payload; - }, - addBucketVersioning: (state, action: PayloadAction) => { - state.addBucketVersioningEnabled = action.payload; - }, - addBucketEnableObjectLocking: (state, action: PayloadAction) => { - state.addBucketLockingEnabled = action.payload; - }, - addBucketQuota: (state, action: PayloadAction) => { - state.addBucketQuotaEnabled = action.payload; - }, - addBucketQuotaType: (state, action: PayloadAction) => { - state.addBucketQuotaType = action.payload; - }, - addBucketQuotaSize: (state, action: PayloadAction) => { - state.addBucketQuotaSize = action.payload; - }, - addBucketQuotaUnit: (state, action: PayloadAction) => { - state.addBucketQuotaUnit = action.payload; - }, - addBucketRetention: (state, action: PayloadAction) => { - state.addBucketRetentionEnabled = action.payload; - }, - addBucketRetentionMode: (state, action: PayloadAction) => { - state.addBucketRetentionMode = action.payload; - }, - addBucketRetentionUnit: (state, action: PayloadAction) => { - state.addBucketRetentionUnit = action.payload; - }, - addBucketRetentionValidity: (state, action: PayloadAction) => { - state.addBucketRetentionValidity = action.payload; - }, - - addBucketReset: (state) => { - state.addBucketName = ""; - state.addBucketVersioningEnabled = false; - state.addBucketLockingEnabled = false; - state.addBucketQuotaEnabled = false; - state.addBucketQuotaType = "hard"; - state.addBucketQuotaSize = "1"; - state.addBucketQuotaUnit = "Ti"; - state.addBucketRetentionEnabled = false; - state.addBucketRetentionMode = "compliance"; - state.addBucketRetentionUnit = "days"; - state.addBucketRetentionValidity = 180; - }, - }, -}); - -export const { - addBucketOpen, - addBucketName, - addBucketVersioning, - addBucketEnableObjectLocking, - addBucketQuota, - addBucketQuotaType, - addBucketQuotaSize, - addBucketQuotaUnit, - addBucketReset, - addBucketRetention, - addBucketRetentionMode, - addBucketRetentionUnit, - addBucketRetentionValidity, -} = bucketsSlice.actions; - -export default bucketsSlice.reducer; diff --git a/portal-ui/src/screens/Console/Common/Components/LicensedConsoleLogo.tsx b/portal-ui/src/screens/Console/Common/Components/LicensedConsoleLogo.tsx index 95fe0826b..342debc69 100644 --- a/portal-ui/src/screens/Console/Common/Components/LicensedConsoleLogo.tsx +++ b/portal-ui/src/screens/Console/Common/Components/LicensedConsoleLogo.tsx @@ -36,7 +36,7 @@ const LicensedConsoleLogo = ({ } else if (plan === "ENTERPRISE") { licenseLogo = ; } else { - licenseLogo = ; + licenseLogo = ; } return licenseLogo; diff --git a/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx b/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx index 45e258ace..3242b513c 100644 --- a/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx +++ b/portal-ui/src/screens/Console/ObjectBrowser/BrowserBreadcrumbs.tsx @@ -34,6 +34,7 @@ import withSuspense from "../Common/Components/withSuspense"; import { setSnackBarMessage } from "../../../systemSlice"; import { AppState } from "../../../store"; import { setVersionsModeEnabled } from "./objectBrowserSlice"; +import RBIconButton from "../Buckets/BucketDetails/SummaryItems/RBIconButton"; const CreatePathModal = withSuspense( React.lazy( @@ -191,9 +192,9 @@ const BrowserBreadcrumbs = ({ {listBreadcrumbs} -