Add project board, loyalty club, and if/then automations.

Separate modules for post-sales kanban, person points/tiers, and deal-won/ticket-resolved rules without overlapping the sales pipeline.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-30 09:42:07 +03:30
co-authored by Cursor
parent 2e6c4779ab
commit c2bfa7749d
29 changed files with 3126 additions and 9 deletions
@@ -0,0 +1,73 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
Query,
UseGuards,
} from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CurrentUser } from '../auth/current-user.decorator';
import { Roles } from '../auth/roles.decorator';
import { RolesGuard } from '../auth/roles.guard';
import type { Actor } from '../common/record-scope';
import {
AdjustLoyaltyPointsDto,
CreateLoyaltyTierDto,
UpdateLoyaltyTierDto,
} from './dto';
import { LoyaltyService } from './loyalty.service';
@Controller('loyalty')
@UseGuards(JwtAuthGuard, RolesGuard)
export class LoyaltyController {
constructor(private readonly loyalty: LoyaltyService) {}
@Get('tiers')
listTiers(@CurrentUser() user: Actor) {
return this.loyalty.listTiers(user.organizationId);
}
@Post('tiers')
@Roles('owner', 'admin')
createTier(
@CurrentUser() user: Actor,
@Body() dto: CreateLoyaltyTierDto,
) {
return this.loyalty.createTier(user.organizationId, dto);
}
@Patch('tiers/:id')
@Roles('owner', 'admin')
updateTier(
@CurrentUser() user: Actor,
@Param('id') id: string,
@Body() dto: UpdateLoyaltyTierDto,
) {
return this.loyalty.updateTier(user.organizationId, id, dto);
}
@Delete('tiers/:id')
@Roles('owner', 'admin')
async deleteTier(@CurrentUser() user: Actor, @Param('id') id: string) {
await this.loyalty.deleteTier(user.organizationId, id);
return { ok: true };
}
@Get('members')
listMembers(@CurrentUser() user: Actor, @Query('q') q?: string) {
return this.loyalty.listMembers(user, q);
}
@Post('members/:personId/adjust')
adjust(
@CurrentUser() user: Actor,
@Param('personId') personId: string,
@Body() dto: AdjustLoyaltyPointsDto,
) {
return this.loyalty.adjustPoints(user, personId, dto.delta);
}
}