feat: باشگاه امتیازات کارکنان (موتور امتیاز، جدول رتبه، فروشگاه پاداش، نشانها، پنل مدیریت و گزارش ROI)
- موتور رویدادمحور با شرط، محدودیت، تأیید مدیر و idempotency - اتصال به لید، مشتری، معامله، فعالیت، روتین، پیشفاکتور، فاکتور و تیکت - صفحه /club و /club/admin، اعلان زنده امتیاز، آیتم منو - انتقال فرمساز سرنخ به ابزارها Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -25,6 +25,8 @@ import {
|
||||
ownerWhere,
|
||||
} from '../common/record-scope';
|
||||
import { normalizeTags } from '../common/tags';
|
||||
import { GamificationService } from '../gamification/gamification.service';
|
||||
import { tehranDayKey } from '../routines/routine-dates';
|
||||
import { buildSearchWhere, ci } from '../common/search';
|
||||
import { ActivityStagesService, type StageDefaults } from './activity-stages.service';
|
||||
import {
|
||||
@@ -96,6 +98,7 @@ export class ActivitiesService {
|
||||
private readonly messageAutomations: MessageAutomationsService,
|
||||
private readonly stages: ActivityStagesService,
|
||||
private readonly types: ActivityTypesService,
|
||||
private readonly game: GamificationService,
|
||||
) {}
|
||||
|
||||
private async ctx(organizationId: string): Promise<DtoContext> {
|
||||
@@ -429,6 +432,22 @@ export class ActivitiesService {
|
||||
assigneeId: row.assignee?.id ?? null,
|
||||
ownerId: row.assignee?.id ?? null,
|
||||
});
|
||||
const gameBase = {
|
||||
organizationId,
|
||||
userId,
|
||||
entityType: 'activity',
|
||||
entityId: row.id,
|
||||
entityTitle: row.title,
|
||||
facts: {
|
||||
activityType: row.type,
|
||||
typeName: row.typeDef?.name ?? null,
|
||||
priority: row.priority,
|
||||
hasDescription: !!row.description?.trim(),
|
||||
},
|
||||
};
|
||||
this.game.emit({ ...gameBase, event: 'ACTIVITY_CREATED' });
|
||||
if (row.type === 'call') this.game.emit({ ...gameBase, event: 'CALL_LOGGED' });
|
||||
if (row.type === 'meeting') this.game.emit({ ...gameBase, event: 'MEETING_CREATED' });
|
||||
if (initialStage?.kind === 'done') {
|
||||
await this.fireDone(organizationId, row);
|
||||
}
|
||||
@@ -708,8 +727,26 @@ export class ActivitiesService {
|
||||
company: { id: string } | null;
|
||||
deal: { id: string } | null;
|
||||
assignee: { id: string } | null;
|
||||
type: string;
|
||||
priority: string;
|
||||
dueAt: Date | null;
|
||||
typeDef?: { name: string } | null;
|
||||
},
|
||||
) {
|
||||
this.game.emit({
|
||||
organizationId,
|
||||
userId: row.assignee?.id,
|
||||
event: 'TASK_COMPLETED',
|
||||
entityType: 'activity',
|
||||
entityId: row.id,
|
||||
entityTitle: row.title,
|
||||
facts: {
|
||||
activityType: row.type,
|
||||
typeName: row.typeDef?.name ?? null,
|
||||
priority: row.priority,
|
||||
onTime: !row.dueAt || tehranDayKey() <= tehranDayKey(row.dueAt),
|
||||
},
|
||||
});
|
||||
const ctx = {
|
||||
organizationId,
|
||||
entityType: 'activity' as const,
|
||||
|
||||
Reference in New Issue
Block a user