53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
// WF-P8 Subframe delta (S00 boards start page) — server helper for
|
|
// `/projects/boards`. Lists the viewer-visible pipelines (listPipelines applies
|
|
// the deny-based pipeline access scope) and adds stage/project counts. Project
|
|
// counts use the same read scope as the board/list (projectReadScopeWhere) so
|
|
// the numbers match what the viewer sees on the board; demo rows are excluded
|
|
// like the list default. Server-only (.server) — never imported by a component.
|
|
|
|
import { withTenantRLS } from "~/utils/db/.server/tenantRls.server";
|
|
import { listPipelines, projectReadScopeWhere } from "./projectService.server";
|
|
import type { ProjectRouteContext } from "./projectAccess.server";
|
|
|
|
export type BoardsStartPipeline = {
|
|
id: string;
|
|
name: string;
|
|
typHint: "sales" | "auftrag" | "custom";
|
|
isDefault: boolean;
|
|
stageCount: number;
|
|
projectCount: number;
|
|
};
|
|
|
|
export async function loadBoardsStartPipelines({
|
|
tenantId,
|
|
ctx,
|
|
}: {
|
|
tenantId: string;
|
|
ctx: ProjectRouteContext;
|
|
}): Promise<BoardsStartPipeline[]> {
|
|
const [pipelines, projectTotals] = await Promise.all([
|
|
listPipelines({ tenantId, ctx }),
|
|
withTenantRLS(tenantId, async (tx) =>
|
|
tx.projekt.groupBy({
|
|
by: ["pipelineId"],
|
|
where: {
|
|
tenantId,
|
|
archivedAt: null,
|
|
isDemoData: false,
|
|
...projectReadScopeWhere(tenantId, ctx),
|
|
},
|
|
_count: { _all: true },
|
|
}),
|
|
),
|
|
]);
|
|
const countsByPipeline = new Map(projectTotals.map((total) => [total.pipelineId, total._count._all]));
|
|
return pipelines.map((pipeline) => ({
|
|
id: pipeline.id,
|
|
name: pipeline.name,
|
|
typHint: pipeline.typHint,
|
|
isDefault: pipeline.isDefault,
|
|
stageCount: pipeline.stages.length,
|
|
projectCount: countsByPipeline.get(pipeline.id) ?? 0,
|
|
}));
|
|
}
|