168 lines
4.0 KiB
TypeScript
168 lines
4.0 KiB
TypeScript
import {
|
|||
|
|
Controller,
|
||
|
|
Get,
|
||
|
|
Param,
|
||
|
|
Query,
|
||
|
|
UseGuards,
|
||
|
|
} from '@nestjs/common';
|
||
|
|
import { IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
|
||
|
|
import { Type } from 'class-transformer';
|
||
|
|
import { CurrentUser } from '../auth/current-user.decorator';
|
||
|
|
import { PrismaService } from '../prisma/prisma.service';
|
||
|
|
import { ApiKeyAuthGuard } from './api-key-auth.guard';
|
||
|
|
|
||
|
|
class ListQueryDto {
|
||
|
|
@IsOptional()
|
||
|
|
@IsString()
|
||
|
|
q?: string;
|
||
|
|
|
||
|
|
@IsOptional()
|
||
|
|
@Type(() => Number)
|
||
|
|
@IsInt()
|
||
|
|
@Min(1)
|
||
|
|
page?: number = 1;
|
||
|
|
|
||
|
|
@IsOptional()
|
||
|
|
@Type(() => Number)
|
||
|
|
@IsInt()
|
||
|
|
@Min(1)
|
||
|
|
@Max(100)
|
||
|
|
limit?: number = 20;
|
||
|
|
}
|
||
|
|
|
||
|
|
@Controller('public/v1')
|
||
|
|
@UseGuards(ApiKeyAuthGuard)
|
||
|
|
export class PublicApiV1Controller {
|
||
|
|
constructor(private readonly prisma: PrismaService) {}
|
||
|
|
|
||
|
|
@Get('people')
|
||
|
|
async listPeople(
|
||
|
|
@CurrentUser() user: { organizationId: string },
|
||
|
|
@Query() query: ListQueryDto,
|
||
|
|
) {
|
||
|
|
const page = query.page ?? 1;
|
||
|
|
const limit = query.limit ?? 20;
|
||
|
|
const where = {
|
||
|
|
organizationId: user.organizationId,
|
||
|
|
...(query.q?.trim()
|
||
|
|
? {
|
||
|
|
OR: [
|
||
|
|
{ firstName: { contains: query.q.trim(), mode: 'insensitive' as const } },
|
||
|
|
{ lastName: { contains: query.q.trim(), mode: 'insensitive' as const } },
|
||
|
|
{ mobile: { contains: query.q.trim() } },
|
||
|
|
{ email: { contains: query.q.trim(), mode: 'insensitive' as const } },
|
||
|
|
],
|
||
|
|
}
|
||
|
|
: {}),
|
||
|
|
};
|
||
|
|
const [total, items] = await Promise.all([
|
||
|
|
this.prisma.person.count({ where }),
|
||
|
|
this.prisma.person.findMany({
|
||
|
|
where,
|
||
|
|
select: {
|
||
|
|
id: true,
|
||
|
|
firstName: true,
|
||
|
|
lastName: true,
|
||
|
|
email: true,
|
||
|
|
mobile: true,
|
||
|
|
phone: true,
|
||
|
|
createdAt: true,
|
||
|
|
updatedAt: true,
|
||
|
|
},
|
||
|
|
orderBy: { updatedAt: 'desc' },
|
||
|
|
skip: (page - 1) * limit,
|
||
|
|
take: limit,
|
||
|
|
}),
|
||
|
|
]);
|
||
|
|
return {
|
||
|
|
total,
|
||
|
|
page,
|
||
|
|
limit,
|
||
|
|
items: items.map((p) => ({
|
||
|
|
...p,
|
||
|
|
fullName: `${p.firstName} ${p.lastName}`.trim(),
|
||
|
|
createdAt: p.createdAt.toISOString(),
|
||
|
|
updatedAt: p.updatedAt.toISOString(),
|
||
|
|
})),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
@Get('people/:id')
|
||
|
|
async getPerson(
|
||
|
|
@CurrentUser() user: { organizationId: string },
|
||
|
|
@Param('id') id: string,
|
||
|
|
) {
|
||
|
|
const p = await this.prisma.person.findFirst({
|
||
|
|
where: { id, organizationId: user.organizationId },
|
||
|
|
select: {
|
||
|
|
id: true,
|
||
|
|
firstName: true,
|
||
|
|
lastName: true,
|
||
|
|
email: true,
|
||
|
|
mobile: true,
|
||
|
|
phone: true,
|
||
|
|
notes: true,
|
||
|
|
tags: true,
|
||
|
|
createdAt: true,
|
||
|
|
updatedAt: true,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (!p) return null;
|
||
|
|
return {
|
||
|
|
...p,
|
||
|
|
fullName: `${p.firstName} ${p.lastName}`.trim(),
|
||
|
|
createdAt: p.createdAt.toISOString(),
|
||
|
|
updatedAt: p.updatedAt.toISOString(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
@Get('deals')
|
||
|
|
async listDeals(
|
||
|
|
@CurrentUser() user: { organizationId: string },
|
||
|
|
@Query() query: ListQueryDto,
|
||
|
|
) {
|
||
|
|
const page = query.page ?? 1;
|
||
|
|
const limit = query.limit ?? 20;
|
||
|
|
const where = {
|
||
|
|
organizationId: user.organizationId,
|
||
|
|
...(query.q?.trim()
|
||
|
|
? {
|
||
|
|
title: { contains: query.q.trim(), mode: 'insensitive' as const },
|
||
|
|
}
|
||
|
|
: {}),
|
||
|
|
};
|
||
|
|
const [total, items] = await Promise.all([
|
||
|
|
this.prisma.deal.count({ where }),
|
||
|
|
this.prisma.deal.findMany({
|
||
|
|
where,
|
||
|
|
select: {
|
||
|
|
id: true,
|
||
|
|
title: true,
|
||
|
|
status: true,
|
||
|
|
amount: true,
|
||
|
|
currency: true,
|
||
|
|
source: true,
|
||
|
|
stageId: true,
|
||
|
|
pipelineId: true,
|
||
|
|
createdAt: true,
|
||
|
|
updatedAt: true,
|
||
|
|
},
|
||
|
|
orderBy: { updatedAt: 'desc' },
|
||
|
|
skip: (page - 1) * limit,
|
||
|
|
take: limit,
|
||
|
|
}),
|
||
|
|
]);
|
||
|
|
return {
|
||
|
|
total,
|
||
|
|
page,
|
||
|
|
limit,
|
||
|
|
items: items.map((d) => ({
|
||
|
|
...d,
|
||
|
|
amount: d.amount == null ? null : Number(d.amount),
|
||
|
|
createdAt: d.createdAt.toISOString(),
|
||
|
|
updatedAt: d.updatedAt.toISOString(),
|
||
|
|
})),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|