Add per-user saved column visibility for tables

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-05 11:56:49 +03:30
co-authored by Cursor
parent 75ff2c36c4
commit 23c83ffbee
29 changed files with 624 additions and 23 deletions
+2
View File
@@ -37,6 +37,7 @@ import { PlatformModule } from './platform/platform.module';
import { BillingModule } from './billing/billing.module';
import { AuditLogsModule } from './audit-logs/audit-logs.module';
import { SavedViewsModule } from './saved-views/saved-views.module';
import { TablePrefsModule } from './table-prefs/table-prefs.module';
import { PublicApiModule } from './public-api/public-api.module';
import { SmsCampaignsModule } from './sms-campaigns/sms-campaigns.module';
import { TeamsModule } from './teams/teams.module';
@@ -89,6 +90,7 @@ import { ConversationsModule } from './conversations/conversations.module';
TahesabModule,
AuditLogsModule,
SavedViewsModule,
TablePrefsModule,
PublicApiModule,
SmsCampaignsModule,
TrackingModule,
@@ -0,0 +1,92 @@
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<string, string[]> = {};
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 };
}
}
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { AuthModule } from '../auth/auth.module';
import { TablePrefsController } from './table-prefs.controller';
@Module({
imports: [AuthModule],
controllers: [TablePrefsController],
})
export class TablePrefsModule {}