Unified search algorithm across all lists; per-section record visibility (contacts, deals, activities, leads, finance, tickets, notes, projects)
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
IsBoolean,
|
||||
IsIn,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Matches,
|
||||
@@ -89,6 +90,12 @@ export class UpdateUserCommissionDto {
|
||||
}
|
||||
|
||||
export class UpdateUserVisibilityDto {
|
||||
@IsOptional()
|
||||
@IsIn(['own', 'team', 'org'])
|
||||
dataVisibility!: 'own' | 'team' | 'org';
|
||||
dataVisibility?: 'own' | 'team' | 'org';
|
||||
|
||||
/** سطح دید هر بخش؛ null یعنی «مثل پیشفرض». در سرویس اعتبارسنجی میشود. */
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
scopes?: Record<string, 'own' | 'team' | 'org' | null>;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export class UsersController {
|
||||
user.organizationId,
|
||||
user.role,
|
||||
id,
|
||||
dto.dataVisibility,
|
||||
dto,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,16 @@ import {
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import type {
|
||||
DataScope,
|
||||
DataVisibility,
|
||||
InviteUserRequest,
|
||||
OrgUserDto,
|
||||
UpdateOrgUserRequest,
|
||||
UpdateUserVisibilityRequest,
|
||||
UserRole,
|
||||
} from '@mento/shared';
|
||||
import { DATA_VISIBILITIES } from '@mento/shared';
|
||||
import { DATA_SCOPES, DATA_VISIBILITIES } from '@mento/shared';
|
||||
import { parseVisibilityScopes } from '../common/record-scope';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { normalizeMobile } from '../auth/auth.service';
|
||||
@@ -40,6 +43,7 @@ type MembershipRow = {
|
||||
isActive: boolean;
|
||||
commissionPercent: { toString(): string } | number | null;
|
||||
dataVisibility?: string | null;
|
||||
dataVisibilityScopes?: unknown;
|
||||
createdAt: Date;
|
||||
orgRole: { id: string; name: string; color: string } | null;
|
||||
user: {
|
||||
@@ -241,14 +245,28 @@ export class UsersService {
|
||||
organizationId: string,
|
||||
actorRole: UserRole,
|
||||
targetId: string,
|
||||
dataVisibility: DataVisibility,
|
||||
dto: UpdateUserVisibilityRequest,
|
||||
): Promise<OrgUserDto> {
|
||||
if (actorRole !== 'owner' && actorRole !== 'admin') {
|
||||
throw new ForbiddenException('اجازه تغییر سطح دسترسی به پروندهها را ندارید');
|
||||
}
|
||||
if (!DATA_VISIBILITIES.includes(dataVisibility)) {
|
||||
const { dataVisibility, scopes } = dto;
|
||||
if (dataVisibility === undefined && !scopes) {
|
||||
throw new BadRequestException('تغییری ارسال نشده است');
|
||||
}
|
||||
if (dataVisibility !== undefined && !DATA_VISIBILITIES.includes(dataVisibility)) {
|
||||
throw new BadRequestException('سطح دسترسی به پروندهها نامعتبر است');
|
||||
}
|
||||
if (scopes) {
|
||||
for (const [key, value] of Object.entries(scopes)) {
|
||||
if (!DATA_SCOPES.includes(key as DataScope)) {
|
||||
throw new BadRequestException(`بخش نامعتبر: ${key}`);
|
||||
}
|
||||
if (value !== null && !DATA_VISIBILITIES.includes(value as DataVisibility)) {
|
||||
throw new BadRequestException('سطح دسترسی به پروندهها نامعتبر است');
|
||||
}
|
||||
}
|
||||
}
|
||||
const target = await this.memberships.findUserInOrgById(
|
||||
organizationId,
|
||||
targetId,
|
||||
@@ -258,9 +276,23 @@ export class UsersService {
|
||||
'مالک و مدیر همیشه همه پروندهها را میبینند',
|
||||
);
|
||||
}
|
||||
const data: Prisma.MembershipUpdateInput = {};
|
||||
if (dataVisibility !== undefined) data.dataVisibility = dataVisibility;
|
||||
if (scopes) {
|
||||
const current = await this.prisma.membership.findUnique({
|
||||
where: { id: target.id },
|
||||
select: { dataVisibilityScopes: true },
|
||||
});
|
||||
const merged = parseVisibilityScopes(current?.dataVisibilityScopes);
|
||||
for (const [key, value] of Object.entries(scopes)) {
|
||||
if (value === null) delete merged[key as DataScope];
|
||||
else merged[key as DataScope] = value;
|
||||
}
|
||||
data.dataVisibilityScopes = merged;
|
||||
}
|
||||
await this.prisma.membership.update({
|
||||
where: { id: target.id },
|
||||
data: { dataVisibility },
|
||||
data,
|
||||
});
|
||||
return this.dtoOf(organizationId, targetId);
|
||||
}
|
||||
@@ -442,6 +474,10 @@ export class UsersService {
|
||||
: DATA_VISIBILITIES.includes(row.dataVisibility as DataVisibility)
|
||||
? (row.dataVisibility as DataVisibility)
|
||||
: 'own',
|
||||
dataVisibilityScopes:
|
||||
row.role === 'owner' || row.role === 'admin'
|
||||
? {}
|
||||
: parseVisibilityScopes(row.dataVisibilityScopes),
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user