github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/CreatePathModal.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, useState } from "react";
    18  import { useNavigate } from "react-router-dom";
    19  import { connect, useSelector } from "react-redux";
    20  import {
    21    Button,
    22    CreateNewPathIcon,
    23    InputBox,
    24    Grid,
    25    FormLayout,
    26    Box,
    27  } from "mds";
    28  import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
    29  import { modalStyleUtils } from "../../../../Common/FormComponents/common/styleLibrary";
    30  import { encodeURLString } from "../../../../../../common/utils";
    31  import { BucketObjectItem } from "./types";
    32  import { AppState, useAppDispatch } from "../../../../../../store";
    33  import { setModalErrorSnackMessage } from "../../../../../../systemSlice";
    34  
    35  interface ICreatePath {
    36    modalOpen: boolean;
    37    bucketName: string;
    38    folderName: string;
    39    onClose: () => any;
    40    simplePath: string | null;
    41    limitedSubPath?: boolean;
    42  }
    43  
    44  const CreatePathModal = ({
    45    modalOpen,
    46    folderName,
    47    bucketName,
    48    onClose,
    49    simplePath,
    50    limitedSubPath,
    51  }: ICreatePath) => {
    52    const dispatch = useAppDispatch();
    53    const navigate = useNavigate();
    54  
    55    const [pathUrl, setPathUrl] = useState("");
    56    const [isFormValid, setIsFormValid] = useState<boolean>(false);
    57    const [currentPath, setCurrentPath] = useState(bucketName);
    58  
    59    const records = useSelector((state: AppState) => state.objectBrowser.records);
    60  
    61    useEffect(() => {
    62      if (simplePath) {
    63        const newPath = `${bucketName}${
    64          !bucketName.endsWith("/") && !simplePath.startsWith("/") ? "/" : ""
    65        }${simplePath}`;
    66  
    67        setCurrentPath(newPath);
    68      }
    69    }, [simplePath, bucketName]);
    70  
    71    const resetForm = () => {
    72      setPathUrl("");
    73    };
    74  
    75    const createProcess = () => {
    76      let folderPath = "/";
    77  
    78      if (simplePath) {
    79        folderPath = simplePath.endsWith("/") ? simplePath : `${simplePath}/`;
    80      }
    81  
    82      const sharesName = (record: BucketObjectItem) =>
    83        record.name === folderPath + pathUrl;
    84  
    85      if (records.findIndex(sharesName) !== -1) {
    86        dispatch(
    87          setModalErrorSnackMessage({
    88            errorMessage: "Folder cannot have the same name as an existing file",
    89            detailedError: "",
    90          }),
    91        );
    92        return;
    93      }
    94  
    95      const cleanPathURL = pathUrl
    96        .split("/")
    97        .filter((splitItem) => splitItem.trim() !== "")
    98        .join("/");
    99  
   100      if (folderPath.slice(0, 1) === "/") {
   101        folderPath = folderPath.slice(1); //trim '/'
   102      }
   103  
   104      const newPath = `/browser/${bucketName}/${encodeURLString(
   105        `${folderPath}${cleanPathURL}/`,
   106      )}`;
   107  
   108      navigate(newPath);
   109      onClose();
   110    };
   111  
   112    useEffect(() => {
   113      let valid = true;
   114      if (pathUrl.trim().length === 0) {
   115        valid = false;
   116      }
   117      setIsFormValid(valid);
   118    }, [pathUrl]);
   119  
   120    const inputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
   121      setPathUrl(e.target.value);
   122    };
   123  
   124    const keyPressed = (e: any) => {
   125      if (e.code === "Enter" && pathUrl !== "") {
   126        createProcess();
   127      }
   128    };
   129  
   130    return (
   131      <React.Fragment>
   132        <ModalWrapper
   133          modalOpen={modalOpen}
   134          title="Choose or create a new path"
   135          onClose={onClose}
   136          titleIcon={<CreateNewPathIcon />}
   137        >
   138          <FormLayout withBorders={false} containerPadding={false}>
   139            <Box className={"inputItem"} sx={{ display: "flex", gap: 8 }}>
   140              <strong>Current Path:</strong> <br />
   141              <Box
   142                sx={{
   143                  textOverflow: "ellipsis",
   144                  whiteSpace: "nowrap",
   145                  overflow: "hidden",
   146                  fontSize: 14,
   147                  textAlign: "left",
   148                }}
   149                dir={"rtl"}
   150              >
   151                {currentPath}
   152              </Box>
   153            </Box>
   154            <InputBox
   155              value={pathUrl}
   156              label={"New Folder Path"}
   157              id={"folderPath"}
   158              name={"folderPath"}
   159              placeholder={"Enter the new Folder Path"}
   160              onChange={inputChange}
   161              onKeyPress={keyPressed}
   162              required
   163              tooltip={
   164                (limitedSubPath &&
   165                  "You may only have write access on a limited set of subpaths within this path. Please carefully review your User permissions to understand the paths to which you may write.") ||
   166                ""
   167              }
   168            />
   169            <Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
   170              <Button
   171                id={"clear"}
   172                type="button"
   173                color="primary"
   174                variant="regular"
   175                onClick={resetForm}
   176                label={"Clear"}
   177              />
   178              <Button
   179                id={"create"}
   180                type="submit"
   181                variant="callAction"
   182                disabled={!isFormValid}
   183                onClick={createProcess}
   184                label={"Create"}
   185              />
   186            </Grid>
   187          </FormLayout>
   188        </ModalWrapper>
   189      </React.Fragment>
   190    );
   191  };
   192  
   193  const mapStateToProps = ({ objectBrowser }: AppState) => ({
   194    simplePath: objectBrowser.simplePath,
   195  });
   196  
   197  const connector = connect(mapStateToProps);
   198  
   199  export default connector(CreatePathModal);