github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/SetRetention.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 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 import React, { useEffect, useRef, useState } from "react"; 18 import { Box, Button, FormLayout, Grid, RadioGroup, Switch } from "mds"; 19 import { useSelector } from "react-redux"; 20 import { BucketObject, ObjectRetentionMode } from "api/consoleApi"; 21 import { api } from "api"; 22 import { errorToHandler } from "api/errors"; 23 import { encodeURLString } from "common/utils"; 24 import { modalStyleUtils } from "../../../../Common/FormComponents/common/styleLibrary"; 25 import { twoDigitDate } from "../../../../Common/FormComponents/DateSelector/utils"; 26 import { setModalErrorSnackMessage } from "../../../../../../systemSlice"; 27 import { AppState, useAppDispatch } from "../../../../../../store"; 28 import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; 29 import DateSelector from "../../../../Common/FormComponents/DateSelector/DateSelector"; 30 31 interface ISetRetentionProps { 32 open: boolean; 33 closeModalAndRefresh: (updateInfo: boolean) => void; 34 objectName: string; 35 bucketName: string; 36 objectInfo: BucketObject; 37 } 38 39 interface IRefObject { 40 resetDate: () => void; 41 } 42 43 const SetRetention = ({ 44 open, 45 closeModalAndRefresh, 46 objectName, 47 objectInfo, 48 bucketName, 49 }: ISetRetentionProps) => { 50 const dispatch = useAppDispatch(); 51 const retentionConfig = useSelector( 52 (state: AppState) => state.objectBrowser.retentionConfig, 53 ); 54 55 const [statusEnabled, setStatusEnabled] = useState<boolean>(true); 56 const [type, setType] = useState<ObjectRetentionMode | "">(""); 57 const [date, setDate] = useState<string>(""); 58 const [isDateValid, setIsDateValid] = useState<boolean>(false); 59 const [isSaving, setIsSaving] = useState<boolean>(false); 60 const [alreadyConfigured, setAlreadyConfigured] = useState<boolean>(false); 61 62 useEffect(() => { 63 if (objectInfo.retention_mode) { 64 setType(retentionConfig?.mode || ObjectRetentionMode.Governance); 65 setAlreadyConfigured(true); 66 } 67 // get retention_until_date if defined 68 if (objectInfo.retention_until_date) { 69 const valueDate = new Date(objectInfo.retention_until_date); 70 if (valueDate.toString() !== "Invalid Date") { 71 const year = valueDate.getFullYear(); 72 const month = twoDigitDate(valueDate.getMonth() + 1); 73 const day = valueDate.getDate(); 74 if (!isNaN(day) && month !== "NaN" && !isNaN(year)) { 75 setDate(`${year}-${month}-${day}`); 76 } 77 } 78 setAlreadyConfigured(true); 79 } 80 }, [objectInfo, retentionConfig?.mode]); 81 82 const dateElement = useRef<IRefObject>(null); 83 84 const dateFieldDisabled = () => { 85 return !(statusEnabled && (type === "governance" || type === "compliance")); 86 }; 87 88 const onSubmit = (e: React.FormEvent) => { 89 e.preventDefault(); 90 }; 91 92 const resetForm = () => { 93 setStatusEnabled(false); 94 setType(ObjectRetentionMode.Governance); 95 if (dateElement.current) { 96 dateElement.current.resetDate(); 97 } 98 }; 99 100 const addRetention = ( 101 selectedObject: string, 102 versionId: string | null, 103 expireDate: string, 104 ) => { 105 api.buckets 106 .putObjectRetention( 107 bucketName, 108 { 109 prefix: encodeURLString(selectedObject), 110 version_id: versionId || "", 111 }, 112 { 113 expires: expireDate, 114 mode: type as ObjectRetentionMode, 115 }, 116 ) 117 .then(() => { 118 setIsSaving(false); 119 closeModalAndRefresh(true); 120 }) 121 .catch((err) => { 122 dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); 123 setIsSaving(false); 124 }); 125 }; 126 127 const disableRetention = ( 128 selectedObject: string, 129 versionId: string | null, 130 ) => { 131 api.buckets 132 .deleteObjectRetention(bucketName, { 133 prefix: encodeURLString(selectedObject), 134 version_id: versionId || "", 135 }) 136 .then(() => { 137 setIsSaving(false); 138 closeModalAndRefresh(true); 139 }) 140 .catch((err) => { 141 dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); 142 setIsSaving(false); 143 }); 144 }; 145 146 const saveNewRetentionPolicy = () => { 147 setIsSaving(true); 148 const selectedObject = objectInfo.name || ""; 149 const versionId = objectInfo.version_id || null; 150 151 const expireDate = 152 !statusEnabled && type === "governance" ? "" : `${date}T23:59:59Z`; 153 154 if (!statusEnabled && type === "governance") { 155 disableRetention(selectedObject, versionId); 156 157 return; 158 } 159 160 addRetention(selectedObject, versionId, expireDate); 161 }; 162 163 const showSwitcher = 164 alreadyConfigured && (type === "governance" || type === ""); 165 166 return ( 167 <ModalWrapper 168 title="Set Retention Policy" 169 modalOpen={open} 170 onClose={() => { 171 resetForm(); 172 closeModalAndRefresh(false); 173 }} 174 > 175 <form 176 noValidate 177 autoComplete="off" 178 onSubmit={(e: React.FormEvent<HTMLFormElement>) => { 179 onSubmit(e); 180 }} 181 > 182 <FormLayout withBorders={false} containerPadding={false}> 183 <Box className={"inputItem"}> 184 <strong>Selected Object</strong>: {objectName} 185 </Box> 186 {showSwitcher && ( 187 <Switch 188 value="status" 189 id="status" 190 name="status" 191 checked={statusEnabled} 192 onChange={(e: React.ChangeEvent<HTMLInputElement>) => { 193 setStatusEnabled(!statusEnabled); 194 }} 195 label={"Status"} 196 indicatorLabels={["Enabled", "Disabled"]} 197 /> 198 )} 199 <RadioGroup 200 currentValue={type} 201 id="type" 202 name="type" 203 label="Type" 204 disableOptions={ 205 !statusEnabled || (alreadyConfigured && type !== "") 206 } 207 onChange={(e) => { 208 setType(e.target.value as ObjectRetentionMode); 209 }} 210 selectorOptions={[ 211 { label: "Governance", value: ObjectRetentionMode.Governance }, 212 { label: "Compliance", value: ObjectRetentionMode.Compliance }, 213 ]} 214 /> 215 <DateSelector 216 id="date" 217 label="Date" 218 disableOptions={dateFieldDisabled()} 219 ref={dateElement} 220 value={date} 221 borderBottom={true} 222 onDateChange={(date: string, isValid: boolean) => { 223 setIsDateValid(isValid); 224 if (isValid) { 225 setDate(date); 226 } 227 }} 228 /> 229 <Grid item xs={12} sx={modalStyleUtils.modalButtonBar}> 230 <Button 231 id={"reset"} 232 type="button" 233 variant="regular" 234 onClick={resetForm} 235 label={"Reset"} 236 /> 237 <Button 238 id={"save"} 239 type="submit" 240 variant="callAction" 241 disabled={ 242 (statusEnabled && type === "") || 243 (statusEnabled && !isDateValid) || 244 isSaving 245 } 246 onClick={saveNewRetentionPolicy} 247 label={"Save"} 248 /> 249 </Grid> 250 </FormLayout> 251 </form> 252 </ModalWrapper> 253 ); 254 }; 255 256 export default SetRetention;