Skip to content

Commit 2caad99

Browse files
authored
Added legal hold modal (#436)
1 parent d573007 commit 2caad99

File tree

3 files changed

+189
-20
lines changed

3 files changed

+189
-20
lines changed

portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

1717
import React, { useState, useEffect } from "react";
18+
import { connect } from "react-redux";
1819
import get from "lodash/get";
1920
import * as reactMoment from "react-moment";
2021
import clsx from "clsx";
@@ -33,9 +34,11 @@ import {
3334
containerForHeader,
3435
searchField,
3536
} from "../../../../Common/FormComponents/common/styleLibrary";
36-
import history from "../../../../../../history";
37+
import { IFileInfo } from "./types";
38+
import { removeRouteLevel } from "../../../../ObjectBrowser/actions";
3739
import { Route } from "../../../../ObjectBrowser/reducers";
3840
import { download } from "../utils";
41+
import history from "../../../../../../history";
3942
import api from "../../../../../../common/api";
4043
import PageHeader from "../../../../Common/PageHeader/PageHeader";
4144
import ShareIcon from "../../../../../../icons/ShareIcon";
@@ -46,10 +49,9 @@ import PencilIcon from "../../../../Common/TableWrapper/TableActionIcons/PencilI
4649
import SetRetention from "./SetRetention";
4750
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
4851
import DeleteObject from "../ListObjects/DeleteObject";
49-
import { removeRouteLevel } from "../../../../ObjectBrowser/actions";
50-
import { connect } from "react-redux";
5152
import AddTagModal from "./AddTagModal";
5253
import DeleteTagModal from "./DeleteTagModal";
54+
import SetLegalHoldModal from "./SetLegalHoldModal";
5355

5456
const styles = (theme: Theme) =>
5557
createStyles({
@@ -136,18 +138,6 @@ interface IObjectDetailsProps {
136138
removeRouteLevel: (newRoute: string) => any;
137139
}
138140

139-
interface IFileInfo {
140-
is_latest?: boolean;
141-
last_modified: string;
142-
legal_hold_status?: string;
143-
name: string;
144-
retention_mode?: string;
145-
retention_until_date?: string;
146-
size?: string;
147-
tags?: object;
148-
version_id: string;
149-
}
150-
151141
const emptyFile: IFileInfo = {
152142
is_latest: true,
153143
last_modified: "",
@@ -171,6 +161,7 @@ const ObjectDetails = ({
171161
const [tagModalOpen, setTagModalOpen] = useState<boolean>(false);
172162
const [deleteTagModalOpen, setDeleteTagModalOpen] = useState<boolean>(false);
173163
const [selectedTag, setSelectedTag] = useState<[string, string]>(["", ""]);
164+
const [legalholdOpen, setLegalholdOpen] = useState<boolean>(false);
174165
const [actualInfo, setActualInfo] = useState<IFileInfo>(emptyFile);
175166
const [versions, setVersions] = useState<IFileInfo[]>([]);
176167
const [filterVersion, setFilterVersion] = useState<string>("");
@@ -213,22 +204,18 @@ const ObjectDetails = ({
213204

214205
const openRetentionModal = () => {
215206
setRetentionModalOpen(true);
216-
console.log("open retention modal");
217207
};
218208

219209
const closeRetentionModal = () => {
220210
setRetentionModalOpen(false);
221-
console.log("close retention modal");
222211
};
223212

224213
const shareObject = () => {
225214
setShareFileModalOpen(true);
226-
console.log("share object");
227215
};
228216

229217
const closeShareModal = () => {
230218
setShareFileModalOpen(false);
231-
console.log("close share modal");
232219
};
233220

234221
const deleteTag = (tagKey: string, tagLabel: string) => {
@@ -272,6 +259,14 @@ const ObjectDetails = ({
272259
}
273260
};
274261

262+
const closeLegalholdModal = (reload: boolean) => {
263+
setLegalholdOpen(false);
264+
265+
if (reload) {
266+
setLoadObjectData(true);
267+
}
268+
};
269+
275270
const closeDeleteTagModal = (reloadObjectData: boolean) => {
276271
setDeleteTagModalOpen(false);
277272

@@ -325,6 +320,15 @@ const ObjectDetails = ({
325320
selectedTag={selectedTag}
326321
/>
327322
)}
323+
{legalholdOpen && (
324+
<SetLegalHoldModal
325+
open={legalholdOpen}
326+
closeModalAndRefresh={closeLegalholdModal}
327+
objectName={pathInBucket}
328+
bucketName={bucketName}
329+
actualInfo={actualInfo}
330+
/>
331+
)}
328332
<Grid container>
329333
<Grid item xs={12} className={classes.container}>
330334
<Grid item xs={12} className={classes.obTitleSection}>
@@ -350,7 +354,7 @@ const ObjectDetails = ({
350354
size="small"
351355
className={classes.propertiesIcon}
352356
onClick={() => {
353-
console.log("open legal hold modal");
357+
setLegalholdOpen(true);
354358
}}
355359
>
356360
<PencilIcon active={true} />
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import React, { useState, useEffect } from "react";
2+
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
3+
import get from "lodash/get";
4+
import Grid from "@material-ui/core/Grid";
5+
import Button from "@material-ui/core/Button";
6+
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
7+
import { IFileInfo } from "./types";
8+
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
9+
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
10+
import api from "../../../../../../common/api";
11+
12+
const styles = (theme: Theme) =>
13+
createStyles({
14+
objectName: {
15+
fontSize: 18,
16+
fontWeight: 700,
17+
marginBottom: 40,
18+
},
19+
buttonContainer: {
20+
textAlign: "right",
21+
},
22+
errorBlock: {
23+
color: "red",
24+
},
25+
...modalBasic,
26+
});
27+
28+
interface ISetRetentionProps {
29+
classes: any;
30+
open: boolean;
31+
closeModalAndRefresh: (reload: boolean) => void;
32+
objectName: string;
33+
bucketName: string;
34+
actualInfo: IFileInfo;
35+
}
36+
37+
const SetLegalHoldModal = ({
38+
classes,
39+
open,
40+
closeModalAndRefresh,
41+
objectName,
42+
bucketName,
43+
actualInfo,
44+
}: ISetRetentionProps) => {
45+
const [legalHoldEnabled, setLegalHoldEnabled] = useState<boolean>(false);
46+
const [isSaving, setIsSaving] = useState<boolean>(false);
47+
const [error, setError] = useState<string>("");
48+
const versionId = actualInfo.version_id;
49+
50+
useEffect(() => {
51+
const status = get(actualInfo, "legal_hold_status", "OFF");
52+
setLegalHoldEnabled(status === "ON");
53+
}, []);
54+
55+
const onSubmit = (e: React.FormEvent) => {
56+
e.preventDefault();
57+
setIsSaving(true);
58+
59+
api
60+
.invoke(
61+
"PUT",
62+
`/api/v1/buckets/${bucketName}/objects/legalhold?prefix=${objectName}&version_id=${versionId}`,
63+
{ status: legalHoldEnabled ? "enabled" : "disabled" }
64+
)
65+
.then((res) => {
66+
setIsSaving(false);
67+
closeModalAndRefresh(true);
68+
})
69+
.catch((error) => {
70+
setError(error);
71+
setIsSaving(false);
72+
});
73+
};
74+
75+
const resetForm = () => {
76+
setLegalHoldEnabled(false);
77+
};
78+
79+
return (
80+
<ModalWrapper
81+
title="Set Legal Hold"
82+
modalOpen={open}
83+
onClose={() => {
84+
resetForm();
85+
closeModalAndRefresh(false);
86+
}}
87+
>
88+
{error !== "" && <span className={classes.errorBlock}>{error}</span>}
89+
<Grid item xs={12} className={classes.objectName}>
90+
{objectName}
91+
</Grid>
92+
<form
93+
noValidate
94+
autoComplete="off"
95+
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
96+
onSubmit(e);
97+
}}
98+
>
99+
<Grid item xs={12}>
100+
<FormSwitchWrapper
101+
value="legalhold"
102+
id="legalhold"
103+
name="legalhold"
104+
checked={legalHoldEnabled}
105+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
106+
setLegalHoldEnabled(!legalHoldEnabled);
107+
}}
108+
label={"Legal Hold Status"}
109+
indicatorLabels={["Enabled", "Disabled"]}
110+
tooltip={
111+
"To enable this feature you need to enable versioning on the bucket before creation"
112+
}
113+
/>
114+
</Grid>
115+
<Grid item xs={12} className={classes.buttonContainer}>
116+
<button
117+
type="button"
118+
color="primary"
119+
className={classes.clearButton}
120+
onClick={resetForm}
121+
>
122+
Reset
123+
</button>
124+
<Button
125+
type="submit"
126+
variant="contained"
127+
color="primary"
128+
disabled={isSaving}
129+
>
130+
Save
131+
</Button>
132+
</Grid>
133+
</form>
134+
</ModalWrapper>
135+
);
136+
};
137+
138+
export default withStyles(styles)(SetLegalHoldModal);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2020 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
export interface IFileInfo {
18+
is_latest?: boolean;
19+
last_modified: string;
20+
legal_hold_status?: string;
21+
name: string;
22+
retention_mode?: string;
23+
retention_until_date?: string;
24+
size?: string;
25+
tags?: object;
26+
version_id: string;
27+
}

0 commit comments

Comments
 (0)