Add Didar gap features: lost-reason reports, audit logs, tags, card assignees, and saved-views API.

Ignore Graphify local artifacts and expose new modules in superadmin.
This commit is contained in:
2026-09-26 14:59:48 +03:30
parent 35a618d0af
commit 6463275c3d
30 changed files with 1406 additions and 21 deletions
@@ -23,6 +23,7 @@ import {
ownerWhere,
resolveOwnerId,
} from '../common/record-scope';
import { normalizeTags } from '../common/tags';
const companyInclude = {
_count: { select: { people: true } },
@@ -38,6 +39,7 @@ type CompanyRow = {
website: string | null;
address: string | null;
notes: string | null;
tags: string[];
customFields: unknown;
createdAt: Date;
updatedAt: Date;
@@ -108,6 +110,7 @@ export class CompaniesService {
website: emptyToNull(dto.website),
address: emptyToNull(dto.address),
notes: emptyToNull(dto.notes),
tags: normalizeTags(dto.tags),
ownerId: resolveOwnerId(actor, dto.ownerId),
customFields: toJsonCustomFields(dto.customFields),
};
@@ -134,6 +137,7 @@ export class CompaniesService {
if ('website' in dto) data.website = emptyToNull(dto.website);
if ('address' in dto) data.address = emptyToNull(dto.address);
if ('notes' in dto) data.notes = emptyToNull(dto.notes);
if ('tags' in dto) data.tags = normalizeTags(dto.tags);
if ('customFields' in dto) {
data.customFields = toJsonCustomFields(dto.customFields);
}
@@ -228,6 +232,7 @@ export class CompaniesService {
website: row.website,
address: row.address,
notes: row.notes,
tags: row.tags ?? [],
customFields: asCustomFields(row.customFields),
peopleCount: row._count.people,
owner: row.owner,
+21
View File
@@ -1,5 +1,6 @@
import { Type } from 'class-transformer';
import {
IsArray,
IsEmail,
IsIn,
IsInt,
@@ -77,6 +78,11 @@ export class CreateCompanyDto {
@MaxLength(2000)
notes?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];
@IsOptional()
@IsObject()
customFields?: Record<string, unknown>;
@@ -124,6 +130,11 @@ export class UpdateCompanyDto {
@MaxLength(2000)
notes?: string;
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];
@IsOptional()
@IsObject()
customFields?: Record<string, unknown>;
@@ -184,6 +195,11 @@ export class CreatePersonDto {
@IsIn([...PERSON_MOODS])
mood?: (typeof PERSON_MOODS)[number];
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];
@IsOptional()
@ValidateIf((_, v) => v !== '' && v != null)
@IsString()
@@ -249,6 +265,11 @@ export class UpdatePersonDto {
@IsIn([...PERSON_MOODS])
mood?: (typeof PERSON_MOODS)[number];
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];
@IsOptional()
@ValidateIf((_, v) => v !== '' && v != null)
@IsString()
+5
View File
@@ -27,6 +27,7 @@ import {
import { LoyaltyService } from '../loyalty/loyalty.service';
import { AutomationsService } from '../automations/automations.service';
import { MessageAutomationsService } from '../message-automations/message-automations.service';
import { normalizeTags } from '../common/tags';
const personInclude = {
company: { select: { id: true, name: true } },
@@ -48,6 +49,7 @@ type PersonRow = {
referrer: string | null;
notes: string | null;
mood: PersonMood;
tags: string[];
customFields: unknown;
loyaltyPoints: number;
createdAt: Date;
@@ -146,6 +148,7 @@ export class PeopleService {
referrer: emptyToNull(dto.referrer),
notes: emptyToNull(dto.notes),
mood: dto.mood ?? 'newcomer',
tags: normalizeTags(dto.tags),
companyId,
ownerId: resolveOwnerId(actor, dto.ownerId),
customFields: toJsonCustomFields(dto.customFields),
@@ -193,6 +196,7 @@ export class PeopleService {
if ('referrer' in dto) data.referrer = emptyToNull(dto.referrer);
if ('notes' in dto) data.notes = emptyToNull(dto.notes);
if (dto.mood !== undefined) data.mood = dto.mood;
if ('tags' in dto) data.tags = normalizeTags(dto.tags);
if ('customFields' in dto) {
data.customFields = toJsonCustomFields(dto.customFields);
}
@@ -278,6 +282,7 @@ export class PeopleService {
referrer: row.referrer,
notes: row.notes,
mood: row.mood,
tags: row.tags ?? [],
customFields: asCustomFields(row.customFields),
loyaltyPoints: row.loyaltyPoints ?? 0,
loyaltyTier: row.loyaltyTier,