Skip to content

Commit

Permalink
* feature: improve trainee details page (#172)
Browse files Browse the repository at this point in the history
* handling missing application info, also adding download functionality

* fixing error related to download and refactoring

* Update TrainneeDetails.tsx

* handling issues related to deployment

* Fix number can't be shared (#130)

* #102 sidebar links review (#128)

* fix: remove placeholder property

* fix duplicate links

---------

* Ft minimize dashboard menu #110 (#140)

* fix: remove placeholder property

* ft minimize dashboard menu

* fix minimize dashboard by icon and categorize into section

* fix minimize dashboard by icon and categorize into section

* fix minimize dashboard by icon and categorize into section

* fix minimize dashboard by icon and categorize into section

* fix minimize dashboard by icon and categorize into section

* fix minimize dashboard by icon and categorize into section

* fix minimize dashboard scrollbar

* fix minimize dashboard scrollbar

* fix minimize dashboard scrollbar

* Fix layout spacing between sidebar and main content in AdminLayout

* new

* Fix layout spacing between sidebar and main content in AdminLayout

* fix layout

---------

* #118 fx: builtinSuperAdminCreateProgram (#126)

* fix: remove placeholder property

* The built-in superadmin account cannot create a program

---------

* feature: improve trainee details page

* handling missing application info, also adding download functionality

* Update TrainneeDetails.tsx

* adding way to send email and other adjustments

* refining and fixing some issues

* Update webpack.config.js

* customizing way of sending email

* refactoring code to fix issue related to code climate

* fixing issue for deployment

* fixing issues related to refactoring

---------

Co-authored-by: MUREKEZI Ismael <[email protected]>
Co-authored-by: MUGISHA Emmanuel <[email protected]>
Co-authored-by: Mugisha <[email protected]>
Co-authored-by: ISHIMWE Jean Baptiste <[email protected]>
Co-authored-by: ceelogre <[email protected]>
Co-authored-by: ManziPatrick <[email protected]>
Co-authored-by: Prince-Kid <[email protected]>
Co-authored-by: Mucyo Prince <[email protected]>
Co-authored-by: Aime-Patrick <[email protected]>
  • Loading branch information
10 people committed Oct 11, 2024
1 parent bd88713 commit 7df865f
Show file tree
Hide file tree
Showing 21 changed files with 626 additions and 18,142 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^3.3.0",
"@mui/icons-material": "^6.1.1",
"@mui/material": "^5.10.11",
"@mui/x-date-pickers": "^5.0.6",
"@testing-library/jest-dom": "^5.16.5",
Expand Down
38 changes: 38 additions & 0 deletions src/components/application/ApplicationActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import { Link } from "react-router-dom";
import { HiDotsVertical } from "react-icons/hi";

interface ApplicationActionsProps {
_id: string;
isDrop: string | number;
setDrop: (id: string | number) => void;
}

const ApplicationActions: React.FC<ApplicationActionsProps> = ({ _id, isDrop, setDrop }) => {
const toggleDropdown = () => {
if (isDrop === _id) {
setDrop("");
} else {
setDrop(_id);
}
};

return (
<div className="relative">
<HiDotsVertical className="text-gray-600 dark:text-white cursor-pointer" onClick={toggleDropdown} />
<div className={`${isDrop === _id ? "block" : "hidden"} absolute right-12 z-10 mt-2 w-32 bg-white dark:bg-gray-700 rounded-md shadow-lg py-1`}>
<Link to={`/admin/application-details/${_id}`} className="block px-4 py-2 text-sm text-gray-700 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-500">
View Details
</Link>
<button className="block w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-500">
Soft Delete
</button>
<button className="block w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-500">
Hard Delete
</button>
</div>
</div>
);
};

export default ApplicationActions;
65 changes: 65 additions & 0 deletions src/components/application/ApplicationFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable */
import React from "react";

interface ApplicationFilterProps {
searchTerm: string;
setSearchTerm: (value: string) => void;
filterStatus: string;
setFilterStatus: (value: string) => void;
sortBy: string;
setSortBy: (value: string) => void;
sortOrder: string;
setSortOrder: (value: string) => void;
}



const ApplicationFilter: React.FC<ApplicationFilterProps> = ({
searchTerm, setSearchTerm, filterStatus, setFilterStatus, sortBy, setSortBy, sortOrder, setSortOrder
}) => {
return (
<div className="flex items-center justify-between space-x-4 mb-4">
<input
type="text"
placeholder="Search applicants..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="border text-sm dark:border-slate-400 dark:bg-dark-bg dark:text-white p-[0.5rem] rounded-md w-1/4"
/>

<div className="flex gap-4 items-center">
<select value={filterStatus} onChange={(e) => setFilterStatus(e.target.value)}
className="border dark:border-slate-400 p-[0.5rem] text-sm rounded-md dark:bg-dark-bg dark:text-white " >
<option value="All">All Status</option>
<option value="submitted">Submitted</option>
<option value="Shortlisted">Shortlisted</option>
<option value="English assessment">English Assessment</option>
<option value="Technical assessment">Technical Assessment</option>
<option value="Done Technical assessment">Done Technical Assessment</option>
<option value="Invited for Home Challenge">Invited for Home Challenge</option>
<option value="Done Home Challenge">Done Home Challenge</option>
<option value="Invited for Interview">Invited for Interview</option>
<option value="Accepted">Accepted</option>
<option value="Reject">Rejected</option>
<option value="Missed English assessment">Missed English Assessment</option>
<option value="Missed Technical assessment">Missed Technical Assessment</option>
<option value="Missed Interview">Missed Interview</option>
</select>

<select value={sortBy} onChange={(e) => setSortBy(e.target.value)} className="border text-sm dark:border-slate-400 p-[0.5rem] rounded-md dark:bg-dark-bg dark:text-white">
<option value="dateOfSubmission">Date</option>
<option value="firstName">First Name</option>
<option value="lastName">Last Name</option>
<option value="status">Status</option>
</select>
<select value={sortOrder} onChange={(e) => setSortOrder(e.target.value)} className="border text-sm dark:border-slate-400 p-[0.5rem] rounded-md dark:bg-dark-bg dark:text-white">
<option value="desc">DESC</option>
<option value="asc">ASC</option>
</select>

</div>
</div>
);
};

export default ApplicationFilter;
49 changes: 49 additions & 0 deletions src/components/application/ApplicationRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { getStatusClass } from "./statusHelper";
import ApplicationActions from "./ApplicationActions";

interface Application {
_id: string;
firstName: string;
lastName: string;
email: string;
gender: string;
status: string;
dateOfSubmission: string;
}

interface ApplicationRowProps {
application: Application;
isDrop: string | number;
setDrop: (id: string | number) => void;
}

const ApplicationRow: React.FC<ApplicationRowProps> = ({ application, isDrop, setDrop }) => (
<tr className="hover:bg-gray-50 dark:hover:bg-gray-700/50">
<td className="px-5 py-3 border-b-2 border-gray-200 dark:border-dark-tertiary text-left text-[0.8rem] font-semibold text-gray-600 dark:text-white uppercase tracking-wider">
{application.firstName}
</td>
<td className="px-5 py-3 border-b-2 border-gray-200 dark:border-dark-tertiary text-left text-[0.85rem] font-semibold text-gray-600 dark:text-white tracking-wider">
{application.lastName}
</td>
<td className="px-5 py-3 border-b-2 border-gray-200 dark:border-dark-tertiary text-left text-[0.85rem] font-semibold text-gray-600 dark:text-white tracking-wider">
{application.email}
</td>
<td className="px-5 py-3 border-b-2 border-gray-200 dark:border-dark-tertiary text-left text-[0.85rem] font-semibold text-gray-600 dark:text-white tracking-wider">
{application.gender}
</td>
<td className="px-5 py-5 border-b border-gray-200 dark:border-dark-tertiary">
<span className={`inline-block py-1 px-3 rounded-full text-sm ${getStatusClass(application.status)}`}>
{application.status}
</span>
</td>
<td className="px-5 py-3 border-b-2 border-gray-200 dark:border-dark-tertiary text-left text-[0.85rem] font-semibold text-gray-600 dark:text-white tracking-wider">
{application.dateOfSubmission}
</td>
<td className="px-5 py-5 border-b border-gray-200 dark:border-dark-tertiary text-sm">
<ApplicationActions _id={application._id} isDrop={isDrop} setDrop={setDrop} />
</td>
</tr>
);

export default ApplicationRow;
51 changes: 51 additions & 0 deletions src/components/application/ApplicationTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";
import ApplicationRow from "./ApplicationRow";

interface Application {
_id: string;
firstName: string;
lastName: string;
email: string;
gender: string;
status: string;
dateOfSubmission: string;
}

interface ApplicationTableProps {
filteredApplications: Application[];
}

const ApplicationTable: React.FC<ApplicationTableProps> = ({ filteredApplications }) => {
const [isDrop, setDrop] = React.useState<string | number>("");

if (filteredApplications.length === 0) {
return (
<div className="flex justify-center items-center p-10">
<p className="text-gray-500 dark:text-gray-300">No applicants found. Try adjusting your search or filters.</p>
</div>
);
}

return (
<table className="min-w-full leading-normal">
<thead className="bg-gray-100 dark:bg-dark-tertiary">
<tr>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">First Name</th>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">Last Name</th>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">Email</th>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">Gender</th>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">Status</th>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">Submitted At</th>
<th className="px-5 py-3 text-gray-600 text-[0.85rem] dark:text-white uppercase text-left">Action</th>
</tr>
</thead>
<tbody>
{filteredApplications.map((application) => (
<ApplicationRow key={application._id} application={application} isDrop={isDrop} setDrop={setDrop} />
))}
</tbody>
</table>
);
};

export default ApplicationTable;
40 changes: 40 additions & 0 deletions src/components/application/WarningModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable */
import React from 'react';

interface WarningModalProps {
isOpen: boolean;
message: string;
onConfirm: () => void;
onCancel: () => void;
}

const WarningModal: React.FC<WarningModalProps> = ({ isOpen, message, onConfirm, onCancel }) => {
if (!isOpen) return null;

return (
<div className="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 z-50">
<div className="bg-white border dark:border-gray-500 dark:bg-dark-bg rounded-lg p-6">
<h3 className="text-lg font-semibold text-red-600">Warning: Irreversible Action</h3>
<p className="mt-4 text-gray-700 dark:text-gray-300">
{message}
</p>
<div className="mt-6 flex justify-end space-x-4">
<button
className="px-4 py-2 bg-gray-300 hover:bg-gray-400 text-gray-700 rounded-md"
onClick={onCancel}
>
Cancel
</button>
<button
className="px-4 py-2 bg-red-600 hover:bg-red-500 text-white rounded-md"
onClick={onConfirm}
>
Confirm
</button>
</div>
</div>
</div>
);
};

export default WarningModal;
32 changes: 32 additions & 0 deletions src/components/application/statusHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const getStatusClass = (status: string): string => {
switch (status) {
case "submitted":
return "bg-blue-500 text-white";
case "Shortlisted":
return "bg-teal-500 text-white";
case "English assessment":
return "bg-yellow-500 text-white";
case "Technical assessment":
return "bg-orange-500 text-white";
case "Done Technical assessment":
return "bg-purple-500 text-white";
case "Invited for Home Challenge":
return "bg-teal-500 text-white";
case "Done Home Challenge":
return "bg-indigo-500 text-white";
case "Invited for Interview":
return "bg-pink-500 text-white";
case "Accepted":
return "bg-teal-700 text-white";
case "Rejected":
return "bg-red-500 text-white";
case "Missed English assessment":
case "Missed Technical assessment":
case "Missed Interview":
case "Missed Home Challenge":
return "bg-gray-500 text-white";
default:
return "bg-gray-300 text-black";
}
};

11 changes: 3 additions & 8 deletions src/components/form/SignInForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,18 @@ const LoginForm = () => {
});

const redirectAfterLogin = async () => {
const lastAttemptedRoute = localStorage.getItem('lastAttemptedRoute');
if (lastAttemptedRoute) {
localStorage.removeItem('lastAttemptedRoute');
navigate(lastAttemptedRoute);
} else {
await Token();
const role = localStorage.getItem("roleName") as string;

if (role === "applicant") {
navigate("/applicant");
} else if (role === "superAdmin" || role === "admin") {
} else if (role === "superAdmin" || "Admin") {
navigate("/admin");
} else {
const searchParams = new URLSearchParams(location.search);
const returnUrl = searchParams.get('returnUrl') || '/';
navigate(returnUrl);
}
}
}

const onSubmit = async (data: loginFormData) => {
Expand Down Expand Up @@ -188,7 +183,7 @@ const LoginForm = () => {
</div>
<p className="text-sm mt-3 mb-2 text-[#616161] dark:text-gray-300">
Don't have an account?{" "}
<Link to="/register" className="text-[#56C870]">
<Link to={'/signup'} className="text-[#56C870]">
Sign up
</Link>
</p>
Expand Down
9 changes: 8 additions & 1 deletion src/components/sidebar/navHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const logo: string = require("../../assets/logo.svg").default;
const profile: string = require("../../assets/avatar.png").default;
const LogoWhite: string = require("../../assets/logoWhite.svg").default;
import jwtDecode from "jwt-decode";
import {destination} from '../../utils/utils'

const placeholderImage = profile;

Expand All @@ -20,6 +21,7 @@ const onImageError = (e) => {
}

function NavBar() {
const userDestination = destination();
const access_token = localStorage.getItem("access_token");
//@ts-ignore
const user = access_token ? jwtDecode(access_token).picture : profile;
Expand All @@ -35,7 +37,10 @@ function NavBar() {
const handleShowProfileDropdown = () =>
setShowprofileDropdown(!showProfileDropdown);



return (

<div className="flex items-center dark:bg-zinc-800 ">
{showProfileDropdown && (
<ProfileDropdown
Expand All @@ -59,8 +64,9 @@ function NavBar() {
<IoClose className="w-7 text-9xl dark:text-dark-text-fill" />
)}
</span>

<span>
<Link to="/" className="flex items-center">
<Link to={userDestination} className="flex items-center">
{theme ? (
<img
className="cursor-pointer mx-2 fill-[blue]"
Expand All @@ -79,6 +85,7 @@ function NavBar() {
</h1>
</Link>
</span>

</div>
<div className="flex items-center mr-4">
<span className="flex items-center">
Expand Down
2 changes: 1 addition & 1 deletion src/components/sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const Sidebar = ({ expanded, setExpanded }) => {
</ul>
<button
onClick={handleLogout}
className="flex items-center p-1 font-semibold hover:font-bold text-white focus:outline-none hover:text-[#56c770] mt-4 ml-4 mt-3"
className="flex items-center p-1 font-semibold hover:font-bold text-white focus:outline-none hover:text-[#56c770] mt-4 ml-4"
>
<Icon icon="hugeicons:logout-circle-02" className="mr-3" />
{expanded && <span>Logout</span>}
Expand Down
1 change: 1 addition & 0 deletions src/components/sidebar/sidebarItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const applicantSidebarItems = [
title: "Job Post ",
},
];

export const sidebarItems2 = [
{
path: "/documents",
Expand Down
Loading

0 comments on commit 7df865f

Please sign in to comment.