Skip to content

Commit a42f1ff

Browse files
bexsoftBenjamin Perez
andauthored
Added buckets-object browser view (#307)
Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
1 parent 98f897e commit a42f1ff

File tree

3 files changed

+334
-7
lines changed

3 files changed

+334
-7
lines changed

portal-ui/src/screens/Console/Common/TableWrapper/TableWrapper.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ interface IColumns {
4949
elementKey: string;
5050
sortable?: boolean;
5151
renderFunction?: (input: any) => any;
52+
globalClass?: any;
5253
}
5354

5455
interface IPaginatorConfig {
@@ -161,7 +162,10 @@ const styles = (theme: Theme) =>
161162
const titleColumnsMap = (columns: IColumns[]) => {
162163
return columns.map((column: IColumns, index: number) => {
163164
return (
164-
<TableCell key={`tbCT-${column.elementKey}-${index}`}>
165+
<TableCell
166+
key={`tbCT-${column.elementKey}-${index}`}
167+
className={column.globalClass}
168+
>
165169
{column.label}
166170
</TableCell>
167171
);
@@ -279,7 +283,10 @@ const TableWrapper = ({
279283
</TableCell>
280284
)}
281285
{titleColumnsMap(columns)}
282-
{itemActions && itemActions.length > 0 && (
286+
{((itemActions && itemActions.length > 1) ||
287+
(itemActions &&
288+
itemActions.length === 1 &&
289+
itemActions[0].type !== "view")) && (
283290
<TableCell
284291
align="center"
285292
className={classes.actionsContainer}
@@ -329,7 +336,10 @@ const TableWrapper = ({
329336
</TableCell>
330337
)}
331338
{rowColumnsMap(columns, record, classes, isSelected)}
332-
{itemActions && itemActions.length > 0 && (
339+
{((itemActions && itemActions.length > 1) ||
340+
(itemActions &&
341+
itemActions.length === 1 &&
342+
itemActions[0].type !== "view")) && (
333343
<TableCell
334344
align="center"
335345
className={classes.actionsContainer}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
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+
import React, { useState, useEffect } from "react";
18+
import get from "lodash/get";
19+
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
20+
import AddBucket from "../Buckets/ListBuckets/AddBucket";
21+
import Grid from "@material-ui/core/Grid";
22+
import Typography from "@material-ui/core/Typography";
23+
import TextField from "@material-ui/core/TextField";
24+
import InputAdornment from "@material-ui/core/InputAdornment";
25+
import SearchIcon from "@material-ui/icons/Search";
26+
import { Button } from "@material-ui/core";
27+
import { CreateIcon } from "../../../icons";
28+
import { niceBytes } from "../../../common/utils";
29+
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
30+
import { Bucket, BucketList } from "../Buckets/types";
31+
import TableWrapper from "../Common/TableWrapper/TableWrapper";
32+
import api from "../../../common/api";
33+
34+
const styles = (theme: Theme) =>
35+
createStyles({
36+
seeMore: {
37+
marginTop: theme.spacing(3),
38+
},
39+
paper: {
40+
display: "flex",
41+
overflow: "auto",
42+
flexDirection: "column",
43+
},
44+
45+
addSideBar: {
46+
width: "320px",
47+
padding: "20px",
48+
},
49+
errorBlock: {
50+
color: "red",
51+
},
52+
tableToolbar: {
53+
paddingLeft: theme.spacing(2),
54+
paddingRight: theme.spacing(0),
55+
},
56+
minTableHeader: {
57+
color: "#393939",
58+
"& tr": {
59+
"& th": {
60+
fontWeight: "bold",
61+
},
62+
},
63+
},
64+
actionsTray: {
65+
textAlign: "right",
66+
"& button": {
67+
marginLeft: 10,
68+
},
69+
},
70+
searchField: {
71+
background: "#FFFFFF",
72+
padding: 12,
73+
borderRadius: 5,
74+
boxShadow: "0px 3px 6px #00000012",
75+
},
76+
usedSpaceCol: {
77+
width: 150,
78+
},
79+
subTitleLabel: {
80+
alignItems: "center",
81+
display: "flex",
82+
},
83+
});
84+
85+
interface IBrowseBucketsProps {
86+
classes: any;
87+
}
88+
89+
const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
90+
const [loading, setLoading] = useState<boolean>(true);
91+
const [page, setPage] = useState<number>(0);
92+
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
93+
const [error, setError] = useState<string>("");
94+
const [records, setRecords] = useState<Bucket[]>([]);
95+
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
96+
const [filterBuckets, setFilterBuckets] = useState<string>("");
97+
98+
const offset = page * rowsPerPage;
99+
100+
useEffect(() => {
101+
if (loading) {
102+
api
103+
.invoke("GET", `/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`)
104+
.then((res: BucketList) => {
105+
const buckets = get(res, "buckets", []);
106+
107+
setLoading(false);
108+
setRecords(buckets);
109+
setError("");
110+
// if we get 0 results, and page > 0 , go down 1 page
111+
if (
112+
(res.buckets === undefined ||
113+
res.buckets == null ||
114+
res.buckets.length === 0) &&
115+
page > 0
116+
) {
117+
const newPage = page - 1;
118+
setPage(newPage);
119+
setLoading(true);
120+
}
121+
})
122+
.catch((err: any) => {
123+
setLoading(false);
124+
setError(err);
125+
});
126+
}
127+
}, [loading]);
128+
129+
const closeAddModalAndRefresh = () => {
130+
setAddScreenOpen(false);
131+
setLoading(false);
132+
};
133+
134+
const filteredRecords = records
135+
.filter((b: Bucket) => {
136+
if (filterBuckets === "") {
137+
return true;
138+
}
139+
return b.name.indexOf(filterBuckets) >= 0;
140+
})
141+
.slice(offset, offset + rowsPerPage);
142+
143+
const handleChangePage = (event: unknown, newPage: number) => {
144+
setPage(newPage);
145+
};
146+
147+
const handleChangeRowsPerPage = (
148+
event: React.ChangeEvent<HTMLInputElement>
149+
) => {
150+
const rPP = parseInt(event.target.value, 10);
151+
setPage(0);
152+
setRowsPerPage(rPP);
153+
};
154+
155+
return (
156+
<React.Fragment>
157+
{addScreenOpen && (
158+
<AddBucket
159+
open={addScreenOpen}
160+
closeModalAndRefresh={closeAddModalAndRefresh}
161+
/>
162+
)}
163+
<Grid container>
164+
<Grid item xs={6} className={classes.subTitleLabel}>
165+
<Typography variant="h6">Buckets</Typography>
166+
</Grid>
167+
<Grid item xs={6} className={classes.actionsTray}>
168+
<TextField
169+
placeholder="Search Buckets"
170+
className={classes.searchField}
171+
id="search-resource"
172+
label=""
173+
onChange={(val) => {
174+
setFilterBuckets(val.target.value);
175+
}}
176+
InputProps={{
177+
disableUnderline: true,
178+
startAdornment: (
179+
<InputAdornment position="start">
180+
<SearchIcon />
181+
</InputAdornment>
182+
),
183+
}}
184+
/>
185+
<Button
186+
variant="contained"
187+
color="primary"
188+
startIcon={<CreateIcon />}
189+
onClick={() => {
190+
setAddScreenOpen(true);
191+
}}
192+
>
193+
Add Bucket
194+
</Button>
195+
</Grid>
196+
<Grid item xs={12}>
197+
<br />
198+
</Grid>
199+
<Grid item xs={12}>
200+
{error !== "" && <span className={classes.errorBlock}>{error}</span>}
201+
<TableWrapper
202+
itemActions={[
203+
{ type: "view", to: `/object-browser`, sendOnlyId: true },
204+
]}
205+
columns={[
206+
{ label: "Name", elementKey: "name" },
207+
{
208+
label: "Used Space",
209+
elementKey: "size",
210+
renderFunction: niceBytes,
211+
globalClass: classes.usedSpaceCol,
212+
},
213+
]}
214+
isLoading={loading}
215+
records={filteredRecords}
216+
entityName="Buckets"
217+
idField="name"
218+
paginatorConfig={{
219+
rowsPerPageOptions: [5, 10, 25],
220+
colSpan: 3,
221+
count: filteredRecords.length,
222+
rowsPerPage: rowsPerPage,
223+
page: page,
224+
SelectProps: {
225+
inputProps: { "aria-label": "rows per page" },
226+
native: true,
227+
},
228+
onChangePage: handleChangePage,
229+
onChangeRowsPerPage: handleChangeRowsPerPage,
230+
ActionsComponent: MinTablePaginationActions,
231+
}}
232+
/>
233+
</Grid>
234+
</Grid>
235+
</React.Fragment>
236+
);
237+
};
238+
239+
export default withStyles(styles)(BrowseBuckets);
Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,87 @@
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+
117
import React from "react";
18+
import get from "lodash/get";
19+
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
20+
import { Grid, Typography } from "@material-ui/core";
21+
import BrowseBuckets from "./BrowseBuckets";
22+
23+
interface IObjectBrowserProps {
24+
match: any;
25+
classes: any;
26+
}
27+
28+
const styles = (theme: Theme) =>
29+
createStyles({
30+
watchList: {
31+
background: "white",
32+
maxHeight: "400",
33+
overflow: "auto",
34+
"& ul": {
35+
margin: "4",
36+
padding: "0",
37+
},
38+
"& ul li": {
39+
listStyle: "none",
40+
margin: "0",
41+
padding: "0",
42+
borderBottom: "1px solid #dedede",
43+
},
44+
},
45+
actionsTray: {
46+
textAlign: "right",
47+
"& button": {
48+
marginLeft: 10,
49+
},
50+
},
51+
inputField: {
52+
background: "#FFFFFF",
53+
padding: 12,
54+
borderRadius: 5,
55+
marginLeft: 10,
56+
boxShadow: "0px 3px 6px #00000012",
57+
},
58+
fieldContainer: {
59+
background: "#FFFFFF",
60+
padding: 0,
61+
borderRadius: 5,
62+
marginLeft: 10,
63+
textAlign: "left",
64+
minWidth: "206",
65+
boxShadow: "0px 3px 6px #00000012",
66+
},
67+
lastElementWPadding: {
68+
paddingRight: "78",
69+
},
70+
});
271

3-
const ObjectBrowser = (props: any) => {
4-
console.log(props);
72+
const ObjectBrowser = ({ match, classes }: IObjectBrowserProps) => {
73+
const pathIn = get(match, "path", "");
574

6-
return <React.Fragment>Object Browser</React.Fragment>;
75+
return (
76+
<React.Fragment>
77+
<Grid container>
78+
<Grid item xs={12}>
79+
<Typography variant="h6">Object Browser</Typography>
80+
</Grid>
81+
{pathIn === "/object-browser" && <BrowseBuckets />}
82+
</Grid>
83+
</React.Fragment>
84+
);
785
};
886

9-
export default ObjectBrowser;
87+
export default withStyles(styles)(ObjectBrowser);

0 commit comments

Comments
 (0)