78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
import { Body, Controller, Delete, Get, HttpCode, IsArray, IsOptional, IsString, NotFoundException, Param, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common';
|
|
import { and, desc, eq, isNull } from 'drizzle-orm';
|
|
import { CurrentSession } from '../auth/session.decorator';
|
|
import type { AuthenticatedSession } from '../auth/tenant.guard';
|
|
import { db } from '../db/client';
|
|
import { contacts } from '../db/schema';
|
|
|
|
class CreateContactDto {
|
|
@IsString() fullName!: string;
|
|
@IsOptional() @IsString() email?: string;
|
|
@IsOptional() @IsString() phone?: string;
|
|
@IsOptional() @IsString() role?: string;
|
|
@IsOptional() @IsString() organizationId?: string;
|
|
@IsOptional() @IsString() linkedinUrl?: string;
|
|
@IsOptional() @IsString() notes?: string;
|
|
@IsOptional() @IsArray() tags?: string[];
|
|
@IsOptional() @IsString() source?: string;
|
|
@IsOptional() @IsString() consentStatus?: string;
|
|
}
|
|
|
|
class UpdateContactDto {
|
|
@IsOptional() @IsString() fullName?: string;
|
|
@IsOptional() @IsString() email?: string;
|
|
@IsOptional() @IsString() phone?: string;
|
|
@IsOptional() @IsString() role?: string;
|
|
@IsOptional() @IsString() organizationId?: string;
|
|
@IsOptional() @IsString() linkedinUrl?: string;
|
|
@IsOptional() @IsString() notes?: string;
|
|
@IsOptional() @IsArray() tags?: string[];
|
|
@IsOptional() @IsString() consentStatus?: string;
|
|
}
|
|
|
|
@Controller('contacts')
|
|
export class ContactsController {
|
|
@Get()
|
|
async list(@CurrentSession() session: AuthenticatedSession, @Query('limit') limitStr?: string) {
|
|
const limit = Math.min(Math.max(1, parseInt(limitStr ?? '200', 10) || 200), 500);
|
|
return db.query.contacts.findMany({
|
|
where: and(eq(contacts.tenantId, session.tenantId), isNull(contacts.deletedAt)),
|
|
orderBy: desc(contacts.createdAt),
|
|
limit,
|
|
});
|
|
}
|
|
|
|
@Post()
|
|
async create(@CurrentSession() session: AuthenticatedSession, @Body() dto: CreateContactDto) {
|
|
const [contact] = await db.insert(contacts).values({
|
|
...dto,
|
|
tenantId: session.tenantId,
|
|
tags: dto.tags ?? [],
|
|
}).returning();
|
|
return contact;
|
|
}
|
|
|
|
@Get(':id')
|
|
async getOne(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) {
|
|
const contact = await db.query.contacts.findFirst({
|
|
where: and(eq(contacts.id, id), eq(contacts.tenantId, session.tenantId), isNull(contacts.deletedAt)),
|
|
});
|
|
if (!contact) throw new NotFoundException('Contact not found');
|
|
return contact;
|
|
}
|
|
|
|
@Patch(':id')
|
|
async update(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string, @Body() dto: UpdateContactDto) {
|
|
const [updated] = await db.update(contacts).set({ ...dto, updatedAt: new Date() })
|
|
.where(and(eq(contacts.id, id), eq(contacts.tenantId, session.tenantId))).returning();
|
|
if (!updated) throw new NotFoundException('Contact not found');
|
|
return updated;
|
|
}
|
|
|
|
@Delete(':id')
|
|
@HttpCode(204)
|
|
async remove(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) {
|
|
await db.update(contacts).set({ deletedAt: new Date() })
|
|
.where(and(eq(contacts.id, id), eq(contacts.tenantId, session.tenantId)));
|
|
}
|
|
}
|