Compare commits
11 Commits
9b32f35582
...
743d1eb79d
| Author | SHA1 | Date | |
|---|---|---|---|
| 743d1eb79d | |||
| c2e0cb1bc6 | |||
| 6ad18d8f3e | |||
| db61030b44 | |||
| 31e3b5f00d | |||
| ff4a8dedba | |||
| 87772f46dd | |||
| 26b8268f73 | |||
| 3d59a2fa40 | |||
| bf98b8b9ce | |||
| 89d85e6d24 |
@@ -0,0 +1,28 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class TaskmanagerProject1782889623829 implements MigrationInterface {
|
||||||
|
name = 'TaskmanagerProject1782889623829'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "tm_project_users" ("projectId" uuid NOT NULL, "userId" uuid NOT NULL, CONSTRAINT "PK_5969064e19a1efa76b91de6a065" PRIMARY KEY ("projectId", "userId"))`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_fcc8e135e4891925d288dbb14e" ON "tm_project_users" ("projectId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_8e2d2c9d639d5fd806159f8698" ON "tm_project_users" ("userId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" ADD "backgroundPicture" text`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" ADD "backgroundColor" character varying(20)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" ADD "isActive" boolean NOT NULL DEFAULT true`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" ADD CONSTRAINT "FK_fcc8e135e4891925d288dbb14e5" FOREIGN KEY ("projectId") REFERENCES "tm_projects"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" ADD CONSTRAINT "FK_8e2d2c9d639d5fd806159f86980" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" DROP CONSTRAINT "FK_8e2d2c9d639d5fd806159f86980"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_project_users" DROP CONSTRAINT "FK_fcc8e135e4891925d288dbb14e5"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" DROP COLUMN "isActive"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" DROP COLUMN "backgroundColor"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "tm_projects" DROP COLUMN "backgroundPicture"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_8e2d2c9d639d5fd806159f8698"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_fcc8e135e4891925d288dbb14e"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "tm_project_users"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||||||
|
import { CheckListItemService } from '../providers/check-list-item.service';
|
||||||
|
import { CreateCheckListItemDto } from '../dto/check-list-item/create-check-list-item.dto';
|
||||||
|
import { UpdateCheckListItemDto } from '../dto/check-list-item/update-check-list-item.dto';
|
||||||
|
import { TMCheckListItem } from '../entities/check-list-item.entity';
|
||||||
|
|
||||||
|
@ApiTags('Task Manager - Check List Items')
|
||||||
|
@Controller('task-manager/check-list-items')
|
||||||
|
export class CheckListItemController {
|
||||||
|
constructor(private readonly checkListItemService: CheckListItemService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: 'Get all check list items, optionally filtered by task' })
|
||||||
|
@ApiQuery({ name: 'taskId', required: false, description: 'Filter check list items by task ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'List of check list items', type: [TMCheckListItem] })
|
||||||
|
findAll(@Query('taskId') taskId?: string): Promise<TMCheckListItem[]> {
|
||||||
|
if (taskId) return this.checkListItemService.findByTask(taskId);
|
||||||
|
return this.checkListItemService.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Get a check list item by ID' })
|
||||||
|
@ApiParam({ name: 'id', description: 'CheckListItem ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'CheckListItem found', type: TMCheckListItem })
|
||||||
|
@ApiResponse({ status: 404, description: 'CheckListItem not found' })
|
||||||
|
findOne(@Param('id') id: string): Promise<TMCheckListItem> {
|
||||||
|
return this.checkListItemService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: 'Create a new check list item' })
|
||||||
|
@ApiResponse({ status: 201, description: 'CheckListItem created', type: TMCheckListItem })
|
||||||
|
@ApiResponse({ status: 404, description: 'Task not found' })
|
||||||
|
create(@Body() body: CreateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
return this.checkListItemService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ summary: 'Update a check list item' })
|
||||||
|
@ApiParam({ name: 'id', description: 'CheckListItem ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'CheckListItem updated', type: TMCheckListItem })
|
||||||
|
@ApiResponse({ status: 404, description: 'CheckListItem or Task not found' })
|
||||||
|
update(@Param('id') id: string, @Body() body: UpdateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
return this.checkListItemService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: 'Delete a check list item' })
|
||||||
|
@ApiParam({ name: 'id', description: 'CheckListItem ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'CheckListItem deleted' })
|
||||||
|
@ApiResponse({ status: 404, description: 'CheckListItem not found' })
|
||||||
|
remove(@Param('id') id: string): Promise<void> {
|
||||||
|
return this.checkListItemService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||||||
|
import { ProjectService } from '../providers/project.service';
|
||||||
|
import { CreateProjectDto } from '../dto/project/create-project.dto';
|
||||||
|
import { UpdateProjectDto } from '../dto/project/update-project.dto';
|
||||||
|
import { TMProject } from '../entities/project.entity';
|
||||||
|
|
||||||
|
@ApiTags('Task Manager - Projects')
|
||||||
|
@Controller('task-manager/projects')
|
||||||
|
export class ProjectController {
|
||||||
|
constructor(private readonly projectService: ProjectService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: 'Get all projects, optionally filtered by workspace' })
|
||||||
|
@ApiQuery({ name: 'workspaceId', required: false, description: 'Filter projects by workspace ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'List of projects', type: [TMProject] })
|
||||||
|
findAll(@Query('workspaceId') workspaceId?: string): Promise<TMProject[]> {
|
||||||
|
if (workspaceId) return this.projectService.findByWorkspace(workspaceId);
|
||||||
|
return this.projectService.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Get a project by ID' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Project ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Project found', type: TMProject })
|
||||||
|
@ApiResponse({ status: 404, description: 'Project not found' })
|
||||||
|
findOne(@Param('id') id: string): Promise<TMProject> {
|
||||||
|
return this.projectService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: 'Create a new project' })
|
||||||
|
@ApiResponse({ status: 201, description: 'Project created', type: TMProject })
|
||||||
|
@ApiResponse({ status: 400, description: 'One or more users are not members of the workspace' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Workspace not found' })
|
||||||
|
create(@Body() body: CreateProjectDto): Promise<TMProject> {
|
||||||
|
return this.projectService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ summary: 'Update a project' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Project ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Project updated', type: TMProject })
|
||||||
|
@ApiResponse({ status: 400, description: 'One or more users are not members of the workspace' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Project not found' })
|
||||||
|
update(@Param('id') id: string, @Body() body: UpdateProjectDto): Promise<TMProject> {
|
||||||
|
return this.projectService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: 'Delete a project' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Project ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Project deleted' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Project not found' })
|
||||||
|
remove(@Param('id') id: string): Promise<TMProject> {
|
||||||
|
return this.projectService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from "@nestjs/common";
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from "@nestjs/swagger";
|
||||||
|
import { TaskPhaseService } from "../providers/task-phase.service";
|
||||||
|
import { CreateTaskPhaseDto } from "../dto/task-phase/create-task-phase.dto";
|
||||||
|
import { UpdateTaskPhaseDto } from "../dto/task-phase/update-task-phase.dto";
|
||||||
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
|
|
||||||
|
@ApiTags("Task Manager - Task Phases")
|
||||||
|
@Controller("task-manager/task-phases")
|
||||||
|
export class TaskPhaseController {
|
||||||
|
constructor(private readonly taskPhaseService: TaskPhaseService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: "Get all task phases, optionally filtered by project" })
|
||||||
|
@ApiQuery({ name: "projectId", required: false, description: "Filter task phases by project ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "List of task phases", type: [TMTaskPhase] })
|
||||||
|
findAll(@Query("projectId") projectId?: string): Promise<TMTaskPhase[]> {
|
||||||
|
if (projectId) return this.taskPhaseService.findByProject(projectId);
|
||||||
|
return this.taskPhaseService.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(":id")
|
||||||
|
@ApiOperation({ summary: "Get a task phase by ID" })
|
||||||
|
@ApiParam({ name: "id", description: "TaskPhase ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "TaskPhase found", type: TMTaskPhase })
|
||||||
|
@ApiResponse({ status: 404, description: "TaskPhase not found" })
|
||||||
|
findOne(@Param("id") id: string): Promise<TMTaskPhase> {
|
||||||
|
return this.taskPhaseService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: "Create a new task phase" })
|
||||||
|
@ApiResponse({ status: 201, description: "TaskPhase created", type: TMTaskPhase })
|
||||||
|
@ApiResponse({ status: 404, description: "Project not found" })
|
||||||
|
create(@Body() body: CreateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
return this.taskPhaseService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id")
|
||||||
|
@ApiOperation({ summary: "Update a task phase" })
|
||||||
|
@ApiParam({ name: "id", description: "TaskPhase ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "TaskPhase updated", type: TMTaskPhase })
|
||||||
|
@ApiResponse({ status: 404, description: "TaskPhase or Project not found" })
|
||||||
|
update(@Param("id") id: string, @Body() body: UpdateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
return this.taskPhaseService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":id")
|
||||||
|
@ApiOperation({ summary: "Delete a task phase" })
|
||||||
|
@ApiParam({ name: "id", description: "TaskPhase ID" })
|
||||||
|
@ApiResponse({ status: 200, description: "TaskPhase deleted" })
|
||||||
|
@ApiResponse({ status: 404, description: "TaskPhase not found" })
|
||||||
|
remove(@Param("id") id: string): Promise<void> {
|
||||||
|
return this.taskPhaseService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Controller, Get, Post, Patch, Delete, Param, Body, Query } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
|
||||||
|
import { TaskService } from '../providers/task.service';
|
||||||
|
import { CreateTaskDto } from '../dto/task/create-task.dto';
|
||||||
|
import { UpdateTaskDto } from '../dto/task/update-task.dto';
|
||||||
|
import { ChangeTaskPhaseDto } from '../dto/task/change-task-phase.dto';
|
||||||
|
import { TMTask } from '../entities/task.entity';
|
||||||
|
|
||||||
|
@ApiTags('Task Manager - Tasks')
|
||||||
|
@Controller('task-manager/tasks')
|
||||||
|
export class TaskController {
|
||||||
|
constructor(private readonly taskService: TaskService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: 'Get all tasks, optionally filtered by phase' })
|
||||||
|
@ApiQuery({ name: 'taskPhaseId', required: false, description: 'Filter tasks by task phase ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'List of tasks', type: [TMTask] })
|
||||||
|
findAll(@Query('taskPhaseId') taskPhaseId?: string): Promise<TMTask[]> {
|
||||||
|
if (taskPhaseId) return this.taskService.findByPhase(taskPhaseId);
|
||||||
|
return this.taskService.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Get a task by ID' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Task ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Task found', type: TMTask })
|
||||||
|
@ApiResponse({ status: 404, description: 'Task not found' })
|
||||||
|
findOne(@Param('id') id: string): Promise<TMTask> {
|
||||||
|
return this.taskService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: 'Create a new task' })
|
||||||
|
@ApiResponse({ status: 201, description: 'Task created', type: TMTask })
|
||||||
|
@ApiResponse({ status: 404, description: 'TaskPhase not found' })
|
||||||
|
create(@Body() body: CreateTaskDto): Promise<TMTask> {
|
||||||
|
return this.taskService.create(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
@ApiOperation({ summary: 'Update a task' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Task ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Task updated', type: TMTask })
|
||||||
|
@ApiResponse({ status: 404, description: 'Task or TaskPhase not found' })
|
||||||
|
update(@Param('id') id: string, @Body() body: UpdateTaskDto): Promise<TMTask> {
|
||||||
|
return this.taskService.update(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id/change-phase')
|
||||||
|
@ApiOperation({ summary: 'Move a task to a different phase within the same project' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Task ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Task phase changed', type: TMTask })
|
||||||
|
@ApiResponse({ status: 400, description: 'Target phase belongs to a different project' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Task or TaskPhase not found' })
|
||||||
|
changePhase(@Param('id') id: string, @Body() body: ChangeTaskPhaseDto): Promise<TMTask> {
|
||||||
|
return this.taskService.changePhase(id, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: 'Delete a task' })
|
||||||
|
@ApiParam({ name: 'id', description: 'Task ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Task deleted' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Task not found' })
|
||||||
|
remove(@Param('id') id: string): Promise<void> {
|
||||||
|
return this.taskService.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsNotEmpty, IsUUID, IsBoolean, MaxLength } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateCheckListItemDto {
|
||||||
|
@ApiProperty({ description: "Title of the check list item", example: "Write unit tests" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(150)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Whether the check list item is done", example: false })
|
||||||
|
@IsBoolean()
|
||||||
|
@IsNotEmpty()
|
||||||
|
isDone: boolean;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task this check list item belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { CreateCheckListItemDto } from './create-check-list-item.dto';
|
||||||
|
|
||||||
|
export class UpdateCheckListItemDto extends PartialType(CreateCheckListItemDto) {}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, MaxLength } from "class-validator";
|
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, IsBoolean, MaxLength } from "class-validator";
|
||||||
|
|
||||||
export class CreateProjectDto {
|
export class CreateProjectDto {
|
||||||
@ApiProperty({ description: "Name of the project", example: "Mobile App Redesign" })
|
@ApiProperty({ description: "Name of the project", example: "Mobile App Redesign" })
|
||||||
@@ -33,8 +33,29 @@ export class CreateProjectDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
backgroundPicture?: string;
|
backgroundPicture?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Background color of the project (hex or named)", required: false, example: "#4F46E5" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
backgroundColor?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Whether the project is active", required: false, default: true, example: true })
|
||||||
|
@IsBoolean()
|
||||||
|
@IsOptional()
|
||||||
|
isActive?: boolean;
|
||||||
|
|
||||||
@ApiProperty({ description: "ID of the workspace this project belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
@ApiProperty({ description: "ID of the workspace this project belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "IDs of users to assign to the project (must be members of the workspace)",
|
||||||
|
required: false,
|
||||||
|
type: [String],
|
||||||
|
example: ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
|
||||||
|
})
|
||||||
|
@IsUUID("4", { each: true })
|
||||||
|
@IsOptional()
|
||||||
|
userIds?: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PartialType } from '@nestjs/swagger';
|
import { PartialType } from "@nestjs/swagger";
|
||||||
import { CreateProjectDto } from './create-project.dto';
|
import { CreateProjectDto } from "./create-project.dto";
|
||||||
|
|
||||||
export class UpdateProjectDto extends PartialType(CreateProjectDto) {}
|
export class UpdateProjectDto extends PartialType(CreateProjectDto) {}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsInt, MaxLength, Min } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTaskPhaseDto {
|
||||||
|
@ApiProperty({ description: "Name of the task phase", example: "In Progress" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(100)
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Display order of the task phase", required: false, example: 1 })
|
||||||
|
@IsInt()
|
||||||
|
@IsOptional()
|
||||||
|
@Min(0)
|
||||||
|
order?: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Color code for the task phase", required: false, example: "#4F46E5" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
color?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the project this phase belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
projectId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateTaskPhaseDto } from "./create-task-phase.dto";
|
||||||
|
|
||||||
|
export class UpdateTaskPhaseDto extends PartialType(CreateTaskPhaseDto) {}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsUUID, IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class ChangeTaskPhaseDto {
|
||||||
|
@ApiProperty({ description: 'ID of the new task phase', example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskPhaseId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, MaxLength } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTaskDto {
|
||||||
|
@ApiProperty({ description: "Title of the task", example: "Implement login page" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@MaxLength(150)
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Description of the task", required: false, example: "Implement the login page with JWT auth" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Color code for the task", required: false, example: "#4F46E5" })
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
@MaxLength(20)
|
||||||
|
color?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "Start date of the task", required: false, example: "2026-07-01" })
|
||||||
|
@IsDateString()
|
||||||
|
@IsOptional()
|
||||||
|
startDate?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "End date of the task", required: false, example: "2026-07-15" })
|
||||||
|
@IsDateString()
|
||||||
|
@IsOptional()
|
||||||
|
endDate?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: "ID of the task phase this task belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
taskPhaseId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: "IDs of users to assign to the task",
|
||||||
|
required: false,
|
||||||
|
type: [String],
|
||||||
|
example: ["b2c3d4e5-f6a7-8901-bcde-f12345678901"],
|
||||||
|
})
|
||||||
|
@IsUUID("4", { each: true })
|
||||||
|
@IsOptional()
|
||||||
|
userIds?: string[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from "@nestjs/swagger";
|
||||||
|
import { CreateTaskDto } from "./create-task.dto";
|
||||||
|
|
||||||
|
export class UpdateTaskDto extends PartialType(CreateTaskDto) {}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
|
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany } from "typeorm";
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
import { TMWorkspace } from "./workspace.entity";
|
import { TMWorkspace } from "./workspace.entity";
|
||||||
import { TMTaskPhase } from "./task-phase.entity";
|
import { TMTaskPhase } from "./task-phase.entity";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
|
||||||
@Entity("tm_projects")
|
@Entity("tm_projects")
|
||||||
export class TMProject extends BaseEntity {
|
export class TMProject extends BaseEntity {
|
||||||
@@ -20,6 +21,12 @@ export class TMProject extends BaseEntity {
|
|||||||
@Column({ type: "text", nullable: true })
|
@Column({ type: "text", nullable: true })
|
||||||
backgroundPicture: string;
|
backgroundPicture: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 20, nullable: true })
|
||||||
|
backgroundColor: string;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: true })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
// --- Relations ---
|
// --- Relations ---
|
||||||
|
|
||||||
@ManyToOne(() => TMWorkspace, (workspace) => workspace.projects, {
|
@ManyToOne(() => TMWorkspace, (workspace) => workspace.projects, {
|
||||||
@@ -36,4 +43,12 @@ export class TMProject extends BaseEntity {
|
|||||||
cascade: ["insert", "update"],
|
cascade: ["insert", "update"],
|
||||||
})
|
})
|
||||||
taskPhases: TMTaskPhase[];
|
taskPhases: TMTaskPhase[];
|
||||||
|
|
||||||
|
@ManyToMany(() => User, (user) => user.projects)
|
||||||
|
@JoinTable({
|
||||||
|
name: "tm_project_users",
|
||||||
|
joinColumn: { name: "projectId", referencedColumnName: "id" },
|
||||||
|
inverseJoinColumn: { name: "userId", referencedColumnName: "id" },
|
||||||
|
})
|
||||||
|
users: User[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { CheckListItemRepository } from "../repositories/check-list-item.repository";
|
||||||
|
import { TaskRepository } from "../repositories/task.repository";
|
||||||
|
import { TMCheckListItem } from "../entities/check-list-item.entity";
|
||||||
|
import { CreateCheckListItemDto } from "../dto/check-list-item/create-check-list-item.dto";
|
||||||
|
import { UpdateCheckListItemDto } from "../dto/check-list-item/update-check-list-item.dto";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CheckListItemService {
|
||||||
|
constructor(
|
||||||
|
private readonly checkListItemRepository: CheckListItemRepository,
|
||||||
|
private readonly taskRepository: TaskRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll(): Promise<TMCheckListItem[]> {
|
||||||
|
return this.checkListItemRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
findByTask(taskId: string): Promise<TMCheckListItem[]> {
|
||||||
|
return this.checkListItemRepository.findByTask(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<TMCheckListItem> {
|
||||||
|
const checkListItem = await this.checkListItemRepository.findOneById(id);
|
||||||
|
if (!checkListItem) throw new NotFoundException(`CheckListItem #${id} not found`);
|
||||||
|
return checkListItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
const task = await this.taskRepository.findOneById(dto.taskId);
|
||||||
|
if (!task) throw new NotFoundException(`Task #${dto.taskId} not found`);
|
||||||
|
|
||||||
|
const checkListItem = this.checkListItemRepository.create(dto);
|
||||||
|
return this.checkListItemRepository.save(checkListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateCheckListItemDto): Promise<TMCheckListItem> {
|
||||||
|
const checkListItem = await this.findOne(id);
|
||||||
|
|
||||||
|
if (dto.taskId) {
|
||||||
|
const task = await this.taskRepository.findOneById(dto.taskId);
|
||||||
|
if (!task) throw new NotFoundException(`Task #${dto.taskId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(checkListItem, dto);
|
||||||
|
return this.checkListItemRepository.save(checkListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOne(id);
|
||||||
|
await this.checkListItemRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { BadRequestException, 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 { User } from "../../users/entities/user.entity";
|
||||||
|
import { WorkspaceRepository } from "../repositories/workspace.repository";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ProjectService {
|
||||||
|
constructor(
|
||||||
|
private readonly projectRepository: ProjectRepository,
|
||||||
|
private readonly workspaceRepository: WorkspaceRepository,
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll(): Promise<TMProject[]> {
|
||||||
|
return this.projectRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
findByWorkspace(workspaceId: string): Promise<TMProject[]> {
|
||||||
|
return this.projectRepository.findByWorkspace(workspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<TMProject> {
|
||||||
|
const project = await this.projectRepository.findOneById(id);
|
||||||
|
if (!project) throw new NotFoundException(`Project #${id} not found`);
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateProjectDto): Promise<TMProject> {
|
||||||
|
const { userIds, startDate, ...rest } = dto;
|
||||||
|
|
||||||
|
const workspace = await this.workspaceRepository.findOneById(dto.workspaceId);
|
||||||
|
if (!workspace) throw new NotFoundException(`Workspace #${dto.workspaceId} not found`);
|
||||||
|
|
||||||
|
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(`Users [${invalidUsers.join(", ")}] are not members of this workspace`);
|
||||||
|
}
|
||||||
|
project.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.projectRepository.save(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateProjectDto): Promise<TMProject> {
|
||||||
|
const project = await this.findOne(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.workspaceRepository.findOneById(workspaceId);
|
||||||
|
if (!workspace) throw new NotFoundException(`Workspace #${workspaceId} not found`);
|
||||||
|
|
||||||
|
const workspaceUserIds = workspace.users.map((u) => u.id);
|
||||||
|
const invalidUsers = userIds.filter((uid) => !workspaceUserIds.includes(uid));
|
||||||
|
if (invalidUsers.length) {
|
||||||
|
throw new BadRequestException(`Users [${invalidUsers.join(", ")}] are not members of this workspace`);
|
||||||
|
}
|
||||||
|
project.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||||
|
} else {
|
||||||
|
project.users = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.projectRepository.save(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<TMProject> {
|
||||||
|
const project = await this.findOne(id);
|
||||||
|
await this.projectRepository.delete(id);
|
||||||
|
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
|
import { TaskPhaseRepository } from "../repositories/task-phase.repository";
|
||||||
|
import { ProjectRepository } from "../repositories/project.repository";
|
||||||
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
|
import { CreateTaskPhaseDto } from "../dto/task-phase/create-task-phase.dto";
|
||||||
|
import { UpdateTaskPhaseDto } from "../dto/task-phase/update-task-phase.dto";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskPhaseService {
|
||||||
|
constructor(
|
||||||
|
private readonly taskPhaseRepository: TaskPhaseRepository,
|
||||||
|
private readonly projectRepository: ProjectRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll(): Promise<TMTaskPhase[]> {
|
||||||
|
return this.taskPhaseRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
findByProject(projectId: string): Promise<TMTaskPhase[]> {
|
||||||
|
return this.taskPhaseRepository.findByProject(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<TMTaskPhase> {
|
||||||
|
const taskPhase = await this.taskPhaseRepository.findOneById(id);
|
||||||
|
if (!taskPhase) throw new NotFoundException(`TaskPhase #${id} not found`);
|
||||||
|
return taskPhase;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
const project = await this.projectRepository.findOneById(dto.projectId);
|
||||||
|
if (!project) throw new NotFoundException(`Project #${dto.projectId} not found`);
|
||||||
|
|
||||||
|
const taskPhase = this.taskPhaseRepository.create(dto);
|
||||||
|
return this.taskPhaseRepository.save(taskPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateTaskPhaseDto): Promise<TMTaskPhase> {
|
||||||
|
const taskPhase = await this.findOne(id);
|
||||||
|
|
||||||
|
if (dto.projectId) {
|
||||||
|
const project = await this.projectRepository.findOneById(dto.projectId);
|
||||||
|
if (!project) throw new NotFoundException(`Project #${dto.projectId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(taskPhase, dto);
|
||||||
|
return this.taskPhaseRepository.save(taskPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOne(id);
|
||||||
|
await this.taskPhaseRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { In, Repository } from 'typeorm';
|
||||||
|
import { TaskRepository } from '../repositories/task.repository';
|
||||||
|
import { TaskPhaseRepository } from '../repositories/task-phase.repository';
|
||||||
|
import { TMTask } from '../entities/task.entity';
|
||||||
|
import { CreateTaskDto } from '../dto/task/create-task.dto';
|
||||||
|
import { UpdateTaskDto } from '../dto/task/update-task.dto';
|
||||||
|
import { ChangeTaskPhaseDto } from '../dto/task/change-task-phase.dto';
|
||||||
|
import { User } from '../../users/entities/user.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskService {
|
||||||
|
constructor(
|
||||||
|
private readonly taskRepository: TaskRepository,
|
||||||
|
private readonly taskPhaseRepository: TaskPhaseRepository,
|
||||||
|
@InjectRepository(User)
|
||||||
|
private readonly userRepository: Repository<User>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll(): Promise<TMTask[]> {
|
||||||
|
return this.taskRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
findByPhase(taskPhaseId: string): Promise<TMTask[]> {
|
||||||
|
return this.taskRepository.findByPhase(taskPhaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<TMTask> {
|
||||||
|
const task = await this.taskRepository.findOneById(id);
|
||||||
|
if (!task) throw new NotFoundException(`Task #${id} not found`);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(dto: CreateTaskDto): Promise<TMTask> {
|
||||||
|
const { userIds, startDate, endDate, ...rest } = dto;
|
||||||
|
|
||||||
|
const taskPhase = await this.taskPhaseRepository.findOneById(dto.taskPhaseId);
|
||||||
|
if (!taskPhase) throw new NotFoundException(`TaskPhase #${dto.taskPhaseId} not found`);
|
||||||
|
|
||||||
|
const task = this.taskRepository.create({
|
||||||
|
...rest,
|
||||||
|
startDate: startDate ? new Date(startDate) : undefined,
|
||||||
|
endDate: endDate ? new Date(endDate) : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userIds?.length) {
|
||||||
|
task.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.taskRepository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, dto: UpdateTaskDto): Promise<TMTask> {
|
||||||
|
const task = await this.findOne(id);
|
||||||
|
const { userIds, startDate, endDate, ...rest } = dto;
|
||||||
|
|
||||||
|
if (dto.taskPhaseId) {
|
||||||
|
const taskPhase = await this.taskPhaseRepository.findOneById(dto.taskPhaseId);
|
||||||
|
if (!taskPhase) throw new NotFoundException(`TaskPhase #${dto.taskPhaseId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(task, {
|
||||||
|
...rest,
|
||||||
|
startDate: startDate ? new Date(startDate) : task.startDate,
|
||||||
|
endDate: endDate ? new Date(endDate) : task.endDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userIds) {
|
||||||
|
task.users = userIds.length
|
||||||
|
? await this.userRepository.findBy({ id: In(userIds) })
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.taskRepository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePhase(id: string, dto: ChangeTaskPhaseDto): Promise<TMTask> {
|
||||||
|
const task = await this.findOne(id);
|
||||||
|
|
||||||
|
const newPhase = await this.taskPhaseRepository.findOneById(dto.taskPhaseId);
|
||||||
|
if (!newPhase) throw new NotFoundException(`TaskPhase #${dto.taskPhaseId} not found`);
|
||||||
|
|
||||||
|
if (task.taskPhase.projectId !== newPhase.projectId) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`TaskPhase #${dto.taskPhaseId} does not belong to the same project as the current phase`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
task.taskPhaseId = dto.taskPhaseId;
|
||||||
|
task.taskPhase = newPhase;
|
||||||
|
|
||||||
|
return this.taskRepository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<void> {
|
||||||
|
await this.findOne(id);
|
||||||
|
await this.taskRepository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { TMCheckListItem } from '../entities/check-list-item.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CheckListItemRepository {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMCheckListItem)
|
||||||
|
private readonly repository: Repository<TMCheckListItem>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return this.repository.find({
|
||||||
|
relations: ['task'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findByTask(taskId: string) {
|
||||||
|
return this.repository.find({
|
||||||
|
where: { taskId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findOneById(id: string) {
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: ['task'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data: Partial<TMCheckListItem>) {
|
||||||
|
return this.repository.create(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(checkListItem: TMCheckListItem) {
|
||||||
|
return this.repository.save(checkListItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: string, data: Partial<TMCheckListItem>) {
|
||||||
|
return this.repository.update(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string) {
|
||||||
|
return this.repository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { TMProject } from "../entities/project.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ProjectRepository {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMProject)
|
||||||
|
private readonly repository: Repository<TMProject>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return this.repository.find({
|
||||||
|
relations: ["workspace", "taskPhases", "users"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findByWorkspace(workspaceId: string) {
|
||||||
|
return this.repository.find({
|
||||||
|
where: { workspaceId },
|
||||||
|
relations: ["taskPhases", "users"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findOneById(id: string) {
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: ["workspace", "taskPhases", "users"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data: Partial<TMProject>) {
|
||||||
|
return this.repository.create(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(project: TMProject) {
|
||||||
|
return this.repository.save(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: string, data: Partial<TMProject>) {
|
||||||
|
return this.repository.update(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string) {
|
||||||
|
return this.repository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { TMTaskPhase } from "../entities/task-phase.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskPhaseRepository {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMTaskPhase)
|
||||||
|
private readonly repository: Repository<TMTaskPhase>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return this.repository.find({
|
||||||
|
relations: ["project", "tasks"],
|
||||||
|
order: { order: "ASC" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findByProject(projectId: string) {
|
||||||
|
return this.repository.find({
|
||||||
|
where: { projectId },
|
||||||
|
relations: ["tasks"],
|
||||||
|
order: { order: "ASC" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findOneById(id: string) {
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: ["project", "tasks"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data: Partial<TMTaskPhase>) {
|
||||||
|
return this.repository.create(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(taskPhase: TMTaskPhase) {
|
||||||
|
return this.repository.save(taskPhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: string, data: Partial<TMTaskPhase>) {
|
||||||
|
return this.repository.update(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string) {
|
||||||
|
return this.repository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { TMTask } from "../entities/task.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TaskRepository {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(TMTask)
|
||||||
|
private readonly repository: Repository<TMTask>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
findAll() {
|
||||||
|
return this.repository.find({
|
||||||
|
relations: ["taskPhase", "remarks", "attachments", "checkListItems", "users"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findByPhase(taskPhaseId: string) {
|
||||||
|
return this.repository.find({
|
||||||
|
where: { taskPhaseId },
|
||||||
|
relations: ["remarks", "attachments", "checkListItems", "users"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findOneById(id: string) {
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: ["taskPhase", "remarks", "attachments", "checkListItems", "users"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data: Partial<TMTask>) {
|
||||||
|
return this.repository.create(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(task: TMTask) {
|
||||||
|
return this.repository.save(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: string, data: Partial<TMTask>) {
|
||||||
|
return this.repository.update(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string) {
|
||||||
|
return this.repository.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,29 +16,58 @@ import { User } from "../users/entities/user.entity";
|
|||||||
import { WorkspaceTypeService } from "./providers/workspace-type.service";
|
import { WorkspaceTypeService } from "./providers/workspace-type.service";
|
||||||
import { WorkspaceTypeRepository } from "./repositories/workspace-type.repository";
|
import { WorkspaceTypeRepository } from "./repositories/workspace-type.repository";
|
||||||
import { WorkspaceTypeController } from "./controllers/workspace-type.controller";
|
import { WorkspaceTypeController } from "./controllers/workspace-type.controller";
|
||||||
|
import { ProjectController } from "./controllers/project.controller";
|
||||||
// import { WorkspaceTypeService } from './providers/workspace-type.service';
|
import { ProjectRepository } from "./repositories/project.repository";
|
||||||
// import { WorkspaceService } from './providers/workspace.service';
|
import { ProjectService } from "./providers/project.service";
|
||||||
// import { ProjectService } from './providers/project.service';
|
import { TaskPhaseService } from "./providers/task-phase.service";
|
||||||
// import { TaskPhaseService } from './providers/task-phase.service';
|
import { TaskPhaseRepository } from "./repositories/task-phase.repository";
|
||||||
// import { TaskService } from './providers/task.service';
|
import { TaskRepository } from "./repositories/task.repository";
|
||||||
|
import { TaskService } from "./providers/task.service";
|
||||||
// import { TaskManagerController } from './task-manager.controller';
|
import { CheckListItemRepository } from "./repositories/check-list-item.repository";
|
||||||
|
import { CheckListItemService } from "./providers/check-list-item.service";
|
||||||
|
import { TaskPhaseController } from "./controllers/task-phase.controller";
|
||||||
|
import { TaskController } from "./controllers/task.controller";
|
||||||
|
import { CheckListItemController } from "./controllers/check-list-item.controller";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([TMWorkspaceType, TMWorkspace, TMProject, TMTaskPhase, TMTask, TMRemark, TMAttachment, TMCheckListItem, User]),
|
TypeOrmModule.forFeature([TMWorkspaceType, TMWorkspace, TMProject, TMTaskPhase, TMTask, TMRemark, TMAttachment, TMCheckListItem, User]),
|
||||||
],
|
],
|
||||||
controllers: [WorkspaceController, WorkspaceTypeController],
|
controllers: [
|
||||||
|
WorkspaceController,
|
||||||
|
WorkspaceTypeController,
|
||||||
|
ProjectController,
|
||||||
|
TaskPhaseController,
|
||||||
|
TaskController,
|
||||||
|
CheckListItemController,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
WorkspaceRepository,
|
WorkspaceRepository,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
WorkspaceTypeRepository,
|
WorkspaceTypeRepository,
|
||||||
WorkspaceTypeService,
|
WorkspaceTypeService,
|
||||||
// ProjectService,
|
ProjectRepository,
|
||||||
// TaskPhaseService,
|
ProjectService,
|
||||||
// TaskService,
|
TaskPhaseRepository,
|
||||||
|
TaskPhaseService,
|
||||||
|
TaskRepository,
|
||||||
|
TaskService,
|
||||||
|
CheckListItemRepository,
|
||||||
|
CheckListItemService,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
WorkspaceRepository,
|
||||||
|
WorkspaceService,
|
||||||
|
WorkspaceTypeRepository,
|
||||||
|
WorkspaceTypeService,
|
||||||
|
ProjectRepository,
|
||||||
|
ProjectService,
|
||||||
|
TaskPhaseRepository,
|
||||||
|
TaskPhaseService,
|
||||||
|
TaskRepository,
|
||||||
|
TaskService,
|
||||||
|
CheckListItemRepository,
|
||||||
|
CheckListItemService,
|
||||||
],
|
],
|
||||||
exports: [WorkspaceService, WorkspaceRepository, WorkspaceTypeService, WorkspaceTypeRepository],
|
|
||||||
})
|
})
|
||||||
export class TaskManagerModule {}
|
export class TaskManagerModule {}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { Reseller } from "../../reseller/entities/reseller.entity";
|
|||||||
import { UserBankAccount } from "../../reseller/entities/user-bank-account.entity";
|
import { UserBankAccount } from "../../reseller/entities/user-bank-account.entity";
|
||||||
import { TMWorkspace } from "../../task-manager/entities/workspace.entity";
|
import { TMWorkspace } from "../../task-manager/entities/workspace.entity";
|
||||||
import { TMTask } from "../../task-manager/entities/task.entity";
|
import { TMTask } from "../../task-manager/entities/task.entity";
|
||||||
|
import { TMProject } from "../../task-manager/entities/project.entity";
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class User extends BaseEntity {
|
export class User extends BaseEntity {
|
||||||
@@ -185,4 +186,7 @@ export class User extends BaseEntity {
|
|||||||
|
|
||||||
@ManyToMany(() => TMTask, (task) => task.users)
|
@ManyToMany(() => TMTask, (task) => task.users)
|
||||||
tasks: TMTask[];
|
tasks: TMTask[];
|
||||||
|
|
||||||
|
@ManyToMany(() => TMProject, (project) => project.users)
|
||||||
|
projects: TMProject[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user