Added functionality for create folder & replaced icons (#368)

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2020-11-02 23:45:47 -06:00
committed by GitHub
parent b9f2a39d50
commit 5000aafba6
7 changed files with 274 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
// This file is part of MinIO Console Server
// Copyright (c) 2019 MinIO, Inc.
// Copyright (c) 2020 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
@@ -15,21 +15,36 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import {SvgIcon} from "@material-ui/core";
import { SvgIcon } from "@material-ui/core";
class CreateIcon extends React.Component {
render() {
return (<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<title>ic_h_create-new_sl</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path className="cls-1"
d="M0,0V16H16V0ZM11.886,9.048H9.048v2.838h-2.1V9.048H4.114v-2.1H6.952V4.114h2.1V6.952h2.838Z"/>
</g>
</g>
</svg>
</SvgIcon>)
}
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
<g
id="Group_55"
data-name="Group 55"
transform="translate(1002 -2555)"
>
<rect
id="Rectangle_29"
width="2"
height="12"
transform="translate(-997 2555)"
fill="#fff"
/>
<rect
id="Rectangle_30"
width="2"
height="12"
transform="translate(-990 2560) rotate(90)"
fill="#fff"
/>
</g>
</svg>
</SvgIcon>
);
}
}
export default CreateIcon;

View File

@@ -0,0 +1,41 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 { SvgIcon } from "@material-ui/core";
class UploadFile extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
<g transform="translate(-63.686 -70.783)">
<path
className="a"
d="M74.736,79.879v1.95h-9.1v-1.95h-1.95v3.9h13v-3.9Z"
/>
<path
className="a"
d="M69.211,80.533h1.95V73.861h1.525l-2.5-3.078-2.5,3.078h1.525Z"
/>
</g>
</svg>
</SvgIcon>
);
}
}
export default UploadFile;

View File

@@ -0,0 +1,116 @@
// This file is part of MinIO Console Server
// Copyright (c) 2020 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, { useState } from "react";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
import { Button, Grid, LinearProgress } from "@material-ui/core";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
import { connect } from "react-redux";
import { createFolder } from "../../../../ObjectBrowser/actions";
interface ICreateFolder {
classes: any;
modalOpen: boolean;
folderName: string;
createFolder: (newFolder: string) => any;
onClose: () => any;
}
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
pathLabel: {
marginTop: 0,
marginBottom: 32,
},
...modalBasic,
});
const CreateFolderModal = ({
modalOpen,
folderName,
onClose,
createFolder,
classes,
}: ICreateFolder) => {
const [pathUrl, setPathUrl] = useState("");
const resetForm = () => {
setPathUrl("");
};
const createProcess = () => {
createFolder(pathUrl);
onClose();
};
const folderTruncated = folderName.split("/").slice(2).join("/");
return (
<React.Fragment>
<ModalWrapper modalOpen={modalOpen} title="Add Folder" onClose={onClose}>
<Grid container>
<h3 className={classes.pathLabel}>
Current Path: {folderTruncated}/
</h3>
<Grid item xs={12}>
<InputBoxWrapper
value={pathUrl}
label={"Folder Path"}
id={"folderPath"}
name={"folderPath"}
placeholder={"Enter Folder Path"}
onChange={(e) => {
setPathUrl(e.target.value);
}}
/>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={resetForm}
>
Clear
</button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={pathUrl.trim() === ""}
onClick={createProcess}
>
Save
</Button>
</Grid>
</Grid>
</ModalWrapper>
</React.Fragment>
);
};
const mapDispatchToProps = {
createFolder,
};
const connector = connect(null, mapDispatchToProps);
export default connector(withStyles(styles)(CreateFolderModal));

View File

@@ -45,6 +45,9 @@ import { withRouter } from "react-router-dom";
import { addRoute, setAllRoutes } from "../../../../ObjectBrowser/actions";
import { connect } from "react-redux";
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
import CreateFolderModal from "./CreateFolderModal";
import { create } from "domain";
import UploadFile from "../../../../../../icons/UploadFile";
const commonIcon = {
backgroundRepeat: "no-repeat",
@@ -96,6 +99,11 @@ const styles = (theme: Theme) =>
backgroundImage: "url(/images/ob_file_clear.svg)",
...commonIcon,
},
buttonsContainer: {
"& .MuiButtonBase-root": {
marginLeft: 10,
},
},
"@global": {
".rowElementRaw:hover .iconFileElm": {
backgroundImage: "url(/images/ob_file_filled.svg)",
@@ -134,6 +142,7 @@ const ListObjects = ({
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string>("");
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [createFolderOpen, setCreateFolderOpen] = useState<boolean>(false);
const [deleteError, setDeleteError] = useState<string>("");
const [selectedObject, setSelectedObject] = useState<string>("");
const [selectedBucket, setSelectedBucket] = useState<string>("");
@@ -182,6 +191,10 @@ const ListObjects = ({
}
};
const closeAddFolderModal = () => {
setCreateFolderOpen(false);
};
const showSnackBarMessage = (text: string) => {
setSnackbarMessage(text);
setOpenSnackbar(true);
@@ -310,10 +323,21 @@ const ListObjects = ({
};
const uploadObject = (e: any): void => {
// TODO: handle deeper paths/folders
// Handle of deeper routes.
const currentPath = routesList[routesList.length - 1].route;
const splitPaths = currentPath
.split("/")
.filter((item) => item.trim() !== "");
let path = "";
if (splitPaths.length > 2) {
path = `${splitPaths.slice(2).join("/")}/`;
}
let file = e.target.files[0];
showSnackBarMessage(`Uploading: ${file.name}`);
upload(e, selectedBucket, "");
upload(e, selectedBucket, path);
};
const snackBarAction = (
@@ -375,6 +399,13 @@ const ListObjects = ({
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
/>
)}
{createFolderOpen && (
<CreateFolderModal
modalOpen={createFolderOpen}
folderName={routesList[routesList.length - 1].route}
onClose={closeAddFolderModal}
/>
)}
<Snackbar
open={openSnackbar}
message={snackBarMessage}
@@ -387,14 +418,25 @@ const ListObjects = ({
<div>
<BrowserBreadcrumbs />
</div>
<div>
<div className={classes.buttonsContainer}>
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
component="label"
onClick={() => {
setCreateFolderOpen(true);
}}
>
Upload Object
Create Folder
</Button>
<Button
variant="contained"
color="primary"
startIcon={<UploadFile />}
component="label"
>
File
<Input
type="file"
onChange={(e) => uploadObject(e)}

View File

@@ -20,6 +20,7 @@ export const OBJECT_BROWSER_RESET_ROUTES_LIST =
export const OBJECT_BROWSER_REMOVE_ROUTE_LEVEL =
"OBJECT_BROWSER/REMOVE_ROUTE_LEVEL";
export const OBJECT_BROWSER_SET_ALL_ROUTES = "OBJECT_BROWSER/SET_ALL_ROUTES";
export const OBJECT_BROWSER_CREATE_FOLDER = "OBJECT_BROWSER/CREATE_FOLDER";
interface AddRouteAction {
type: typeof OBJECT_BROWSER_ADD_ROUTE;
@@ -42,11 +43,17 @@ interface SetAllRoutes {
currentRoute: string;
}
interface CreateFolder {
type: typeof OBJECT_BROWSER_CREATE_FOLDER;
newRoute: string;
}
export type ObjectBrowserActionTypes =
| AddRouteAction
| ResetRoutesList
| RemoveRouteLevel
| SetAllRoutes;
| SetAllRoutes
| CreateFolder;
export const addRoute = (route: string, label: string) => {
return {
@@ -77,3 +84,10 @@ export const setAllRoutes = (currentRoute: string) => {
currentRoute,
};
};
export const createFolder = (newRoute: string) => {
return {
type: OBJECT_BROWSER_CREATE_FOLDER,
newRoute,
};
};

View File

@@ -17,6 +17,7 @@ import history from "../../../history";
import {
OBJECT_BROWSER_ADD_ROUTE,
OBJECT_BROWSER_CREATE_FOLDER,
OBJECT_BROWSER_REMOVE_ROUTE_LEVEL,
OBJECT_BROWSER_RESET_ROUTES_LIST,
OBJECT_BROWSER_SET_ALL_ROUTES,
@@ -85,6 +86,28 @@ export function objectBrowserReducer(
...state,
routesList: newSetOfRoutes,
};
case OBJECT_BROWSER_CREATE_FOLDER:
const newFoldersRoutes = [...state.routesList];
let lastRoute = state.routesList[state.routesList.length - 1].route;
const splitElements = action.newRoute.split("/");
splitElements.forEach((element) => {
const folderTrim = element.trim();
if (folderTrim !== "") {
lastRoute = `${lastRoute}/${folderTrim}`;
const newItem = { route: lastRoute, label: folderTrim };
newFoldersRoutes.push(newItem);
}
});
history.push(lastRoute);
return {
...state,
routesList: newFoldersRoutes,
};
default:
return state;
}

View File

@@ -79,6 +79,9 @@ const theme = createMuiTheme({
fontWeight: 600,
color: "#767676",
},
"& .MuiButton-iconSizeMedium > *:first-child": {
fontSize: 12,
},
},
},
},