Spaces:
Running
Running
Gowrisankar Cursor commited on
Commit ·
e0122d1
1
Parent(s): 6c34734
Round project allocation display to half-hour increments.
Browse filesShows hours per day as 4h or 3.5h instead of minute-level values on timeline bars and summaries.
Co-authored-by: Cursor <cursoragent@cursor.com>
frontend/src/components/projects/ProjectPlannerBlock.tsx
CHANGED
|
@@ -5,6 +5,8 @@ import { ProjectAllocationTimeline } from "./ProjectAllocationTimeline";
|
|
| 5 |
import { ProjectTimelineRow } from "../planner/ProjectTimelineRow";
|
| 6 |
import {
|
| 7 |
allocatedHoursInRange,
|
|
|
|
|
|
|
| 8 |
visibleWeekdayCount,
|
| 9 |
} from "../../planner/allocationTimeline";
|
| 10 |
import type { Allocation, Person, Project } from "../../types";
|
|
@@ -55,7 +57,7 @@ export function ProjectPlannerBlock({
|
|
| 55 |
}
|
| 56 |
const capacity = Array.from(seen).reduce((sum, personId) => {
|
| 57 |
const person = peopleById.get(personId);
|
| 58 |
-
return sum + (person ? (person.weekly_capacity_hrs / 5) * weekdayCount : 0);
|
| 59 |
}, 0);
|
| 60 |
return { allocatedHours: allocated, capacityHours: capacity, developerCount: seen.size };
|
| 61 |
}, [allocations, peopleById, dayDates, weekdayCount]);
|
|
@@ -63,9 +65,9 @@ export function ProjectPlannerBlock({
|
|
| 63 |
const hoursPct = capacityHours > 0 ? Math.min(100, (allocatedHours / capacityHours) * 100) : 0;
|
| 64 |
const hoursLabel =
|
| 65 |
capacityHours > 0
|
| 66 |
-
? `${
|
| 67 |
: developerCount > 0
|
| 68 |
-
? `${
|
| 69 |
: "No developers assigned";
|
| 70 |
|
| 71 |
const roleGroups = useMemo(() => {
|
|
|
|
| 5 |
import { ProjectTimelineRow } from "../planner/ProjectTimelineRow";
|
| 6 |
import {
|
| 7 |
allocatedHoursInRange,
|
| 8 |
+
formatHoursAmount,
|
| 9 |
+
roundToHalfHour,
|
| 10 |
visibleWeekdayCount,
|
| 11 |
} from "../../planner/allocationTimeline";
|
| 12 |
import type { Allocation, Person, Project } from "../../types";
|
|
|
|
| 57 |
}
|
| 58 |
const capacity = Array.from(seen).reduce((sum, personId) => {
|
| 59 |
const person = peopleById.get(personId);
|
| 60 |
+
return sum + (person ? roundToHalfHour(person.weekly_capacity_hrs / 5) * weekdayCount : 0);
|
| 61 |
}, 0);
|
| 62 |
return { allocatedHours: allocated, capacityHours: capacity, developerCount: seen.size };
|
| 63 |
}, [allocations, peopleById, dayDates, weekdayCount]);
|
|
|
|
| 65 |
const hoursPct = capacityHours > 0 ? Math.min(100, (allocatedHours / capacityHours) * 100) : 0;
|
| 66 |
const hoursLabel =
|
| 67 |
capacityHours > 0
|
| 68 |
+
? `${formatHoursAmount(allocatedHours)} / ${formatHoursAmount(capacityHours)}`
|
| 69 |
: developerCount > 0
|
| 70 |
+
? `${formatHoursAmount(allocatedHours)} allocated`
|
| 71 |
: "No developers assigned";
|
| 72 |
|
| 73 |
const roleGroups = useMemo(() => {
|
frontend/src/planner/allocationTimeline.ts
CHANGED
|
@@ -32,12 +32,22 @@ export function buildAllocationBarSegment(
|
|
| 32 |
|
| 33 |
export { projectBarStyle };
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
export function formatHoursPerDay(allocationPct: number, weeklyCapacityHrs: number): string {
|
| 36 |
-
const
|
| 37 |
-
|
| 38 |
-
const minutes = Math.round((hrs - hours) * 60);
|
| 39 |
-
if (minutes <= 0) return `${hours}h/day`;
|
| 40 |
-
return `${hours}h ${minutes}m/day`;
|
| 41 |
}
|
| 42 |
|
| 43 |
export function visibleWeekdayCount(dayDates: Date[], startDate: string, endDate: string): number {
|
|
@@ -52,6 +62,6 @@ export function allocatedHoursInRange(
|
|
| 52 |
weeklyCapacityHrs: number,
|
| 53 |
weekdayCount: number,
|
| 54 |
): number {
|
| 55 |
-
const daily = (allocationPct
|
| 56 |
return daily * weekdayCount;
|
| 57 |
}
|
|
|
|
| 32 |
|
| 33 |
export { projectBarStyle };
|
| 34 |
|
| 35 |
+
/** Round to nearest 30 minutes (0.5h). */
|
| 36 |
+
export function roundToHalfHour(hours: number): number {
|
| 37 |
+
return Math.round(hours * 2) / 2;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
export function hoursPerDayFromAllocation(allocationPct: number, weeklyCapacityHrs: number): number {
|
| 41 |
+
return (allocationPct / 100) * weeklyCapacityHrs / 5;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
export function formatHoursAmount(hours: number): string {
|
| 45 |
+
return `${roundToHalfHour(hours)}h`;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
export function formatHoursPerDay(allocationPct: number, weeklyCapacityHrs: number): string {
|
| 49 |
+
const daily = roundToHalfHour(hoursPerDayFromAllocation(allocationPct, weeklyCapacityHrs));
|
| 50 |
+
return `${formatHoursAmount(daily)}/day`;
|
|
|
|
|
|
|
|
|
|
| 51 |
}
|
| 52 |
|
| 53 |
export function visibleWeekdayCount(dayDates: Date[], startDate: string, endDate: string): number {
|
|
|
|
| 62 |
weeklyCapacityHrs: number,
|
| 63 |
weekdayCount: number,
|
| 64 |
): number {
|
| 65 |
+
const daily = roundToHalfHour(hoursPerDayFromAllocation(allocationPct, weeklyCapacityHrs));
|
| 66 |
return daily * weekdayCount;
|
| 67 |
}
|