From c36c8b3c970b506537270e1ba1e6f0056b3a8234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Mon, 15 Jul 2019 10:56:58 +0200 Subject: [PATCH] Update the admin panel to manage users' polochons --- frontend/js/app.js | 2 +- frontend/js/components/admins/modules.js | 28 +++++ frontend/js/components/admins/panel.js | 57 ++-------- frontend/js/components/admins/stat.js | 30 +++++ frontend/js/components/admins/stats.js | 93 +++++++--------- frontend/js/components/admins/torrentsStat.js | 21 ++++ frontend/js/components/admins/user.js | 60 ++++++++++ frontend/js/components/admins/userEdit.js | 104 ++++++++++++++++++ frontend/js/components/admins/userList.js | 63 +++++++++++ frontend/js/reducers/admins.js | 7 +- 10 files changed, 362 insertions(+), 103 deletions(-) create mode 100644 frontend/js/components/admins/modules.js create mode 100644 frontend/js/components/admins/stat.js create mode 100644 frontend/js/components/admins/torrentsStat.js create mode 100644 frontend/js/components/admins/user.js create mode 100644 frontend/js/components/admins/userEdit.js create mode 100644 frontend/js/components/admins/userList.js diff --git a/frontend/js/app.js b/frontend/js/app.js index b892f35..9bae870 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -25,7 +25,7 @@ import { ProtectedRoute, AdminRoute } from "./auth" import store, { history } from "./store" // Components -import AdminPanel from "./components/admins/panel" +import { AdminPanel } from "./components/admins/panel" import Alert from "./components/alerts/alert" import MovieList from "./components/movies/list" import MoviesRoute from "./components/movies/route" diff --git a/frontend/js/components/admins/modules.js b/frontend/js/components/admins/modules.js new file mode 100644 index 0000000..6b99cb8 --- /dev/null +++ b/frontend/js/components/admins/modules.js @@ -0,0 +1,28 @@ +import React, { useEffect } from "react" +import PropTypes from "prop-types" +import { connect } from "react-redux" +import { getAdminModules} from "../../actions/admins" + +import Modules from "../modules/modules" + +const AdminModulesConnected = ({ modules, loading, getAdminModules }) => { + useEffect(() => { + getAdminModules(); + }, [getAdminModules]) + + return ( + + ) +} +AdminModulesConnected.propTypes = { + modules: PropTypes.object, + loading: PropTypes.bool, + getAdminModules: PropTypes.func.isRequired, +}; + +const mapStateToProps = state => ({ + loading: state.adminStore.get("fetchingModules"), + modules: state.adminStore.get("modules"), +}); + +export const AdminModules = connect(mapStateToProps, {getAdminModules})(AdminModulesConnected); diff --git a/frontend/js/components/admins/panel.js b/frontend/js/components/admins/panel.js index f32905b..874f082 100644 --- a/frontend/js/components/admins/panel.js +++ b/frontend/js/components/admins/panel.js @@ -1,50 +1,13 @@ -import React, { useState } from "react" -import PropTypes from "prop-types" -import { connect } from "react-redux" -import { - getUsers, getStats, - getAdminModules, updateUser -} from "../../actions/admins" +import React from "react" -import Modules from "../modules/modules" -import { UserList } from "./users" +import { UserList } from "./userList" import { Stats } from "./stats" +import { AdminModules } from "./modules" -const AdminPanel = props => { - const [fetched, setIsFetched] = useState(false); - if (!fetched) { - props.getUsers(); - props.getStats(); - props.getAdminModules(); - setIsFetched(true); - } - - return ( - - - - - - ) -} -AdminPanel.propTypes = { - stats: PropTypes.object, - users: PropTypes.object, - modules: PropTypes.object, - updateUser: PropTypes.func.isRequired, - getUsers: PropTypes.func.isRequired, - getStats: PropTypes.func.isRequired, - getAdminModules: PropTypes.func.isRequired, -}; - -const mapStateToProps = state => ({ - users: state.adminStore.get("users"), - stats: state.adminStore.get("stats"), - modules: state.adminStore.get("modules"), -}); -const mapDispatchToProps = { - getUsers, getStats, - getAdminModules, updateUser -} - -export default connect(mapStateToProps, mapDispatchToProps)(AdminPanel); +export const AdminPanel = () => ( + + + + + +) diff --git a/frontend/js/components/admins/stat.js b/frontend/js/components/admins/stat.js new file mode 100644 index 0000000..f4164a2 --- /dev/null +++ b/frontend/js/components/admins/stat.js @@ -0,0 +1,30 @@ +import React from "react" +import PropTypes from "prop-types" + +import { TorrentsStat } from "./torrentsStat" + +export const Stat = ({ name, count, torrentCount, torrentCountById }) => ( +
+
+
+

+ {name} + {count} +

+
+
+ +
+
+
+) +Stat.propTypes = { + name: PropTypes.string, + count: PropTypes.number, + torrentCount: PropTypes.number, + torrentCountById: PropTypes.number, +}; diff --git a/frontend/js/components/admins/stats.js b/frontend/js/components/admins/stats.js index 184ac0c..fe8b132 100644 --- a/frontend/js/components/admins/stats.js +++ b/frontend/js/components/admins/stats.js @@ -1,61 +1,46 @@ -import React from "react" +import React, { useEffect } from "react" import PropTypes from "prop-types" +import { connect } from "react-redux" -export const Stats = props => ( -
- - - -
-) -Stats.propTypes = { stats: PropTypes.object } +import { Stat } from "./stat" -const Stat = props => ( -
-
-
-

- {props.name} - {props.count} -

-
-
- -
-
-
-) -Stat.propTypes = { - name: PropTypes.string, - count: PropTypes.number, -}; +import { getStats } from "../../actions/admins" -const TorrentsStat = function(props) { - if (props.data.torrentCount === undefined) { - return (No torrents); - } +const StatsConnected = ({ stats, getStats }) => { + useEffect(() => { + getStats(); + }, [getStats]) - const percentage = Math.floor((props.data.torrentCountById * 100) / props.data.count); return ( - - {percentage}% with torrents -   - {props.data.torrentCount} total - - ); +
+ + + +
+ ) } -TorrentsStat.propTypes = { data: PropTypes.object }; +StatsConnected.propTypes = { + stats: PropTypes.object, + getStats: PropTypes.func, +} + +const mapStateToProps = state => ({ + stats: state.adminStore.get("stats"), +}); + +export const Stats = connect(mapStateToProps, {getStats})(StatsConnected); diff --git a/frontend/js/components/admins/torrentsStat.js b/frontend/js/components/admins/torrentsStat.js new file mode 100644 index 0000000..fa8abbe --- /dev/null +++ b/frontend/js/components/admins/torrentsStat.js @@ -0,0 +1,21 @@ +import React from "react" +import PropTypes from "prop-types" + +export const TorrentsStat = ({ count, torrentCount, torrentCountById }) => { + if (torrentCount === undefined) { + return (No torrents); + } + + const percentage = Math.floor((torrentCountById * 100) / count); + return ( + + {percentage}% with torrents +   - {torrentCount} total + + ); +} +TorrentsStat.propTypes = { + count: PropTypes.number, + torrentCount: PropTypes.number, + torrentCountById: PropTypes.number, +}; diff --git a/frontend/js/components/admins/user.js b/frontend/js/components/admins/user.js new file mode 100644 index 0000000..b6a9ad5 --- /dev/null +++ b/frontend/js/components/admins/user.js @@ -0,0 +1,60 @@ +import React from "react" +import PropTypes from "prop-types" + +import { UserEdit } from "./userEdit" + +export const User = ({ + id, + admin, + activated, + name, + polochonActivated, + polochonUrl, + polochonName, + polochonId, + token, +}) => { + return ( + + {id} + {name} + + + + + + + + {polochonName !== "" ? polochonName : "-"} + {polochonUrl !== "" && + ({polochonUrl}) + } + + {token} + + + + + + + + ); +} +User.propTypes = { + id: PropTypes.string, + name: PropTypes.string, + polochonId: PropTypes.string, + polochonUrl: PropTypes.string, + polochonName: PropTypes.string, + token: PropTypes.string, + admin: PropTypes.bool, + activated: PropTypes.bool, + polochonActivated: PropTypes.bool, +}; diff --git a/frontend/js/components/admins/userEdit.js b/frontend/js/components/admins/userEdit.js new file mode 100644 index 0000000..fa3c1ce --- /dev/null +++ b/frontend/js/components/admins/userEdit.js @@ -0,0 +1,104 @@ +import React, { useState } from "react" +import PropTypes from "prop-types" +import { connect } from "react-redux" +import { List } from "immutable" + +import { updateUser } from "../../actions/admins" + +import Toggle from "react-bootstrap-toggle"; + +import { PolochonSelect } from "../polochons/select" +import { FormModal } from "../forms/modal" +import { FormInput } from "../forms/input" + +const UserEditConnect = ({ + id, + admin: initAdmin, + activated: initActivated, + polochonToken, + polochonId: initPolochonId, + polochonActivated: initPolochonActivated, + updateUser, + publicPolochons, +}) => { + const [modal, setModal] = useState(false); + const [admin, setAdmin] = useState(initAdmin); + const [activated, setActivated] = useState(initActivated); + const [token, setToken] = useState(polochonToken); + const [polochonId, setPolochonId] = useState(initPolochonId); + const [polochonActivated, setPolochonActivated] = useState(initPolochonActivated); + const [password, setPassword] = useState(""); + + const handleSubmit = function(e) { + if (e) { e.preventDefault(); } + updateUser({ + userId: id, + polochonToken: token, + admin, + activated, + polochonId, + polochonActivated, + password, + }); + setModal(false); + }; + + return ( + + setModal(true)} /> + + + +
+ + setActivated(!activated)} + /> +
+ +
+ + setAdmin(!admin)} /> +
+ + + + + +
+ + setPolochonActivated(!polochonActivated)} + /> +
+ +
+
+ ); +} +UserEditConnect.propTypes = { + id: PropTypes.string, + activated: PropTypes.bool, + admin: PropTypes.bool, + data: PropTypes.object, + updateUser: PropTypes.func, + polochonToken: PropTypes.string, + polochonId: PropTypes.string, + polochonActivated: PropTypes.bool, + publicPolochons: PropTypes.instanceOf(List), +}; + +const mapStateToProps = (state) => ({ + publicPolochons: state.polochon.get("public"), +}); + +export const UserEdit = connect(mapStateToProps, {updateUser})(UserEditConnect); diff --git a/frontend/js/components/admins/userList.js b/frontend/js/components/admins/userList.js new file mode 100644 index 0000000..8e5a85f --- /dev/null +++ b/frontend/js/components/admins/userList.js @@ -0,0 +1,63 @@ +import React, { useEffect } from "react" +import PropTypes from "prop-types" +import { List } from "immutable" +import { connect } from "react-redux" + +import { User } from "./user" + +import { getUsers } from "../../actions/admins" +import { getPolochons } from "../../actions/polochon" + +const mapStateToProps = state => ({ + users: state.adminStore.get("users"), +}); +const mapDispatchToProps = { getUsers, getPolochons }; + +const UserListConnect = ({ users, getUsers, getPolochons }) => { + useEffect(() => { + getUsers(); + getPolochons(); + }, [getUsers, getPolochons]) + + return ( +
+ + + + + + + + + + + + + + + {users.map((el, index) => + + )} + +
#NameActivatedAdminPolochon URLPolochon tokenPolochon activatedActions
+
+ ); +}; +UserListConnect.propTypes = { + getUsers: PropTypes.func, + getPolochons: PropTypes.func, + users: PropTypes.PropTypes.instanceOf(List), +}; + +export const UserList = connect(mapStateToProps, mapDispatchToProps)(UserListConnect); diff --git a/frontend/js/reducers/admins.js b/frontend/js/reducers/admins.js index 330f7ed..35c4e92 100644 --- a/frontend/js/reducers/admins.js +++ b/frontend/js/reducers/admins.js @@ -1,6 +1,7 @@ import { Map, List, fromJS } from "immutable" export const defaultState = Map({ + "fetchingModules": false, "users": List(), "stats": Map({}), "modules": Map({}), @@ -9,7 +10,11 @@ export const defaultState = Map({ const handlers = { "ADMIN_LIST_USERS_FULFILLED": (state, action) => state.set("users", fromJS(action.payload.response.data)), "ADMIN_GET_STATS_FULFILLED": (state, action) => state.set("stats", fromJS(action.payload.response.data)), - "ADMIN_GET_MODULES_FULFILLED": (state, action) => state.set("modules", fromJS(action.payload.response.data)), + "ADMIN_GET_MODULES_PENDING": state => state.set("fetchingModules", true), + "ADMIN_GET_MODULES_FULFILLED": (state, action) => state.merge(fromJS({ + "modules": action.payload.response.data, + "fetchingModules": false, + })), } export default (state = defaultState, action) =>