feat: SMS campaigns for people or leads with filters and batching

Add audience tabs, province/city filters, select-all ids, dynamic SMS tokens, and 50-sized create/send chunks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-17 20:54:32 +03:30
co-authored by Cursor
parent 32c722f42a
commit d62e004e90
10 changed files with 1040 additions and 131 deletions
+32
View File
@@ -231,6 +231,34 @@ export class LeadsController {
return new StreamableFile(buffer);
}
@Get('ids')
listIds(
@CurrentUser() user: Actor,
@Query('q') q?: string,
@Query('stageId') stageId?: string,
@Query('assigneeId') assigneeId?: string,
@Query('city') city?: string,
@Query('region') region?: string,
@Query('trade') trade?: string,
@Query('source') source?: string,
@Query('unassigned') unassigned?: string,
@Query('archived') archived?: string,
@Query('hasMobile') hasMobile?: string,
) {
return this.leads.listIds(user, {
q,
stageId,
assigneeId,
city,
region,
trade,
source,
unassigned: unassigned === '1' || unassigned === 'true',
archived: archived === '1' || archived === 'true',
hasMobile: hasMobile === '1' || hasMobile === 'true',
});
}
@Get()
list(
@CurrentUser() user: Actor,
@@ -240,11 +268,13 @@ export class LeadsController {
@Query('stageId') stageId?: string,
@Query('assigneeId') assigneeId?: string,
@Query('city') city?: string,
@Query('region') region?: string,
@Query('trade') trade?: string,
@Query('source') source?: string,
@Query('unassigned') unassigned?: string,
@Query('archived') archived?: string,
@Query('sort') sort?: string,
@Query('hasMobile') hasMobile?: string,
) {
return this.leads.list(user, {
q,
@@ -253,11 +283,13 @@ export class LeadsController {
stageId,
assigneeId,
city,
region,
trade,
source,
unassigned: unassigned === '1' || unassigned === 'true',
archived: archived === '1' || archived === 'true',
sort,
hasMobile: hasMobile === '1' || hasMobile === 'true',
});
}