diff --git a/frontend/src/components/AvailabilitiesTable.jsx b/frontend/src/components/AvailabilitiesTable.jsx
index 2b31f64..e5d53a9 100644
--- a/frontend/src/components/AvailabilitiesTable.jsx
+++ b/frontend/src/components/AvailabilitiesTable.jsx
@@ -38,8 +38,8 @@ function AvailabilitiesTable({ authUser }) {
startDate: moment("2024-11-18T09:45:00").format(),
endDate: moment("2024-11-18T10:30:00").format(),
interview: {
- status: "published",
availabilityId: 0,
+ howManyInterviewers: 3,
interviewers: [
{
firstName: "Federico",
@@ -60,7 +60,6 @@ function AvailabilitiesTable({ authUser }) {
role: "",
},
],
- applicant: null, // Slot not taken
},
},
{
@@ -68,8 +67,8 @@ function AvailabilitiesTable({ authUser }) {
startDate: moment("2024-11-20T09:45:00").format(),
endDate: moment("2024-11-20T10:30:00").format(),
interview: {
- status: "booked",
availabilityId: 1,
+ howManyInterviewers: 3,
interviewers: [
{
firstName: "Federico",
@@ -90,11 +89,6 @@ function AvailabilitiesTable({ authUser }) {
role: "",
},
],
- applicant: {
- firstName: "Claudio",
- lastName: "Neri",
- email: "claudio.neri@hknpolito.org",
- },
},
},
{
@@ -102,8 +96,8 @@ function AvailabilitiesTable({ authUser }) {
startDate: moment("2024-11-21T09:45:00").format(),
endDate: moment("2024-11-21T10:30:00").format(),
interview: {
- status: "draft",
availabilityId: 2,
+ howManyInterviewers: 3,
interviewers: [
{
firstName: "Federico",
@@ -124,7 +118,6 @@ function AvailabilitiesTable({ authUser }) {
role: "",
},
],
- applicant: null,
},
},
{
@@ -132,8 +125,8 @@ function AvailabilitiesTable({ authUser }) {
startDate: moment("2024-11-22T09:45:00").format(),
endDate: moment("2024-11-22T10:30:00").format(),
interview: {
- status: "draft",
availabilityId: 3,
+ howManyInterviewers: 3,
interviewers: [
{
firstName: "Federico",
@@ -154,12 +147,15 @@ function AvailabilitiesTable({ authUser }) {
role: "",
},
],
- applicant: null,
},
},
];
const handleAddAvailability = (timeSlotStart, timeSlotEnd) => {
+ if (timeSlotStart.isBefore(minDate) || timeSlotStart.isAfter(maxDate)) {
+ console.log("Date out of range. Cannot add availability.");
+ return;
+ }
console.log(
`Adding availability for ${timeSlotStart.format(
"dddd D/MM/YYYY"
@@ -175,7 +171,7 @@ function AvailabilitiesTable({ authUser }) {
endDate: timeSlotEnd.format(),
interview: {
availabilityId: id, // Unique ID for the availability
- status: "draft",
+ howManyInterviewers: 1,
interviewers: [
{
firstName: authUser.given_name,
@@ -189,7 +185,6 @@ function AvailabilitiesTable({ authUser }) {
: "", // Enforce role presence
},
],
- applicant: null, // Slot not taken initially
},
};
@@ -204,6 +199,7 @@ function AvailabilitiesTable({ authUser }) {
...a,
interview: {
...a.interview,
+ howManyInterviewers: a.interview.howManyInterviewers + 1,
interviewers: [
...a.interview.interviewers,
{
@@ -220,74 +216,22 @@ function AvailabilitiesTable({ authUser }) {
setAvailabilities(updatedAvailabilities);
};
- const handleRemoveAvailability = (availabilityId) => {
- console.log(`Removing availability with id: ${availabilityId}`);
- setAvailabilities(
- availabilities.filter((a) => a.availabilityId !== availabilityId)
- );
- };
-
- const handleUnpublish = (availabilityId) => {
- console.log(`Unpublishing availability with ID ${availabilityId}`);
-
- const updatedAvailabilities = availabilities.map((availability) =>
- availability.availabilityId === availabilityId
- ? {
- ...availability,
- interview: {
- ...availability.interview,
- status: "draft", // Change the status to "draft"
- },
- }
- : availability
- );
-
- setAvailabilities(updatedAvailabilities);
- };
-
- const handleRemoveApplicant = (availabilityId) => {
- console.log(
- `Removing applicant from availability with id: ${availabilityId}`
- );
- const updatedAvailabilities = availabilities.map((a) =>
- a.availabilityId === availabilityId
- ? {
- ...a,
- interview: {
- ...a.interview,
- applicant: null, // Remove the applicant
- status: "published", // Update status to "published"
- },
- }
- : a
- );
- setAvailabilities(updatedAvailabilities);
- };
-
const renderInterview = (interview) => {
const isAuthUserInterviewer = interview.interviewers.some(
(interviewer) => interviewer.email === authUser.email
);
- const getBackgroundColor = () => {
- if (interview.status === "booked") {
- return "#f8d7da"; // Red: Applicant present
- } else if (interview.status === "published") {
- return "#d1e7dd"; // Green: published
- } else {
- return "#fff3cd"; // Yellow: draft
- }
- };
return (
- {interview.interviewers &&
+ {isSupervisor &&
+ interview.interviewers &&
interview.interviewers.map((interviewer) => {
if (interviewer.role === "board") {
return (
@@ -313,6 +257,12 @@ function AvailabilitiesTable({ authUser }) {
}
return null; // If the role is unexpected, render nothing
})}
+ {interview.howManyInterviewers && (
+
+ In this slot {`${interview.howManyInterviewers}`} interviewers have
+ given availability.
+
+ )}
{interview.applicant && (
@@ -321,25 +271,12 @@ function AvailabilitiesTable({ authUser }) {
)}
- {isAuthUserInterviewer && interview.applicant && (
- {}}>
-
-
- )}
- {isSupervisor && (
- handleRemoveAvailability(interview.availabilityId)}
- >
-
-
- )}
{isSupervisor && (
{}}>
)}
- {isAuthUserInterviewer && interview.status === "draft" && (
+ {isAuthUserInterviewer && (
@@ -352,7 +289,7 @@ function AvailabilitiesTable({ authUser }) {
My availability
)}
- {!isAuthUserInterviewer && interview.status === "draft" && (
+ {!isAuthUserInterviewer && (
handleAddInterviewer(interview.availabilityId)}
@@ -360,59 +297,13 @@ function AvailabilitiesTable({ authUser }) {
Join interview
)}
- {isSupervisor && interview.applicant && (
- <>
- handleRemoveApplicant(interview.availabilityId)}
- >
- Applicant
-
- >
- )}
-
- {isSupervisor && interview.status === "published" && (
- handleUnpublish(interview.availabilityId)}
- >
- Unpublish
-
- )}
- {isSupervisor && interview.status === "draft" && (
- handleMakePublic(interview.availabilityId)}
- >
- Publish
-
- )}
);
};
- const handleMakePublic = (availabilityId) => {
- console.log(`Making availability with ID ${availabilityId} public`);
-
- // Update the status of the selected availability
- const updatedAvailabilities = availabilities.map((availability) =>
- availability.availabilityId === availabilityId
- ? {
- ...availability,
- interview: {
- ...availability.interview,
- status: "published", // Set the status to "published"
- },
- }
- : availability
- );
-
- setAvailabilities(updatedAvailabilities); // Update the state
- };
-
const handleRemoveInterviewer = (availabilityId, email) => {
- console.log(`Removing interviewer with email: ${(authUser, email)}`);
+ console.log(`Removing interviewer with email: ${email}`);
const updatedAvailabilities = availabilities
.map((availability) => {
@@ -427,11 +318,13 @@ function AvailabilitiesTable({ authUser }) {
return null;
}
- // Otherwise, return the updated availability
+ // Otherwise, decrement howManyInterviewers and return the updated availability
return {
...availability,
interview: {
...availability.interview,
+ howManyInterviewers:
+ availability.interview.howManyInterviewers - 1,
interviewers: updatedInterviewers,
},
};
@@ -444,6 +337,9 @@ function AvailabilitiesTable({ authUser }) {
setAvailabilities(updatedAvailabilities);
};
+ const minDate = moment("2024-11-01"); // Earliest date to display
+ const maxDate = moment("2024-11-30"); // Latest date to display
+
return (
@@ -467,12 +363,22 @@ function AvailabilitiesTable({ authUser }) {
: "Switch to Supervisor View"}
+ Start of the recruitment session:
+ {minDate.format("dddd D/MM/YYYY")}
+
+ End of the recruitment session:
+ {maxDate.format("dddd D/MM/YYYY")}
+
setStartDate(startDate.clone().subtract(1, "weeks"))
}
+ disabled={startDate
+ .clone()
+ .subtract(1, "weeks")
+ .isBefore(minDate, "week")}
>
Previous Week
@@ -483,6 +389,10 @@ function AvailabilitiesTable({ authUser }) {
setStartDate(startDate.clone().add(1, "weeks"))}
+ disabled={startDate
+ .clone()
+ .add(1, "weeks")
+ .isAfter(maxDate, "week")}
>
Next Week
@@ -518,6 +428,13 @@ function AvailabilitiesTable({ authUser }) {
)} - ${rowTimeSlotEnd.format("HH:mm")}`}
{Array.from({ length: 5 }).map((_, j) => {
const day = startDate.clone().add(j, "days");
+
+ if (
+ day.isBefore(minDate, "day") ||
+ day.isAfter(maxDate, "day")
+ ) {
+ return ; // Empty cell for out-of-range dates
+ }
const timeSlotStart = day
.clone()
.startOf("day")
diff --git a/frontend/src/pages/AvailabilitiesPage.jsx b/frontend/src/pages/AvailabilitiesPage.jsx
index 8831cbc..3258232 100644
--- a/frontend/src/pages/AvailabilitiesPage.jsx
+++ b/frontend/src/pages/AvailabilitiesPage.jsx
@@ -25,60 +25,6 @@ function AvailabilitiesPage() {
Availabilities
This page will be used by members to manage their availabilities.
- {/* Legend */}
-
-
-
- Legend:
-
-
-
- Green slots: The slot is visible to the
- applicants but it is not taken yet. Applicants can apply for and
- interviewers cannot anymore assign themselves to these slots.
-
-
-
- Yellow slots: The slot is a draft and is not
- visible to the applicants. Interviewers can assign themselves to
- these slots.
-
-
-
- Red slots: The slot is taken by an applicant.
-
-
-
-
-
-
>
);
diff --git a/package.json b/package.json
index 4d14467..11f4bd0 100644
--- a/package.json
+++ b/package.json
@@ -22,5 +22,8 @@
},
"keywords": [],
"author": "",
- "license": "ISC"
+ "license": "ISC",
+ "dependencies": {
+ "@mui/icons-material": "^6.1.7"
+ }
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 35a9a5a..49b88e4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,7 +6,11 @@ settings:
importers:
- .: {}
+ .:
+ dependencies:
+ '@mui/icons-material':
+ specifier: ^6.1.7
+ version: 6.1.7(@mui/material@6.1.7(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
api:
dependencies:
@@ -938,6 +942,17 @@ packages:
'@mui/core-downloads-tracker@6.1.7':
resolution: {integrity: sha512-POuIBi80BZBogQkG4PQKIGwy4QFwB+kOr+OI4k7Znh7LqMAIhwB9OC00l6M+w1GrZJYj3T8R5WX8G6QAIvoVEw==}
+ '@mui/icons-material@6.1.7':
+ resolution: {integrity: sha512-RGzkeHNArIVy5ZQ12bq/8VYNeICEyngngsFskTJ/2hYKhIeIII3iRGtaZaSvLpXh7h3Fg3VKTulT+QU0w5K4XQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@mui/material': ^6.1.7
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@mui/material@6.1.7':
resolution: {integrity: sha512-KsjujQL/A2hLd1PV3QboF+W6SSL5QqH6ZlSuQoeYz9r69+TnyBFIevbYLxdjJcJmGBjigL5pfpn7hTGop+vhSg==}
engines: {node: '>=14.0.0'}
@@ -5013,7 +5028,7 @@ snapshots:
'@emotion/babel-plugin@11.12.0':
dependencies:
'@babel/helper-module-imports': 7.24.3
- '@babel/runtime': 7.24.5
+ '@babel/runtime': 7.26.0
'@emotion/hash': 0.9.2
'@emotion/memoize': 0.9.0
'@emotion/serialize': 1.3.2
@@ -5474,6 +5489,14 @@ snapshots:
'@mui/core-downloads-tracker@6.1.7': {}
+ '@mui/icons-material@6.1.7(@mui/material@6.1.7(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@mui/material': 6.1.7(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.2
+
'@mui/material@6.1.7(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.26.0
@@ -5738,7 +5761,7 @@ snapshots:
'@restart/ui@1.6.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@babel/runtime': 7.24.5
+ '@babel/runtime': 7.26.0
'@popperjs/core': 2.11.8
'@react-aria/ssr': 3.9.3(react@18.3.1)
'@restart/hooks': 0.4.16(react@18.3.1)
@@ -6386,7 +6409,7 @@ snapshots:
babel-plugin-macros@3.1.0:
dependencies:
- '@babel/runtime': 7.24.5
+ '@babel/runtime': 7.26.0
cosmiconfig: 7.1.0
resolve: 1.22.8
@@ -6774,7 +6797,7 @@ snapshots:
date-fns@2.30.0:
dependencies:
- '@babel/runtime': 7.24.5
+ '@babel/runtime': 7.26.0
debug@2.6.9:
dependencies:
@@ -6846,7 +6869,7 @@ snapshots:
dom-helpers@5.2.1:
dependencies:
- '@babel/runtime': 7.24.5
+ '@babel/runtime': 7.26.0
csstype: 3.1.3
dom-serializer@1.4.1:
@@ -9560,7 +9583,7 @@ snapshots:
uncontrollable@7.2.1(react@18.3.1):
dependencies:
- '@babel/runtime': 7.24.5
+ '@babel/runtime': 7.26.0
'@types/react': 18.3.2
invariant: 2.2.4
react: 18.3.1