Added functionality for create folder & replaced icons (#368)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// This file is part of MinIO Console Server
|
// 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
|
// 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
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {SvgIcon} from "@material-ui/core";
|
import { SvgIcon } from "@material-ui/core";
|
||||||
class CreateIcon extends React.Component {
|
class CreateIcon extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (<SvgIcon>
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
<SvgIcon>
|
||||||
<title>ic_h_create-new_sl</title>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||||
<g id="Layer_2" data-name="Layer 2">
|
<g
|
||||||
<g id="Layer_1-2" data-name="Layer 1">
|
id="Group_55"
|
||||||
<path className="cls-1"
|
data-name="Group 55"
|
||||||
d="M0,0V16H16V0ZM11.886,9.048H9.048v2.838h-2.1V9.048H4.114v-2.1H6.952V4.114h2.1V6.952h2.838Z"/>
|
transform="translate(1002 -2555)"
|
||||||
</g>
|
>
|
||||||
</g>
|
<rect
|
||||||
</svg>
|
id="Rectangle_29"
|
||||||
</SvgIcon>)
|
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;
|
export default CreateIcon;
|
||||||
|
|||||||
41
portal-ui/src/icons/UploadFile.tsx
Normal file
41
portal-ui/src/icons/UploadFile.tsx
Normal 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;
|
||||||
@@ -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));
|
||||||
@@ -45,6 +45,9 @@ import { withRouter } from "react-router-dom";
|
|||||||
import { addRoute, setAllRoutes } from "../../../../ObjectBrowser/actions";
|
import { addRoute, setAllRoutes } from "../../../../ObjectBrowser/actions";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
|
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
|
||||||
|
import CreateFolderModal from "./CreateFolderModal";
|
||||||
|
import { create } from "domain";
|
||||||
|
import UploadFile from "../../../../../../icons/UploadFile";
|
||||||
|
|
||||||
const commonIcon = {
|
const commonIcon = {
|
||||||
backgroundRepeat: "no-repeat",
|
backgroundRepeat: "no-repeat",
|
||||||
@@ -96,6 +99,11 @@ const styles = (theme: Theme) =>
|
|||||||
backgroundImage: "url(/images/ob_file_clear.svg)",
|
backgroundImage: "url(/images/ob_file_clear.svg)",
|
||||||
...commonIcon,
|
...commonIcon,
|
||||||
},
|
},
|
||||||
|
buttonsContainer: {
|
||||||
|
"& .MuiButtonBase-root": {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
"@global": {
|
"@global": {
|
||||||
".rowElementRaw:hover .iconFileElm": {
|
".rowElementRaw:hover .iconFileElm": {
|
||||||
backgroundImage: "url(/images/ob_file_filled.svg)",
|
backgroundImage: "url(/images/ob_file_filled.svg)",
|
||||||
@@ -134,6 +142,7 @@ const ListObjects = ({
|
|||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
|
const [createFolderOpen, setCreateFolderOpen] = useState<boolean>(false);
|
||||||
const [deleteError, setDeleteError] = useState<string>("");
|
const [deleteError, setDeleteError] = useState<string>("");
|
||||||
const [selectedObject, setSelectedObject] = useState<string>("");
|
const [selectedObject, setSelectedObject] = useState<string>("");
|
||||||
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
||||||
@@ -182,6 +191,10 @@ const ListObjects = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeAddFolderModal = () => {
|
||||||
|
setCreateFolderOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
const showSnackBarMessage = (text: string) => {
|
const showSnackBarMessage = (text: string) => {
|
||||||
setSnackbarMessage(text);
|
setSnackbarMessage(text);
|
||||||
setOpenSnackbar(true);
|
setOpenSnackbar(true);
|
||||||
@@ -310,10 +323,21 @@ const ListObjects = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const uploadObject = (e: any): void => {
|
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];
|
let file = e.target.files[0];
|
||||||
showSnackBarMessage(`Uploading: ${file.name}`);
|
showSnackBarMessage(`Uploading: ${file.name}`);
|
||||||
upload(e, selectedBucket, "");
|
upload(e, selectedBucket, path);
|
||||||
};
|
};
|
||||||
|
|
||||||
const snackBarAction = (
|
const snackBarAction = (
|
||||||
@@ -375,6 +399,13 @@ const ListObjects = ({
|
|||||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{createFolderOpen && (
|
||||||
|
<CreateFolderModal
|
||||||
|
modalOpen={createFolderOpen}
|
||||||
|
folderName={routesList[routesList.length - 1].route}
|
||||||
|
onClose={closeAddFolderModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Snackbar
|
<Snackbar
|
||||||
open={openSnackbar}
|
open={openSnackbar}
|
||||||
message={snackBarMessage}
|
message={snackBarMessage}
|
||||||
@@ -387,14 +418,25 @@ const ListObjects = ({
|
|||||||
<div>
|
<div>
|
||||||
<BrowserBreadcrumbs />
|
<BrowserBreadcrumbs />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={classes.buttonsContainer}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
startIcon={<CreateIcon />}
|
startIcon={<CreateIcon />}
|
||||||
component="label"
|
component="label"
|
||||||
|
onClick={() => {
|
||||||
|
setCreateFolderOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Upload Object
|
Create Folder
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<UploadFile />}
|
||||||
|
component="label"
|
||||||
|
>
|
||||||
|
File
|
||||||
<Input
|
<Input
|
||||||
type="file"
|
type="file"
|
||||||
onChange={(e) => uploadObject(e)}
|
onChange={(e) => uploadObject(e)}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const OBJECT_BROWSER_RESET_ROUTES_LIST =
|
|||||||
export const OBJECT_BROWSER_REMOVE_ROUTE_LEVEL =
|
export const OBJECT_BROWSER_REMOVE_ROUTE_LEVEL =
|
||||||
"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_SET_ALL_ROUTES = "OBJECT_BROWSER/SET_ALL_ROUTES";
|
||||||
|
export const OBJECT_BROWSER_CREATE_FOLDER = "OBJECT_BROWSER/CREATE_FOLDER";
|
||||||
|
|
||||||
interface AddRouteAction {
|
interface AddRouteAction {
|
||||||
type: typeof OBJECT_BROWSER_ADD_ROUTE;
|
type: typeof OBJECT_BROWSER_ADD_ROUTE;
|
||||||
@@ -42,11 +43,17 @@ interface SetAllRoutes {
|
|||||||
currentRoute: string;
|
currentRoute: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CreateFolder {
|
||||||
|
type: typeof OBJECT_BROWSER_CREATE_FOLDER;
|
||||||
|
newRoute: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type ObjectBrowserActionTypes =
|
export type ObjectBrowserActionTypes =
|
||||||
| AddRouteAction
|
| AddRouteAction
|
||||||
| ResetRoutesList
|
| ResetRoutesList
|
||||||
| RemoveRouteLevel
|
| RemoveRouteLevel
|
||||||
| SetAllRoutes;
|
| SetAllRoutes
|
||||||
|
| CreateFolder;
|
||||||
|
|
||||||
export const addRoute = (route: string, label: string) => {
|
export const addRoute = (route: string, label: string) => {
|
||||||
return {
|
return {
|
||||||
@@ -77,3 +84,10 @@ export const setAllRoutes = (currentRoute: string) => {
|
|||||||
currentRoute,
|
currentRoute,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createFolder = (newRoute: string) => {
|
||||||
|
return {
|
||||||
|
type: OBJECT_BROWSER_CREATE_FOLDER,
|
||||||
|
newRoute,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import history from "../../../history";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
OBJECT_BROWSER_ADD_ROUTE,
|
OBJECT_BROWSER_ADD_ROUTE,
|
||||||
|
OBJECT_BROWSER_CREATE_FOLDER,
|
||||||
OBJECT_BROWSER_REMOVE_ROUTE_LEVEL,
|
OBJECT_BROWSER_REMOVE_ROUTE_LEVEL,
|
||||||
OBJECT_BROWSER_RESET_ROUTES_LIST,
|
OBJECT_BROWSER_RESET_ROUTES_LIST,
|
||||||
OBJECT_BROWSER_SET_ALL_ROUTES,
|
OBJECT_BROWSER_SET_ALL_ROUTES,
|
||||||
@@ -85,6 +86,28 @@ export function objectBrowserReducer(
|
|||||||
...state,
|
...state,
|
||||||
routesList: newSetOfRoutes,
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ const theme = createMuiTheme({
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
color: "#767676",
|
color: "#767676",
|
||||||
},
|
},
|
||||||
|
"& .MuiButton-iconSizeMedium > *:first-child": {
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user