38 lines
1.9 KiB
TypeScript
38 lines
1.9 KiB
TypeScript
// WF-P8 M04 — route-side assembly of the handover dialog's view-model.
|
|
//
|
|
// Two screens reach the same state: the board (S01, right after a card lands in
|
|
// the final stage) and the detail page (S02, when the project is already
|
|
// sitting there). Both need the identical answer to "is a handover pending, and
|
|
// what does it contain", so the permission gate, the preview load and the name
|
|
// resolution live here once instead of in two route files that could drift.
|
|
|
|
import { buildPipelineHandoverVM, type PipelineHandoverVM } from "@hivegrid/view-models";
|
|
import { resolveUserLabels } from "~/utils/integrations/crm/crmService.server";
|
|
import { loadPendingHandover } from "./pipelineHandoverService.server";
|
|
import { toProjektNotizContext } from "./projektNotizService.server";
|
|
import type { ProjectRouteContext } from "./projectAccess.server";
|
|
|
|
/**
|
|
* The M04 payload for a project, or null when no handover is pending — which is
|
|
* the normal case and never an error: without the permission, without a
|
|
* successor board, or anywhere but the final stage there is simply nothing to
|
|
* offer.
|
|
*/
|
|
export async function loadHandoverVM(input: {
|
|
tenantId: string;
|
|
projectId: string;
|
|
ctx: ProjectRouteContext;
|
|
}): Promise<PipelineHandoverVM | null> {
|
|
if (!input.ctx.permissions.includes("projects.handover")) return null;
|
|
const pending = await loadPendingHandover({
|
|
tenantId: input.tenantId,
|
|
projectId: input.projectId,
|
|
ctx: toProjektNotizContext(input.ctx),
|
|
});
|
|
if (!pending) return null;
|
|
// The preview carries raw author/assignee ids. Resolve exactly those — the
|
|
// view-model drops what it cannot name rather than print a cuid at a user.
|
|
const labels = await resolveUserLabels(pending.candidates.map((candidate) => candidate.personId).filter((id): id is string => Boolean(id)));
|
|
return buildPipelineHandoverVM({ ...pending, userLabels: Object.fromEntries(labels) });
|
|
}
|