Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implment Monthly-Checking API Endpoint #172

Merged
merged 26 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
19b6e96
Merge pull request #4 from sef-global/main
mayura-andrew Jul 14, 2024
cfd59e8
Merge branch 'sef-global:main' into main
mayura-andrew Jul 28, 2024
3604289
Merge branch 'sef-global:main' into main
mayura-andrew Jul 31, 2024
2cdce35
Merge branch 'sef-global:main' into main
mayura-andrew Aug 3, 2024
350d485
Merge branch 'sef-global:main' into main
mayura-andrew Aug 10, 2024
adda905
Merge branch 'sef-global:main' into main
mayura-andrew Aug 17, 2024
5013738
Merge branch 'sef-global:main' into main
mayura-andrew Aug 30, 2024
996760f
Merge branch 'sef-global:main' into main
mayura-andrew Sep 4, 2024
1401096
Merge branch 'sef-global:main' into main
mayura-andrew Sep 8, 2024
c0af853
Merge branch 'sef-global:main' into main
mayura-andrew Sep 10, 2024
b01ef18
Merge branch 'sef-global:main' into main
mayura-andrew Sep 17, 2024
574f92f
Refactor Mentee entity to include MonthlyCheckIn relationship
mayura-andrew Sep 17, 2024
a9631d3
Refactor Mentee entity to include MonthlyCheckIn relationship
mayura-andrew Sep 18, 2024
dc1f126
Refactor Mentee entity to include MonthlyCheckIn relationship
mayura-andrew Sep 18, 2024
cab216e
Refactor Mentee service to include MonthlyCheckIn relationship
mayura-andrew Sep 18, 2024
5c8383f
Refactor Mentee service to include MonthlyCheckIn relationship
mayura-andrew Sep 20, 2024
f27ed70
Merge branch 'sef-global:main' into main
mayura-andrew Sep 23, 2024
7681a50
Added tags column into monthly-checking-in table
mayura-andrew Sep 24, 2024
37e63cb
Refactor Mentee service to include MonthlyCheckIn feedback functionality
mayura-andrew Sep 24, 2024
b878909
Refactor Mentee service to include MonthlyCheckIn feedback functionality
mayura-andrew Sep 27, 2024
d31bb0e
Refactor Mentee service to include MonthlyCheckIn feedback functionality
mayura-andrew Sep 27, 2024
d738745
Refactor Mentee service to remove unnecessary code
mayura-andrew Sep 27, 2024
368a7f9
Refactor: Separate Monthly Checking Services and Controllers
mayura-andrew Sep 29, 2024
cedd5cd
removed sudo file
mayura-andrew Sep 29, 2024
87a7e02
Refactor: Make mentor feedback optional in addFeedbackMonthlyCheckInS…
mayura-andrew Sep 29, 2024
7d6395c
Refactor MonthlyChecking service to include MonthlyCheckInResponse type
mayura-andrew Oct 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
95 changes: 95 additions & 0 deletions src/controllers/monthlyChecking.controller.ts
Original file line number Diff line number Diff line change
@@ -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<Response<ApiResponse<MonthlyCheckIn>>> => {
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<Response<ApiResponse<MonthlyCheckIn>>> => {
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<void> => {
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
}
}
63 changes: 63 additions & 0 deletions src/entities/checkin.entity.ts
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions src/entities/mentee.entity.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<string, unknown>,
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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ export class RemoveUniqueConstraintFromProfileUuid1722051742722
name = 'RemoveUniqueConstraintFromProfileUuid1722051742722'

public async up(queryRunner: QueryRunner): Promise<void> {
// 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`
)
}

Expand Down
14 changes: 14 additions & 0 deletions src/migrations/1727197270336-monthly-checking-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class MonthlyCheckingTags1727197270336 implements MigrationInterface {
name = 'MonthlyCheckingTags1727197270336'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monthly-check-in" ADD "tags" json`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "monthly-check-in" DROP COLUMN "tags"`);
}

}
21 changes: 21 additions & 0 deletions src/routes/mentee/mentee.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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
34 changes: 34 additions & 0 deletions src/schemas/mentee-routes.schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
Loading
Loading