diff --git a/src/modules/project/repositories/project.repository.js b/src/modules/project/repositories/project.repository.js index 5b941cf..e2c989c 100644 --- a/src/modules/project/repositories/project.repository.js +++ b/src/modules/project/repositories/project.repository.js @@ -390,6 +390,32 @@ class ProjectRepository { data: { status }, }); } + + async findActiveMembershipsByUserIds(userIds) { + return prisma.projectMember.findMany({ + where: { + id_user: { + in: userIds.map((id) => parseInt(id)), + }, + status: "active", + }, + include: { + user: { + select: { + id: true, + full_name: true, + email: true, + }, + }, + project: { + select: { + id: true, + project_name: true, + }, + }, + }, + }); + } } module.exports = new ProjectRepository(); diff --git a/src/modules/project/services/project.service.js b/src/modules/project/services/project.service.js index d1775d1..a5c64a0 100644 --- a/src/modules/project/services/project.service.js +++ b/src/modules/project/services/project.service.js @@ -58,6 +58,27 @@ class ProjectService { } } + const userIds = users.map((user) => user.id); + + if (userIds.length > 0) { + const activeMemberships = await projectRepository.findActiveMembershipsByUserIds(userIds); + + if (activeMemberships.length > 0) { + const assignedInterns = activeMemberships.map((membership) => { + const email = membership.user?.email || "unknown email"; + const projectName = membership.project?.project_name || "Unknown Project"; + + return `${email} already assigned to ${projectName}`; + }); + + return error( + new ConflictError( + `One or more interns already have an active project: ${assignedInterns.join(", ")}` + ) + ); + } + } + const newProjectData = { id_admin: parseInt(actor.id), project_icon: projectData.project_icon, @@ -69,7 +90,6 @@ class ProjectService { status: "active", }; - const userIds = users.map((user) => user.id); const project = await projectRepository.createWithMembers( newProjectData, userIds, @@ -441,7 +461,7 @@ class ProjectService { const activeMembership = intern.project_members?.[0] || null; const projectName = activeMembership?.project?.project_name || "Unassigned"; const role = intern.lamaran_magang?.lowongan_magang?.posisi || "-"; - + return { id: intern.id, name: intern.full_name, diff --git a/src/routes/project.routes.js b/src/routes/project.routes.js index 49655f9..a695391 100644 --- a/src/routes/project.routes.js +++ b/src/routes/project.routes.js @@ -74,6 +74,7 @@ const router = express.Router(); * example: 2026-08-10 * member_emails: * type: array + * description: Optional intern emails to invite. Each intern must already have an active user account and must not be assigned to another active project. * items: * type: string * format: email @@ -459,7 +460,7 @@ const router = express.Router(); * /project-api/add: * post: * summary: Create a new project - * description: Create a new Internify LMS project. Admin can optionally invite interns by email. + * description: Create a new Internify LMS project. Admin can optionally invite interns by email. Invited interns must not be active members of another project. * tags: [Project] * security: * - bearerAuth: []