Unified search algorithm across all lists; per-section record visibility (contacts, deals, activities, leads, finance, tickets, notes, projects)
This commit is contained in:
@@ -19,6 +19,7 @@ import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
type Actor,
|
||||
elevated,
|
||||
forScope,
|
||||
} from '../common/record-scope';
|
||||
import {
|
||||
asAddresses,
|
||||
@@ -28,6 +29,7 @@ import {
|
||||
import { emptyToNull, normalizePhone } from '../contacts/normalize';
|
||||
import { LocalFileStorage } from '../storage/local-file.storage';
|
||||
import { LeadStagesService } from './lead-stages.service';
|
||||
import { buildSearchWhere, ci } from '../common/search';
|
||||
|
||||
const LOGO_MAX_BYTES = 2 * 1024 * 1024;
|
||||
const LOGO_EXT: Record<string, string> = {
|
||||
@@ -48,7 +50,8 @@ function mobileKey(raw: string | null | undefined): string | null {
|
||||
return d.startsWith('0') ? d : d;
|
||||
}
|
||||
|
||||
function leadAccessWhere(actor: Actor): Prisma.LeadWhereInput {
|
||||
function leadAccessWhere(baseActor: Actor): Prisma.LeadWhereInput {
|
||||
const actor = forScope(baseActor, 'leads');
|
||||
if (elevated(actor.role)) return {};
|
||||
if (actor.dataVisibility === 'org') return {};
|
||||
if (
|
||||
@@ -95,7 +98,17 @@ export class LeadsService {
|
||||
await this.stages.ensureDefaults(actor.organizationId);
|
||||
const page = Math.max(1, opts.page ?? 1);
|
||||
const limit = Math.min(100, Math.max(1, opts.limit ?? 20));
|
||||
const q = opts.q?.trim();
|
||||
const search = buildSearchWhere<Prisma.LeadWhereInput>(opts.q, (t) => [
|
||||
{ title: ci(t) },
|
||||
{ contactName: ci(t) },
|
||||
{ mobile: { contains: t } },
|
||||
{ phone: { contains: t } },
|
||||
{ city: ci(t) },
|
||||
{ region: ci(t) },
|
||||
{ trade: ci(t) },
|
||||
{ instagram: ci(t) },
|
||||
{ source: ci(t) },
|
||||
]);
|
||||
|
||||
const where: Prisma.LeadWhereInput = {
|
||||
organizationId: actor.organizationId,
|
||||
@@ -109,17 +122,7 @@ export class LeadsService {
|
||||
...(opts.source
|
||||
? { source: { contains: opts.source, mode: 'insensitive' } }
|
||||
: {}),
|
||||
...(q
|
||||
? {
|
||||
OR: [
|
||||
{ title: { contains: q, mode: 'insensitive' } },
|
||||
{ contactName: { contains: q, mode: 'insensitive' } },
|
||||
{ mobile: { contains: q } },
|
||||
{ city: { contains: q, mode: 'insensitive' } },
|
||||
{ trade: { contains: q, mode: 'insensitive' } },
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
...(search ? { AND: [search] } : {}),
|
||||
};
|
||||
|
||||
const orderBy: Prisma.LeadOrderByWithRelationInput =
|
||||
|
||||
Reference in New Issue
Block a user