diff --git a/packages/frontend/src/pages/Timeline.tsx b/packages/frontend/src/pages/Timeline.tsx index aea2715..2fc1c41 100644 --- a/packages/frontend/src/pages/Timeline.tsx +++ b/packages/frontend/src/pages/Timeline.tsx @@ -10,6 +10,7 @@ import { useSelection } from "@/components/selection-provider"; import { SprintForm } from "@/components/sprint-form"; import StatusTag from "@/components/status-tag"; import TopBar from "@/components/top-bar"; +import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; import { BREATHING_ROOM } from "@/lib/layout"; import { useIssues, @@ -21,8 +22,10 @@ import { import { cn } from "@/lib/utils"; const DAY_MS = 24 * 60 * 60 * 1000; -const TIMELINE_LABEL_WIDTH = 240; -const WEEK_COLUMN_WIDTH = 140; +const TIMELINE_LABEL_DEFAULT_SIZE = 420; +const TIMELINE_LABEL_MIN_SIZE = 300; +const TIMELINE_LABEL_MAX_SIZE = "55%"; +const WEEK_COLUMN_WIDTH = 160; const addDays = (value: Date, days: number) => new Date(value.getFullYear(), value.getMonth(), value.getDate() + days); @@ -176,9 +179,9 @@ export default function Timeline() { const statuses = selectedOrganisation?.Organisation.statuses ?? {}; - const gridTemplateColumns = useMemo(() => { - if (weeks.length === 0) return `${TIMELINE_LABEL_WIDTH}px 1fr`; - return `${TIMELINE_LABEL_WIDTH}px repeat(${weeks.length}, ${WEEK_COLUMN_WIDTH}px)`; + const weekGridTemplateColumns = useMemo(() => { + if (weeks.length === 0) return "1fr"; + return `repeat(${weeks.length}, ${WEEK_COLUMN_WIDTH}px)`; }, [weeks.length]); const todayMarker = useMemo(() => { @@ -243,152 +246,139 @@ export default function Timeline() { {selectedOrganisationId && selectedProjectId && sprints.length > 0 && (
-
-
+ -
+
Sprint
- {weeks.map((week) => ( -
- {formatWeekLabel(week)} -
- ))} -
- - {sprints.map((sprint, sprintIndex) => { - const sprintIssues = issueGroup.issuesBySprint.get(sprint.id) ?? []; - const barStyle = getSprintBarStyle(sprint); - const showTodayLabel = sprintIndex === 0; - return ( -
+ {sprints.map((sprint) => { + const sprintIssues = issueGroup.issuesBySprint.get(sprint.id) ?? []; + return (
-
- - {sprint.name} - -
- {getSprintDateRange(sprint)} -
-
- {sprintIssues.length === 0 && ( -
No issues assigned.
- )} - {sprintIssues.length > 0 && ( -
- {sprintIssues.map((issue) => ( - - ))} -
- )} +
-
+ ); + })} +
+ +
+
+ + +
+
+
+ {weeks.map((week, index) => (
- {weeks.map((week, index) => ( -
- ))} + {formatWeekLabel(week)}
- {todayMarker && ( + ))} +
+ + {sprints.map((sprint, sprintIndex) => { + const sprintIssues = issueGroup.issuesBySprint.get(sprint.id) ?? []; + const barStyle = getSprintBarStyle(sprint); + const showTodayLabel = sprintIndex === 0; + return ( +
-
- {showTodayLabel && ( -
- - TODAY - +
+
+
+ {weeks.map((week, index) => ( +
+ ))} +
+ {todayMarker && ( +
+
+ {showTodayLabel && ( +
+ + TODAY + +
+ )}
)} -
- )} - {barStyle && ( - + } /> - } - /> - )} + )} +
+
+ ); + })} + +
+
+
- ); - })} - -
-
-
Backlog
- {issueGroup.unassigned.length === 0 && ( -
No unassigned issues.
- )} - {issueGroup.unassigned.length > 0 && ( -
- {issueGroup.unassigned.map((issue) => ( - - ))} -
- )}
-
-
-
+ +
)}
@@ -396,6 +386,74 @@ export default function Timeline() { ); } +function SprintLabelContent({ + sprint, + sprintIssues, + statuses, +}: { + sprint: SprintRecord; + sprintIssues: IssueResponse[]; + statuses: Record; +}) { + return ( + <> +
+ + {sprint.name} + +
{getSprintDateRange(sprint)}
+
+ {sprintIssues.length === 0 && ( +
No issues assigned.
+ )} + {sprintIssues.length > 0 && ( +
+ {sprintIssues.map((issue) => ( + + ))} +
+ )} + + ); +} + +function BacklogLabelContent({ + issueGroup, + statuses, +}: { + issueGroup: IssueGroup; + statuses: Record; +}) { + return ( + <> +
Backlog
+ {issueGroup.unassigned.length === 0 && ( +
No unassigned issues.
+ )} + {issueGroup.unassigned.length > 0 && ( +
+ {issueGroup.unassigned.map((issue) => ( + + ))} +
+ )} + + ); +} + function IssueLine({ issue, statusColour }: { issue: IssueResponse; statusColour: string }) { const [open, setOpen] = useState(false); @@ -412,8 +470,8 @@ function IssueLine({ issue, statusColour }: { issue: IssueResponse; statusColour "hover:text-foreground cursor-pointer", )} > + {issue.Issue.number.toString().padStart(3, "0")} - #{issue.Issue.number.toString().padStart(3, "0")} {issue.Issue.title} }