Files
acmcc/src/components/dashboard/DateCalculatorPopover.tsx
T
2026-06-01 20:19:26 -04:00

149 lines
6.5 KiB
TypeScript

import { useState, useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { CalendarClock } from "lucide-react";
import { addDays, addMonths, addYears, differenceInDays, differenceInBusinessDays, format, parseISO } from "date-fns";
const todayStr = () => format(new Date(), "yyyy-MM-dd");
export function DateCalculatorPopover() {
// Difference tab
const [from, setFrom] = useState(todayStr());
const [to, setTo] = useState(todayStr());
// Add/subtract tab
const [base, setBase] = useState(todayStr());
const [amount, setAmount] = useState<number>(30);
const [unit, setUnit] = useState<"days" | "months" | "years">("days");
const [direction, setDirection] = useState<"add" | "subtract">("add");
const diff = useMemo(() => {
try {
const f = parseISO(from);
const t = parseISO(to);
return {
days: differenceInDays(t, f),
business: differenceInBusinessDays(t, f),
weeks: Math.floor(differenceInDays(t, f) / 7),
};
} catch {
return { days: 0, business: 0, weeks: 0 };
}
}, [from, to]);
const result = useMemo(() => {
try {
const b = parseISO(base);
const n = direction === "subtract" ? -amount : amount;
const d = unit === "days" ? addDays(b, n) : unit === "months" ? addMonths(b, n) : addYears(b, n);
return format(d, "EEEE, MMMM d, yyyy");
} catch {
return "—";
}
}, [base, amount, unit, direction]);
return (
<Popover>
<Tooltip>
<TooltipTrigger asChild>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground"
>
<CalendarClock className="h-3.5 w-3.5" />
</Button>
</PopoverTrigger>
</TooltipTrigger>
<TooltipContent>Date Calculator</TooltipContent>
</Tooltip>
<PopoverContent align="end" className="w-80 p-3">
<div className="space-y-3">
<h4 className="text-[13px] font-semibold">Date Calculator</h4>
<Tabs defaultValue="diff">
<TabsList className="grid w-full grid-cols-2 h-8">
<TabsTrigger value="diff" className="text-[12px]">Difference</TabsTrigger>
<TabsTrigger value="add" className="text-[12px]">Add / Subtract</TabsTrigger>
</TabsList>
<TabsContent value="diff" className="space-y-2 pt-3">
<div className="space-y-1.5">
<Label className="text-[11px]">From</Label>
<Input type="date" value={from} onChange={(e) => setFrom(e.target.value)} className="h-8 text-[12px]" />
</div>
<div className="space-y-1.5">
<Label className="text-[11px]">To</Label>
<Input type="date" value={to} onChange={(e) => setTo(e.target.value)} className="h-8 text-[12px]" />
</div>
<div className="bg-muted/50 rounded-lg p-2.5 space-y-1 mt-2">
<div className="flex justify-between text-[12px]">
<span className="text-muted-foreground">Calendar days</span>
<span className="font-mono font-semibold">{diff.days}</span>
</div>
<div className="flex justify-between text-[12px]">
<span className="text-muted-foreground">Business days</span>
<span className="font-mono font-semibold">{diff.business}</span>
</div>
<div className="flex justify-between text-[12px]">
<span className="text-muted-foreground">Weeks</span>
<span className="font-mono font-semibold">{diff.weeks}</span>
</div>
</div>
</TabsContent>
<TabsContent value="add" className="space-y-2 pt-3">
<div className="space-y-1.5">
<Label className="text-[11px]">Start date</Label>
<Input type="date" value={base} onChange={(e) => setBase(e.target.value)} className="h-8 text-[12px]" />
</div>
<div className="grid grid-cols-3 gap-2">
<div className="space-y-1.5">
<Label className="text-[11px]">Action</Label>
<Select value={direction} onValueChange={(v) => setDirection(v as any)}>
<SelectTrigger className="h-8 text-[12px]"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="add" className="text-[12px]">Add</SelectItem>
<SelectItem value="subtract" className="text-[12px]">Subtract</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-1.5">
<Label className="text-[11px]">Amount</Label>
<Input
type="number"
value={amount}
onChange={(e) => setAmount(parseInt(e.target.value) || 0)}
className="h-8 text-[12px]"
/>
</div>
<div className="space-y-1.5">
<Label className="text-[11px]">Unit</Label>
<Select value={unit} onValueChange={(v) => setUnit(v as any)}>
<SelectTrigger className="h-8 text-[12px]"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="days" className="text-[12px]">Days</SelectItem>
<SelectItem value="months" className="text-[12px]">Months</SelectItem>
<SelectItem value="years" className="text-[12px]">Years</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="bg-muted/50 rounded-lg p-2.5 mt-2">
<p className="text-[11px] text-muted-foreground">Result</p>
<p className="text-[13px] font-semibold mt-0.5">{result}</p>
</div>
</TabsContent>
</Tabs>
</div>
</PopoverContent>
</Popover>
);
}