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:
@@ -57,6 +57,11 @@ export class CreateProjectCardDto {
|
||||
@MaxLength(4000)
|
||||
notes?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
tags?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@IsString()
|
||||
@@ -76,6 +81,11 @@ export class CreateProjectCardDto {
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@IsString()
|
||||
ownerId?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
assigneeIds?: string[];
|
||||
}
|
||||
|
||||
export class UpdateProjectCardDto {
|
||||
@@ -90,6 +100,11 @@ export class UpdateProjectCardDto {
|
||||
@MaxLength(4000)
|
||||
notes?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
tags?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@IsString()
|
||||
@@ -109,6 +124,11 @@ export class UpdateProjectCardDto {
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@IsString()
|
||||
ownerId?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
assigneeIds?: string[];
|
||||
}
|
||||
|
||||
export class MoveProjectCardDto {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
ownerWhere,
|
||||
resolveOwnerId,
|
||||
} from '../common/record-scope';
|
||||
import { normalizeTags } from '../common/tags';
|
||||
|
||||
const DEFAULT_STAGES = ['صف', 'در حال انجام', 'بازبینی', 'انجامشده'] as const;
|
||||
|
||||
@@ -27,6 +28,7 @@ const cardInclude = {
|
||||
company: { select: { id: true, name: true } },
|
||||
deal: { select: { id: true, title: true } },
|
||||
owner: { select: { id: true, fullName: true } },
|
||||
assignees: { select: { id: true, fullName: true } },
|
||||
} satisfies Prisma.ProjectCardInclude;
|
||||
|
||||
type CardActor = Pick<Actor, 'userId' | 'role'>;
|
||||
@@ -192,6 +194,7 @@ export class ProjectBoardsService {
|
||||
_max: { position: true },
|
||||
});
|
||||
|
||||
const assigneeIds = [...new Set((dto.assigneeIds ?? []).map((id) => id.trim()).filter(Boolean))];
|
||||
const created = await this.prisma.projectCard.create({
|
||||
data: {
|
||||
organizationId,
|
||||
@@ -199,11 +202,15 @@ export class ProjectBoardsService {
|
||||
stageId: stage.id,
|
||||
title: dto.title.trim(),
|
||||
notes: emptyToNull(dto.notes),
|
||||
tags: normalizeTags(dto.tags),
|
||||
personId: emptyToNull(dto.personId),
|
||||
companyId: emptyToNull(dto.companyId),
|
||||
dealId: emptyToNull(dto.dealId),
|
||||
ownerId: resolveOwnerId(actor, dto.ownerId),
|
||||
position: (maxPos._max.position ?? -1) + 1,
|
||||
...(assigneeIds.length
|
||||
? { assignees: { connect: assigneeIds.map((id) => ({ id })) } }
|
||||
: {}),
|
||||
},
|
||||
include: cardInclude,
|
||||
});
|
||||
@@ -234,6 +241,7 @@ export class ProjectBoardsService {
|
||||
const data: Prisma.ProjectCardUncheckedUpdateInput = {};
|
||||
if (dto.title !== undefined) data.title = dto.title.trim();
|
||||
if ('notes' in dto) data.notes = emptyToNull(dto.notes ?? null);
|
||||
if ('tags' in dto) data.tags = normalizeTags(dto.tags);
|
||||
if ('personId' in dto) data.personId = emptyToNull(dto.personId ?? null);
|
||||
if ('companyId' in dto) data.companyId = emptyToNull(dto.companyId ?? null);
|
||||
if ('dealId' in dto) data.dealId = emptyToNull(dto.dealId ?? null);
|
||||
@@ -246,7 +254,18 @@ export class ProjectBoardsService {
|
||||
|
||||
const row = await this.prisma.projectCard.update({
|
||||
where: { id },
|
||||
data,
|
||||
data: {
|
||||
...data,
|
||||
...(dto.assigneeIds
|
||||
? {
|
||||
assignees: {
|
||||
set: [...new Set(dto.assigneeIds.map((x) => x.trim()).filter(Boolean))].map(
|
||||
(uid) => ({ id: uid }),
|
||||
),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
include: cardInclude,
|
||||
});
|
||||
return this.toCardDto(row);
|
||||
@@ -411,6 +430,7 @@ export class ProjectBoardsService {
|
||||
id: string;
|
||||
title: string;
|
||||
notes: string | null;
|
||||
tags?: string[];
|
||||
position: number;
|
||||
boardId: string;
|
||||
stageId: string;
|
||||
@@ -420,11 +440,13 @@ export class ProjectBoardsService {
|
||||
company: { id: string; name: string } | null;
|
||||
deal: { id: string; title: string } | null;
|
||||
owner: { id: string; fullName: string } | null;
|
||||
assignees?: Array<{ id: string; fullName: string }>;
|
||||
}): ProjectCardDto {
|
||||
return {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
notes: row.notes,
|
||||
tags: row.tags ?? [],
|
||||
position: row.position,
|
||||
boardId: row.boardId,
|
||||
stageId: row.stageId,
|
||||
@@ -437,6 +459,7 @@ export class ProjectBoardsService {
|
||||
company: row.company,
|
||||
deal: row.deal,
|
||||
owner: row.owner,
|
||||
assignees: row.assignees ?? [],
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
updatedAt: row.updatedAt.toISOString(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user