Skip to content

Commit

Permalink
feat(ui): added delete group page
Browse files Browse the repository at this point in the history
Signed-off-by: dushimsam <dushsam@gmail.com>
  • Loading branch information
dushimsam committed Aug 26, 2022
1 parent 4bf185e commit b645717
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.idea*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@testing-library/user-event": "^12.1.10",
"array-to-tree": "^3.3.2",
"bootstrap": "4.6.0",
"jquery": "^3.6.0",
"js-cookie": "^2.2.1",
"prop-types": "^15.8.1",
"query-string": "^7.1.1",
Expand Down
13 changes: 12 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

// React Imports
import React, { useContext } from "react";
import React, { useContext, useEffect } from "react";

// Theme Provider
import { ThemeProvider } from "styled-components";
Expand All @@ -29,6 +29,9 @@ import { GlobalContext, GlobalProvider } from "context";
// Routes
import Routes from "Routes";

// eslint-disable-next-line import/no-extraneous-dependencies
import "popper.js";

// Global CSS (Bootstrap, Tree View of Folders, Custom Styling)
import "bootstrap/dist/css/bootstrap.min.css";
import "react-virtualized-tree/lib/main.css";
Expand All @@ -46,6 +49,14 @@ function App() {
}

function AppWrapper() {
useEffect(() => {
import("jquery").then(($) => {
// jQuery must be installed to the `window`:
// eslint-disable-next-line no-multi-assign
window.$ = window.jQuery = $;
return import("bootstrap");
});
}, []);
return (
<GlobalProvider>
<App />
Expand Down
6 changes: 6 additions & 0 deletions src/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const UploadDelete = React.lazy(() => import("pages/Organize/Uploads/Delete"));

// Admin Pages
const GroupCreate = React.lazy(() => import("pages/Admin/Group/Create"));
const DeleteGroup = React.lazy(() => import("pages/Admin/Group/Delete"));
const DeleteUser = React.lazy(() => import("pages/Admin/Users/Delete"));
const AddUser = React.lazy(() => import("pages/Admin/Users/Add"));
const AddLicense = React.lazy(() => import("pages/Admin/License/Create"));
Expand Down Expand Up @@ -285,6 +286,11 @@ const Routes = () => {
path={routes.admin.group.create}
component={GroupCreate}
/>
<AdminLayout
exact
path={routes.admin.group.delete}
component={DeleteGroup}
/>
<AdminLayout
exact
path={routes.admin.license.create}
Expand Down
26 changes: 26 additions & 0 deletions src/api/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ export const getAllGroupsApi = () => {
});
};

// Fetching all deletable groups
export const getAllDeletableGroupsApi = () => {
const url = endpoints.admin.groups.getAllDeletable();
return sendRequest({
url,
method: "GET",
headers: {
Authorization: getToken(),
},
addGroupName: false,
});
};

// Creating a group
export const createGroupApi = (name) => {
const url = endpoints.admin.groups.create();
Expand All @@ -50,3 +63,16 @@ export const createGroupApi = (name) => {
addGroupName: false,
});
};

// Delete a group
export const deleteGroupApi = (id) => {
const url = endpoints.admin.groups.delete(id);
return sendRequest({
url,
method: "DELETE",
headers: {
Authorization: getToken(),
},
addGroupName: false,
});
};
71 changes: 71 additions & 0 deletions src/components/Modals/DeleteConfirmation/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from "react";
import PropTypes from "prop-types";
import Spinner from "react-bootstrap/Spinner";
import { Button } from "../../Widgets";

const DeleteConfirmation = ({ callBack, loading }) => {
return (
<>
<div
className="modal fade"
id="deleteConfirmationModal"
tabIndex="-1"
role="dialog"
aria-hidden="true"
>
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Are you sure ?</h5>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div className="modal-body">
Do you really want to delete this group ? This process can not be
undone.
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-secondary border-dark border-rounded px-4 py-2 mr-2"
data-dismiss="modal"
onClick={() => {}}
>
Cancel
</button>
<Button
type="button"
onClick={() => callBack()}
className="btn btn-danger border-dark border-rounded px-5 py-2"
>
{loading ? (
<Spinner
as="span"
animation="border"
size="sm"
role="status"
aria-hidden="true"
/>
) : (
"YES"
)}
</Button>
</div>
</div>
</div>
</div>
</>
);
};

DeleteConfirmation.propTypes = {
callBack: PropTypes.func.isRequired,
loading: PropTypes.bool.isRequired,
};
export default DeleteConfirmation;
19 changes: 18 additions & 1 deletion src/components/Widgets/Button/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,25 @@ import React from "react";
import PropTypes from "prop-types";
import styles from "styled-components";

const Button = ({ type, onClick, className, children }) => {
const Button = ({
type,
onClick,
className,
children,
dataDismiss,
dataToggle,
dataTarget,
disabled = false,
}) => {
return (
<ButtonContainer
type={type}
onClick={onClick}
data-toggle={dataToggle}
data-dismiss={dataDismiss}
data-target={dataTarget}
className={`bg-primary-color text-secondary-color font-demi text-center hover-primary-color ${className}`}
disabled={disabled}
>
{children}
</ButtonContainer>
Expand All @@ -37,6 +50,10 @@ Button.propTypes = {
onClick: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
className: PropTypes.string,
dataTarget: PropTypes.string,
disabled: PropTypes.bool,
dataToggle: PropTypes.string,
dataDismiss: PropTypes.string,
};

const ButtonContainer = styles.button`
Expand Down
2 changes: 2 additions & 0 deletions src/constants/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ const endpoints = {
groups: {
create: () => `${apiUrl}/groups`,
getAll: () => `${apiUrl}/groups`,
getAllDeletable: () => `${apiUrl}/groups/deletable`,
delete: (groupId) => `${apiUrl}/groups/${groupId}`,
},
},
license: {
Expand Down
1 change: 1 addition & 0 deletions src/constants/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const messages = {
noPageShort: "Error: Page Not Found!",
noPageLong: "We could not find the page you were searching for",
groupCreate: "Successfully created the group",
deletedGroup: "Successfully deleted the group",
deletedUser: "Successfully deleted the user",
addedUser: "Successfully added the user",
confirmDeletion: "Deletion not confirmed",
Expand Down
147 changes: 147 additions & 0 deletions src/pages/Admin/Group/Delete/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
Copyright (C) 2022 Samuel Dushimimana
SPDX-License-Identifier: GPL-2.0
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import React, { useEffect, useState } from "react";
import messages from "constants/messages";

// Jquery for handling modal
import $ from "jquery";

// Title
import Title from "components/Title";

// Widgets
import { Alert, Button, InputContainer } from "components/Widgets";

// Required functions for calling APIs
import { deleteGroup, fetchAllDeletableGroups } from "services/groups";
import DeleteConfirmation from "components/Modals/DeleteConfirmation";

const DeleteGroup = () => {
const initialMessage = {
type: "",
text: "",
};

const [groups, setGroups] = useState([]);
const [selectedGroupId, setSelectedGroupId] = useState(null);

// State Variables for handling Error Boundaries
const [loading, setLoading] = useState(false);
const [showMessage, setShowMessage] = useState(false);
const [message, setMessage] = useState(initialMessage);

useEffect(async () => {
try {
const res = await fetchAllDeletableGroups();
setGroups(res);
} catch (e) {
setMessage({
type: "error",
text: e.message,
});
setShowMessage(true);
}
}, []);

useEffect(() => {
if (groups.length > 0) {
setSelectedGroupId(groups[0].id);
}
}, [groups]);

const toggleModal = (modalId, status) => {
// eslint-disable-next-line func-names
$(function () {
if (status === "show") {
$(modalId).modal("show");
} else {
$(modalId).modal("hide");
$(".modal-backdrop").remove();
}
});
};

const deleteItem = async () => {
setLoading(true);
try {
await deleteGroup(selectedGroupId);
setMessage({
type: "success",
text: messages.deletedGroup,
});

setTimeout(() => {
window.location.reload();
}, 1500);
} catch (error) {
setMessage({
type: "danger",
text: error.message,
});
} finally {
setLoading(false);
setShowMessage(true);
toggleModal("#deleteConfirmationModal", "hide");
}
};

return (
<>
<Title title="Delete Group" />
<div className="main-container my-3">
{showMessage && (
<Alert
type={message.type}
setShow={setShowMessage}
message={message.text}
/>
)}
<h1 className="font-size-main-heading">Delete Group</h1>
<br />
<div className="row">
<div className="col-12 col-lg-8">
<form>
<InputContainer
name="group"
type="select"
onChange={(e) => setSelectedGroupId(e.target.value)}
options={groups}
property="name"
>
Select group to delete:
</InputContainer>
<Button
type="button"
dataToggle="modal"
dataTarget="#deleteConfirmationModal"
className="mt-4"
disabled={groups.length === 0}
>
Delete
</Button>
</form>
</div>
<DeleteConfirmation callBack={deleteItem} loading={loading} />
</div>
</div>
</>
);
};

export default DeleteGroup;
Loading

0 comments on commit b645717

Please sign in to comment.