feat(cc-053): GoalsController — CRUD pentru obiective OKR
This commit is contained in:
parent
871dac9f0a
commit
04f142e7ea
1 changed files with 65 additions and 23 deletions
|
|
@ -1,42 +1,84 @@
|
||||||
import { BadRequestException, Body, Controller, Delete, Get, Param, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common';
|
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
|
||||||
|
import { IsOptional, IsString } from 'class-validator';
|
||||||
|
import { and, desc, eq, isNull, or, inArray } from 'drizzle-orm';
|
||||||
import { CurrentSession } from '../auth/session.decorator';
|
import { CurrentSession } from '../auth/session.decorator';
|
||||||
import type { AuthenticatedSession } from '../auth/tenant.guard';
|
import type { AuthenticatedSession } from '../auth/tenant.guard';
|
||||||
import { CreateGoalDto, GOAL_STATUSES, UpdateGoalDto, type GoalStatusValue } from './dto';
|
import { db } from '../db/client';
|
||||||
import { GoalsService } from './goals.service';
|
import { goals } from '../db/schema';
|
||||||
|
|
||||||
|
class CreateGoalDto {
|
||||||
|
@IsString() horizon: string;
|
||||||
|
@IsString() metric: string;
|
||||||
|
@IsString() target: string;
|
||||||
|
@IsOptional() @IsString() workspaceId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateGoalDto {
|
||||||
|
@IsOptional() @IsString() status?: string;
|
||||||
|
@IsOptional() milestones?: unknown[];
|
||||||
|
@IsOptional() @IsString() target?: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Controller('goals')
|
@Controller('goals')
|
||||||
export class GoalsController {
|
export class GoalsController {
|
||||||
constructor(private readonly goalsService: GoalsService) {}
|
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
list(@CurrentSession() session: AuthenticatedSession, @Query('status') status?: string) {
|
async list(@CurrentSession() session: AuthenticatedSession) {
|
||||||
if (status && !GOAL_STATUSES.includes(status as GoalStatusValue)) {
|
return db.query.goals.findMany({
|
||||||
throw new BadRequestException(`status must be one of: ${GOAL_STATUSES.join(', ')}`);
|
where: and(eq(goals.tenantId, session.tenantId), isNull(goals.deletedAt)),
|
||||||
}
|
orderBy: [desc(goals.createdAt)],
|
||||||
return this.goalsService.list(session, status as GoalStatusValue | undefined);
|
limit: 100,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get('at-risk')
|
||||||
getById(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) {
|
async atRisk(@CurrentSession() session: AuthenticatedSession) {
|
||||||
return this.goalsService.getById(session, id);
|
return db.query.goals.findMany({
|
||||||
|
where: and(
|
||||||
|
eq(goals.tenantId, session.tenantId),
|
||||||
|
isNull(goals.deletedAt),
|
||||||
|
inArray(goals.status, ['at_risk', 'not_started']),
|
||||||
|
),
|
||||||
|
orderBy: [desc(goals.createdAt)],
|
||||||
|
limit: 20,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
create(@CurrentSession() session: AuthenticatedSession, @Body() dto: CreateGoalDto) {
|
async create(
|
||||||
return this.goalsService.create(session, dto);
|
@CurrentSession() session: AuthenticatedSession,
|
||||||
|
@Body() dto: CreateGoalDto,
|
||||||
|
) {
|
||||||
|
const [created] = await db
|
||||||
|
.insert(goals)
|
||||||
|
.values({
|
||||||
|
tenantId: session.tenantId,
|
||||||
|
workspaceId: dto.workspaceId ?? null,
|
||||||
|
ownerUserId: session.userId,
|
||||||
|
horizon: dto.horizon,
|
||||||
|
metric: dto.metric,
|
||||||
|
target: dto.target,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
update(
|
async update(
|
||||||
@CurrentSession() session: AuthenticatedSession,
|
@CurrentSession() session: AuthenticatedSession,
|
||||||
@Param('id', ParseUUIDPipe) id: string,
|
@Param('id') id: string,
|
||||||
@Body() dto: UpdateGoalDto,
|
@Body() dto: UpdateGoalDto,
|
||||||
) {
|
) {
|
||||||
return this.goalsService.update(session, id, dto);
|
const [updated] = await db
|
||||||
}
|
.update(goals)
|
||||||
|
.set({
|
||||||
@Delete(':id')
|
...(dto.status !== undefined ? { status: dto.status as any } : {}),
|
||||||
softDelete(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) {
|
...(dto.milestones !== undefined ? { milestones: dto.milestones } : {}),
|
||||||
return this.goalsService.softDelete(session, id);
|
...(dto.target !== undefined ? { target: dto.target } : {}),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
version: db.$count(goals, eq(goals.id, id)),
|
||||||
|
})
|
||||||
|
.where(and(eq(goals.id, id), eq(goals.tenantId, session.tenantId)))
|
||||||
|
.returning();
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue