import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; import { In, Repository } from "typeorm"; import { ProjectRepository } from "../repositories/project.repository"; import { TMProject } from "../entities/project.entity"; import { CreateProjectDto } from "../dto/project/create-project.dto"; import { UpdateProjectDto } from "../dto/project/update-project.dto"; import { PaginationDto } from "../../../common/DTO/pagination.dto"; import { User } from "../../users/entities/user.entity"; import { ProjectMessage, WorkSpaceMessage } from "../../../common/enums/message.enum"; import { WorkspaceService } from "./workspace.service"; import { PermissionEnum } from "../../users/enums/permission.enum"; @Injectable() export class ProjectService { constructor( private readonly projectRepository: ProjectRepository, private readonly workspaceService: WorkspaceService, @InjectRepository(User) private readonly userRepository: Repository, ) {} async findOneOrFail(id: string): Promise { const project = await this.projectRepository.findOne({ where: { id } }); if (!project) throw new NotFoundException(ProjectMessage.PROJECT_NOT_FOUND); return project; } async create(dto: CreateProjectDto): Promise { const { userIds, startDate, ...rest } = dto; const workspace = await this.workspaceService.findOneOrFail(dto.workspaceId); const project = this.projectRepository.create({ ...rest, startDate: new Date(startDate), }); if (userIds?.length) { const workspaceUserIds = workspace.users.map((u) => u.id); const invalidUsers = userIds.filter((id) => !workspaceUserIds.includes(id)); if (invalidUsers.length) { throw new BadRequestException(WorkSpaceMessage.USERS_DONT_BELONG_TO_THIS_WORKSPACE); } project.users = await this.userRepository.findBy({ id: In(userIds) }); } return this.projectRepository.save(project); } async update(id: string, dto: UpdateProjectDto): Promise { const project = await this.findOneOrFail(id); const { userIds, startDate, ...rest } = dto; Object.assign(project, { ...rest, startDate: startDate ? new Date(startDate) : project.startDate, }); if (userIds) { if (userIds.length) { const workspaceId = dto.workspaceId ?? project.workspaceId; const workspace = await this.workspaceService.findOneOrFail(workspaceId); const workspaceUserIds = workspace.users.map((u) => u.id); const invalidUsers = userIds.filter((uid) => !workspaceUserIds.includes(uid)); if (invalidUsers.length) { throw new BadRequestException(WorkSpaceMessage.USERS_DONT_BELONG_TO_THIS_WORKSPACE); } project.users = await this.userRepository.findBy({ id: In(userIds) }); } else { project.users = []; } } return this.projectRepository.save(project); } async remove(id: string): Promise { const project = await this.projectRepository.findOne({ where: { id, }, relations: { taskPhases: true, }, select: { taskPhases: { id: true, }, }, }); if (!project) throw new NotFoundException(ProjectMessage.PROJECT_NOT_FOUND); const taskPhases = project.taskPhases; if (taskPhases !== undefined && taskPhases.length) { throw new BadRequestException(ProjectMessage.PROJECT_CONTAINS_TASK_PHASES); } await this.projectRepository.delete(id); return project; } async findUserProjectsByWorkspace( userId: string, permissions: PermissionEnum[], wsId: string, pagination: PaginationDto, ): Promise<{ projects: TMProject[]; count: number; paginate: true }> { const workspace = await this.workspaceService.findOneOrFail(wsId); const page = pagination.page ?? 1; const limit = pagination.limit ?? 10; const skip = (page - 1) * limit; const isPermissionIncluded = permissions.includes(PermissionEnum.VIEW_ALL_PROJECTS); const isUserMemeberOfWorkspace = workspace.users.some((user) => user.id === userId); var res: [TMProject[], number]; if (isUserMemeberOfWorkspace) { res = await this.projectRepository.findUserProjects(userId, wsId, skip, limit); } else if (isPermissionIncluded) { res = await this.projectRepository.findAndCount({ where: { workspaceId: wsId } }); } else { throw new ForbiddenException(WorkSpaceMessage.FORBIDDEN); } const [projects, count] = res; return { projects, count, paginate: true }; } async getProjectDetail(projectId: string, userId: string, permissions: PermissionEnum[]): Promise { const project = await this.projectRepository.findOne({ where: { id: projectId }, relations: { users: true, }, }); if (!project) throw new NotFoundException(ProjectMessage.PROJECT_NOT_FOUND); const isUserMember = project.users.some((user) => user.id === userId); if (!isUserMember && !permissions.includes(PermissionEnum.VIEW_ALL_PROJECTS)) throw new ForbiddenException(ProjectMessage.NOT_MEMBER_OF_PROJECT); const projectDetail = await this.projectRepository.getProjectDetail(projectId); return projectDetail; } }