Skip to content
85 changes: 85 additions & 0 deletions backend/src/models/availability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import mongoose, { Document, Schema } from "mongoose";

export interface ITimeSlot {
startTime: Date,
endTime: Date,
}

export enum DayOfTheWeek {
MON = "Monday",
TUE = "Tuesday",
WED = "Wednesday",
THU = "Thursday",
FRI = "Friday",
SAT = "Saturday",
SUN = "Sunday",
}

/**
* Represents the times of availability for a specific user on a given day of the week.
* @property userId - the user that defined this availability
* @property timeslots - the sections of the day where this User is available
* @property dayOfWeek - the day of the week that the availability pertains to
* @property isRecurring - whether the specified availability is recurring
* @property createdAt - the date this Availability was created
* @property updatedAt - the date this Availability was last updated
*/
export interface IAvailability extends Document {
userId: Schema.Types.ObjectId,
timeslots: ITimeSlot[],
dayOfWeek: DayOfTheWeek,
isRecurring: boolean,
createdAt: Date,
updatedAt: Date,
}

export const TimeSlotSchema: Schema = new Schema(
{
startTime: {
type: Date,
required: [true, "A start time is required"],
index: true,

},
endTime: {
type: Date,
required: [true, "An end time is required"],
index: true,

},
}
)

export const AvailabilitySchema: Schema = new Schema(
{
userId: {
type: Schema.Types.ObjectId,
required: [true, "User ID is required"],
},
timeslots: {
type: [TimeSlotSchema],
required: true,
default: []
},
dayOfWeek: {
type: String,
enum: Object.values(DayOfTheWeek),
required: [true, "A day of the week must be specified"],
},
isRecurring: {
type: Boolean,
required: true,
default: false,
}
},
{
timestamps: true,
},
);

AvailabilitySchema.index({userId: 1});
AvailabilitySchema.index({dayOfWeek: 1});

const Availability = mongoose.model<IAvailability>("Availability", AvailabilitySchema);

export default Availability;
52 changes: 52 additions & 0 deletions backend/src/models/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import mongoose, { Document, Schema } from "mongoose";

/**
* Represents a Group of Interviewers and Candidates
* @property name - the name of the group
* @property members - an array of references to some Users
* @property teamId - a reference to the Team for which this group is for
* @property createdBy - a reference to the User who created the group
* @property createdAt - the Date of creation
* @property updatedAt - the last time this was updated
*/
export interface IGroup extends Document {
name: string,
members: Schema.Types.ObjectId[],
teamId: Schema.Types.ObjectId,
createdBy: Schema.Types.ObjectId,
createdAt: Date,
updatedAt: Date
}

const GroupSchema: Schema = new Schema(
{
name: {
type: String,
required: [true, "A group name is required"],
trim: true,
},
members: {
type: [Schema.Types.ObjectId],
ref: "User",
required: true,
default: [],
},
teamId: {
type: Schema.Types.ObjectId,
ref: "Team",
required: [true, "A team reference is required"],
},
createdBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: [true, "The creator of the group must be specified"],
},
},
{
timestamps: true,
},
);

const Group = mongoose.model<IGroup>("Group", GroupSchema);

export default Group;
102 changes: 102 additions & 0 deletions backend/src/models/meeting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import mongoose, { Document, Schema, Types } from "mongoose";

export enum MeetingStatus {
SCHEDULED = "scheduled",
CONFIRMED = "confirmed",
RESCHEDULED = "rescheduled",
CANCELLED = "cancelled"
}
/**
* Represents a Meeting instance for a specific Candidate
* @property title - the title of the meeting
* @property description - a description of the meeting's purpose
* @property startTime - the scheduled start time of the meeting
* @property endTime - the scheduled end time of the meeting
* @property interviewerIds - an array of the ids of the assigned interviewers
* @property candidateId - the id of the Candidate
* @property teamId - the Team of which this meeting is held by
* @property status - the status of the meeting
* @property link - the link to the online meeting
* @property createdBy - a reference to the User who created the meeting
* @property createdAt - the time this Meeting was created
* @property updatedAt - the last time this Meeting was updated
*/
export interface IMeeting extends Document {
title: string,
description?: string,
startTime: Date,
endTime: Date,
interviewerIds: Schema.Types.ObjectId[],
candidateId: Schema.Types.ObjectId,
teamId: Schema.Types.ObjectId,
status: MeetingStatus,
link?: string,
createdBy: Schema.Types.ObjectId,
createdAt: Date,
updatedAt: Date,
}

const MeetingSchema: Schema = new Schema(
{
title: {
type: String,
required: [true, "Meeting title is required"],
trim: true,
},
description: {
type: String,
required: false
},
startTime: {
type: Date,
required: [true, "A start time is required"],
},
endTime: {
type: Date,
required: [true, "An end time is required"],
},
interviewerIds: {
type: [Schema.Types.ObjectId],
ref: "User",
required: true,
validate: {
validator: (arr: Schema.Types.ObjectId[]) => arr.length > 0,
message: "At least one interviewer is required"
}
},
candidateId: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
teamId: {
type: Schema.Types.ObjectId,
ref: "Team",
required: true,
},
status: {
type: String,
enum: Object.values(MeetingStatus),
default: MeetingStatus.SCHEDULED,
},
link: {
type: String,
required: false
},
createdBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: [true, "The meeting creator must be specified"]
}
},
{
timestamps: true,
},
);

MeetingSchema.index({start: 1, end: 1});
MeetingSchema.index({status: 1, createdBy: 1});

const Meeting = mongoose.model<IMeeting>("Meeting", MeetingSchema);

export default Meeting;
51 changes: 51 additions & 0 deletions backend/src/models/team.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import mongoose, { Document, Schema } from "mongoose";

/**
* Represents a Team in the app
* @property name - The name of the team
* @property description - A description of the team
* @property adminId - The id of the User who is the admin of the team
* @property isActive - Whether the Team is active or not
* @property createdAt - the date that the Team was created
* @property updatedAt - the date the Team was last updated on
*/
export interface ITeam extends Document {
name: string,
description?: string,
adminId: Schema.Types.ObjectId;
isActive: boolean,
createdAt: Date,
updatedAt: Date
}

const TeamSchema: Schema = new Schema(
{
name: {
type: String,
required: [true, "A team name is required."],
trim: true,
},
description: {
type: String,
required: false,
},
adminId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: [true, "A team admin is required."],
},
isActive: {
type: Boolean,
default: true,
},
},
{
timestamps: true,
}
);

const Team = mongoose.model<ITeam>("Team", TeamSchema);

TeamSchema.index({adminId: 1});

export default Team;
88 changes: 88 additions & 0 deletions backend/src/models/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import mongoose, { Document, Schema } from "mongoose";

export enum UserRole {
ADMIN = "admin",
CANDIDATE = "candidate",
INTERVIEWER = "interviewer",
}


/**
* Represents a User of the app
* @property name - Full name of the user
* @property email - Email of the user
* @property password - The hashed password of the user
* @property teamId - The id of the Team this user belongs to
* @property role - the Users assigned role
* @property groupIds - Array of ids representing the Groups this user belongs to
* @property lastLogin - The date of this user's last login
* @property createdAt - The date this user object was created
* @property updatedAt - The date this user object was last updated
*/
export interface IUser extends Document {
name: string;
email: string;
password: string;
teamId?: Schema.Types.ObjectId;
role?: UserRole,
groupIds: Schema.Types.ObjectId[];
lastLogin?: Date;
createdAt: Date; // Auto-managed by Mongoose
updatedAt: Date; // Auto-managed by Mongoose
}

const UserSchema: Schema = new Schema(
{
name: {
type: String,
required: [true, "A name is required."],
trim: true
},
email: {
type: String,
required: [true, "An email is required."],
trim: true,
lowercase: true,
match: [/[a-z0-9]+@[a-z]+\.[a-z]{2,3}/, "Invalid email address"]
},
password: {
type: String,
required: [true, "A password is required."],
minLength: [8, "Password must be at least 8 characters long."],
},
teamId: {
type: Schema.Types.ObjectId,
required: false,
ref: "Team",
},
role: {
type: Object.values(UserRole),
required: false,
},
groupIds: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Group"
}
],
required: true,
default: []
},
lastLogin: {
type: Date,
required: false,
}
},
{
timestamps: true // Automatically adds and updates createdAt and updatedAt
}
);

UserSchema.index({name: 1, email: 1});
UserSchema.index({role: 1, teamId: 1});


const User = mongoose.model<IUser>("User", UserSchema);

export default User;