Compare commits
13 Commits
e728303d53
...
9b32f35582
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b32f35582 | |||
| a4aba43bc1 | |||
| 77e365a259 | |||
| e886adbcbc | |||
| 62fd234eb4 | |||
| a134429fa4 | |||
| 2077477445 | |||
| 838f2bc4b7 | |||
| b0fba4bb84 | |||
| 9d8ff3603e | |||
| d24abe7ee4 | |||
| ae62cffa58 | |||
| 0bab27871a |
@@ -0,0 +1,66 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class TaskManager1782807558167 implements MigrationInterface {
|
||||
name = 'TaskManager1782807558167'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "tm_workspace_types" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(100) NOT NULL, "description" text, "order" integer NOT NULL DEFAULT '0', CONSTRAINT "UQ_f6eb300d6dfa0f7dbfdf05e2592" UNIQUE ("name"), CONSTRAINT "PK_b3437ae89cc910f1f0ad3de0fc5" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_remarks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "title" character varying(150) NOT NULL, "color" character varying(20), "taskId" uuid NOT NULL, CONSTRAINT "PK_99be294f0b23c3823d9ee784ebf" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TYPE "public"."tm_attachments_type_enum" AS ENUM('file', 'link', 'image', 'video')`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_attachments" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "title" character varying(150) NOT NULL, "type" "public"."tm_attachments_type_enum" NOT NULL, "file" text NOT NULL, "taskId" uuid NOT NULL, CONSTRAINT "PK_85eb42d47a75319a1ff074ff39c" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_check_list_items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "title" character varying(150) NOT NULL, "isDone" boolean NOT NULL DEFAULT false, "taskId" uuid NOT NULL, CONSTRAINT "PK_0bb2fee42ccb8a7f4c5c054ea71" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_tasks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "title" character varying(150) NOT NULL, "description" text, "color" character varying(20), "startDate" TIMESTAMP WITH TIME ZONE, "endDate" TIMESTAMP WITH TIME ZONE, "taskPhaseId" uuid NOT NULL, CONSTRAINT "PK_f975c1e9cc0cfc054482cb36028" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_task_phases" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(100) NOT NULL, "order" integer NOT NULL DEFAULT '0', "color" character varying(20), "projectId" uuid NOT NULL, CONSTRAINT "PK_045ad1778b77407b8e4017cc9b2" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_projects" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(150) NOT NULL, "ownerName" character varying(150), "description" text, "startDate" TIMESTAMP WITH TIME ZONE, "workspaceId" uuid NOT NULL, CONSTRAINT "PK_0ceacba52c6bb5cec6481b910e4" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_workspaces" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(100) NOT NULL, "description" text, "color" character varying(20) NOT NULL, "isActive" boolean NOT NULL DEFAULT true, "workspaceTypeId" uuid NOT NULL, CONSTRAINT "PK_fb4be955b5b4b11aacc5169e9d5" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE TABLE "tm_task_users" ("taskId" uuid NOT NULL, "userId" uuid NOT NULL, CONSTRAINT "PK_bb8f94f4ea4715ad190bc4fb23c" PRIMARY KEY ("taskId", "userId"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_739cfd1807f78bb2ebda02e7bc" ON "tm_task_users" ("taskId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_3fe44ce1acd8a620affb55a750" ON "tm_task_users" ("userId") `);
|
||||
await queryRunner.query(`CREATE TABLE "tm_workspace_users" ("workspaceId" uuid NOT NULL, "userId" uuid NOT NULL, CONSTRAINT "PK_7f8b37c58e5267bc16cf577fcbd" PRIMARY KEY ("workspaceId", "userId"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_ca9b0a16dfec7c4753ea4910f0" ON "tm_workspace_users" ("workspaceId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_1c9d677417216377d55a060011" ON "tm_workspace_users" ("userId") `);
|
||||
await queryRunner.query(`ALTER TABLE "reward_withdraw_request" ADD CONSTRAINT "UQ_79526c10ecb097f553e0120a36e" UNIQUE ("transactionId")`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_remarks" ADD CONSTRAINT "FK_bc03cabb99c42245436fcbb4e6a" FOREIGN KEY ("taskId") REFERENCES "tm_tasks"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_attachments" ADD CONSTRAINT "FK_1e9ab0f9ec5b898abea95d21fb2" FOREIGN KEY ("taskId") REFERENCES "tm_tasks"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_check_list_items" ADD CONSTRAINT "FK_14730b0971bcdc5547889392606" FOREIGN KEY ("taskId") REFERENCES "tm_tasks"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_tasks" ADD CONSTRAINT "FK_f7462cec045e564d5fd54d17177" FOREIGN KEY ("taskPhaseId") REFERENCES "tm_task_phases"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_task_phases" ADD CONSTRAINT "FK_884b8c2fe724031aa61c8181f0b" FOREIGN KEY ("projectId") REFERENCES "tm_projects"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_projects" ADD CONSTRAINT "FK_c3d997618910273be84d58faa8c" FOREIGN KEY ("workspaceId") REFERENCES "tm_workspaces"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_workspaces" ADD CONSTRAINT "FK_253e36f065cfc1eca58abaefb22" FOREIGN KEY ("workspaceTypeId") REFERENCES "tm_workspace_types"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_task_users" ADD CONSTRAINT "FK_739cfd1807f78bb2ebda02e7bce" FOREIGN KEY ("taskId") REFERENCES "tm_tasks"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_task_users" ADD CONSTRAINT "FK_3fe44ce1acd8a620affb55a7500" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_workspace_users" ADD CONSTRAINT "FK_ca9b0a16dfec7c4753ea4910f00" FOREIGN KEY ("workspaceId") REFERENCES "tm_workspaces"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_workspace_users" ADD CONSTRAINT "FK_1c9d677417216377d55a060011d" 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_workspace_users" DROP CONSTRAINT "FK_1c9d677417216377d55a060011d"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_workspace_users" DROP CONSTRAINT "FK_ca9b0a16dfec7c4753ea4910f00"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_task_users" DROP CONSTRAINT "FK_3fe44ce1acd8a620affb55a7500"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_task_users" DROP CONSTRAINT "FK_739cfd1807f78bb2ebda02e7bce"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_workspaces" DROP CONSTRAINT "FK_253e36f065cfc1eca58abaefb22"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_projects" DROP CONSTRAINT "FK_c3d997618910273be84d58faa8c"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_task_phases" DROP CONSTRAINT "FK_884b8c2fe724031aa61c8181f0b"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_tasks" DROP CONSTRAINT "FK_f7462cec045e564d5fd54d17177"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_check_list_items" DROP CONSTRAINT "FK_14730b0971bcdc5547889392606"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_attachments" DROP CONSTRAINT "FK_1e9ab0f9ec5b898abea95d21fb2"`);
|
||||
await queryRunner.query(`ALTER TABLE "tm_remarks" DROP CONSTRAINT "FK_bc03cabb99c42245436fcbb4e6a"`);
|
||||
await queryRunner.query(`ALTER TABLE "reward_withdraw_request" DROP CONSTRAINT "UQ_79526c10ecb097f553e0120a36e"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_1c9d677417216377d55a060011"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_ca9b0a16dfec7c4753ea4910f0"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_workspace_users"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_3fe44ce1acd8a620affb55a750"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_739cfd1807f78bb2ebda02e7bc"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_task_users"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_workspaces"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_projects"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_task_phases"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_tasks"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_check_list_items"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_attachments"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."tm_attachments_type_enum"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_remarks"`);
|
||||
await queryRunner.query(`DROP TABLE "tm_workspace_types"`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -707,6 +707,10 @@ export const enum AdminMessage {
|
||||
ROLE_NAME_LENGTH = "نام نقش باید بین ۳ تا ۱۵۰ کاراکتر باشد",
|
||||
ROLE_EXIST = "نقش با این نام قبلا ثبت شده است",
|
||||
ROLE_CREATED = "نقش با موفقیت ایجاد شد",
|
||||
ROLE_NOT_FOUND = "نقشی با این شناسه یافت نشد",
|
||||
ROLE_UPDATED = "نقش با موفقیت به روز رسانی شد",
|
||||
ROLE_DELETED = "نقش با موفقیت حذف شد",
|
||||
ROLE_HAS_USERS = "امکان حذف نقش وجود ندارد زیرا به کاربرانی اختصاص داده شده است",
|
||||
ADMIN_UPDATED = "مدیر با موفقیت به روز رسانی شد",
|
||||
ADMIN_NOT_FOUND = "مدیری با این شناسه یافت نشد",
|
||||
ADMIN_DELETED = "مدیر با موفقیت حذف شد",
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body } from "@nestjs/common";
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from "@nestjs/swagger";
|
||||
import { WorkspaceTypeService } from "../providers/workspace-type.service";
|
||||
import { CreateWorkspaceTypeDto } from "../dto/workspace-type/create-workspace-type.dto";
|
||||
import { UpdateWorkspaceTypeDto } from "../dto/workspace-type/update-workspace-type.dto";
|
||||
import { TMWorkspaceType } from "../entities/workspace-type.entity";
|
||||
|
||||
@ApiTags("Task Manager")
|
||||
@Controller("task-manager/workspace-types")
|
||||
export class WorkspaceTypeController {
|
||||
constructor(private readonly workspaceTypeService: WorkspaceTypeService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: "Get all workspace types" })
|
||||
@ApiResponse({ status: 200, description: "List of workspace types", type: [TMWorkspaceType] })
|
||||
findAll(): Promise<TMWorkspaceType[]> {
|
||||
return this.workspaceTypeService.findAll();
|
||||
}
|
||||
|
||||
@Get(":id")
|
||||
@ApiOperation({ summary: "Get a workspace type by ID" })
|
||||
@ApiParam({ name: "id", description: "WorkspaceType ID" })
|
||||
@ApiResponse({ status: 200, description: "WorkspaceType found", type: TMWorkspaceType })
|
||||
@ApiResponse({ status: 404, description: "WorkspaceType not found" })
|
||||
findOne(@Param("id") id: string): Promise<TMWorkspaceType> {
|
||||
return this.workspaceTypeService.findOne(id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: "Create a new workspace type" })
|
||||
@ApiResponse({ status: 201, description: "WorkspaceType created", type: TMWorkspaceType })
|
||||
@ApiResponse({ status: 409, description: "WorkspaceType with this name already exists" })
|
||||
create(@Body() body: CreateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
||||
return this.workspaceTypeService.create(body);
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
@ApiOperation({ summary: "Update a workspace type" })
|
||||
@ApiParam({ name: "id", description: "WorkspaceType ID" })
|
||||
@ApiResponse({ status: 200, description: "WorkspaceType updated", type: TMWorkspaceType })
|
||||
@ApiResponse({ status: 404, description: "WorkspaceType not found" })
|
||||
@ApiResponse({ status: 409, description: "WorkspaceType with this name already exists" })
|
||||
update(@Param("id") id: string, @Body() body: UpdateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
||||
return this.workspaceTypeService.update(id, body);
|
||||
}
|
||||
|
||||
@Delete(":id")
|
||||
@ApiOperation({ summary: "Delete a workspace type" })
|
||||
@ApiParam({ name: "id", description: "WorkspaceType ID" })
|
||||
@ApiResponse({ status: 200, description: "WorkspaceType deleted" })
|
||||
@ApiResponse({ status: 404, description: "WorkspaceType not found" })
|
||||
remove(@Param("id") id: string): Promise<TMWorkspaceType> {
|
||||
return this.workspaceTypeService.remove(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import { WorkspaceService } from '../providers/workspace.service';
|
||||
import { CreateWorkspaceDto } from '../dto/workspace/create-workspace.dto';
|
||||
import { UpdateWorkspaceDto } from '../dto/workspace/update-workspace.dto';
|
||||
import { TMWorkspace } from '../entities/workspace.entity';
|
||||
|
||||
@ApiTags('Task Manager')
|
||||
@Controller('task-manager/workspaces')
|
||||
export class WorkspaceController {
|
||||
constructor(private readonly workspaceService: WorkspaceService) {}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Get all workspaces' })
|
||||
@ApiResponse({ status: 200, description: 'List of workspaces', type: [TMWorkspace] })
|
||||
findAll(): Promise<TMWorkspace[]> {
|
||||
return this.workspaceService.findAll();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get a workspace by ID' })
|
||||
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
||||
@ApiResponse({ status: 200, description: 'Workspace found', type: TMWorkspace })
|
||||
@ApiResponse({ status: 404, description: 'Workspace not found' })
|
||||
findOne(@Param('id') id: string): Promise<TMWorkspace> {
|
||||
return this.workspaceService.findOneOrFail(id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create a new workspace' })
|
||||
@ApiResponse({ status: 201, description: 'Workspace created', type: TMWorkspace })
|
||||
create(@Body() body: CreateWorkspaceDto): Promise<TMWorkspace> {
|
||||
return this.workspaceService.create(body);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({ summary: 'Update a workspace' })
|
||||
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
||||
@ApiResponse({ status: 200, description: 'Workspace updated', type: TMWorkspace })
|
||||
@ApiResponse({ status: 404, description: 'Workspace not found' })
|
||||
update(@Param('id') id: string, @Body() body: UpdateWorkspaceDto): Promise<TMWorkspace> {
|
||||
return this.workspaceService.update(id, body);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete a workspace' })
|
||||
@ApiParam({ name: 'id', description: 'Workspace ID' })
|
||||
@ApiResponse({ status: 200, description: 'Workspace deleted' })
|
||||
@ApiResponse({ status: 404, description: 'Workspace not found' })
|
||||
remove(@Param('id') id: string): Promise<TMWorkspace> {
|
||||
return this.workspaceService.remove(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsString, IsOptional, IsNotEmpty, IsUUID, IsDateString, MaxLength } from "class-validator";
|
||||
|
||||
export class CreateProjectDto {
|
||||
@ApiProperty({ description: "Name of the project", example: "Mobile App Redesign" })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(150)
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: "Name of the project owner", example: "Jane Doe" })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(150)
|
||||
ownerName: string;
|
||||
|
||||
@ApiProperty({ description: "Description of the project", required: false, example: "Redesign of the mobile app UI/UX" })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: "Start date of the project", example: "2026-07-01" })
|
||||
@IsDateString()
|
||||
@IsNotEmpty()
|
||||
startDate: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Background picture URL of the project",
|
||||
required: false,
|
||||
example: "https://cdn.example.com/projects/bg1.png",
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
backgroundPicture?: string;
|
||||
|
||||
@ApiProperty({ description: "ID of the workspace this project belongs to", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
workspaceId: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateProjectDto } from './create-project.dto';
|
||||
|
||||
export class UpdateProjectDto extends PartialType(CreateProjectDto) {}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsString, IsOptional, IsInt, IsNotEmpty, MaxLength, Min } from "class-validator";
|
||||
|
||||
export class CreateWorkspaceTypeDto {
|
||||
@ApiProperty({ description: "Name of the workspace type", example: "Engineering" })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
name: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Description of the workspace type",
|
||||
required: false,
|
||||
example: "Workspace type for engineering related work",
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: "Display order of the workspace type", example: 1 })
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
@Min(0)
|
||||
order: number;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateWorkspaceTypeDto } from './create-workspace-type.dto';
|
||||
|
||||
export class UpdateWorkspaceTypeDto extends PartialType(CreateWorkspaceTypeDto) {}
|
||||
@@ -1,25 +1,40 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsString, IsOptional, IsBoolean, IsNotEmpty, IsUUID, MaxLength } from "class-validator";
|
||||
|
||||
export class CreateWorkspaceDto {
|
||||
@ApiProperty({ description: "Name of the workspace", example: "Engineering" })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(100)
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: "ID of the workspace type", example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" })
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
workspaceTypeId: string;
|
||||
|
||||
@ApiProperty({ description: "Description of the workspace", required: false, example: "Workspace for the engineering team" })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: "Color code for the workspace (hex or named)", example: "#4F46E5" })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(20)
|
||||
color: string;
|
||||
|
||||
@ApiProperty({ description: "Whether the workspace is active", required: false, default: true, example: true })
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean;
|
||||
isActive: boolean;
|
||||
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
workspaceTypeId: string;
|
||||
@ApiProperty({
|
||||
description: "IDs of users to assign to 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/mapped-types";
|
||||
import { PartialType } from "@nestjs/swagger";
|
||||
import { CreateWorkspaceDto } from "./create-workspace.dto";
|
||||
|
||||
export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) {}
|
||||
|
||||
@@ -17,6 +17,9 @@ export class TMProject extends BaseEntity {
|
||||
@Column({ type: "timestamptz", nullable: true })
|
||||
startDate: Date;
|
||||
|
||||
@Column({ type: "text", nullable: true })
|
||||
backgroundPicture: string;
|
||||
|
||||
// --- Relations ---
|
||||
|
||||
@ManyToOne(() => TMWorkspace, (workspace) => workspace.projects, {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Injectable, NotFoundException, ConflictException } from "@nestjs/common";
|
||||
import { WorkspaceTypeRepository } from "../repositories/workspace-type.repository";
|
||||
import { TMWorkspaceType } from "../entities/workspace-type.entity";
|
||||
import { CreateWorkspaceTypeDto } from "../dto/workspace-type/create-workspace-type.dto";
|
||||
import { UpdateWorkspaceTypeDto } from "../dto/workspace-type/update-workspace-type.dto";
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceTypeService {
|
||||
constructor(private readonly workspaceTypeRepository: WorkspaceTypeRepository) {}
|
||||
|
||||
findAll(): Promise<TMWorkspaceType[]> {
|
||||
return this.workspaceTypeRepository.findAll();
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<TMWorkspaceType> {
|
||||
const workspaceType = await this.workspaceTypeRepository.findOneById(id);
|
||||
if (!workspaceType) throw new NotFoundException(`WorkspaceType #${id} not found`);
|
||||
return workspaceType;
|
||||
}
|
||||
|
||||
async create(dto: CreateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
||||
const existing = await this.workspaceTypeRepository.findOneByName(dto.name);
|
||||
if (existing) throw new ConflictException(`WorkspaceType with name "${dto.name}" already exists`);
|
||||
|
||||
const workspaceType = this.workspaceTypeRepository.create(dto);
|
||||
return this.workspaceTypeRepository.save(workspaceType);
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateWorkspaceTypeDto): Promise<TMWorkspaceType> {
|
||||
await this.findOne(id);
|
||||
|
||||
if (dto.name) {
|
||||
const existing = await this.workspaceTypeRepository.findOneByName(dto.name);
|
||||
if (existing && existing.id !== id) throw new ConflictException(`WorkspaceType with name "${dto.name}" already exists`);
|
||||
}
|
||||
|
||||
await this.workspaceTypeRepository.update(id, dto);
|
||||
return this.findOne(id);
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<TMWorkspaceType> {
|
||||
const workspaceType = await this.findOne(id);
|
||||
await this.workspaceTypeRepository.delete(id);
|
||||
|
||||
return workspaceType;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,69 @@
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { CreateWorkspaceDto } from "../dto/workspace/create-workspace.dto";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { In, Repository } from "typeorm";
|
||||
import { WorkspaceRepository } from "../repositories/workspace.repository";
|
||||
import { TMWorkspace } from "../entities/workspace.entity";
|
||||
import { CreateWorkspaceDto } from "../dto/workspace/create-workspace.dto";
|
||||
import { UpdateWorkspaceDto } from "../dto/workspace/update-workspace.dto";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { WorkspaceTypeRepository } from "../repositories/workspace-type.repository";
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceService {
|
||||
constructor(
|
||||
private readonly workspaceRepository: WorkspaceRepository,
|
||||
private readonly workspaceTypeRepository: WorkspaceTypeRepository,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
findAll() {
|
||||
findAll(): Promise<TMWorkspace[]> {
|
||||
return this.workspaceRepository.findAll();
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
async findOneOrFail(id: string): Promise<TMWorkspace> {
|
||||
const workspace = await this.workspaceRepository.findOneById(id);
|
||||
|
||||
if (!workspace) {
|
||||
throw new NotFoundException(`Workspace #${id} not found`);
|
||||
}
|
||||
|
||||
if (!workspace) throw new NotFoundException(`Workspace #${id} not found`);
|
||||
return workspace;
|
||||
}
|
||||
|
||||
async create(dto: CreateWorkspaceDto) {
|
||||
const workspace = this.workspaceRepository.create(dto);
|
||||
async create(dto: CreateWorkspaceDto): Promise<TMWorkspace> {
|
||||
const { userIds, workspaceTypeId, ...rest } = dto;
|
||||
|
||||
const workspaceType = await this.workspaceTypeRepository.findOneById(workspaceTypeId);
|
||||
if (!workspaceType) throw new NotFoundException(`WorkSpaceType #${workspaceTypeId} not found!`);
|
||||
|
||||
const workspace = this.workspaceRepository.create(rest);
|
||||
|
||||
if (userIds?.length) {
|
||||
workspace.users = await this.userRepository.findBy({ id: In(userIds) });
|
||||
}
|
||||
|
||||
return this.workspaceRepository.save(workspace);
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateWorkspaceDto) {
|
||||
await this.findOne(id);
|
||||
await this.workspaceRepository.update(id, dto);
|
||||
return this.findOne(id);
|
||||
async update(id: string, dto: UpdateWorkspaceDto): Promise<TMWorkspace> {
|
||||
const workspace = await this.findOneOrFail(id);
|
||||
const { userIds, workspaceTypeId, ...rest } = dto;
|
||||
|
||||
if (workspaceTypeId) {
|
||||
const workspaceType = await this.workspaceTypeRepository.findOneById(workspaceTypeId);
|
||||
if (!workspaceType) throw new NotFoundException(`WorkSpaceType #${workspaceTypeId} not found!`);
|
||||
}
|
||||
|
||||
Object.assign(workspace, rest);
|
||||
|
||||
if (userIds) {
|
||||
workspace.users = userIds.length ? await this.userRepository.findBy({ id: In(userIds) }) : [];
|
||||
}
|
||||
|
||||
return this.workspaceRepository.save(workspace);
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
await this.findOne(id);
|
||||
async remove(id: string): Promise<TMWorkspace> {
|
||||
const workspace = await this.findOneOrFail(id);
|
||||
await this.workspaceRepository.delete(id);
|
||||
|
||||
return workspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { TMWorkspaceType } from "../entities/workspace-type.entity";
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceTypeRepository {
|
||||
constructor(
|
||||
@InjectRepository(TMWorkspaceType)
|
||||
private readonly repository: Repository<TMWorkspaceType>,
|
||||
) {}
|
||||
|
||||
findAll() {
|
||||
return this.repository.find({
|
||||
order: { order: "ASC" },
|
||||
});
|
||||
}
|
||||
|
||||
findOneById(id: string) {
|
||||
return this.repository.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
findOneByName(name: string) {
|
||||
return this.repository.findOne({ where: { name } });
|
||||
}
|
||||
|
||||
create(data: Partial<TMWorkspaceType>) {
|
||||
return this.repository.create(data);
|
||||
}
|
||||
|
||||
save(workspaceType: TMWorkspaceType) {
|
||||
return this.repository.save(workspaceType);
|
||||
}
|
||||
|
||||
update(id: string, data: Partial<TMWorkspaceType>) {
|
||||
return this.repository.update(id, data);
|
||||
}
|
||||
|
||||
delete(id: string) {
|
||||
return this.repository.delete(id);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ export class WorkspaceRepository {
|
||||
findOneById(id: string) {
|
||||
return this.repository.findOne({
|
||||
where: { id },
|
||||
relations: ["workspaceType", "projects", "users"],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body } from '@nestjs/common';
|
||||
import { TMWorkspace } from './entities/workspace.entity';
|
||||
import { WorkspaceService } from './providers/workspace.service';
|
||||
import { CreateWorkspaceDto } from './dto/workspace/create-workspace.dto';
|
||||
import { UpdateWorkspaceDto } from './dto/workspace/update-workspace.dto';
|
||||
|
||||
@Controller('task-manager')
|
||||
export class TaskManagerController {
|
||||
constructor(
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
) {}
|
||||
// --- Workspace ---
|
||||
|
||||
@Get('workspaces')
|
||||
findAllWorkspaces(): Promise<TMWorkspace[]> {
|
||||
return this.workspaceService.findAll();
|
||||
}
|
||||
|
||||
@Get('workspaces/:id')
|
||||
findOneWorkspace(@Param('id') id: string): Promise<TMWorkspace> {
|
||||
return this.workspaceService.findOne(id);
|
||||
}
|
||||
|
||||
@Post('workspaces')
|
||||
createWorkspace(@Body() body: CreateWorkspaceDto): Promise<TMWorkspace> {
|
||||
return this.workspaceService.create(body);
|
||||
}
|
||||
|
||||
@Patch('workspaces/:id')
|
||||
updateWorkspace(@Param('id') id: string, @Body() body: UpdateWorkspaceDto): Promise<TMWorkspace> {
|
||||
return this.workspaceService.update(id, body);
|
||||
}
|
||||
|
||||
@Delete('workspaces/:id')
|
||||
removeWorkspace(@Param('id') id: string): Promise<void> {
|
||||
return this.workspaceService.remove(id);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { TMWorkspaceType } from './entities/workspace-type.entity';
|
||||
import { TMWorkspace } from './entities/workspace.entity';
|
||||
import { TMProject } from './entities/project.entity';
|
||||
import { TMTaskPhase } from './entities/task-phase.entity';
|
||||
import { TMTask } from './entities/task.entity';
|
||||
import { TMRemark } from './entities/remark.entity';
|
||||
import { TMAttachment } from './entities/attachment.entity';
|
||||
import { TMCheckListItem } from './entities/check-list-item.entity';
|
||||
import { WorkspaceRepository } from './repositories/workspace.repository';
|
||||
import { TaskManagerController } from './task-manager.controller';
|
||||
import { WorkspaceService } from './providers/workspace.service';
|
||||
import { TMWorkspaceType } from "./entities/workspace-type.entity";
|
||||
import { TMWorkspace } from "./entities/workspace.entity";
|
||||
import { TMProject } from "./entities/project.entity";
|
||||
import { TMTaskPhase } from "./entities/task-phase.entity";
|
||||
import { TMTask } from "./entities/task.entity";
|
||||
import { TMRemark } from "./entities/remark.entity";
|
||||
import { TMAttachment } from "./entities/attachment.entity";
|
||||
import { TMCheckListItem } from "./entities/check-list-item.entity";
|
||||
import { WorkspaceRepository } from "./repositories/workspace.repository";
|
||||
import { WorkspaceController } from "./controllers/workspace.controller";
|
||||
import { WorkspaceService } from "./providers/workspace.service";
|
||||
import { User } from "../users/entities/user.entity";
|
||||
import { WorkspaceTypeService } from "./providers/workspace-type.service";
|
||||
import { WorkspaceTypeRepository } from "./repositories/workspace-type.repository";
|
||||
import { WorkspaceTypeController } from "./controllers/workspace-type.controller";
|
||||
|
||||
// import { WorkspaceTypeService } from './providers/workspace-type.service';
|
||||
// import { WorkspaceService } from './providers/workspace.service';
|
||||
@@ -23,29 +27,18 @@ import { WorkspaceService } from './providers/workspace.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
TMWorkspaceType,
|
||||
TMWorkspace,
|
||||
TMProject,
|
||||
TMTaskPhase,
|
||||
TMTask,
|
||||
TMRemark,
|
||||
TMAttachment,
|
||||
TMCheckListItem,
|
||||
]),
|
||||
TypeOrmModule.forFeature([TMWorkspaceType, TMWorkspace, TMProject, TMTaskPhase, TMTask, TMRemark, TMAttachment, TMCheckListItem, User]),
|
||||
],
|
||||
controllers: [TaskManagerController],
|
||||
controllers: [WorkspaceController, WorkspaceTypeController],
|
||||
providers: [
|
||||
WorkspaceRepository,
|
||||
WorkspaceService,
|
||||
// WorkspaceTypeService,
|
||||
WorkspaceTypeRepository,
|
||||
WorkspaceTypeService,
|
||||
// ProjectService,
|
||||
// TaskPhaseService,
|
||||
// TaskService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceService,
|
||||
WorkspaceRepository
|
||||
],
|
||||
exports: [WorkspaceService, WorkspaceRepository, WorkspaceTypeService, WorkspaceTypeRepository],
|
||||
})
|
||||
export class TaskManagerModule {}
|
||||
export class TaskManagerModule {}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { PartialType } from "@nestjs/swagger";
|
||||
|
||||
import { CreateRoleDto } from "./create-role.dto";
|
||||
|
||||
export class UpdateRoleDto extends PartialType(CreateRoleDto) {}
|
||||
@@ -13,6 +13,7 @@ import { CreateRoleDto } from "../DTO/create-role.dto";
|
||||
import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto";
|
||||
import { SearchRolesQueryDto } from "../DTO/search-roles.dto";
|
||||
import { UpdateAdminDto } from "../DTO/update-admin.dto";
|
||||
import { UpdateRoleDto } from "../DTO/update-role.dto";
|
||||
import { RoleEnum } from "../enums/role.enum";
|
||||
import { PermissionsRepository } from "../repositories/permissions.repository";
|
||||
import { RoleRepository } from "../repositories/roles.repository";
|
||||
@@ -211,6 +212,62 @@ export class AdminsService {
|
||||
|
||||
/************************************************************ */
|
||||
|
||||
async getRoleById(id: string) {
|
||||
const role = await this.rolesRepository.findOne({
|
||||
where: { id, isAdmin: true },
|
||||
relations: { permissions: true },
|
||||
});
|
||||
if (!role) throw new BadRequestException(AdminMessage.ROLE_NOT_FOUND);
|
||||
return { role };
|
||||
}
|
||||
|
||||
/************************************************************ */
|
||||
|
||||
async updateRole(id: string, updateDto: UpdateRoleDto) {
|
||||
const role = await this.rolesRepository.findOne({
|
||||
where: { id, isAdmin: true },
|
||||
relations: { permissions: true },
|
||||
});
|
||||
if (!role) throw new BadRequestException(AdminMessage.ROLE_NOT_FOUND);
|
||||
|
||||
if (updateDto.name && updateDto.name !== role.name) {
|
||||
const existRole = await this.rolesRepository.findOne({ where: { name: updateDto.name, id: Not(id) } });
|
||||
if (existRole) throw new BadRequestException(AdminMessage.ROLE_EXIST);
|
||||
role.name = updateDto.name;
|
||||
}
|
||||
|
||||
if (updateDto.permissions) {
|
||||
const permissions = await this.permissionsRepository.findBy({ id: In(updateDto.permissions) });
|
||||
if (permissions.length !== updateDto.permissions.length) throw new BadRequestException(AdminMessage.PERMISSIONS_NOT_FOUND);
|
||||
role.permissions = permissions;
|
||||
}
|
||||
|
||||
await this.rolesRepository.save(role);
|
||||
return {
|
||||
message: AdminMessage.ROLE_UPDATED,
|
||||
role,
|
||||
};
|
||||
}
|
||||
|
||||
/************************************************************ */
|
||||
|
||||
async deleteRole(id: string) {
|
||||
const role = await this.rolesRepository.findOne({ where: { id, isAdmin: true } });
|
||||
if (!role) throw new BadRequestException(AdminMessage.ROLE_NOT_FOUND);
|
||||
|
||||
if (role.name === RoleEnum.SUPER_ADMIN) throw new BadRequestException(AdminMessage.NOT_ALLOWED);
|
||||
|
||||
const userCount = await this.userRepository.count({ where: { roles: { id } } });
|
||||
if (userCount > 0) throw new BadRequestException(AdminMessage.ROLE_HAS_USERS);
|
||||
|
||||
await this.rolesRepository.remove(role);
|
||||
return {
|
||||
message: AdminMessage.ROLE_DELETED,
|
||||
};
|
||||
}
|
||||
|
||||
/************************************************************ */
|
||||
|
||||
async getAdmins(queryDto: SearchAdminQueryDto) {
|
||||
const [users, count] = await this.userRepository.getAdmins(queryDto);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { SearchCustomersDto } from "./DTO/search-customers.dto";
|
||||
import { SearchRolesQueryDto } from "./DTO/search-roles.dto";
|
||||
import { UpdateAdminDto } from "./DTO/update-admin.dto";
|
||||
import { UpdateCustomerDto } from "./DTO/update-customer.dto";
|
||||
import { UpdateRoleDto } from "./DTO/update-role.dto";
|
||||
import { UpdateProfileDto } from "./DTO/update-profile.dto";
|
||||
import { CreateUserGroupDto } from "./DTO/user-group.dto";
|
||||
import { EmailVerifyQueryDto } from "./DTO/verify-email-query.dto";
|
||||
@@ -222,6 +223,13 @@ export class UsersController {
|
||||
return this.adminsService.getRoles(queryDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get role by id ==> admin route" })
|
||||
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
|
||||
@Get("roles/:id")
|
||||
getRoleById(@Param() paramDto: ParamDto) {
|
||||
return this.adminsService.getRoleById(paramDto.id);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "create role ==> admin route" })
|
||||
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
|
||||
@Post("roles")
|
||||
@@ -229,5 +237,17 @@ export class UsersController {
|
||||
return this.adminsService.createRole(createDto);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation({ summary: "update role ==> admin route" })
|
||||
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
|
||||
@Patch("roles/:id")
|
||||
updateRole(@Param() paramDto: ParamDto, @Body() updateDto: UpdateRoleDto) {
|
||||
return this.adminsService.updateRole(paramDto.id, updateDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "delete role ==> admin route" })
|
||||
@PermissionsDec(PermissionEnum.ADMINS, PermissionEnum.CUSTOMERS)
|
||||
@Delete("roles/:id")
|
||||
deleteRole(@Param() paramDto: ParamDto) {
|
||||
return this.adminsService.deleteRole(paramDto.id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user