Skip to content

Commit 55875e4

Browse files
berniceuceelogredean-darylBahatiuwituzeb
authored
Ft shareable job post (#139)
* fix: remove placeholder property * Ft():shareable job post page * Feature: Adding shareable job post buttons * Feature: Adding shareable job post buttons * Feature: Adding shareable job post buttons * fix codeclimate issue * add gmail and fix telegram link * add gmail sharing * fix url sharing * fix url * fix url * fix codeclimate * applicant share application --------- Co-authored-by: ceelogre <[email protected]> Co-authored-by: dean-daryl <[email protected]> Co-authored-by: Bahati <[email protected]> Co-authored-by: uwituzeb <[email protected]>
1 parent 6ce7983 commit 55875e4

File tree

11 files changed

+17812
-110
lines changed

11 files changed

+17812
-110
lines changed

src/components/form/SignInForm.tsx

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import InputField from "./InputField";
77
import Button from "./Button";
88
import { zodResolver } from "@hookform/resolvers/zod";
99
import { loginFormData } from "../validation/login";
10-
import { Link, useNavigate } from "react-router-dom";
10+
import { Link, useNavigate, useLocation } from "react-router-dom";
1111
import { toast } from "react-toastify";
1212
import { GraphQLClient } from "graphql-request";
1313
import { loginAction } from "../../redux/actions/login";
14-
import { Token } from "../../utils/utils";
14+
import { Token } from '../../utils/utils';
15+
16+
const googleIcn: string = require("../../assets/assets/googleIcon.jpg").default;
1517

1618
const MY_QUERY = `
1719
{
@@ -38,6 +40,7 @@ const LoginForm = () => {
3840
const [isLoading, setIsLoading] = useState(false);
3941
const [showPassword, setShowPassword] = useState(false);
4042
const navigate = useNavigate();
43+
const location = useLocation();
4144

4245
const {
4346
register,
@@ -47,6 +50,26 @@ const LoginForm = () => {
4750
resolver: zodResolver(loginSchema),
4851
});
4952

53+
const redirectAfterLogin = async () => {
54+
const lastAttemptedRoute = localStorage.getItem('lastAttemptedRoute');
55+
if (lastAttemptedRoute) {
56+
localStorage.removeItem('lastAttemptedRoute');
57+
navigate(lastAttemptedRoute);
58+
} else {
59+
await Token();
60+
const role = localStorage.getItem("roleName") as string;
61+
if (role === "applicant") {
62+
navigate("/applicant");
63+
} else if (role === "superAdmin") {
64+
navigate("/admin");
65+
} else {
66+
const searchParams = new URLSearchParams(location.search);
67+
const returnUrl = searchParams.get('returnUrl') || '/';
68+
navigate(returnUrl);
69+
}
70+
}
71+
}
72+
5073
const onSubmit = async (data: loginFormData) => {
5174
setIsLoading(true);
5275
try {
@@ -56,15 +79,7 @@ const LoginForm = () => {
5679
const token = response?.data?.data?.login?.token;
5780
if (token) {
5881
localStorage.setItem("access_token", token);
59-
await Token()
60-
const role = localStorage.getItem("roleName") as string;
61-
if(role === "applicant"){
62-
navigate("/applicant")
63-
}
64-
else if (role === "superAdmin") {
65-
navigate("/admin");
66-
}
67-
toast.success("Logged in successfully!");
82+
await redirectAfterLogin();
6883
} else {
6984
toast.error(response?.data?.errors[0].message);
7085
}
@@ -188,3 +203,4 @@ const LoginForm = () => {
188203
};
189204

190205
export default LoginForm;
206+

src/pages/Applicant/ApplicantLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const ApplicantLayout = () => {
1919
setExpanded={setSidebarExpanded}
2020
/>
2121
</div>
22-
<main className="flex w-[100%] justify-center items-center flex-1">
22+
<main className="flex w-[100%] justify-center items-center flex-1 dark:bg-dark-frame-bg">
2323
<Outlet />
2424
</main>
2525
</div>

src/pages/JobPost/job.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ const Jobs = (props: any) => {
386386
</li>
387387
<li>
388388
<Link
389-
to={`/Job/Post/${item.id}`}
389+
to={`/admin/Job/Post/${item.id}`}
390390
className="text-sm hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-500 block px-4 py-2"
391391
>
392392
View
@@ -477,7 +477,7 @@ const Jobs = (props: any) => {
477477
Edit
478478
</Link>
479479
<Link
480-
to={`/Job/Post/${item.id}`}
480+
to={`/admin/Job/Post/${item.id}`}
481481
className="text-white bg-green border border-solid border-green rounded-md px-2 text-xs"
482482
>
483483
View

src/pages/JobPost/viewSingleJob.tsx

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,56 @@
1-
import { useParams } from 'react-router';
2-
import NavBar from '../../components/sidebar/navHeader';
3-
import { BsFillPersonLinesFill } from 'react-icons/bs';
4-
import React, { useEffect, useState } from 'react';
5-
import { fetchSingleJobPost } from '../../redux/actions/fetchSingleJobPostAction';
6-
import { useAppDispatch, useAppSelector } from '../../hooks/hooks';
7-
import { connect } from 'react-redux';
1+
import { useParams } from "react-router";
2+
import NavBar from "../../components/sidebar/navHeader";
3+
import { BsFillPersonLinesFill } from "react-icons/bs";
4+
import React, { useEffect, useState } from "react";
5+
import { fetchSingleJobPost } from "../../redux/actions/fetchSingleJobPostAction";
6+
import { useAppDispatch, useAppSelector } from "../../hooks/hooks";
7+
import { connect } from "react-redux";
8+
import { FaLinkedin, FaTelegram, FaTwitter, FaWhatsapp, FaEnvelope } from "react-icons/fa";
9+
import { FcGoogle } from 'react-icons/fc';
810

911
const SingleJobPostDetails = (props: any) => {
1012
const { fetchSingleJobPostStates } = props;
11-
console.log('fetchSingleJobPostStates:', fetchSingleJobPostStates);
1213
const dispatch = useAppDispatch();
1314
const params = useParams();
1415
const [jobPostId, setjobPostId] = useState(params.id);
1516

1617
useEffect(() => {
1718
dispatch(fetchSingleJobPost(jobPostId));
1819
}, [jobPostId]);
20+
21+
const shareMessage = `Check out this job opportunity: ${fetchSingleJobPostStates?.data?.title}\n${window.location.href}`;
22+
23+
const shareOnTwitterDM = () => {
24+
const url = `https://twitter.com/messages/compose?text=${encodeURIComponent(shareMessage)}`;
25+
window.open(url, "_blank", "width=600,height=400");
26+
};
27+
28+
const shareOnWhatsApp = () => {
29+
const url = `https://api.whatsapp.com/send?text=${encodeURIComponent(shareMessage)}`;
30+
window.open(url, "_blank", "width=600,height=400");
31+
};
32+
33+
const shareOnTelegram = () => {
34+
const url = `https://t.me/share/url?url=${encodeURIComponent(window.location.href)}`;
35+
window.open(url, "_blank", "width=600,height=400");
36+
};
37+
38+
const shareOnLinkedIn = () => {
39+
const url = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(window.location.href)}`;
40+
window.open(url, "_blank");
41+
};
42+
43+
const shareOnGmail = () => {
44+
const subject = encodeURIComponent("Interesting Job Opportunity");
45+
const body = encodeURIComponent(shareMessage);
46+
const url = `https://mail.google.com/mail/?view=cm&fs=1&to=&su=${subject}&body=${body}`;
47+
window.open(url, "_blank");
48+
}
49+
1950
return (
2051
<>
21-
<div className="h-screen flex flex-col items-center dark:bg-dark-frame-bg">
22-
<div className="flex flex-col justify-start mt-24 items-start p-5 w-[95%] lg:w-1/2 md_:mx-auto overflow-hidden dark:bg-dark-bg">
52+
<div className="min-h-screen flex flex-col items-center dark:bg-dark-frame-bg">
53+
<div className="flex flex-col justify-start mt-24 items-start p-5 w-[95%] md_:mx-auto overflow-hidden dark:bg-dark-bg">
2354
<h2 className="text-white font-bold my-5">
2455
<BsFillPersonLinesFill className="float-left m-1" />
2556
Job Post information
@@ -29,46 +60,47 @@ const SingleJobPostDetails = (props: any) => {
2960
<>
3061
<div className="flex flex-col">
3162
<h3 className="text-white">Job title</h3>
32-
<p className="text-gray-500 text-sm dark:text-gray-400">
33-
{fetchSingleJobPostStates.data.title}
34-
</p>
63+
<p className="text-gray-500 text-sm dark:text-gray-400">{fetchSingleJobPostStates.data.title}</p>
3564
</div>
3665
<div className="flex flex-col">
3766
<h3 className="text-white">Program</h3>
38-
<p className="text-gray-500 text-sm dark:text-gray-400">
39-
{fetchSingleJobPostStates.data.program.title}
40-
</p>
67+
<p className="text-gray-500 text-sm dark:text-gray-400">{fetchSingleJobPostStates.data.program.title}</p>
4168
</div>
4269
<div className="flex flex-col">
4370
<h3 className="text-white">Cycle</h3>
44-
<p className="text-gray-500 text-sm dark:text-gray-400">
45-
{fetchSingleJobPostStates.data.cycle.name}
46-
</p>
71+
<p className="text-gray-500 text-sm dark:text-gray-400">{fetchSingleJobPostStates.data.cycle.name}</p>
4772
</div>
4873
<div className="flex flex-col">
4974
<h3 className="text-white">Cohort</h3>
50-
<p className="text-gray-500 text-sm dark:text-gray-400">
51-
{fetchSingleJobPostStates.data.cohort.title}
52-
</p>
75+
<p className="text-gray-500 text-sm dark:text-gray-400">{fetchSingleJobPostStates.data.cohort.title}</p>
5376
</div>
5477
<div className="flex flex-col">
5578
<h3 className="text-white">Program description</h3>
56-
<p className="text-gray-500 text-sm dark:text-gray-400">
57-
{fetchSingleJobPostStates.data.description}
58-
</p>
79+
<p className="text-gray-500 text-sm dark:text-gray-400">{fetchSingleJobPostStates.data.description}</p>
80+
</div>
81+
<div className="text-white">Share Job Post</div>
82+
<div className="flex fle gap-4 mt-6">
83+
<button onClick={shareOnTwitterDM} className="flex items-center gap-2 px-4 py-2 bg-blue-400 text-white rounded hover:bg-blue-500 transition-colors">
84+
<FaTwitter />
85+
</button>
86+
<button onClick={shareOnWhatsApp} className="flex items-center gap-2 px-4 py-2 bg-green text-white rounded hover:bg-green-600 transition-colors">
87+
<FaWhatsapp />
88+
</button>
89+
<button onClick={shareOnTelegram} className="flex items-center gap-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors">
90+
<FaTelegram />
91+
</button>
92+
<button onClick={shareOnLinkedIn} className="flex items-center gap-2 px-4 py-2 bg-blue-800 text-white rounded hover:bg-blue-900 transition-colors">
93+
<FaLinkedin />
94+
</button>
95+
<button onClick={shareOnGmail} className="flex items-center justify-center gap-2 px-4 py-2 bg-white text-gray-700 rounded hover:bg-gray-100 transition-colors">
96+
<FcGoogle size={20} />
97+
</button>
5998
</div>
60-
<button
61-
type="submit"
62-
className="flex justify-self-start self-start rounded w-15 px-5 py-2 mt-10 bg-green ml-2 sm:ml-80 text-white transition-colors hover:bg-dark-frame-bg hover:text-green hover:border hover:border-green"
63-
>
64-
Share Post
65-
</button>
6699
</>
67100
)}
68101
</div>
69102
</div>
70-
</div>
71-
103+
</div>
72104
</>
73105
);
74106
};

src/pages/PrivateRoute.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
import { Navigate } from "react-router-dom";
1+
import { Navigate, useLocation } from "react-router-dom";
22
import { Token } from "../utils/utils";
3+
import { useEffect } from "react";
34

45
const PrivateRoute = ({ children }) => {
56
const access_token = Token();
7+
const location = useLocation();
68
const user =
79
//@ts-ignore
810
access_token !== null && access_token !== undefined && access_token !== '';
11+
12+
useEffect(() => {
13+
if(!user){
14+
localStorage.setItem('lastAttemptedRoute', location.pathname);
15+
}
16+
}, [user, location])
917
return user ? children : <Navigate to="/login" />;
1018
};
1119

20+
1221
export default PrivateRoute;

src/pages/ShareApplication.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useParams } from 'react-router';
2+
import NavBar from '../components/sidebar/navHeader';
3+
import React, { useEffect } from 'react';
4+
import { fetchSingleJobPost } from '../redux/actions/fetchSingleJobPostAction';
5+
import { connect, useDispatch } from 'react-redux';
6+
import { useAppSelector } from 'hooks/hooks';
7+
import { Link } from 'react-router-dom';
8+
9+
type Props = {};
10+
11+
const ShareApplication = (props: any) => {
12+
const { fetchSingleJobPostStates, updateJobPostStates } = props;
13+
const dispatch = useDispatch();
14+
const { id } = useParams();
15+
useEffect(() => {
16+
dispatch(fetchSingleJobPost(id));
17+
}, [id]);
18+
return (
19+
<div className="flex flex-col h-screen absolute w-[100%]">
20+
<div className="flex flex-row">
21+
<div className="w-full">
22+
<div>
23+
<div className="bg-light-bg dark:bg-dark-frame-bg min-h-screen overflow-y-hidden overflow-x-hidden flex justify-center ">
24+
<div className="flex flex-col w-[60%] dark:bg-dark-tertiary mt-[7rem] mb-[5rem] rounded-lg p-5 md:ml-0 md:w-[90%] ">
25+
<div className="flex justify-center">
26+
<p className="text-white font-semibold underline font-size-10">{fetchSingleJobPostStates?.data?.title}</p>
27+
</div>
28+
<div className="flex justify-start width-[80%] ml-3 mt-5">
29+
<p className="text-white font-sans">{fetchSingleJobPostStates?.data?.description}</p>
30+
</div>
31+
<div className="flex flex-col justify-start width-[80%] ml-5 mt-5 ">
32+
<p className="text-white font-semibold">Here are the requirements:</p>
33+
<ul className="list-disc ml-5">
34+
{fetchSingleJobPostStates?.data?.program?.requirements.map(
35+
(item: any) => (
36+
<li className="text-white font-sans">{item}</li>
37+
),
38+
)}
39+
</ul>
40+
</div>
41+
<div className="flex justify-center mt-5">
42+
<Link to={`/jobPost/${id}/apply`}>
43+
<button className="flex bg-primary dark:bg-[#56C870] rounded-md py-2 px-4 text-white font-medium cursor-pointer">Apply</button>
44+
</Link>
45+
</div>
46+
</div>
47+
</div>
48+
</div>
49+
</div>
50+
</div>
51+
</div>
52+
);
53+
};
54+
55+
// export default ShareApplication;
56+
57+
const mapState = (state: any) => ({
58+
fetchSingleJobPostStates: state.fetchSingleJobPost,
59+
});
60+
61+
export default connect(mapState, {
62+
fetchSingleJobPost,
63+
})(ShareApplication);

0 commit comments

Comments
 (0)