From 40d85e5d867c8ec97e90bf1b17812afc5f3dbe2f Mon Sep 17 00:00:00 2001 From: Rayhan Fadhlan Date: Mon, 16 Dec 2024 19:05:52 +0700 Subject: [PATCH 1/3] feat : competition timeline --- src/controllers/competition.controller.ts | 19 ++++++++++ src/controllers/team.controller.ts | 2 ++ src/repositories/competition.repository.ts | 31 +++++++++++++++- src/routes/competition.route.ts | 42 ++++++++++++++++++++++ src/types/competition.type.ts | 7 +++- 5 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/controllers/competition.controller.ts b/src/controllers/competition.controller.ts index 05aa779..d293b79 100644 --- a/src/controllers/competition.controller.ts +++ b/src/controllers/competition.controller.ts @@ -5,11 +5,15 @@ import { getAnnouncementsByCompetitionId, getCompetition, getCompetitionParticipant, + getCompetitionTimelines, + getCompetitionTImelinesByCompetitionId, postAnnouncement, } from '~/repositories/competition.repository'; import { getAdminCompAnnouncementRoute, getCompetitionParticipantRoute, + getCompetitionTimeLineByCompetitionIdRoute, + getCompetitionTimelineRoute, postAdminCompAnnouncementRoute, } from '~/routes/competition.route'; @@ -79,3 +83,18 @@ competitionProtectedRouter.openapi( return c.json(announcement, 200); }, ); + +competitionProtectedRouter.openapi(getCompetitionTimelineRoute, async (c) => { + + const user = c.get('user'); + const userId = user.id; + const timelines = await getCompetitionTimelines(db, userId); + return c.json(timelines, 200); +}); + +competitionProtectedRouter.openapi(getCompetitionTimeLineByCompetitionIdRoute, async (c) => { + + const { competitionId } = c.req.valid('param'); + const timelines = await getCompetitionTImelinesByCompetitionId(db, competitionId); + return c.json(timelines, 200); +}); \ No newline at end of file diff --git a/src/controllers/team.controller.ts b/src/controllers/team.controller.ts index d99bee0..f980bcd 100644 --- a/src/controllers/team.controller.ts +++ b/src/controllers/team.controller.ts @@ -3,6 +3,7 @@ import { roleMiddleware } from '~/middlewares/role-access.middleware'; import { changeTeamName, createTeam, + deleteTeam, deleteTeamMember, getTeamById, getTeamsByCompetitionId, @@ -15,6 +16,7 @@ import { getTeamCompetitionRoute, getTeamDetailRoute, postCreateTeamRoute, + postQuitTeamRoute, postTeamDocumentRoute, postTeamVerificationRoute, putChangeTeamNameRoute, diff --git a/src/repositories/competition.repository.ts b/src/repositories/competition.repository.ts index 0a9f072..e480b91 100644 --- a/src/repositories/competition.repository.ts +++ b/src/repositories/competition.repository.ts @@ -1,6 +1,6 @@ import { eq } from 'drizzle-orm'; import type { Database } from '../db/drizzle'; -import { competition, competitionAnnouncement, team } from '../db/schema'; +import { competition, competitionAnnouncement, competitionTimeline, team, teamMember } from '../db/schema'; import type { PostCompAnnouncementBodySchema } from '~/types/competition.type'; import type { z } from 'zod'; import { first } from '~/db/helper'; @@ -96,3 +96,32 @@ export const postAnnouncement = async ( .returning() .then(first); }; + +export const getCompetitionTimelines = async ( + db: Database, + userId: string +) => { + // Get user's competitions + const userTeams = await db.query.team.findMany({ + where: eq(teamMember.userId, userId), + with: { + competition: { + with: { + timeline: true + } + } + } + }); + + return userTeams.flatMap(team => team.competition.timeline); +}; + +export const getCompetitionTImelinesByCompetitionId = async ( + db: Database, + competitionId: string +) => { + const result = await db.query.competitionTimeline.findMany({ + where: eq(competitionTimeline.competitionId, competitionId), + }); + return result; +} \ No newline at end of file diff --git a/src/routes/competition.route.ts b/src/routes/competition.route.ts index eaab15e..f39e405 100644 --- a/src/routes/competition.route.ts +++ b/src/routes/competition.route.ts @@ -2,6 +2,7 @@ import { createRoute } from '@hono/zod-openapi'; import { AnnouncementSchema, CompetitionParticipantSchema, + CompetitionTimelineSchema, GetCompetitionTimeQuerySchema, PostCompAnnouncementBodySchema, } from '~/types/competition.type'; @@ -83,3 +84,44 @@ export const postAdminCompAnnouncementRoute = createRoute({ 500: createErrorResponse('GENERIC', 'Internal server error'), }, }); + +export const getCompetitionTimelineRoute = createRoute({ + operationId: 'getCompetitionTimeline', + tags: ['competition'], + method: 'get', + path: '/competition/timeline', + responses: { + 200: { + content: { + 'application/json': { + schema: CompetitionTimelineSchema + } + }, + description: 'Successfully fetched competition timelines' + }, + 400: createErrorResponse('UNION', 'Bad request error'), + 500: createErrorResponse('GENERIC', 'Internal server error') + } +}) + +export const getCompetitionTimeLineByCompetitionIdRoute = createRoute({ + operationId: 'getCompetitionTimelineWithCompetitionId', + tags: ['competition'], + method: 'get', + path: '/competition/{competitionId}/timeline', + request: { + params: CompetitionIdParam, + }, + responses: { + 200: { + content: { + 'application/json': { + schema: CompetitionTimelineSchema, + }, + }, + description: 'Successfully fetched competition timelines', + }, + 400: createErrorResponse('UNION', 'Bad request error'), + 500: createErrorResponse('GENERIC', 'Internal server error'), + }, +}); diff --git a/src/types/competition.type.ts b/src/types/competition.type.ts index f5dd616..b5d2840 100644 --- a/src/types/competition.type.ts +++ b/src/types/competition.type.ts @@ -1,6 +1,6 @@ import { createInsertSchema, createSelectSchema } from 'drizzle-zod'; import { z } from 'zod'; -import { competitionAnnouncement } from '~/db/schema'; +import { competitionAnnouncement, competitionTimeline } from '~/db/schema'; import { TeamSchema } from './team.type'; export const AnnouncementSchema = createSelectSchema(competitionAnnouncement, { @@ -56,3 +56,8 @@ export const GetCompetitionTimeQuerySchema = z.object({ }, }), }); + + +export const CompetitionTimelineSchema = z.array( + createSelectSchema(competitionTimeline).openapi("CompetitionTimeline") +); \ No newline at end of file From 5f5726d549724356891978a252f2665174742ae8 Mon Sep 17 00:00:00 2001 From: Rayhan Fadhlan Date: Mon, 16 Dec 2024 19:10:22 +0700 Subject: [PATCH 2/3] fix : admin list team add admin middleware --- src/controllers/team.controller.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/controllers/team.controller.ts b/src/controllers/team.controller.ts index f980bcd..66d85ee 100644 --- a/src/controllers/team.controller.ts +++ b/src/controllers/team.controller.ts @@ -175,6 +175,11 @@ teamProtectedRouter.openapi(postTeamDocumentRoute, async (c) => { return c.json(updatedTeam, 200); }); +teamProtectedRouter.get( + getTeamCompetitionRoute.getRoutingPath(), + roleMiddleware('admin'), +); + teamProtectedRouter.openapi(getTeamCompetitionRoute, async (c) => { const { competitionId } = c.req.valid('param'); const teams = await getTeamsByCompetitionId(db, competitionId); @@ -182,6 +187,11 @@ teamProtectedRouter.openapi(getTeamCompetitionRoute, async (c) => { return c.json(teams, 200); }); +teamProtectedRouter.get( + getTeamDetailRoute.getRoutingPath(), + roleMiddleware('admin'), +); + teamProtectedRouter.openapi(getTeamDetailRoute, async (c) => { const { competitionId, teamId } = c.req.valid('param'); const team = await getTeamById(db, teamId, { teamMember: true }); From d7a7796143878374d6b8115e94a979688a389d34 Mon Sep 17 00:00:00 2001 From: Valentino Triadi <13522164@std.stei.itb.ac.id> Date: Tue, 24 Dec 2024 11:19:12 +0700 Subject: [PATCH 3/3] style: change function name --- src/controllers/competition.controller.ts | 4 ++-- src/repositories/competition.repository.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/competition.controller.ts b/src/controllers/competition.controller.ts index d293b79..320cfaa 100644 --- a/src/controllers/competition.controller.ts +++ b/src/controllers/competition.controller.ts @@ -6,7 +6,7 @@ import { getCompetition, getCompetitionParticipant, getCompetitionTimelines, - getCompetitionTImelinesByCompetitionId, + getCompetitionTimelinesByCompetitionId, postAnnouncement, } from '~/repositories/competition.repository'; import { @@ -95,6 +95,6 @@ competitionProtectedRouter.openapi(getCompetitionTimelineRoute, async (c) => { competitionProtectedRouter.openapi(getCompetitionTimeLineByCompetitionIdRoute, async (c) => { const { competitionId } = c.req.valid('param'); - const timelines = await getCompetitionTImelinesByCompetitionId(db, competitionId); + const timelines = await getCompetitionTimelinesByCompetitionId(db, competitionId); return c.json(timelines, 200); }); \ No newline at end of file diff --git a/src/repositories/competition.repository.ts b/src/repositories/competition.repository.ts index e480b91..1f45b28 100644 --- a/src/repositories/competition.repository.ts +++ b/src/repositories/competition.repository.ts @@ -116,7 +116,7 @@ export const getCompetitionTimelines = async ( return userTeams.flatMap(team => team.competition.timeline); }; -export const getCompetitionTImelinesByCompetitionId = async ( +export const getCompetitionTimelinesByCompetitionId = async ( db: Database, competitionId: string ) => {