From a6a58260f8b08f77441fa4995c6d333d784c83fd Mon Sep 17 00:00:00 2001 From: Oliver Bryan Date: Sat, 24 Jan 2026 12:50:23 +0000 Subject: [PATCH] calendar QoL changes --- .../frontend/src/components/sprint-form.tsx | 8 +++ .../frontend/src/components/ui/calendar.tsx | 58 ++++++++++++++++--- todo.md | 16 +++-- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/packages/frontend/src/components/sprint-form.tsx b/packages/frontend/src/components/sprint-form.tsx index f892adc..0af3a5b 100644 --- a/packages/frontend/src/components/sprint-form.tsx +++ b/packages/frontend/src/components/sprint-form.tsx @@ -243,6 +243,7 @@ export function SprintForm({ { if (!value) return; @@ -250,6 +251,9 @@ export function SprintForm({ }} autoFocus sprints={calendarSprints} + showWeekNumber + showOutsideDays={false} + defaultMonth={startDate} /> @@ -266,6 +270,7 @@ export function SprintForm({ { if (!value) return; @@ -274,6 +279,9 @@ export function SprintForm({ autoFocus sprints={calendarSprints} isEnd + showWeekNumber + showOutsideDays={false} + defaultMonth={endDate} /> diff --git a/packages/frontend/src/components/ui/calendar.tsx b/packages/frontend/src/components/ui/calendar.tsx index 8999406..8df6b2c 100644 --- a/packages/frontend/src/components/ui/calendar.tsx +++ b/packages/frontend/src/components/ui/calendar.tsx @@ -5,7 +5,7 @@ import { Button, buttonVariants } from "@/components/ui/button"; import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; -function Calendar({ +export function Calendar({ className, classNames, showOutsideDays = true, @@ -15,11 +15,13 @@ function Calendar({ components, sprints, isEnd, + currentSprint, ...props }: React.ComponentProps & { buttonVariant?: React.ComponentProps["variant"]; sprints?: SprintRecord[]; isEnd?: boolean; + currentSprint?: { colour: string; startDate: Date; endDate: Date }; }) { const defaultClassNames = getDefaultClassNames(); @@ -112,7 +114,9 @@ function Calendar({ return ; }, - DayButton: (props) => , + DayButton: (props) => ( + + ), WeekNumber: ({ children, ...props }) => { return ( @@ -129,7 +133,7 @@ function Calendar({ ); } -function CalendarDayButton({ +export function CalendarDayButton({ className, day, modifiers, @@ -137,8 +141,13 @@ function CalendarDayButton({ style, disabled, isEnd, + currentSprint, ...props -}: React.ComponentProps & { sprints?: SprintRecord[]; isEnd?: boolean }) { +}: React.ComponentProps & { + sprints?: SprintRecord[]; + isEnd?: boolean; + currentSprint?: { colour: string; startDate: Date; endDate: Date }; +}) { const defaultClassNames = getDefaultClassNames(); const ref = React.useRef(null); @@ -172,11 +181,47 @@ function CalendarDayButton({ "flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal", "[&>span]:text-xs [&>span]:opacity-70", !sprint?.color && "hover:bg-primary/90 hover:text-foreground", - "data-[selected-single=true]:!bg-foreground data-[selected-single=true]:!text-background data-[selected-single=true]:hover:!bg-foreground/90", + !currentSprint?.startDate && + "data-[selected-single=true]:!bg-foreground data-[selected-single=true]:!text-background data-[selected-single=true]:hover:!bg-foreground/90", "data-[range-start=true]:!bg-foreground data-[range-start=true]:!text-background", "data-[range-middle=true]:!bg-foreground data-[range-middle=true]:!text-background", "data-[range-end=true]:!bg-foreground data-[range-end=true]:!text-background", + sprint?.color && "border-t border-b !border-(--sprint-color) !bg-(--sprint-color)/5", + + // part of new sprint + currentSprint?.startDate && + currentSprint?.endDate && + day.date >= currentSprint?.startDate && + day.date <= currentSprint?.endDate && + "!border-t !border-b !border-(--current-sprint-color) !bg-(--current-sprint-color)/5 hover:!bg-(--current-sprint-color)/50 border-dashed", + + // is start of new sprint + currentSprint?.startDate && + day.date.getDate() === currentSprint?.startDate.getDate() && + day.date.getMonth() === currentSprint?.startDate.getMonth() && + "!border-l !border-(--current-sprint-color)", + + // is start of new sprint + currentSprint?.endDate && + day.date.getDate() === currentSprint?.endDate.getDate() && + day.date.getMonth() === currentSprint?.endDate.getMonth() && + "!border-r !border-(--current-sprint-color)", + + // is selected + "data-[selected-single=true]:!bg-(--current-sprint-color)/75 data-[selected-single=true]:hover:!bg-(--current-sprint-color)/60", + + // is start and after end date (disable) + !isEnd && + currentSprint?.endDate && + day.date > currentSprint?.endDate && + "opacity-50 pointer-events-none cursor-not-allowed", + // isEnd and before start date (disable) + isEnd && + currentSprint?.startDate && + day.date < currentSprint?.startDate && + "opacity-50 pointer-events-none cursor-not-allowed", + defaultClassNames.day, className, )} @@ -184,6 +229,7 @@ function CalendarDayButton({ { ...style, "--sprint-color": sprint?.color ? sprint.color : null, + "--current-sprint-color": currentSprint?.colour ? currentSprint.colour : null, borderLeft: sprint && day.date.getUTCDate() === new Date(sprint.startDate).getUTCDate() ? `1px solid ${sprint?.color}` @@ -203,5 +249,3 @@ function CalendarDayButton({ /> ); } - -export { Calendar, CalendarDayButton }; diff --git a/todo.md b/todo.md index e76805f..53958dd 100644 --- a/todo.md +++ b/todo.md @@ -1,19 +1,19 @@ # HIGH PRIORITY -- BUGS: -- date picker opens at today's date instead of the selected date -- january: date picker is above, february: date picker is below (likely due to how many rows worth of sprints are displayed) - -my-1/-m-px on dates that have a sprint - FEATURES: - issues - issue type (options stored on Organisation) - filters - -# LOW PRIORITY - -- setup guide +- time tracking: + - add overlay in the bottom left for active timers if there are any. this should be minimal with the issue key (API-005), the time, and a play/pause + end button - pricing page - see jira and other competitors - explore payment providers (stripe is the only one i know) + +# LOW PRIORITY + +- disable self-hosting stuff +- make closed source - dedicated /register route (currently login/register are combined on /login) - real logo - org settings @@ -26,8 +26,6 @@ - deadline - comments - admins are capable of deleting comments from members who are at their permission level or below (not sure if this should apply, or if ANYONE should have control over others' comments - people in an org tend to be trusted to not be trolls) -- time tracking: - - add overlay in the bottom left for active timers if there are any. this should be minimal with the issue key (API-005), the time, and a play/pause + end button - user preferences - colour scheme - "assign to me by default" option for new issues