feat: log outbound SMS and show history in compose modal
Persist manual, campaign, and automation SMS; expose history APIs for people and leads. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -132,6 +132,19 @@ export class PeopleController {
|
||||
return this.peopleService.birthdaysThisMonth(user);
|
||||
}
|
||||
|
||||
@Get(':id/sms-history')
|
||||
smsHistory(
|
||||
@CurrentUser() user: Actor,
|
||||
@Param('id') id: string,
|
||||
@Query('limit') limit?: string,
|
||||
) {
|
||||
return this.peopleService.listSmsHistory(
|
||||
user,
|
||||
id,
|
||||
limit ? Number(limit) : 50,
|
||||
);
|
||||
}
|
||||
|
||||
@Post(':id/sms')
|
||||
sms(
|
||||
@CurrentUser() user: Actor,
|
||||
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
import { FILTERED_IDS_MAX } from '@mento/shared';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { OutboundSmsLogService } from '../outbound-sms/outbound-sms-log.service';
|
||||
import { CompaniesService } from './companies.service';
|
||||
import { emptyToNull, normalizeEmail, normalizePhone } from './normalize';
|
||||
import {
|
||||
@@ -111,6 +112,7 @@ export class PeopleService implements OnModuleInit {
|
||||
private readonly webhooks: WebhooksService,
|
||||
private readonly game: GamificationService,
|
||||
private readonly sms: SmsService,
|
||||
private readonly outboundSms: OutboundSmsLogService,
|
||||
) {}
|
||||
|
||||
private personListWhere(
|
||||
@@ -633,6 +635,16 @@ export class PeopleService implements OnModuleInit {
|
||||
});
|
||||
if (!message) throw new BadRequestException('متن پیامک خالی است.');
|
||||
const result = await this.sms.sendSmsDetailed(mobile, message);
|
||||
await this.outboundSms.record({
|
||||
organizationId: actor.organizationId,
|
||||
personId: person.id,
|
||||
mobile,
|
||||
body: message,
|
||||
source: 'manual',
|
||||
status: result.ok ? 'sent' : 'failed',
|
||||
error: result.ok ? null : result.reason,
|
||||
actorUserId: actor.userId,
|
||||
});
|
||||
if (!result.ok) {
|
||||
throw new ServiceUnavailableException(
|
||||
result.reason === 'SMS not configured'
|
||||
@@ -659,6 +671,16 @@ export class PeopleService implements OnModuleInit {
|
||||
});
|
||||
if (!message) throw new BadRequestException('متن پیامک خالی است.');
|
||||
const result = await this.sms.sendSmsDetailed(mobile, message);
|
||||
await this.outboundSms.record({
|
||||
organizationId: actor.organizationId,
|
||||
personId: person.id,
|
||||
mobile,
|
||||
body: message,
|
||||
source: 'manual',
|
||||
status: result.ok ? 'sent' : 'failed',
|
||||
error: result.ok ? null : result.reason,
|
||||
actorUserId: actor.userId,
|
||||
});
|
||||
if (!result.ok) {
|
||||
throw new ServiceUnavailableException(
|
||||
result.reason === 'SMS not configured'
|
||||
@@ -669,6 +691,12 @@ export class PeopleService implements OnModuleInit {
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
listSmsHistory(actor: Actor, personId: string, limit?: number) {
|
||||
return this.get(actor, personId).then(() =>
|
||||
this.outboundSms.historyForPerson(actor.organizationId, personId, limit),
|
||||
);
|
||||
}
|
||||
|
||||
private toDto(row: PersonRow): PersonDto {
|
||||
const previousFullName = `${row.previousFirstName ?? ''} ${row.previousLastName ?? ''}`.trim();
|
||||
const importSource = row.importSource || 'manual';
|
||||
|
||||
Reference in New Issue
Block a user