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:
2026-11-09 15:09:49 +03:30
co-authored by Cursor
parent 4764562bd4
commit 8b8d01976d
3 changed files with 60 additions and 17 deletions
@@ -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')
+39 -8
View File
@@ -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),
})); }));
} }
+16 -7
View File
@@ -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 },