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>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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 (
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<boolean>(false);
|
||||
const [sendEnabled, setSendEnabled] = useState<boolean>(false);
|
||||
const [lockingFieldDisabled, setLockingFieldDisabled] =
|
||||
useState<boolean>(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 (
|
||||
<Fragment>
|
||||
<PageHeader label={<BackLink to={"/buckets"} label={"Buckets"} />} />
|
||||
@@ -312,21 +180,13 @@ const AddBucket = ({ classes }: IAddBucketProps) => {
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
addRecord(e);
|
||||
e.preventDefault();
|
||||
dispatch(addBucketAsync());
|
||||
}}
|
||||
>
|
||||
<Grid container marginTop={1} spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
autoFocus={true}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(addBucketName(event.target.value));
|
||||
}}
|
||||
label="Bucket Name"
|
||||
value={bucketName}
|
||||
/>
|
||||
<AddBucketName />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SectionTitle>Features</SectionTitle>
|
||||
@@ -368,7 +228,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => {
|
||||
name="versioned"
|
||||
checked={versioningEnabled}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
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) => {
|
||||
<InputUnitMenu
|
||||
id={"quota_unit"}
|
||||
onUnitChange={(newValue) => {
|
||||
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<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
dispatch(
|
||||
addBucketRetentionValidity(e.target.valueAsNumber)
|
||||
);
|
||||
dispatch(setRetentionValidity(e.target.valueAsNumber));
|
||||
}}
|
||||
label="Validity"
|
||||
value={String(retentionValidity)}
|
||||
@@ -492,7 +346,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => {
|
||||
<InputUnitMenu
|
||||
id={"retention_unit"}
|
||||
onUnitChange={(newValue) => {
|
||||
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
|
||||
</Button>
|
||||
@@ -520,7 +374,7 @@ const AddBucket = ({ classes }: IAddBucketProps) => {
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !sendEnabled}
|
||||
disabled={addLoading || valid}
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 (
|
||||
<InputBoxWrapper
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
autoFocus={true}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(setName(event.target.value));
|
||||
}}
|
||||
label="Bucket Name"
|
||||
value={bucketName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBucketName;
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<string>) => {
|
||||
state.name = action.payload;
|
||||
if (state.name.trim() === "") {
|
||||
state.valid = false;
|
||||
}
|
||||
},
|
||||
setVersioning: (state, action: PayloadAction<boolean>) => {
|
||||
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<boolean>) => {
|
||||
state.lockingEnabled = action.payload;
|
||||
},
|
||||
setQuota: (state, action: PayloadAction<boolean>) => {
|
||||
state.quotaEnabled = action.payload;
|
||||
},
|
||||
setQuotaSize: (state, action: PayloadAction<string>) => {
|
||||
state.quotaSize = action.payload;
|
||||
|
||||
if (state.quotaEnabled && state.valid) {
|
||||
if (state.quotaSize.trim() === "" || parseInt(state.quotaSize) === 0) {
|
||||
state.valid = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
setQuotaUnit: (state, action: PayloadAction<string>) => {
|
||||
state.quotaUnit = action.payload;
|
||||
},
|
||||
setRetention: (state, action: PayloadAction<boolean>) => {
|
||||
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<string>) => {
|
||||
state.retentionMode = action.payload;
|
||||
},
|
||||
setRetentionUnit: (state, action: PayloadAction<string>) => {
|
||||
state.retentionUnit = action.payload;
|
||||
},
|
||||
setRetentionValidity: (state, action: PayloadAction<number>) => {
|
||||
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;
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<boolean>) => {
|
||||
state.open = action.payload;
|
||||
},
|
||||
addBucketName: (state, action: PayloadAction<string>) => {
|
||||
state.addBucketName = action.payload;
|
||||
},
|
||||
addBucketVersioning: (state, action: PayloadAction<boolean>) => {
|
||||
state.addBucketVersioningEnabled = action.payload;
|
||||
},
|
||||
addBucketEnableObjectLocking: (state, action: PayloadAction<boolean>) => {
|
||||
state.addBucketLockingEnabled = action.payload;
|
||||
},
|
||||
addBucketQuota: (state, action: PayloadAction<boolean>) => {
|
||||
state.addBucketQuotaEnabled = action.payload;
|
||||
},
|
||||
addBucketQuotaType: (state, action: PayloadAction<string>) => {
|
||||
state.addBucketQuotaType = action.payload;
|
||||
},
|
||||
addBucketQuotaSize: (state, action: PayloadAction<string>) => {
|
||||
state.addBucketQuotaSize = action.payload;
|
||||
},
|
||||
addBucketQuotaUnit: (state, action: PayloadAction<string>) => {
|
||||
state.addBucketQuotaUnit = action.payload;
|
||||
},
|
||||
addBucketRetention: (state, action: PayloadAction<boolean>) => {
|
||||
state.addBucketRetentionEnabled = action.payload;
|
||||
},
|
||||
addBucketRetentionMode: (state, action: PayloadAction<string>) => {
|
||||
state.addBucketRetentionMode = action.payload;
|
||||
},
|
||||
addBucketRetentionUnit: (state, action: PayloadAction<string>) => {
|
||||
state.addBucketRetentionUnit = action.payload;
|
||||
},
|
||||
addBucketRetentionValidity: (state, action: PayloadAction<number>) => {
|
||||
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;
|
||||
@@ -36,7 +36,7 @@ const LicensedConsoleLogo = ({
|
||||
} else if (plan === "ENTERPRISE") {
|
||||
licenseLogo = <ConsoleEnterprise />;
|
||||
} else {
|
||||
licenseLogo = <ConsoleAgpl />;
|
||||
licenseLogo = <ConsoleAgpl style={{ width: 170 }} />;
|
||||
}
|
||||
|
||||
return licenseLogo;
|
||||
|
||||
@@ -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}
|
||||
</div>
|
||||
<CopyToClipboard text={`${bucketName}/${splitPaths.join("/")}`}>
|
||||
<Button
|
||||
<RBIconButton
|
||||
id={"copy-path"}
|
||||
startIcon={<CopyIcon />}
|
||||
icon={<CopyIcon />}
|
||||
disableTouchRipple
|
||||
disableRipple
|
||||
focusRipple={false}
|
||||
@@ -206,17 +207,15 @@ const BrowserBreadcrumbs = ({
|
||||
padding: "0",
|
||||
color: "#969FA8",
|
||||
border: "#969FA8 1px solid",
|
||||
width: "20px",
|
||||
height: "20px",
|
||||
minHeight: "20px",
|
||||
minWidth: "28px",
|
||||
"&.MuiButton-root": {
|
||||
width: "28px",
|
||||
height: "28px",
|
||||
|
||||
"& .MuiButton-root": {
|
||||
height: "28px",
|
||||
},
|
||||
"& .min-icon": {
|
||||
width: "12px",
|
||||
height: "12px",
|
||||
marginLeft: "12px",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -20,7 +20,7 @@ import logReducer from "./screens/Console/Logs/logsSlice";
|
||||
import healthInfoReducer from "./screens/Console/HealthInfo/healthInfoSlice";
|
||||
import watchReducer from "./screens/Console/Watch/watchSlice";
|
||||
import consoleReducer from "./screens/Console/consoleSlice";
|
||||
import bucketsReducer from "./screens/Console/Buckets/bucketsSlice";
|
||||
import bucketsReducer from "./screens/Console/Buckets/ListBuckets/AddBucket/addBucketsSlice";
|
||||
import bucketDetailsReducer from "./screens/Console/Buckets/BucketDetails/bucketDetailsSlice";
|
||||
import objectBrowserReducer from "./screens/Console/ObjectBrowser/objectBrowserSlice";
|
||||
import tenantsReducer from "./screens/Console/Tenants/tenantsSlice";
|
||||
@@ -36,7 +36,7 @@ const rootReducer = combineReducers({
|
||||
logs: logReducer,
|
||||
watch: watchReducer,
|
||||
console: consoleReducer,
|
||||
buckets: bucketsReducer,
|
||||
addBucket: bucketsReducer,
|
||||
bucketDetails: bucketDetailsReducer,
|
||||
objectBrowser: objectBrowserReducer,
|
||||
healthInfo: healthInfoReducer,
|
||||
|
||||
Reference in New Issue
Block a user