Capital Planning: show beyond-window projects in Future bucket, 2-col layout

Projects with target years beyond the 5-year planning window now
appear in the Future column of the Kanban board (previously they
were invisible). Cards for these projects show their specific target
year as a badge. The Future column uses a 2-column grid layout when
it has more than 3 projects to maximize screen utilization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 12:16:20 -05:00
parent b0b36df4e4
commit ad2f16d93b

View File

@@ -74,6 +74,9 @@ interface KanbanCardProps {
function KanbanCard({ project, onEdit, onDragStart }: KanbanCardProps) {
const plannedLabel = formatPlannedDate(project.planned_date);
// For projects in the Future bucket with a specific year, show the year
const currentYear = new Date().getFullYear();
const isBeyondWindow = project.target_year > currentYear + 4 && project.target_year !== FUTURE_YEAR;
return (
<Card
@@ -105,6 +108,11 @@ function KanbanCard({ project, onEdit, onDragStart }: KanbanCardProps) {
<Badge size="xs" color={priorityColor(project.priority)} variant="outline">
P{project.priority}
</Badge>
{isBeyondWindow && (
<Badge size="xs" variant="light" color="gray">
{project.target_year}
</Badge>
)}
</Group>
<Text size="xs" ff="monospace" fw={500} mb={4}>
@@ -145,14 +153,16 @@ function KanbanColumn({
isDragOver, onDragOverHandler, onDragLeave,
}: KanbanColumnProps) {
const totalEst = projects.reduce((s, p) => s + parseFloat(p.estimated_cost || '0'), 0);
const isFuture = year === FUTURE_YEAR;
const useWideLayout = isFuture && projects.length > 3;
return (
<Paper
withBorder
radius="md"
p="sm"
miw={280}
maw={320}
miw={useWideLayout ? 580 : 280}
maw={useWideLayout ? 640 : 320}
style={{
flexShrink: 0,
display: 'flex',
@@ -167,8 +177,10 @@ function KanbanColumn({
>
<Group justify="space-between" mb="sm">
<Title order={5}>{yearLabel(year)}</Title>
<Group gap={6}>
<Badge size="sm" variant="light">{fmt(totalEst)}</Badge>
</Group>
</Group>
<Text size="xs" c="dimmed" mb="xs">
{projects.length} project{projects.length !== 1 ? 's' : ''}
@@ -179,6 +191,16 @@ function KanbanColumn({
<Text size="xs" c="dimmed" ta="center" py="lg">
Drop projects here
</Text>
) : useWideLayout ? (
<div style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: 'var(--mantine-spacing-xs)',
}}>
{projects.map((p) => (
<KanbanCard key={p.id} project={p} onEdit={onEdit} onDragStart={onDragStart} />
))}
</div>
) : (
projects.map((p) => (
<KanbanCard key={p.id} project={p} onEdit={onEdit} onDragStart={onDragStart} />
@@ -530,11 +552,16 @@ export function CapitalProjectsPage() {
// ---- Render: Kanban view ----
const maxPlannedYear = currentYear + 4; // last year in the 5-year window
const renderKanbanView = () => (
<ScrollArea type="auto" offsetScrollbars>
<Group align="flex-start" wrap="nowrap" gap="md" py="sm" style={{ minWidth: kanbanYears.length * 300 }}>
{kanbanYears.map((year) => {
const yearProjects = projects.filter((p) => p.target_year === year);
// Future bucket: collect projects with target_year === 9999 OR beyond the 5-year window
const yearProjects = year === FUTURE_YEAR
? projects.filter((p) => p.target_year === FUTURE_YEAR || p.target_year > maxPlannedYear)
: projects.filter((p) => p.target_year === year);
return (
<KanbanColumn
key={year}