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