diff --git a/package.json b/package.json index 0713614..92dfade 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "typeorm:db": "npm run build && npx typeorm -d dist/src/configs/dbConfig.js", "migration:generate": "npm run typeorm:db -- migration:generate", "migration:run": "npm run typeorm:db -- migration:run", + "migration:revert": "npm run typeorm:db -- migration:revert", "sync:db": "npm run typeorm:db schema:sync", "seed": "npm run build && node dist/src/scripts/seed-db.js" }, diff --git a/src/controllers/admin/mentor.controller.ts b/src/controllers/admin/mentor.controller.ts index b0a2b26..4d9e660 100644 --- a/src/controllers/admin/mentor.controller.ts +++ b/src/controllers/admin/mentor.controller.ts @@ -51,6 +51,8 @@ export const updateMentorHandler = async ( const mentorUpdateData: Partial = { ...data } const profileUpdateData: Partial = { ...data.profile } + const categoryId: string = data.categoryId + const countryId: string = data.countryId if (req.file) { profileUpdateData.image_url = `${IMG_HOST}/${req.file.filename}` @@ -61,7 +63,9 @@ export const updateMentorHandler = async ( const { mentor, statusCode, message } = await updateMentorDetails( mentorId, mentorUpdateData, - profileUpdateData + profileUpdateData, + categoryId, + countryId ) return res.status(statusCode).json({ mentor, message }) } catch (err) { diff --git a/src/controllers/mentor.controller.ts b/src/controllers/mentor.controller.ts index 7c93a20..6263015 100644 --- a/src/controllers/mentor.controller.ts +++ b/src/controllers/mentor.controller.ts @@ -18,12 +18,13 @@ export const mentorApplicationHandler = async ( ): Promise> => { try { const user = req.user as Profile - const { application, categoryId } = req.body + const { application, categoryId, countryId } = req.body const { mentor, statusCode, message } = await createMentor( user, application, - categoryId + categoryId, + countryId ) return res.status(statusCode).json({ mentor, message }) diff --git a/src/controllers/monthlyChecking.controller.ts b/src/controllers/monthlyChecking.controller.ts new file mode 100644 index 0000000..bb6ed6e --- /dev/null +++ b/src/controllers/monthlyChecking.controller.ts @@ -0,0 +1,95 @@ +import type { Request, Response } from 'express' +import { type ApiResponse } from '../types' +import type MonthlyCheckIn from '../entities/checkin.entity' + +import { + addFeedbackByMentor, + addMonthlyCheckInByMentee, + fetchMonthlyCheckIns +} from '../services/monthlyChecking.service' + +export const postMonthlyCheckIn = async ( + req: Request, + res: Response +): Promise>> => { + try { + const { + menteeId, + title, + generalUpdatesAndFeedback, + progressTowardsGoals, + mediaContentLinks + } = req.body + + const newCheckIn = await addMonthlyCheckInByMentee( + menteeId, + title, + generalUpdatesAndFeedback, + progressTowardsGoals, + mediaContentLinks + ) + + return res + .status(201) + .json({ checkIn: newCheckIn, message: 'Check-in added successfully' }) + } catch (err) { + if (err instanceof Error) { + console.error('Error executing query', err) + return res + .status(500) + .json({ error: 'Internal server error', message: err.message }) + } + throw err + } +} + +export const getMonthlyCheckIns = async ( + req: Request, + res: Response +): Promise>> => { + try { + const { menteeId } = req.params + + const { statusCode, checkIns, message } = await fetchMonthlyCheckIns( + menteeId + ) + + return res.status(statusCode).json({ checkIns, message }) + } catch (err) { + if (err instanceof Error) { + console.error('Error executing query', err) + return res + .status(500) + .json({ error: 'Internal server error', message: err.message }) + } + throw err + } +} + +export const addFeedbackMonthlyCheckIn = async ( + req: Request, + res: Response +): Promise => { + try { + const { checkInId, menteeId, mentorFeedback, isCheckedByMentor } = req.body + + const newMentorFeedbackCheckIn = await addFeedbackByMentor( + menteeId, + checkInId, + mentorFeedback, + isCheckedByMentor + ) + + res.status(201).json({ + feedbackCheckIn: newMentorFeedbackCheckIn + }) + } catch (err) { + if (err instanceof Error) { + console.error('Error executing query', err) + res + .status(500) + .json({ error: 'Internal server error', message: err.message }) + } + throw err + } +} diff --git a/src/entities/checkin.entity.ts b/src/entities/checkin.entity.ts new file mode 100644 index 0000000..31693ad --- /dev/null +++ b/src/entities/checkin.entity.ts @@ -0,0 +1,63 @@ +import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm' +import BaseEntity from './baseEntity' +import Mentee from './mentee.entity' + +@Entity('monthly-check-in') +class MonthlyCheckIn extends BaseEntity { + @Column({ type: 'text' }) + title: string + + @Column({ type: 'text' }) + generalUpdatesAndFeedback: string + + @Column({ type: 'text' }) + progressTowardsGoals: string + + @Column({ type: 'json' }) + mediaContentLinks: string[] + + @Column({ type: 'text', nullable: true }) + mentorFeedback: string + + @Column({ type: 'boolean', default: false }) + isCheckedByMentor: boolean + + @Column({ type: 'timestamp', nullable: true }) + mentorCheckedDate: Date + + @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) + checkInDate: Date + + @ManyToOne(() => Mentee, (mentee) => mentee.checkIns) + @JoinColumn({ name: 'menteeId' }) + mentee: Mentee + + constructor( + title: string, + generalUpdatesAndFeedback: string, + progressTowardsGoals: string, + mediaContentLinks: string[], + mentorFeedback: string, + isCheckedByMentor: boolean, + mentorCheckedDate: Date, + checkInDate: Date, + mentee: Mentee + ) { + super() + this.title = title + this.generalUpdatesAndFeedback = generalUpdatesAndFeedback + this.progressTowardsGoals = progressTowardsGoals + this.mediaContentLinks = mediaContentLinks + this.mentorFeedback = mentorFeedback + this.isCheckedByMentor = isCheckedByMentor + this.mentorCheckedDate = mentorCheckedDate + this.checkInDate = checkInDate + this.mentee = mentee + } + + validate(): boolean { + return this.mediaContentLinks.length >= 3 + } +} + +export default MonthlyCheckIn diff --git a/src/entities/country.entity.ts b/src/entities/country.entity.ts index 48628c8..810f7d9 100644 --- a/src/entities/country.entity.ts +++ b/src/entities/country.entity.ts @@ -1,5 +1,6 @@ -import { Column, Entity } from 'typeorm' +import { Column, Entity, OneToMany } from 'typeorm' import BaseEntity from './baseEntity' +import Mentor from './mentor.entity' @Entity() export class Country extends BaseEntity { @@ -9,6 +10,9 @@ export class Country extends BaseEntity { @Column() name: string + @OneToMany(() => Mentor, (mentor) => mentor.country) + mentors?: Mentor[] + constructor(code: string, name: string) { super() this.code = code diff --git a/src/entities/mentee.entity.ts b/src/entities/mentee.entity.ts index d2d4813..dbfbf5b 100644 --- a/src/entities/mentee.entity.ts +++ b/src/entities/mentee.entity.ts @@ -1,9 +1,10 @@ -import { Column, Entity, ManyToOne } from 'typeorm' +import { Column, Entity, ManyToOne, OneToMany } from 'typeorm' import Mentor from './mentor.entity' import profileEntity from './profile.entity' import { MenteeApplicationStatus, StatusUpdatedBy } from '../enums' import BaseEntity from './baseEntity' import { UUID } from 'typeorm/driver/mongodb/bson.typings' +import MonthlyCheckIn from './checkin.entity' @Entity('mentee') class Mentee extends BaseEntity { @@ -35,17 +36,22 @@ class Mentee extends BaseEntity { @ManyToOne(() => Mentor, (mentor) => mentor.mentees) mentor: Mentor + @OneToMany(() => MonthlyCheckIn, (checkIn) => checkIn.mentee) + checkIns?: MonthlyCheckIn[] + constructor( state: MenteeApplicationStatus, application: Record, profile: profileEntity, - mentor: Mentor + mentor: Mentor, + checkIns?: MonthlyCheckIn[] ) { super() this.state = state || MenteeApplicationStatus.PENDING this.application = application this.profile = profile this.mentor = mentor + this.checkIns = checkIns } } diff --git a/src/entities/mentor.entity.ts b/src/entities/mentor.entity.ts index cd6ddfa..dd1310c 100644 --- a/src/entities/mentor.entity.ts +++ b/src/entities/mentor.entity.ts @@ -4,6 +4,7 @@ import Mentee from './mentee.entity' import Category from './category.entity' import { MentorApplicationStatus } from '../enums' import BaseEntity from './baseEntity' +import { Country } from './country.entity' @Entity('mentor') class Mentor extends BaseEntity { @@ -28,6 +29,10 @@ class Mentor extends BaseEntity { @JoinColumn() profile: Profile + @ManyToOne(() => Country, (country) => country.mentors) + @JoinColumn() + country: Country + @OneToMany(() => Mentee, (mentee) => mentee.mentor) mentees?: Mentee[] @@ -36,7 +41,8 @@ class Mentor extends BaseEntity { category: Category, application: Record, availability: boolean, - profile: Profile + profile: Profile, + country: Country ) { super() this.state = state @@ -44,6 +50,7 @@ class Mentor extends BaseEntity { this.application = application this.availability = availability this.profile = profile + this.country = country } } diff --git a/src/migrations/1722051742722-RemoveUniqueConstraintFromProfileUuid.ts b/src/migrations/1722051742722-RemoveUniqueConstraintFromProfileUuid.ts index 936370d..03384d7 100644 --- a/src/migrations/1722051742722-RemoveUniqueConstraintFromProfileUuid.ts +++ b/src/migrations/1722051742722-RemoveUniqueConstraintFromProfileUuid.ts @@ -6,14 +6,25 @@ export class RemoveUniqueConstraintFromProfileUuid1722051742722 name = 'RemoveUniqueConstraintFromProfileUuid1722051742722' public async up(queryRunner: QueryRunner): Promise { + // Check if the constraint exists before attempting to drop it + const constraintExists = await queryRunner.query(` + SELECT 1 + FROM information_schema.table_constraints + WHERE constraint_name = 'REL_f671cf2220d1bd0621a1a5e92e' + AND table_name = 'mentee' + `) + + if (constraintExists.length > 0) { + await queryRunner.query( + `ALTER TABLE "mentee" DROP CONSTRAINT "REL_f671cf2220d1bd0621a1a5e92e"` + ) + } + await queryRunner.query( `ALTER TABLE "mentee" DROP CONSTRAINT "FK_f671cf2220d1bd0621a1a5e92e7"` ) await queryRunner.query( - `ALTER TABLE "mentee" DROP CONSTRAINT "REL_f671cf2220d1bd0621a1a5e92e"` - ) - await queryRunner.query( - `ALTER TABLE "mentee" ADD CONSTRAINT "FK_f671cf2220d1bd0621a1a5e92e7" FOREIGN KEY ("profileUuid") REFERENCES "profile"("uuid") ON DELETE NO ACTION ON UPDATE NO ACTION` + `ALTER TABLE "mentee" ADD CONSTRAINT "FK_f671cf2220d1bd0621a1a5e92e7" FOREIGN KEY ("profileUuid") REFERENCES "profile"("uuid") ON DELETE CASCADE ON UPDATE NO ACTION` ) } diff --git a/src/migrations/1726930041488-UpdateMentorTableWithCountry.ts b/src/migrations/1726930041488-UpdateMentorTableWithCountry.ts new file mode 100644 index 0000000..5000549 --- /dev/null +++ b/src/migrations/1726930041488-UpdateMentorTableWithCountry.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class UpdateMentorTableWithCountry1726930041488 + implements MigrationInterface +{ + name = 'UpdateMentorTableWithCountry1726930041488' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "mentor" ADD "countryUuid" uuid`) + await queryRunner.query( + `ALTER TABLE "mentor" ADD CONSTRAINT "FK_3302c22eb1636f239d605eb61c3" FOREIGN KEY ("countryUuid") REFERENCES "country"("uuid") ON DELETE NO ACTION ON UPDATE NO ACTION` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "mentor" DROP CONSTRAINT "FK_3302c22eb1636f239d605eb61c3"` + ) + await queryRunner.query(`ALTER TABLE "mentor" DROP COLUMN "countryUuid"`) + } +} diff --git a/src/migrations/1727197270336-monthly-checking-tags.ts b/src/migrations/1727197270336-monthly-checking-tags.ts new file mode 100644 index 0000000..2a1009e --- /dev/null +++ b/src/migrations/1727197270336-monthly-checking-tags.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MonthlyCheckingTags1727197270336 implements MigrationInterface { + name = 'MonthlyCheckingTags1727197270336' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "monthly-check-in" ADD "tags" json`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "monthly-check-in" DROP COLUMN "tags"`); + } + +} diff --git a/src/migrations/1727636762101-monthlychecking.ts b/src/migrations/1727636762101-monthlychecking.ts new file mode 100644 index 0000000..030e5fb --- /dev/null +++ b/src/migrations/1727636762101-monthlychecking.ts @@ -0,0 +1,13 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm' + +export class Monthlychecking1727636762101 implements MigrationInterface { + name = 'Monthlychecking1727636762101' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "monthly-check-in" DROP COLUMN "tags"`) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "monthly-check-in" ADD "tags" json`) + } +} diff --git a/src/routes/mentee/mentee.route.ts b/src/routes/mentee/mentee.route.ts index 24d255b..bf6e731 100644 --- a/src/routes/mentee/mentee.route.ts +++ b/src/routes/mentee/mentee.route.ts @@ -9,9 +9,16 @@ import { } from '../../controllers/mentee.controller' import { requestBodyValidator } from '../../middlewares/requestValidator' import { + addFeedbackMonthlyCheckInSchema, menteeApplicationSchema, + postMonthlyCheckInSchema, updateMenteeStatusSchema } from '../../schemas/mentee-routes.schemas' +import { + addFeedbackMonthlyCheckIn, + getMonthlyCheckIns, + postMonthlyCheckIn +} from '../../controllers/monthlyChecking.controller' const menteeRouter = express.Router() @@ -29,4 +36,18 @@ menteeRouter.put( ) menteeRouter.put('/revoke-application', requireAuth, revokeApplication) +menteeRouter.post( + '/checkin', + [requireAuth, requestBodyValidator(postMonthlyCheckInSchema)], + postMonthlyCheckIn +) + +menteeRouter.get('/checkin/:menteeId', requireAuth, getMonthlyCheckIns) + +menteeRouter.put( + '/checking/feedback', + [requireAuth, requestBodyValidator(addFeedbackMonthlyCheckInSchema)], + addFeedbackMonthlyCheckIn +) + export default menteeRouter diff --git a/src/schemas/mentee-routes.schemas.ts b/src/schemas/mentee-routes.schemas.ts index 0f6d287..4069319 100644 --- a/src/schemas/mentee-routes.schemas.ts +++ b/src/schemas/mentee-routes.schemas.ts @@ -9,3 +9,37 @@ export const menteeApplicationSchema = z.object({ export const updateMenteeStatusSchema = z.object({ state: z.nativeEnum(MenteeApplicationStatus) }) + +export const postMonthlyCheckInSchema = z.object({ + menteeId: z.string(), + title: z.enum([ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ]), + generalUpdatesAndFeedback: z + .string() + .min(1, 'Please provide a valid feedback'), + progressTowardsGoals: z + .string() + .min(1, 'Please provide a valid progress report'), + mediaContentLinks: z + .array(z.string()) + .min(1, 'Please provide at least 1 media content links') +}) + +export const addFeedbackMonthlyCheckInSchema = z.object({ + menteeId: z.string(), + checkInId: z.string(), + mentorFeedback: z.string().optional(), + isCheckedByMentor: z.boolean() +}) diff --git a/src/schemas/mentor-routes.schema.ts b/src/schemas/mentor-routes.schema.ts index 751bb06..79247fc 100644 --- a/src/schemas/mentor-routes.schema.ts +++ b/src/schemas/mentor-routes.schema.ts @@ -3,7 +3,8 @@ import { MentorApplicationStatus } from '../enums' export const mentorApplicationSchema = z.object({ application: z.record(z.unknown()), - categoryId: z.string() + categoryId: z.string(), + countryId: z.string().optional() }) export const getMenteesByMentorSchema = z.object({ diff --git a/src/services/admin/mentor.service.ts b/src/services/admin/mentor.service.ts index bd2fc85..c078b2d 100644 --- a/src/services/admin/mentor.service.ts +++ b/src/services/admin/mentor.service.ts @@ -1,5 +1,6 @@ import { dataSource } from '../../configs/dbConfig' import Category from '../../entities/category.entity' +import { Country } from '../../entities/country.entity' import Mentor from '../../entities/mentor.entity' import Profile from '../../entities/profile.entity' import type { MentorApplicationStatus } from '../../enums' @@ -59,7 +60,9 @@ export const updateMentorStatus = async ( export const updateMentorDetails = async ( mentorId: string, mentorData: Partial, - profileData?: Partial + profileData?: Partial, + categoryId?: string, + countryId?: string ): Promise<{ statusCode: number mentor?: Mentor | null @@ -69,6 +72,7 @@ export const updateMentorDetails = async ( const mentorRepository = dataSource.getRepository(Mentor) const profileRepository = dataSource.getRepository(Profile) const categoryRepository = dataSource.getRepository(Category) + const countryRepository = dataSource.getRepository(Country) const mentor = await mentorRepository.findOne({ where: { uuid: mentorId }, @@ -86,20 +90,32 @@ export const updateMentorDetails = async ( mentor.availability = mentorData.availability } - if (mentorData.category) { - if (typeof mentorData.category === 'string') { - const category = await categoryRepository.findOne({ - where: { uuid: mentorData.category } - }) - - if (!category) { - return { - statusCode: 404, - message: 'Category not found' - } + if (categoryId) { + const category = await categoryRepository.findOne({ + where: { uuid: categoryId } + }) + + if (!category) { + return { + statusCode: 404, + message: 'Category not found' } - mentor.category = category } + mentor.category = category + } + + if (countryId) { + const country = await countryRepository.findOne({ + where: { uuid: countryId } + }) + + if (!country) { + return { + statusCode: 404, + message: 'Country not found' + } + } + mentor.country = country } // will override values of keys if exisitng keys provided. add new key-value pairs if not exists @@ -138,7 +154,7 @@ export const updateMentorDetails = async ( const updatedMentor = await mentorRepository.findOne({ where: { uuid: mentorId }, - relations: ['profile', 'category'] + relations: ['profile', 'category', 'country'] }) return { diff --git a/src/services/mentor.service.ts b/src/services/mentor.service.ts index dd7f4fa..bded9c3 100644 --- a/src/services/mentor.service.ts +++ b/src/services/mentor.service.ts @@ -1,5 +1,6 @@ import { dataSource } from '../configs/dbConfig' import Category from '../entities/category.entity' +import { Country } from '../entities/country.entity' import Mentee from '../entities/mentee.entity' import Mentor from '../entities/mentor.entity' import type Profile from '../entities/profile.entity' @@ -15,7 +16,8 @@ import { sendEmail } from './admin/email.service' export const createMentor = async ( user: Profile, application: Record, - categoryId: string + categoryId: string, + countryId: string ): Promise<{ statusCode: number mentor?: Mentor | null @@ -25,6 +27,7 @@ export const createMentor = async ( const mentorRepository = dataSource.getRepository(Mentor) const categoryRepository = dataSource.getRepository(Category) const menteeRepository = dataSource.getRepository(Mentee) + const countryRepository = dataSource.getRepository(Country) const mentee = await menteeRepository.findOne({ where: { @@ -57,6 +60,17 @@ export const createMentor = async ( } } + const country = await countryRepository.findOne({ + where: { uuid: countryId } + }) + + if (!country) { + return { + statusCode: 404, + message: 'Country not found' + } + } + for (const mentor of existingMentorApplications) { switch (mentor.state) { case MentorApplicationStatus.PENDING: @@ -86,7 +100,8 @@ export const createMentor = async ( category, application, true, - user + user, + country ) const savedMentor = await mentorRepository.save(newMentor) @@ -241,7 +256,7 @@ export const getAllMentors = async ({ state: MentorApplicationStatus.APPROVED } : { state: MentorApplicationStatus.APPROVED }, - relations: ['profile', 'category'], + relations: ['profile', 'category', 'mentees', 'mentees.profile'], select: ['application', 'uuid', 'availability'], order: { availability: 'DESC' diff --git a/src/services/monthlyChecking.service.ts b/src/services/monthlyChecking.service.ts new file mode 100644 index 0000000..6fe1fc8 --- /dev/null +++ b/src/services/monthlyChecking.service.ts @@ -0,0 +1,131 @@ +import { dataSource } from '../configs/dbConfig' +import MonthlyCheckIn from '../entities/checkin.entity' +import Mentee from '../entities/mentee.entity' +import { type MonthlyCheckInResponse } from '../types' + +export const addFeedbackByMentor = async ( + menteeId: string, + checkInId: string, + mentorfeedback: string, + isCheckedByMentor: boolean +): Promise<{ + statusCode: number + message: string +}> => { + try { + const menteeRepository = dataSource.getRepository(Mentee) + const checkInRepository = dataSource.getRepository(MonthlyCheckIn) + + const mentee = await menteeRepository.findOne({ + where: { uuid: menteeId } + }) + + if (!mentee) { + return { statusCode: 404, message: 'Mentee not found' } + } + + const checkIn = await checkInRepository.findOne({ + where: { uuid: checkInId, mentee: { uuid: menteeId } } + }) + + if (!checkIn) { + return { statusCode: 404, message: 'Check-in not found' } + } + + checkIn.mentorFeedback = mentorfeedback + checkIn.isCheckedByMentor = isCheckedByMentor + checkIn.mentorCheckedDate = new Date() + + await checkInRepository.save(checkIn) + + return { statusCode: 200, message: 'feedback added' } + } catch (err) { + console.error('Error in addFeedbackToMonthlyCheckIn', err) + return { statusCode: 500, message: 'Internal server error' } + } +} + +export const addMonthlyCheckInByMentee = async ( + menteeId: string, + title: string, + generalUpdatesAndFeedback: string, + progressTowardsGoals: string, + mediaContentLinks: string[] +): Promise<{ + statusCode: number + message: string +}> => { + try { + const menteeRepository = dataSource.getRepository(Mentee) + const checkInRepository = dataSource.getRepository(MonthlyCheckIn) + + const mentee = await menteeRepository.findOne({ + where: { + uuid: menteeId + } + }) + + if (!mentee) { + return { statusCode: 404, message: 'Mentee not found' } + } + + const newCheckIn = checkInRepository.create({ + title, + generalUpdatesAndFeedback, + progressTowardsGoals, + mediaContentLinks, + checkInDate: new Date(), + mentee + }) + + await checkInRepository.save(newCheckIn) + + return { statusCode: 200, message: 'monthly checking inserted' } + } catch (err) { + console.error('Error in addMonthlyCheckIn', err) + throw new Error('Error in addMonthlyCheckIn') + } +} + +export const fetchMonthlyCheckIns = async ( + menteeId: string +): Promise<{ + statusCode: number + checkIns: MonthlyCheckInResponse[] + message: string +}> => { + try { + const checkInRepository = dataSource.getRepository(MonthlyCheckIn) + + const mentee = await dataSource.getRepository(Mentee).findOne({ + where: { uuid: menteeId } + }) + + if (!mentee) { + return { statusCode: 404, checkIns: [], message: 'Mentee not found' } + } + + const checkIns = await checkInRepository.find({ + where: { mentee: { uuid: menteeId } }, + relations: ['mentee'], + order: { checkInDate: 'DESC' } + }) + + if (checkIns.length === 0 || !checkIns) { + return { + statusCode: 404, + checkIns: [], + message: 'No check-ins found' + } + } + + return { + statusCode: 200, + checkIns, + message: 'Check-ins found' + } + } catch (err) { + console.error('Error in fetchMonthlyCheckIns', err) + return { statusCode: 500, checkIns: [], message: 'Internal server error' } + } +} diff --git a/src/types.ts b/src/types.ts index b5e27a7..8c39f18 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import type Mentee from './entities/mentee.entity' + export interface ApiResponse { statusCode: number message?: string @@ -32,3 +34,16 @@ export interface LinkedInProfile { picture: string email: string } + +export interface MonthlyCheckInResponse { + uuid: string + title: string + generalUpdatesAndFeedback: string + progressTowardsGoals: string + mediaContentLinks: string[] + mentorFeedback: string | null + isCheckedByMentor: boolean + mentorCheckedDate: Date | null + checkInDate: Date + mentee: Mentee +} diff --git a/src/utils.ts b/src/utils.ts index 51d1a0f..cb90429 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -34,12 +34,24 @@ export const getMentorPublicData = (mentor: Mentor): Mentor => { delete profile.created_at delete profile.updated_at + let appliedMenteesCount = 0 + let availableMenteeSlots = mentor.application.noOfMentees as number + if (mentor.mentees) { + appliedMenteesCount = mentor.mentees.length + let approvedMenteesCount = 0 + mentor.mentees = mentor.mentees.map((mentee) => { + if (mentee.state === MenteeApplicationStatus.APPROVED) { + approvedMenteesCount++ + } return getMenteePublicData(mentee) }) - } + availableMenteeSlots -= approvedMenteesCount + } + mentor.application.appliedMenteesCount = appliedMenteesCount + mentor.application.availableMenteeSlots = availableMenteeSlots return mentor }