Build fully custom CalendarPopover with date-fns + Radix Popover. Month/year dropdown selectors, today button, clear button, past dates dimmed. Rewrite DueDatePicker to use it instead of <input type=date>.
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import { format, formatDistanceToNow, isPast, isToday } from "date-fns";
|
|
import { X } from "lucide-react";
|
|
import { useBoardStore } from "@/stores/board-store";
|
|
import { CalendarPopover } from "@/components/card-detail/CalendarPopover";
|
|
|
|
interface DueDatePickerProps {
|
|
cardId: string;
|
|
dueDate: string | null;
|
|
}
|
|
|
|
export function DueDatePicker({ cardId, dueDate }: DueDatePickerProps) {
|
|
const updateCard = useBoardStore((s) => s.updateCard);
|
|
|
|
const dateObj = dueDate ? new Date(dueDate) : null;
|
|
const overdue = dateObj != null && isPast(dateObj) && !isToday(dateObj);
|
|
|
|
function handleSelect(date: Date) {
|
|
updateCard(cardId, { dueDate: format(date, "yyyy-MM-dd") });
|
|
}
|
|
|
|
function handleClear() {
|
|
updateCard(cardId, { dueDate: null });
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col gap-2">
|
|
{/* Header with clear button */}
|
|
<div className="flex items-center justify-between">
|
|
<h4 className="font-mono text-xs uppercase tracking-widest text-pylon-text-secondary">
|
|
Due Date
|
|
</h4>
|
|
{dueDate && (
|
|
<button
|
|
onClick={handleClear}
|
|
className="rounded p-0.5 text-pylon-text-secondary transition-colors hover:bg-pylon-danger/10 hover:text-pylon-danger"
|
|
aria-label="Clear due date"
|
|
>
|
|
<X className="size-3.5" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Clickable date display -> opens calendar */}
|
|
<CalendarPopover
|
|
selectedDate={dateObj}
|
|
onSelect={handleSelect}
|
|
onClear={handleClear}
|
|
>
|
|
<button className="flex w-full items-center gap-2 rounded-md px-1 py-1 text-left transition-colors hover:bg-pylon-column/60">
|
|
{dateObj ? (
|
|
<>
|
|
<span
|
|
className={`text-sm font-medium ${
|
|
overdue ? "text-pylon-danger" : "text-pylon-text"
|
|
}`}
|
|
>
|
|
{format(dateObj, "MMM d, yyyy")}
|
|
</span>
|
|
<span
|
|
className={`text-xs ${
|
|
overdue ? "text-pylon-danger" : "text-pylon-text-secondary"
|
|
}`}
|
|
>
|
|
{overdue
|
|
? `overdue by ${formatDistanceToNow(dateObj)}`
|
|
: isToday(dateObj)
|
|
? "today"
|
|
: `in ${formatDistanceToNow(dateObj)}`}
|
|
</span>
|
|
</>
|
|
) : (
|
|
<span className="text-sm italic text-pylon-text-secondary/60">
|
|
Click to set date...
|
|
</span>
|
|
)}
|
|
</button>
|
|
</CalendarPopover>
|
|
</div>
|
|
);
|
|
}
|