From 423f95d50c5ceb160f9b69d8bf596cc34155f62f Mon Sep 17 00:00:00 2001 From: Muhammad Zhafran Ilham Date: Wed, 1 Jul 2026 20:53:28 +0700 Subject: [PATCH] feat(project): menambahkan endpoint assignable interns untuk admin atau mentor --- .../project/controllers/project.controller.js | 28 +++++++ .../repositories/project.repository.js | 32 ++++++++ .../project/services/project.service.js | 36 +++++++++ src/routes/project.routes.js | 76 +++++++++++++++++++ 4 files changed, 172 insertions(+) diff --git a/src/modules/project/controllers/project.controller.js b/src/modules/project/controllers/project.controller.js index 17f63fa..19a677a 100644 --- a/src/modules/project/controllers/project.controller.js +++ b/src/modules/project/controllers/project.controller.js @@ -247,6 +247,34 @@ class ProjectController { } } + async getAssignableInterns(req, res) { + try { + const actor = getActor(req); + + const result = await projectService.getAssignableInterns(actor); + + if (result.err) { + return response(res, "fail", result); + } + + return response( + res, + "success", + result, + "Assignable interns retrieved successfully", + SUCCESS.OK + ); + } catch (err) { + return response( + res, + "fail", + error(new InternalServerError(err.message)), + "Unexpected error occurred", + ERROR.INTERNAL_ERROR + ); + } + } + async assignMember(req, res) { try { const actor = getActor(req); diff --git a/src/modules/project/repositories/project.repository.js b/src/modules/project/repositories/project.repository.js index e18b641..5644875 100644 --- a/src/modules/project/repositories/project.repository.js +++ b/src/modules/project/repositories/project.repository.js @@ -404,6 +404,38 @@ class ProjectRepository { }); } + async findAssignableInterns() { + return prisma.user.findMany({ + where: { + role: "intern", + is_active: true, + project_members: { + none: { + status: "active", + project: { + status: "active", + }, + }, + }, + }, + orderBy: { + full_name: "asc", + }, + include: { + lamaran_magang: { + include: { + lowongan_magang: { + select: { + posisi: true, + kelompok_peminatan: true, + }, + }, + }, + }, + }, + }); + } + async assignMember(id_project, id_user) { return prisma.projectMember.create({ data: { diff --git a/src/modules/project/services/project.service.js b/src/modules/project/services/project.service.js index 4aa6f53..52de8b6 100644 --- a/src/modules/project/services/project.service.js +++ b/src/modules/project/services/project.service.js @@ -559,6 +559,42 @@ class ProjectService { } } + async getAssignableInterns(actor = {}) { + try { + if (!actor?.id) { + return error(new BadRequestError("Admin ID is required")); + } + + if (actor.role !== "admin" && actor.role !== "mentor") { + return error( + new ForbiddenError("Access denied: only mentors/admins can view assignable interns") + ); + } + + const interns = await projectRepository.findAssignableInterns(); + + const mappedInterns = interns.map((intern) => { + const lowongan = intern.lamaran_magang?.lowongan_magang; + + return { + id: intern.id, + full_name: intern.full_name, + email: intern.email, + professional_bio: intern.professional_bio || null, + position: lowongan?.posisi || intern.intern_position || null, + kelompok_peminatan: + lowongan?.kelompok_peminatan || intern.intern_interest_group || null, + is_active: intern.is_active, + is_assignable: true, + }; + }); + + return data(mappedInterns); + } catch (err) { + return error(err); + } + } + async assignMember(payload, actor = {}) { try { if (!actor?.id) { diff --git a/src/routes/project.routes.js b/src/routes/project.routes.js index f5ac657..7762a8e 100644 --- a/src/routes/project.routes.js +++ b/src/routes/project.routes.js @@ -481,6 +481,52 @@ const router = express.Router(); * code: * type: integer * example: 417 + * AssignableInternItem: + * type: object + * properties: + * id: + * type: integer + * example: 4 + * full_name: + * type: string + * example: Dina Pratama + * email: + * type: string + * example: dina@student.com + * professional_bio: + * type: string + * nullable: true + * example: Backend developer intern + * position: + * type: string + * nullable: true + * example: Web Developer + * kelompok_peminatan: + * type: string + * nullable: true + * example: Software Engineering + * is_active: + * type: boolean + * example: true + * is_assignable: + * type: boolean + * example: true + * AssignableInternListResponse: + * type: object + * properties: + * status: + * type: boolean + * example: true + * data: + * type: array + * items: + * $ref: '#/components/schemas/AssignableInternItem' + * message: + * type: string + * example: Assignable interns retrieved successfully + * code: + * type: integer + * example: 200 */ /** @@ -818,6 +864,36 @@ router.get('/mentor-projects', verifyJWT, isMentorOrAdmin, projectController.get */ router.get('/interns', verifyJWT, isMentorOrAdmin, projectController.getInterns); +/** + * @swagger + * /project-api/assignable-interns: + * get: + * summary: Get assignable interns + * description: Retrieve active intern accounts that can be assigned to a project. Interns are assignable if they do not have an active membership in any active project. Accessible only by admin/mentor. + * tags: [Project] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: Assignable interns retrieved successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/AssignableInternListResponse' + * 401: + * description: Unauthorized + * 403: + * description: Forbidden. Only admin/mentor can access this endpoint. + * 500: + * description: Internal server error + */ +router.get( + "/assignable-interns", + verifyJWT, + isMentorOrAdmin, + projectController.getAssignableInterns +); + /** * @swagger * /project-api/assign-member: