Add UI pagination, update Redux and API service lib

This commit is contained in:
Nathan Barber
2021-05-05 18:41:48 -04:00
parent 5e2ca7bcff
commit 0439a0d274
14 changed files with 334 additions and 149 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { compose, withProps } from "recompose";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
@@ -7,18 +7,22 @@ import { jhapiRequest } from "../../util/jhapiUtil";
const AddUser = (props) => {
var [users, setUsers] = useState([]),
[admin, setAdmin] = useState(false);
[admin, setAdmin] = useState(false),
limit = useSelector((state) => state.limit);
var dispatch = useDispatch();
var dispatchUserData = (data) => {
var dispatchPageChange = (data, page) => {
dispatch({
type: "USER_DATA",
value: data,
type: "USER_PAGE",
value: {
data: data,
page: page,
},
});
};
var { addUsers, failRegexEvent, refreshUserData, history } = props;
var { addUsers, failRegexEvent, updateUsers, history } = props;
return (
<>
@@ -78,12 +82,12 @@ const AddUser = (props) => {
}
addUsers(filtered_users, admin)
.then(
refreshUserData()
.then((data) => dispatchUserData(data))
.then(() =>
updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/"))
.catch((err) => console.log(err))
)
.then(() => history.push("/"))
.catch((err) => console.log(err));
}}
>
@@ -101,7 +105,7 @@ const AddUser = (props) => {
AddUser.propTypes = {
addUsers: PropTypes.func,
failRegexEvent: PropTypes.func,
refreshUserData: PropTypes.func,
updateUsers: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,
}),

View File

@@ -1,23 +1,25 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { compose, withProps } from "recompose";
import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import { jhapiRequest } from "../../util/jhapiUtil";
const CreateGroup = (props) => {
var [groupName, setGroupName] = useState("");
var [groupName, setGroupName] = useState(""),
limit = useSelector((state) => state.limit);
var dispatch = useDispatch();
var dispatchGroupsData = (data) => {
var dispatchPageUpdate = (data, page) => {
dispatch({
type: "GROUPS_DATA",
value: data,
value: {
data: data,
page: page,
},
});
};
var { createGroup, refreshGroupsData, history } = props;
var { createGroup, updateGroups, history } = props;
return (
<>
@@ -53,11 +55,11 @@ const CreateGroup = (props) => {
onClick={() => {
createGroup(groupName)
.then(
refreshGroupsData()
.then((data) => dispatchGroupsData(data))
updateGroups(0, limit)
.then((data) => dispatchPageUpdate(data, 0))
.then(history.push("/groups"))
.catch((err) => console.log(err))
)
.then(history.push("/groups"))
.catch((err) => console.log(err));
}}
>
@@ -74,7 +76,7 @@ const CreateGroup = (props) => {
CreateGroup.propTypes = {
createGroup: PropTypes.func,
refreshGroupsData: PropTypes.func,
updateGroups: PropTypes.func,
failRegexEvent: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,

View File

@@ -1,15 +1,20 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
const EditUser = (props) => {
var limit = useSelector((state) => state.limit);
var dispatch = useDispatch();
var dispatchUserData = (data) => {
var dispatchPageChange = (data, page) => {
dispatch({
type: "USER_DATA",
value: data,
type: "USER_PAGE",
value: {
data: data,
page: page,
},
});
};
@@ -18,7 +23,7 @@ const EditUser = (props) => {
deleteUser,
failRegexEvent,
noChangeEvent,
refreshUserData,
updateUsers,
history,
} = props;
@@ -70,9 +75,9 @@ const EditUser = (props) => {
onClick={() => {
deleteUser(username)
.then((data) => {
history.push("/");
refreshUserData()
.then((data) => dispatchUserData(data))
updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/"))
.catch((err) => console.log(err));
})
.catch((err) => console.log(err));
@@ -106,9 +111,9 @@ const EditUser = (props) => {
admin
)
.then((data) => {
history.push("/");
refreshUserData()
.then((data) => dispatchUserData(data))
updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/"))
.catch((err) => console.log(err));
})
.catch((err) => {});
@@ -119,9 +124,9 @@ const EditUser = (props) => {
} else {
editUser(username, username, admin)
.then((data) => {
history.push("/");
refreshUserData()
.then((data) => dispatchUserData(data))
updateUsers(0, limit)
.then((data) => dispatchPageChange(data, 0))
.then(() => history.push("/"))
.catch((err) => console.log(err));
})
.catch((err) => {});
@@ -153,7 +158,7 @@ EditUser.propTypes = {
deleteUser: PropTypes.func,
failRegexEvent: PropTypes.func,
noChangeEvent: PropTypes.func,
refreshUserData: PropTypes.func,
updateUsers: PropTypes.func,
};
export default EditUser;

View File

@@ -10,14 +10,18 @@ const GroupEdit = (props) => {
var [selected, setSelected] = useState([]),
[changed, setChanged] = useState(false),
[added, setAdded] = useState(undefined),
[removed, setRemoved] = useState(undefined);
[removed, setRemoved] = useState(undefined),
limit = useSelector((state) => state.limit);
var dispatch = useDispatch();
const dispatchGroupsData = (data) => {
const dispatchPageUpdate = (data, page) => {
dispatch({
type: "GROUPS_DATA",
value: data,
type: "GROUPS_PAGE",
value: {
data: data,
page: page,
},
});
};
@@ -25,7 +29,7 @@ const GroupEdit = (props) => {
addToGroup,
removeFromGroup,
deleteGroup,
refreshGroupsData,
updateGroups,
history,
location,
} = props;
@@ -88,10 +92,12 @@ const GroupEdit = (props) => {
);
Promise.all(promiseQueue)
.then((e) => callback())
.then((e) => {
updateGroups(0, limit)
.then((data) => dispatchPageUpdate(data, 0))
.then(() => history.push("/groups"));
})
.catch((err) => console.log(err));
history.push("/groups");
}}
>
Apply
@@ -103,10 +109,11 @@ const GroupEdit = (props) => {
onClick={() => {
var groupName = group_data.name;
deleteGroup(groupName)
.then(
refreshGroupsData().then((data) => dispatchGroupsData(data))
)
.then(history.push("/groups"))
.then((e) => {
updateGroups(0, limit)
.then((data) => dispatchPageUpdate(data, 0))
.then(() => history.push("/groups"));
})
.catch((err) => console.log(err));
}}
>
@@ -134,7 +141,7 @@ GroupEdit.propTypes = {
addToGroup: PropTypes.func,
removeFromGroup: PropTypes.func,
deleteGroup: PropTypes.func,
refreshGroupsData: PropTypes.func,
updateGroups: PropTypes.func,
};
export default GroupEdit;

View File

@@ -4,32 +4,41 @@ import { compose, withProps } from "recompose";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import { jhapiRequest } from "../../util/jhapiUtil";
import PaginationFooter from "../PaginationFooter/PaginationFooter";
const Groups = (props) => {
var user_data = useSelector((state) => state.user_data),
groups_data = useSelector((state) => state.groups_data),
dispatch = useDispatch();
groups_page = useSelector((state) => state.groups_page),
user_page = useSelector((state) => state.user_page),
limit = useSelector((state) => state.limit),
dispatch = useDispatch(),
page = parseInt(new URLSearchParams(props.location.search).get("page"));
var { refreshGroupsData, refreshUserData, history } = props;
page = isNaN(page) ? 0 : page;
var slice = [page * limit, limit];
var { updateGroups, history } = props;
if (!groups_data || !user_data) {
return <div></div>;
}
const dispatchGroupsData = (data) => {
const dispatchPageChange = (data, page) => {
dispatch({
type: "GROUPS_DATA",
value: data,
type: "GROUPS_PAGE",
value: {
data: data,
page: page,
},
});
};
const dispatchUserData = (data) => {
dispatch({
type: "USER_DATA",
value: data,
if (groups_page != page) {
updateGroups(...slice).then((data) => {
dispatchPageChange(data, page);
});
};
}
return (
<div className="container">
@@ -40,37 +49,39 @@ const Groups = (props) => {
<h4>Groups</h4>
</div>
<div className="panel-body">
{groups_data.length > 0 ? (
groups_data.map((e, i) => (
<div key={"group-edit" + i} className="group-edit-link">
<h4>
<ul className="list-group">
{groups_data.length > 0 ? (
groups_data.map((e, i) => (
<li className="list-group-item" key={"group-item" + i}>
<span className="badge badge-pill badge-success">
{e.users.length + " users"}
</span>
<Link
to={{
pathname: "/group-edit",
state: {
group_data: e,
user_data: user_data,
callback: () => {
refreshGroupsData()
.then((data) => dispatchGroupsData(data))
.catch((err) => console.log(err));
refreshUserData()
.then((data) => dispatchUserData(data))
.catch((err) => console.log(err));
},
},
}}
>
{e.name}
</Link>
</h4>
</li>
))
) : (
<div>
<h4>no groups created...</h4>
</div>
))
) : (
<div>
<h4>no groups created...</h4>
</div>
)}
)}
</ul>
<PaginationFooter
endpoint="/groups"
page={page}
limit={limit}
numOffset={slice[0]}
numElements={groups_data.length}
/>
</div>
<div className="panel-footer">
<button className="btn btn-light adjacent-span-spacing">
@@ -95,11 +106,14 @@ const Groups = (props) => {
Groups.propTypes = {
user_data: PropTypes.array,
groups_data: PropTypes.array,
refreshUserData: PropTypes.func,
refreshGroupsData: PropTypes.func,
updateUsers: PropTypes.func,
updateGroups: PropTypes.func,
history: PropTypes.shape({
push: PropTypes.func,
}),
location: PropTypes.shape({
search: PropTypes.string,
}),
};
export default Groups;

View File

@@ -0,0 +1,50 @@
import React from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import "./pagination-footer.css";
const PaginationFooter = (props) => {
let { endpoint, page, limit, numOffset, numElements } = props;
return (
<div className="pagination-footer">
<p>
Displaying {numOffset}-{numOffset + numElements}
<br></br>
<br></br>
{page >= 1 ? (
<button className="btn btn-sm btn-light spaced">
<Link to={`${endpoint}?page=${page - 1}`}>
<span className="active-pagination">Previous</span>
</Link>
</button>
) : (
<button className="btn btn-sm btn-light spaced">
<span className="inactive-pagination">Previous</span>
</button>
)}
{numElements >= limit ? (
<button className="btn btn-sm btn-light spaced">
<Link to={`${endpoint}?page=${page + 1}`}>
<span className="active-pagination">Next</span>
</Link>
</button>
) : (
<button className="btn btn-sm btn-light spaced">
<span className="inactive-pagination">Next</span>
</button>
)}
</p>
</div>
);
};
PaginationFooter.propTypes = {
endpoint: PropTypes.string,
page: PropTypes.number,
limit: PropTypes.number,
numOffset: PropTypes.number,
numElements: PropTypes.number,
};
export default PaginationFooter;

View File

@@ -0,0 +1,14 @@
@import url(../../style/root.css);
.pagination-footer * button {
margin-right: 10px;
}
.pagination-footer * .inactive-pagination {
color: gray;
cursor: not-allowed;
}
.pagination-footer * button.spaced {
color: var(--blue);
}

View File

@@ -10,6 +10,7 @@ import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
import "./server-dashboard.css";
import { timeSince } from "../../util/timeSince";
import { jhapiRequest } from "../../util/jhapiUtil";
import PaginationFooter from "../PaginationFooter/PaginationFooter";
const ServerDashboard = (props) => {
// sort methods
@@ -30,10 +31,13 @@ const ServerDashboard = (props) => {
var [sortMethod, setSortMethod] = useState(null);
var user_data = useSelector((state) => state.user_data);
var user_page = useSelector((state) => state.user_page);
var limit = useSelector((state) => state.limit);
var page = parseInt(new URLSearchParams(props.location.search).get("page"));
var user_data = useSelector((state) => state.user_data),
user_page = useSelector((state) => state.user_page),
limit = useSelector((state) => state.limit),
page = parseInt(new URLSearchParams(props.location.search).get("page"));
console.log(user_page);
page = isNaN(page) ? 0 : page;
var slice = [page * limit, limit];
@@ -49,14 +53,7 @@ const ServerDashboard = (props) => {
history,
} = props;
var dispatchUserUpdate = (data) => {
dispatch({
type: "USER_DATA",
value: data,
});
};
var dispatchPageChange = (data, page) => {
var dispatchPageUpdate = (data, page) => {
dispatch({
type: "USER_PAGE",
value: {
@@ -71,9 +68,7 @@ const ServerDashboard = (props) => {
}
if (page != user_page) {
updateUsers(...slice)
.then((data) => data.json())
.then((data) => dispatchPageChange(data, page));
updateUsers(...slice).then((data) => dispatchPageUpdate(data, page));
}
if (sortMethod != null) {
@@ -138,9 +133,8 @@ const ServerDashboard = (props) => {
Promise.all(startAll(user_data.map((e) => e.name)))
.then((res) => {
updateUsers(...slice)
.then((data) => data.json())
.then((data) => {
dispatchUserUpdate(data);
dispatchPageUpdate(data, page);
})
.catch((err) => console.log(err));
return res;
@@ -159,9 +153,8 @@ const ServerDashboard = (props) => {
Promise.all(stopAll(user_data.map((e) => e.name)))
.then((res) => {
updateUsers(...slice)
.then((data) => data.json())
.then((data) => {
dispatchUserUpdate(data);
dispatchPageUpdate(data, page);
})
.catch((err) => console.log(err));
return res;
@@ -198,11 +191,9 @@ const ServerDashboard = (props) => {
onClick={() =>
stopServer(e.name)
.then((res) => {
updateUsers(...slice)
.then((data) => data.json())
.then((data) => {
dispatchUserUpdate(data);
});
updateUsers(...slice).then((data) => {
dispatchPageUpdate(data, page);
});
return res;
})
.catch((err) => console.log(err))
@@ -217,11 +208,9 @@ const ServerDashboard = (props) => {
onClick={() =>
startServer(e.name)
.then((res) => {
updateUsers(...slice)
.then((data) => data.json())
.then((data) => {
dispatchUserUpdate(data);
});
updateUsers(...slice).then((data) => {
dispatchPageUpdate(data, page);
});
return res;
})
.catch((err) => console.log(err))
@@ -253,24 +242,13 @@ const ServerDashboard = (props) => {
))}
</tbody>
</table>
<br></br>
<p>
Displaying users {slice[0]}-{slice[0] + user_data.length}
{user_data.length >= limit ? (
<button className="btn btn-link">
<Link to={`/?page=${page + 1}`}>Next</Link>
</button>
) : (
<></>
)}
{page >= 1 ? (
<button className="btn btn-link">
<Link to={`/?page=${page - 1}`}>Previous</Link>
</button>
) : (
<></>
)}
</p>
<PaginationFooter
endpoint="/"
page={page}
limit={limit}
numOffset={slice[0]}
numElements={user_data.length}
/>
<br></br>
</div>
</div>