Skip to content

Commit

Permalink
Schedule interview page
Browse files Browse the repository at this point in the history
  • Loading branch information
pradnya-barve committed Jan 2, 2024
2 parents 7f856ac + 8596748 commit e9c946e
Show file tree
Hide file tree
Showing 15 changed files with 572 additions and 115 deletions.
17 changes: 15 additions & 2 deletions client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900&display=swap" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900&display=swap"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand All @@ -25,7 +28,17 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Accountill- Free Invoicing App for Freelancers and Small Businesses</title>
<title>Aiinterviewer</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css
"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js
"
/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
2 changes: 2 additions & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Homepage from './components/Homepage/Homepage';
import RoleSelect from './components/RoleSelect/RoleSelect';
import VideosdkMeeting from './components/VideosdkMeeting/VideosdkMeeting';
import Schedule from './components/Schedule/Schedule';
import SeeScheduledInterviews from './components/SeeScheduledInterviews/SeeScheduledInterviews';

function App() {

Expand All @@ -28,6 +29,7 @@ function App() {
<Route path="/login" element={<Login/>} />
<Route path="/homepage" element={<Homepage/>} />
<Route path="/profile" element={<Profile/>} />
<Route path="/scheduledinterviews" element={<SeeScheduledInterviews/>} />
<Route path="/forgot" element={<Forgot/>} />
<Route path="/reset/:token" element={<Reset/>} />
<Route path="/select" element={<RoleSelect/>} />
Expand Down
27 changes: 25 additions & 2 deletions client/src/actions/interviews.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import { LIST_MEETINGS, GET_MEETING, SCHEDULE_MEETING } from './constants';
import { GET_INTERVIEWS, START_LOADING, LIST_MEETINGS, GET_MEETING, SCHEDULE_MEETING } from './constants.js';
import * as api from '../api/index.js';

export const getInterviewsCandidate = (id) => async (dispatch) => {
try {
dispatch({ type: START_LOADING })
const { data } = await api.getInterviewsCandidate(id);
return data;
} catch (error) {
console.log(error.response);
return error;
}
};

export const getInterviewsHR = (id) => async (dispatch) => {
try {
dispatch({ type: START_LOADING })
const { data } = await api.getInterviewsHR(id);

return data;
} catch (error) {
console.log(error.response);
return error;
}
};

export const listMeetings = () => async (dispatch) => {
try {
const { data } = await api.listMeetings();
Expand All @@ -26,4 +49,4 @@ export const scheduleMeeting = (meetingData) => async (dispatch) => {
} catch (error) {
console.log(error);
}
};
};
4 changes: 3 additions & 1 deletion client/src/actions/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export const getProfile = (id) => async (dispatch) => {
const { data } = await api.fetchProfile(id);


dispatch({ type: FETCH_PROFILE, payload: data });
// dispatch({ type: FETCH_PROFILE, payload: data });
// dispatch({ type: END_LOADING })

return data;

} catch (error) {
console.log(error.response);
} };
Expand Down
3 changes: 3 additions & 0 deletions client/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export const createProfile = (newProfile) => API.post('/profiles', newProfile);
export const updateProfile = (id, updatedProfile) => API.patch(`/profiles`, updatedProfile);
export const deleteProfile = (id) => API.delete(`/profiles/${id}`);

export const getInterviewsCandidate = (id) => API.get(`/interviews/candidate/${id}`);
export const getInterviewsHR = (id) => API.get(`/interviews/hr/${id}`);

export const listMeetings = () => API.get('/meetings');
export const getMeeting = (id) => API.get(`/meetings/${id}`);
// export const scheduleMeeting = (meetingData) => API.post('/schedule', meetingData);
Expand Down
10 changes: 1 addition & 9 deletions client/src/components/Homepage/Homepage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import Footer from "../Footer/Footer";
import { decode } from "jsonwebtoken";
import { useState, useEffect } from "react";

let prevUserToken = null;

const Homepage = () => {
const navigate = useNavigate();
const user = JSON.parse(localStorage.getItem("profile"));
Expand All @@ -20,7 +18,6 @@ const Homepage = () => {

useEffect(() => {
const checkUserRole = async () => {
// console.log(user.token)
try {
const decodedToken = decode(user.token);
if (decodedToken) {
Expand All @@ -34,14 +31,9 @@ const Homepage = () => {
setUserRole("");
}
};

// Check if the user data has changed before making the request
if (user && user.token !== prevUserToken) {
if (user) {
checkUserRole();
}

// Update the previous user token for the next comparison
prevUserToken = user.token;
}, [user]);

return (
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/NavBar/NavBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function NavBar({ userRole }) {
className="navbar-btn"
type="button"
disabled={isButtonDisabled}
onClick={handleChatBtnClick}
onClick={() => navigate("/scheduledinterviews")}
>
<FontAwesomeIcon icon={faCalendarDays} /> See scheduled interviews
</button>
Expand Down
158 changes: 158 additions & 0 deletions client/src/components/SeeScheduledInterviews/SeeScheduledInterviews.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React, { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
import { decode } from "jsonwebtoken";
import { useEffect } from "react";
import styles from "./SeeScheduledInterviews.module.css";
import { useSnackbar } from "react-simple-snackbar";
import { useDispatch } from "react-redux";
import {
getInterviewsCandidate,
getInterviewsHR,
} from "../../actions/interviews";

const SeeScheduledInterviews = () => {
const navigate = useNavigate();
const user = JSON.parse(localStorage.getItem("profile"));
const [userRole, setUserRole] = useState("");
const [openSnackbar, closeSnackbar] = useSnackbar();
const dispatch = useDispatch();
const [interviews, setInterviews] = useState([]);
const [dataFetched, setDataFetched] = useState(false);

const checkUserRole = async () => {
try {
const decodedToken = decode(user.token);
if (decodedToken) {
const userRole = decodedToken.role;
setUserRole(userRole);
} else {
setUserRole("");
}
return decodedToken.id;
} catch (error) {
console.error("Error fetching user profile:", error);
setUserRole("");
}
};

const fetchData = async (id) => {
try {
let response = null;

if (userRole === "candidate") {
response = await dispatch(getInterviewsCandidate(id));
} else if (userRole === "hr") {
response = await dispatch(getInterviewsHR(id));
}

if (response && response[0]) {
// Sort interviews by date and time before setting the state
const sortedInterviews = response.sort((a, b) => {
// Compare start dates
const dateComparison = new Date(a.startDate) - new Date(b.startDate);
if (dateComparison !== 0) {
return dateComparison;
}

// If start dates are equal, compare start times
return a.startTime.localeCompare(b.startTime);
});

setInterviews(sortedInterviews);
setDataFetched(true);
} else if (!response[0]) {
console.error("No interviews found");
} else {
console.error("Failed to get interviews:", response.statusText);
}
} catch (error) {
console.error("Error getting interviews:", error.message);
}
};

useEffect(() => {
if (!user) {
navigate("/login");
}

let id = null;

if (user) {
id = checkUserRole();
}

if (id && interviews.length === 0 && !dataFetched) {
fetchData(id);
}
}, [user, interviews, dataFetched]);

// Function to parse time string (HH:MM) and create a Date object
const parseTimeString = (timeString, startDate) => {
const [hours, minutes] = timeString.split(':');
const date = new Date(startDate);
date.setHours(parseInt(hours, 10));
date.setMinutes(parseInt(minutes, 10));
date.setSeconds(0);

return date;
};

// Function to check if the current time is between start and end time
const isTimeBetween = (startTime, endTime, startDate) => {
const now = new Date();
const start = parseTimeString(startTime, startDate);
const end = parseTimeString(endTime, startDate);
return start <= now && now <= end;
};

return (
<div className={styles.scheduled_interviews_container}>
<h2>Scheduled Interviews</h2>

{interviews.length === 0 ? (
<p>No interviews scheduled.</p>
) : (
<table className={styles["interview-table"]}>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Date</th>
<th>Time</th>
{userRole === "candidate" ? <th>HR</th> : null}
{userRole === "hr" ? <th>Candidate</th> : null}
<th>Status</th>
<th>Join</th>
</tr>
</thead>
<tbody>
{interviews.map((interview) => (
<tr key={interview._id}>
<td>{interview.title}</td>
<td>{interview.description}</td>
<td>{new Date(interview.startDate).toLocaleDateString()}</td>
<td>{`${interview.startTime} - ${interview.endTime}`}</td>
{userRole === "candidate" ? <td>{interview.hrName}</td> : null}
{userRole === "hr" ? <td>{interview.candidateName}</td> : null}
<td>{interview.status}</td>
{userRole === "candidate" && isTimeBetween(interview.startTime, interview.endTime, interview.startDate) ? (
<button className="btn btn-success btn-sm" style={{margin: "5px", marginTop: "10px"}}>Join Interview</button>
) : <button className="btn btn-success btn-sm disabled" style={{margin: "5px", marginTop: "10px"}}>Join Interview</button>}
</tr>
))}
</tbody>
</table>
)}
<Link to={"/homepage"}>
<button className="btn btn-secondary" style={{marginRight: "20px", marginTop: "10px"}} >Back</button>
</Link>
{/* TODO change the route to schedule page */}
{userRole==="hr" && <Link to={"/homepage"}>
<button className="btn btn-primary"style={{marginTop: "10px"}}>Schedule Interview</button>
</Link>}

</div>
);
};

export default SeeScheduledInterviews;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* SeeScheduledInterviews.module.css */

.scheduled_interviews_container {
max-width: 800px;
margin: 20px auto;
}

.interview-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}

.interview-table th,
.interview-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}

.interview-table th {
background-color: #f2f2f2;
}

.join_button {
background-color: #007bff;
color: #fff;
border: none;
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
}

.back_button {
background-color: #396491;
color: #fff;
border: none;
padding: 10px;
border-radius: 5px;
font-size: 16px;
}
Loading

0 comments on commit e9c946e

Please sign in to comment.