fix(pipeline): hide prior-only sales paths from current work
The board lists a pipeline in current work only when it has a deal in that period, and keeps archive-only paths for the prior-data view. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -31,8 +31,11 @@ export class PipelinesController {
|
|||||||
constructor(private readonly pipelinesService: PipelinesService) {}
|
constructor(private readonly pipelinesService: PipelinesService) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
list(@CurrentUser() user: { organizationId: string }) {
|
list(
|
||||||
return this.pipelinesService.list(user.organizationId);
|
@CurrentUser() user: Actor,
|
||||||
|
@Query('forLens') forLens?: string,
|
||||||
|
) {
|
||||||
|
return this.pipelinesService.list(user, forLens === '1');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('board')
|
@Get('board')
|
||||||
|
|||||||
@@ -102,24 +102,55 @@ export class PipelinesService {
|
|||||||
return this.toPipelineDto(created);
|
return this.toPipelineDto(created);
|
||||||
}
|
}
|
||||||
|
|
||||||
async list(organizationId: string): Promise<PipelineDto[]> {
|
async list(actorOrOrgId: Actor | string, forLens = false): Promise<PipelineDto[]> {
|
||||||
|
const organizationId =
|
||||||
|
typeof actorOrOrgId === 'string' ? actorOrOrgId : actorOrOrgId.organizationId;
|
||||||
|
const actor = typeof actorOrOrgId === 'string' ? undefined : actorOrOrgId;
|
||||||
await this.ensureDefaultPipeline(organizationId);
|
await this.ensureDefaultPipeline(organizationId);
|
||||||
const rows = await this.prisma.pipeline.findMany({
|
const rows = await this.prisma.pipeline.findMany({
|
||||||
where: { organizationId },
|
where: { organizationId },
|
||||||
include: { stages: { orderBy: { position: 'asc' } } },
|
include: { stages: { orderBy: { position: 'asc' } } },
|
||||||
orderBy: [{ isDefault: 'desc' }, { name: 'asc' }],
|
orderBy: [{ isDefault: 'desc' }, { name: 'asc' }],
|
||||||
});
|
});
|
||||||
const counts = await this.prisma.deal.groupBy({
|
const lens = actor?.priorLens;
|
||||||
by: ['pipelineId'],
|
const lensActive = forLens && (lens === 'current' || lens === 'archive');
|
||||||
where: { organizationId },
|
const access = actor ? dealAccessWhere(actor) : {};
|
||||||
_count: { _all: true },
|
const prior = lensActive
|
||||||
});
|
? dealPriorWhere(actor?.priorLens, actor?.priorCutoff)
|
||||||
|
: {};
|
||||||
|
const [counts, lensCounts] = await Promise.all([
|
||||||
|
this.prisma.deal.groupBy({
|
||||||
|
by: ['pipelineId'],
|
||||||
|
where: lensActive ? { organizationId, AND: [access] } : { organizationId },
|
||||||
|
_count: { _all: true },
|
||||||
|
}),
|
||||||
|
lensActive
|
||||||
|
? this.prisma.deal.groupBy({
|
||||||
|
by: ['pipelineId'],
|
||||||
|
where: { organizationId, AND: [access, prior] },
|
||||||
|
_count: { _all: true },
|
||||||
|
})
|
||||||
|
: Promise.resolve([]),
|
||||||
|
]);
|
||||||
const dealCountByPipeline = new Map(
|
const dealCountByPipeline = new Map(
|
||||||
counts.map((row) => [row.pipelineId, row._count._all]),
|
counts.map((row) => [row.pipelineId, row._count._all]),
|
||||||
);
|
);
|
||||||
return rows.map((row) => ({
|
const lensCountByPipeline = new Map(
|
||||||
|
lensCounts.map((row) => [row.pipelineId, row._count._all]),
|
||||||
|
);
|
||||||
|
const visible = lensActive
|
||||||
|
? rows.filter((row) => {
|
||||||
|
const inLens = lensCountByPipeline.get(row.id) ?? 0;
|
||||||
|
const total = dealCountByPipeline.get(row.id) ?? 0;
|
||||||
|
if (lens === 'archive') return inLens > 0;
|
||||||
|
return inLens > 0 || total === 0;
|
||||||
|
})
|
||||||
|
: rows;
|
||||||
|
return visible.map((row) => ({
|
||||||
...this.toPipelineDto(row),
|
...this.toPipelineDto(row),
|
||||||
dealCount: dealCountByPipeline.get(row.id) ?? 0,
|
dealCount: lensActive
|
||||||
|
? (lensCountByPipeline.get(row.id) ?? 0)
|
||||||
|
: (dealCountByPipeline.get(row.id) ?? 0),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,9 @@ function findAccent(dealId: string): PipelineStage['color'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadPipelines() {
|
async function loadPipelines() {
|
||||||
const { data } = await api.get<PipelineDto[]>('/pipelines');
|
const { data } = await api.get<PipelineDto[]>('/pipelines', {
|
||||||
|
params: { forLens: '1' },
|
||||||
|
});
|
||||||
pipelines.value = data;
|
pipelines.value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,16 +188,23 @@ async function load() {
|
|||||||
error.value = '';
|
error.value = '';
|
||||||
try {
|
try {
|
||||||
await loadPipelines();
|
await loadPipelines();
|
||||||
const pipelineId =
|
const requested =
|
||||||
typeof route.query.pipelineId === 'string'
|
typeof route.query.pipelineId === 'string' ? route.query.pipelineId : '';
|
||||||
? route.query.pipelineId
|
const pipelineId = pipelines.value.some((item) => item.id === requested)
|
||||||
: undefined;
|
? requested
|
||||||
|
: pipelines.value[0]?.id;
|
||||||
|
if (!pipelineId) {
|
||||||
|
boardPipelineId.value = '';
|
||||||
|
stages.value = [];
|
||||||
|
if (requested) await router.replace({ name: 'pipeline', query: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { data } = await api.get<PipelineBoardDto>('/pipelines/board', {
|
const { data } = await api.get<PipelineBoardDto>('/pipelines/board', {
|
||||||
params: pipelineId ? { pipelineId } : undefined,
|
params: { pipelineId },
|
||||||
});
|
});
|
||||||
boardPipelineId.value = data.pipeline.id;
|
boardPipelineId.value = data.pipeline.id;
|
||||||
stages.value = mapBoardToStages(data, formatDealDate);
|
stages.value = mapBoardToStages(data, formatDealDate);
|
||||||
if (!pipelineId || pipelineId !== data.pipeline.id) {
|
if (requested !== data.pipeline.id) {
|
||||||
await router.replace({
|
await router.replace({
|
||||||
name: 'pipeline',
|
name: 'pipeline',
|
||||||
query: { pipelineId: data.pipeline.id },
|
query: { pipelineId: data.pipeline.id },
|
||||||
|
|||||||
Reference in New Issue
Block a user