mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 06:26:40 +02:00
Backend (CRM-Expert Phase 1): - New enums: ContactSource, EntityStatus, CompanySize, OwnerRole, LostReason, EmailType, PhoneType - Contact: add linkedinUrl, birthday, source, department, status - Company: add vatId, taxId, tradeRegisterNumber, registerCourt, companySize, deliveryAddress, dataEnrichedAt/Source, status - Deal: add lostReason + lostReasonText (required when status=LOST) - Multi-value emails/phones tables (contact_emails, contact_phones) - Owner m:n model (contact_owners, company_owners, deal_owners) - Redis Pub/Sub CRM events (crm.contact.created, crm.deal.won, etc.) - Activity due_soon scheduler (cron every 15 min) - SQL migration with data migration for existing records Frontend integration: - types.ts: all new enums, interfaces, label maps - api.ts: owner CRUD endpoints (add/remove for contacts/companies/deals) - hooks.ts: 6 new owner mutation hooks - ContactFormModal: LinkedIn, birthday, source, department, status fields - ContactDetailPage: display new fields (LinkedIn, department, birthday, source, status badge) - CompanyDetailPage: display vatId, taxId, trade register, company size, delivery address, data enrichment info - DealFormModal: lost reason dropdown + text (shown when status=LOST) - DealDetailPage: display lost reason with label - CompaniesPage: EntityStatus-aware status dots (ACTIVE/INACTIVE/BLOCKED) - ActivityType: add FOLLOWUP to all label maps Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
3.9 KiB
TypeScript
150 lines
3.9 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
Post,
|
|
Patch,
|
|
Delete,
|
|
Body,
|
|
Param,
|
|
Query,
|
|
ParseUUIDPipe,
|
|
HttpCode,
|
|
HttpStatus,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import {
|
|
ApiTags,
|
|
ApiOperation,
|
|
ApiBearerAuth,
|
|
ApiParam,
|
|
} from '@nestjs/swagger';
|
|
import { DealsService } from './deals.service';
|
|
import { CreateDealDto } from './dto/create-deal.dto';
|
|
import { UpdateDealDto } from './dto/update-deal.dto';
|
|
import { QueryDealsDto } from './dto/query-deals.dto';
|
|
import { AddOwnerDto } from '../common/dto/owner.dto';
|
|
import { OwnersService } from '../owners/owners.service';
|
|
import { CurrentUser, JwtPayload } from '../common/decorators';
|
|
import { TenantGuard } from '../auth/guards/tenant.guard';
|
|
import {
|
|
paginatedResponse,
|
|
singleResponse,
|
|
} from '../common/dto/pagination.dto';
|
|
|
|
@ApiTags('Vorgaenge (Deals)')
|
|
@ApiBearerAuth('access-token')
|
|
@UseGuards(TenantGuard)
|
|
@Controller('deals')
|
|
export class DealsController {
|
|
constructor(
|
|
private readonly dealsService: DealsService,
|
|
private readonly ownersService: OwnersService,
|
|
) {}
|
|
|
|
@Post()
|
|
@HttpCode(HttpStatus.CREATED)
|
|
@ApiOperation({ summary: 'Vorgang erstellen' })
|
|
async create(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Body() dto: CreateDealDto,
|
|
) {
|
|
const deal = await this.dealsService.create(
|
|
user.tenantId!,
|
|
user.sub,
|
|
dto,
|
|
);
|
|
return singleResponse(deal);
|
|
}
|
|
|
|
@Get()
|
|
@ApiOperation({ summary: 'Vorgaenge auflisten (paginiert, filterbar)' })
|
|
async findAll(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Query() query: QueryDealsDto,
|
|
) {
|
|
const result = await this.dealsService.findAll(user.tenantId!, query);
|
|
return paginatedResponse(
|
|
result.data,
|
|
result.total,
|
|
result.page,
|
|
result.pageSize,
|
|
);
|
|
}
|
|
|
|
@Get(':id')
|
|
@ApiOperation({ summary: 'Vorgangsdetails abrufen' })
|
|
@ApiParam({ name: 'id', type: 'string', format: 'uuid' })
|
|
async findOne(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
) {
|
|
const deal = await this.dealsService.findOne(user.tenantId!, id);
|
|
return singleResponse(deal);
|
|
}
|
|
|
|
@Patch(':id')
|
|
@ApiOperation({ summary: 'Vorgang aktualisieren' })
|
|
@ApiParam({ name: 'id', type: 'string', format: 'uuid' })
|
|
async update(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: UpdateDealDto,
|
|
) {
|
|
const deal = await this.dealsService.update(
|
|
user.tenantId!,
|
|
id,
|
|
user.sub,
|
|
dto,
|
|
);
|
|
return singleResponse(deal);
|
|
}
|
|
|
|
@Delete(':id')
|
|
@ApiOperation({ summary: 'Vorgang loeschen' })
|
|
@ApiParam({ name: 'id', type: 'string', format: 'uuid' })
|
|
async remove(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
) {
|
|
const deal = await this.dealsService.remove(user.tenantId!, id);
|
|
return singleResponse(deal);
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Owner Endpoints
|
|
// --------------------------------------------------------
|
|
|
|
@Post(':id/owners')
|
|
@HttpCode(HttpStatus.CREATED)
|
|
@ApiOperation({ summary: 'Owner zu Vorgang hinzufuegen' })
|
|
@ApiParam({ name: 'id', type: 'string', format: 'uuid' })
|
|
async addOwner(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Body() dto: AddOwnerDto,
|
|
) {
|
|
const owner = await this.ownersService.addDealOwner(
|
|
user.tenantId!,
|
|
id,
|
|
dto,
|
|
);
|
|
return singleResponse(owner);
|
|
}
|
|
|
|
@Delete(':id/owners/:userId')
|
|
@ApiOperation({ summary: 'Owner von Vorgang entfernen' })
|
|
@ApiParam({ name: 'id', type: 'string', format: 'uuid' })
|
|
@ApiParam({ name: 'userId', type: 'string', format: 'uuid' })
|
|
async removeOwner(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Param('userId', ParseUUIDPipe) userId: string,
|
|
) {
|
|
const owner = await this.ownersService.removeDealOwner(
|
|
user.tenantId!,
|
|
id,
|
|
userId,
|
|
);
|
|
return singleResponse(owner);
|
|
}
|
|
}
|