Skip to content
This repository has been archived by the owner on Jul 30, 2023. It is now read-only.

E2348 Replicate Roles and Institution UIs using ReactJS #7

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,745 changes: 2,153 additions & 2,592 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./components/Layout/Home";
import RootLayout from "./components/Layout/Root";
import Users from "./components/Users/Users";
import Roles from "./components/Roles/Roles";
import Institutions from "./components/Institutions/Institutions";
import Instructors from "./components/Instructors/Instructors";
import Administrators from "./components/Administrators/Administrators";
import SuperAdministrators from "./components/SuperAdministrators/SuperAdministrators";

function App() {
const router = createBrowserRouter([
Expand All @@ -12,6 +17,11 @@ function App() {
children: [
{ index: true, element: <Home /> },
{ path: "users", element: <Users /> },
{ path: "roles", element: <Roles /> },
{ path: "institutions", element: <Institutions /> },
{ path: "administrators", element: <Administrators /> },
{ path: "super_administrators", element: <SuperAdministrators /> },
{ path: "instructors", element: <Instructors /> },
],
},
]);
Expand Down
62 changes: 62 additions & 0 deletions src/components/Administrators/Administrators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { Button, Col, Container, Row } from "react-bootstrap";
import { useDispatch } from "react-redux";
import useAPI from "../../hooks/use-api";
import { alertActions } from "../../store/alert";
import Table from "../UI/Table/Table";
import { USER_ADMINISTRATOR_COLUMNS } from "./userAdministratorColumns";

const Users = () => {
const dispatch = useDispatch();
const {
error,
isLoading,
data: userData,
sendRequest: fetchUsers,
} = useAPI();

useEffect(
() => fetchUsers({ url: "/roles/administrator/users", method: "get" }),
[fetchUsers]
);

// Error alert
useEffect(() => {
if (error) {
dispatch(
alertActions.showAlert({
variant: "danger",
message: error,
})
);
}
}, [error, dispatch]);

const tableColumns = useMemo(() => USER_ADMINISTRATOR_COLUMNS(), []);
const tableData = useMemo(
() => (isLoading ? [] : userData),
[userData, isLoading]
);

const initialState = { hiddenColumns: ["id", "institution"] };

return (
<Container fluid className="px-md-4">
<Row className="mt-md-2 mb-md-2">
<Col md={{ span: 4, offset: 4 }}>
<h1>Administrators</h1>
</Col>
<hr />
</Row>
<Row>
<Table
data={tableData}
columns={tableColumns}
initialState={initialState}
/>
</Row>
</Container>
);
};

export default Users;
31 changes: 31 additions & 0 deletions src/components/Administrators/userAdministratorColumns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Link } from "react-router-dom";

export const USER_ADMINISTRATOR_COLUMNS = () => [
{
Header: "Id",
accessor: "id",
disableFilters: true,
},
{
Header: "Username",
accessor: "name",
Cell: ({ row }) => (
<Link to={`/users/${row.original.id}`}> {row.original.name}</Link>
),
},
{
Header: "Full Name",
accessor: "fullname",
},
{
Header: "Email",
accessor: "email",
},

{
id: "institution",
Header: "Institution",
accessor: (d) => d.institution.name,
disableFilters: true,
},
];
120 changes: 120 additions & 0 deletions src/components/Institutions/CreateInstitution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {Form, Formik} from "formik";
import {useEffect, useState} from "react";
import {Button, Col, InputGroup, Modal, Row} from "react-bootstrap";
import {useDispatch} from "react-redux";
import * as Yup from "yup";
import useAPI from "../../hooks/use-api";
import {alertActions} from "../../store/alert";
import FormCheckboxGroup from "../UI/Form/FormCheckboxGroup";
import FormInput from "../UI/Form/FormInput";
import FormSelect from "../UI/Form/FormSelect";
import {transformInstitutionsRequest} from "./util";

// Get the logged-in user from the session
const loggedInUser = null;

const initialValues = {
name: "",
};

const validationSchema = Yup.object({
name: Yup.string()
.required("Required")
.min(3, "Institution Name must be at least 3 characters")
.max(20, "Institution Name must be at most 20 characters"),
});

const CreateInstitution = ({onClose}) => {
const dispatch = useDispatch();
const [show, setShow] = useState(true);
const {
data: createdInstitution,
error: institutionError,
sendRequest: createInstitution,
} = useAPI();

useEffect(() => {
if (institutionError) {
dispatch(alertActions.showAlert({
variant: "danger",
message: institutionError
}));
}
}, [institutionError, dispatch]);

useEffect(() => {
if (createdInstitution.length > 0) {
setShow(false);
onClose(createdInstitution[0]);
}
}, [institutionError, createdInstitution, onClose]);

const onSubmit = (values, submitProps) => {
createInstitution({
url: "/institutions",
method: "post",
data: {...values, parent: loggedInUser},
transformRequest: transformInstitutionsRequest,
});
submitProps.resetForm();
submitProps.setSubmitting(false);
};

const handleClose = () => {
setShow(false);
onClose();
};

return (
<Modal
size="lg"
centered
show={show}
onHide={handleClose}
backdrop="static"
>
<Modal.Header closeButton>
<Modal.Title>Create Institution</Modal.Title>
</Modal.Header>
<Modal.Body>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
validateOnChange={false}
>
{(formik) => {
return (
<Form>
<Row>
<FormInput
as={Col}
controlId="institution-name"
label="Institution Name"
name="name"
/>
</Row>
<Modal.Footer>
<Button variant="outline-secondary" onClick={handleClose}>
Close
</Button>
<Button
variant="outline-success"
type="submit"
disabled={
!(formik.isValid && formik.dirty) || formik.isSubmitting
}
>
Create Institution
</Button>
</Modal.Footer>
</Form>
);
}}
</Formik>
</Modal.Body>
</Modal>
);
};

export default CreateInstitution;
62 changes: 62 additions & 0 deletions src/components/Institutions/DeleteInstitution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {useEffect, useState} from "react";
import {Button, Modal} from "react-bootstrap";
import {useDispatch} from "react-redux";
import useAPI from "../../hooks/use-api";
import {alertActions} from "../../store/alert";

const DeleteInstitution = ({institutionData, onClose}) => {
const dispatch = useDispatch();
const {
data: deletedInstitution,
error: institutionError,
sendRequest: deleteInstitution,
} = useAPI();
const [show, setShow] = useState(true);

const deleteHandler = () =>
deleteInstitution({url: `/institutions/${institutionData.id}`, method: "DELETE"});

useEffect(() => {
if (institutionError) {
dispatch(alertActions.showAlert({
variant: "danger",
message: institutionError,
}));
}
}, [institutionError, dispatch]);

useEffect(() => {
if (deletedInstitution.length > 0) {
setShow(false);
onClose(deletedInstitution[0]);
}
}, [deletedInstitution, onClose]);

const closeHandler = () => {
setShow(false);
onClose();
};

return (
<Modal show={show} onHide={closeHandler}>
<Modal.Header closeButton>
<Modal.Title>Delete Institution</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
Are you sure you want to delete Institution <b>{institutionData.name}?</b>
</p>
</Modal.Body>
<Modal.Footer>
<Button variant="outline-secondary" onClick={closeHandler}>
Cancel
</Button>
<Button variant="outline-danger" onClick={deleteHandler}>
Delete
</Button>
</Modal.Footer>
</Modal>
);
};

export default DeleteInstitution;
Loading