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:
@@ -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,7 +177,9 @@ 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>
|
||||||
<Badge size="sm" variant="light">{fmt(totalEst)}</Badge>
|
<Group gap={6}>
|
||||||
|
<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">
|
||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user