import { Body, Controller, Delete, Get, Param, Put, UseGuards, } from '@nestjs/common'; import { ArrayMaxSize, IsArray, IsString, Matches, MaxLength, } from 'class-validator'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { CurrentUser } from '../auth/current-user.decorator'; import { PrismaService } from '../prisma/prisma.service'; const TABLE_KEY_RE = /^[a-z0-9_.-]{1,80}$/i; class SaveTablePrefDto { @IsArray() @ArrayMaxSize(80) @IsString({ each: true }) @MaxLength(80, { each: true }) hiddenColumns!: string[]; } class TableKeyParam { @Matches(TABLE_KEY_RE) key!: string; } type Actor = { userId: string; organizationId: string }; @Controller('table-prefs') @UseGuards(JwtAuthGuard) export class TablePrefsController { constructor(private readonly prisma: PrismaService) {} @Get() async list(@CurrentUser() user: Actor) { const rows = await this.prisma.userTablePreference.findMany({ where: { userId: user.userId, organizationId: user.organizationId }, select: { tableKey: true, hiddenColumns: true }, }); const out: Record = {}; for (const row of rows) out[row.tableKey] = row.hiddenColumns; return out; } @Put(':key') async save( @CurrentUser() user: Actor, @Param() params: TableKeyParam, @Body() dto: SaveTablePrefDto, ) { const hiddenColumns = [...new Set(dto.hiddenColumns.map((c) => c.trim()).filter(Boolean))]; const row = await this.prisma.userTablePreference.upsert({ where: { userId_organizationId_tableKey: { userId: user.userId, organizationId: user.organizationId, tableKey: params.key, }, }, create: { userId: user.userId, organizationId: user.organizationId, tableKey: params.key, hiddenColumns, }, update: { hiddenColumns }, select: { tableKey: true, hiddenColumns: true }, }); return row; } @Delete(':key') async reset(@CurrentUser() user: Actor, @Param() params: TableKeyParam) { await this.prisma.userTablePreference.deleteMany({ where: { userId: user.userId, organizationId: user.organizationId, tableKey: params.key, }, }); return { ok: true }; } }