feat(platform): checkpoint current implementation state
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import {
|
||||
applyVisualOverrides,
|
||||
type TimelineVisualOverrides,
|
||||
} from "./allocationVisualState.js";
|
||||
import type {
|
||||
TimelineAssignmentEntry,
|
||||
TimelineDemandEntry,
|
||||
useTimelineContext,
|
||||
} from "./TimelineContext.js";
|
||||
import { PROJECT_HEADER_HEIGHT, ROW_HEIGHT, SUB_LANE_HEIGHT } from "./timelineConstants.js";
|
||||
import { getProjectRowMetricsKey } from "./timelineProjectMetrics.js";
|
||||
|
||||
export type TimelineProjectGroup = NonNullable<ReturnType<typeof useTimelineContext>["projectGroups"]>[number];
|
||||
|
||||
export type OpenDemandRowLayout = {
|
||||
visibleOpenDemands: TimelineDemandEntry[];
|
||||
laneMap: Map<string, number>;
|
||||
laneCount: number;
|
||||
rowHeight: number;
|
||||
};
|
||||
|
||||
export type ProjectFlatRow =
|
||||
| {
|
||||
type: "header";
|
||||
key: string;
|
||||
project: TimelineProjectGroup;
|
||||
}
|
||||
| {
|
||||
type: "open-demand";
|
||||
key: string;
|
||||
projectId: string;
|
||||
openDemandCount: number;
|
||||
layout: OpenDemandRowLayout;
|
||||
}
|
||||
| {
|
||||
type: "resource";
|
||||
key: string;
|
||||
project: TimelineProjectGroup;
|
||||
resource: TimelineProjectGroup["resourceRows"][number]["resource"];
|
||||
allocs: TimelineAssignmentEntry[];
|
||||
metricsKey: string;
|
||||
};
|
||||
|
||||
export function estimateProjectRowHeight(row: ProjectFlatRow | undefined) {
|
||||
if (!row) return ROW_HEIGHT;
|
||||
if (row.type === "header") return PROJECT_HEADER_HEIGHT;
|
||||
if (row.type === "open-demand") return row.layout.rowHeight;
|
||||
return ROW_HEIGHT;
|
||||
}
|
||||
|
||||
export function buildProjectFlatRows(
|
||||
visualProjectGroups: TimelineProjectGroup[],
|
||||
openDemandsByProject: Map<string, TimelineDemandEntry[]>,
|
||||
optimisticAllocations: TimelineVisualOverrides,
|
||||
): ProjectFlatRow[] {
|
||||
const rows: ProjectFlatRow[] = [];
|
||||
|
||||
for (const project of visualProjectGroups) {
|
||||
rows.push({ type: "header", key: `header-${project.id}`, project });
|
||||
|
||||
const openDemands = openDemandsByProject.get(project.id) ?? [];
|
||||
if (openDemands.length > 0) {
|
||||
rows.push({
|
||||
type: "open-demand",
|
||||
key: `open-demand-${project.id}`,
|
||||
projectId: project.id,
|
||||
openDemandCount: openDemands.length,
|
||||
layout: buildOpenDemandRowLayout(openDemands, optimisticAllocations),
|
||||
});
|
||||
}
|
||||
|
||||
for (const { resource, allocs } of project.resourceRows) {
|
||||
rows.push({
|
||||
type: "resource",
|
||||
key: `${project.id}-${resource.id}`,
|
||||
project,
|
||||
resource,
|
||||
allocs,
|
||||
metricsKey: getProjectRowMetricsKey(project.id, resource.id),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
function assignDemandLanes(demands: TimelineDemandEntry[]): Map<string, number> {
|
||||
const laneMap = new Map<string, number>();
|
||||
const laneEnds: Date[] = [];
|
||||
|
||||
const sorted = [...demands].sort(
|
||||
(a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime(),
|
||||
);
|
||||
|
||||
for (const demand of sorted) {
|
||||
const start = new Date(demand.startDate);
|
||||
let assigned = -1;
|
||||
for (let i = 0; i < laneEnds.length; i++) {
|
||||
if (laneEnds[i]! < start) {
|
||||
assigned = i;
|
||||
laneEnds[i] = new Date(demand.endDate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (assigned === -1) {
|
||||
assigned = laneEnds.length;
|
||||
laneEnds.push(new Date(demand.endDate));
|
||||
}
|
||||
laneMap.set(demand.id, assigned);
|
||||
}
|
||||
|
||||
return laneMap;
|
||||
}
|
||||
|
||||
function buildOpenDemandRowLayout(
|
||||
openDemands: TimelineDemandEntry[],
|
||||
optimisticAllocations: TimelineVisualOverrides,
|
||||
): OpenDemandRowLayout {
|
||||
const visibleOpenDemands = applyVisualOverrides(openDemands, optimisticAllocations);
|
||||
const laneMap = assignDemandLanes(visibleOpenDemands);
|
||||
const laneCount = laneMap.size > 0 ? Math.max(...laneMap.values()) + 1 : 1;
|
||||
|
||||
return {
|
||||
visibleOpenDemands,
|
||||
laneMap,
|
||||
laneCount,
|
||||
rowHeight: Math.max(ROW_HEIGHT, laneCount * SUB_LANE_HEIGHT + 16),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user