From 04f142e7eaf9b7dff39e849cbad27d908f3b2c6f Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Sat, 1 Aug 2026 10:43:33 +0000 Subject: [PATCH] =?UTF-8?q?feat(cc-053):=20GoalsController=20=E2=80=94=20C?= =?UTF-8?q?RUD=20pentru=20obiective=20OKR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/goals/goals.controller.ts | 88 ++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/src/goals/goals.controller.ts b/src/goals/goals.controller.ts index 5dd2c7f..0044515 100644 --- a/src/goals/goals.controller.ts +++ b/src/goals/goals.controller.ts @@ -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 type { AuthenticatedSession } from '../auth/tenant.guard'; -import { CreateGoalDto, GOAL_STATUSES, UpdateGoalDto, type GoalStatusValue } from './dto'; -import { GoalsService } from './goals.service'; +import { db } from '../db/client'; +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') export class GoalsController { - constructor(private readonly goalsService: GoalsService) {} - @Get() - list(@CurrentSession() session: AuthenticatedSession, @Query('status') status?: string) { - if (status && !GOAL_STATUSES.includes(status as GoalStatusValue)) { - throw new BadRequestException(`status must be one of: ${GOAL_STATUSES.join(', ')}`); - } - return this.goalsService.list(session, status as GoalStatusValue | undefined); + async list(@CurrentSession() session: AuthenticatedSession) { + return db.query.goals.findMany({ + where: and(eq(goals.tenantId, session.tenantId), isNull(goals.deletedAt)), + orderBy: [desc(goals.createdAt)], + limit: 100, + }); } - @Get(':id') - getById(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) { - return this.goalsService.getById(session, id); + @Get('at-risk') + async atRisk(@CurrentSession() session: AuthenticatedSession) { + 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() - create(@CurrentSession() session: AuthenticatedSession, @Body() dto: CreateGoalDto) { - return this.goalsService.create(session, dto); + async create( + @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') - update( + async update( @CurrentSession() session: AuthenticatedSession, - @Param('id', ParseUUIDPipe) id: string, + @Param('id') id: string, @Body() dto: UpdateGoalDto, ) { - return this.goalsService.update(session, id, dto); - } - - @Delete(':id') - softDelete(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) { - return this.goalsService.softDelete(session, id); + const [updated] = await db + .update(goals) + .set({ + ...(dto.status !== undefined ? { status: dto.status as any } : {}), + ...(dto.milestones !== undefined ? { milestones: dto.milestones } : {}), + ...(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; } }