Add desk-style task workflow on Activities and Project boards.
Managers can switch my desk vs team, reassign activities, and filter project cards to their work; assignees can see and move cards.
This commit is contained in:
@@ -62,6 +62,7 @@ export class ActivitiesController {
|
||||
from: query.from,
|
||||
to: query.to,
|
||||
},
|
||||
query.desk,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ export class ActivitiesService {
|
||||
from?: string;
|
||||
to?: string;
|
||||
},
|
||||
desk?: 'mine' | 'team',
|
||||
): Promise<PaginatedResponse<ActivityDto>> {
|
||||
const where = this.buildWhere(
|
||||
organizationId,
|
||||
@@ -136,6 +137,7 @@ export class ActivitiesService {
|
||||
q,
|
||||
entity,
|
||||
range,
|
||||
desk,
|
||||
);
|
||||
const take = Math.min(Math.max(limit, 1), 200);
|
||||
const [rows, total] = await this.prisma.$transaction([
|
||||
@@ -324,11 +326,14 @@ export class ActivitiesService {
|
||||
from?: string;
|
||||
to?: string;
|
||||
},
|
||||
desk?: 'mine' | 'team',
|
||||
): Prisma.ActivityWhereInput {
|
||||
const { start, end } = dayBounds();
|
||||
const scope =
|
||||
desk === 'mine' ? { assigneeId: actor.userId } : assigneeWhere(actor);
|
||||
const base: Prisma.ActivityWhereInput = {
|
||||
organizationId,
|
||||
...assigneeWhere(actor),
|
||||
...scope,
|
||||
...(entity?.personId ? { personId: entity.personId } : {}),
|
||||
...(entity?.companyId ? { companyId: entity.companyId } : {}),
|
||||
...(entity?.dealId ? { dealId: entity.dealId } : {}),
|
||||
|
||||
@@ -28,6 +28,10 @@ export class ListActivitiesQueryDto {
|
||||
@IsString()
|
||||
q?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['mine', 'team'])
|
||||
desk?: 'mine' | 'team';
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
personId?: string;
|
||||
|
||||
@@ -62,6 +62,20 @@ export function dealAccessWhere(
|
||||
};
|
||||
}
|
||||
|
||||
/** کارت پروژه: مالک یا یکی از مسئولان در محدوده دید */
|
||||
export function projectCardAccessWhere(
|
||||
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
|
||||
): Prisma.ProjectCardWhereInput {
|
||||
const ids = scopedOwnerIds(actor);
|
||||
if (!ids) return {};
|
||||
return {
|
||||
OR: [
|
||||
{ ownerId: { in: ids } },
|
||||
{ assignees: { some: { id: { in: ids } } } },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/** فعالیتها بر اساس مسئول */
|
||||
export function assigneeWhere(
|
||||
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
canReassignOwner,
|
||||
dealAccessWhere,
|
||||
ownerWhere,
|
||||
projectCardAccessWhere,
|
||||
resolveOwnerId,
|
||||
} from '../common/record-scope';
|
||||
import { normalizeTags } from '../common/tags';
|
||||
@@ -32,7 +33,10 @@ const cardInclude = {
|
||||
assignees: { select: { id: true, fullName: true } },
|
||||
} satisfies Prisma.ProjectCardInclude;
|
||||
|
||||
type CardActor = Pick<Actor, 'userId' | 'role'>;
|
||||
type CardActor = Pick<
|
||||
Actor,
|
||||
'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'
|
||||
>;
|
||||
|
||||
@Injectable()
|
||||
export class ProjectBoardsService {
|
||||
@@ -113,7 +117,7 @@ export class ProjectBoardsService {
|
||||
where: {
|
||||
organizationId,
|
||||
boardId: resolved.id,
|
||||
...(actor ? ownerWhere(actor) : {}),
|
||||
...(actor ? projectCardAccessWhere(actor) : {}),
|
||||
},
|
||||
include: cardInclude,
|
||||
orderBy: [{ position: 'asc' }, { updatedAt: 'desc' }],
|
||||
@@ -143,7 +147,7 @@ export class ProjectBoardsService {
|
||||
actor: CardActor,
|
||||
): Promise<ProjectCardDto> {
|
||||
const row = await this.prisma.projectCard.findFirst({
|
||||
where: { id, organizationId, ...ownerWhere(actor) },
|
||||
where: { id, organizationId, ...projectCardAccessWhere(actor) },
|
||||
include: cardInclude,
|
||||
});
|
||||
if (!row) throw new NotFoundException('کارت پیدا نشد');
|
||||
@@ -225,7 +229,7 @@ export class ProjectBoardsService {
|
||||
dto: UpdateProjectCardRequest,
|
||||
): Promise<ProjectCardDto> {
|
||||
const existing = await this.prisma.projectCard.findFirst({
|
||||
where: { id, organizationId, ...ownerWhere(actor) },
|
||||
where: { id, organizationId, ...projectCardAccessWhere(actor) },
|
||||
});
|
||||
if (!existing) throw new NotFoundException('کارت پیدا نشد');
|
||||
|
||||
@@ -279,7 +283,7 @@ export class ProjectBoardsService {
|
||||
dto: MoveProjectCardRequest,
|
||||
): Promise<ProjectCardDto> {
|
||||
const card = await this.prisma.projectCard.findFirst({
|
||||
where: { id, organizationId, ...ownerWhere(actor) },
|
||||
where: { id, organizationId, ...projectCardAccessWhere(actor) },
|
||||
select: { id: true, boardId: true },
|
||||
});
|
||||
if (!card) throw new NotFoundException('کارت پیدا نشد');
|
||||
@@ -312,7 +316,7 @@ export class ProjectBoardsService {
|
||||
actor: CardActor,
|
||||
): Promise<void> {
|
||||
const found = await this.prisma.projectCard.findFirst({
|
||||
where: { id, organizationId, ...ownerWhere(actor) },
|
||||
where: { id, organizationId, ...projectCardAccessWhere(actor) },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!found) throw new NotFoundException('کارت پیدا نشد');
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import type { ImportContactsResult } from '@mento/shared';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import type { Actor } from '../common/record-scope';
|
||||
import { ownerWhere } from '../common/record-scope';
|
||||
import { projectCardAccessWhere } from '../common/record-scope';
|
||||
import { normalizePhone } from '../contacts/normalize';
|
||||
import {
|
||||
excelFieldsFromColumns,
|
||||
@@ -68,7 +68,7 @@ export class ProjectCardsExcelService {
|
||||
|
||||
async export(actor: Actor): Promise<Buffer> {
|
||||
const rows = await this.prisma.projectCard.findMany({
|
||||
where: { organizationId: actor.organizationId, ...ownerWhere(actor) },
|
||||
where: { organizationId: actor.organizationId, ...projectCardAccessWhere(actor) },
|
||||
include: {
|
||||
person: {
|
||||
select: { id: true, firstName: true, lastName: true, mobile: true },
|
||||
|
||||
Reference in New Issue
Block a user