"use client";
import { trpc } from "~/lib/trpc/client.js";
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
import { ProgressRing } from "~/components/ui/ProgressRing.js";
interface BalanceCardProps {
resourceId: string;
year?: number;
compact?: boolean;
}
export function BalanceCard({ resourceId, year = new Date().getFullYear(), compact = false }: BalanceCardProps) {
const { data: balance, isLoading } = trpc.entitlement.getBalanceDetail.useQuery(
{ resourceId, year },
{ staleTime: 30_000 },
);
if (isLoading) {
return (
);
}
if (!balance) return null;
const pct = balance.entitlement > 0
? Math.round((balance.taken / balance.entitlement) * 100)
: 0;
if (compact) {
return (
{balance.remaining}d remaining
·
{balance.taken}d used of {balance.entitlement}d
{balance.pending > 0 && (
<>
·
{balance.pending}d pending
>
)}
);
}
const ringColor = pct > 90
? "var(--color-red-500, #ef4444)"
: pct >= 70
? "var(--color-amber-500, #f59e0b)"
: "var(--color-emerald-500, #10b981)";
const holidayBasisVariants = balance.deductionSummary?.holidayBasisVariants ?? [];
const excludedHolidayCount = balance.deductionSummary?.excludedHolidayDates.length ?? 0;
const excludedHolidayTooltip = (balance.vacations ?? [])
.flatMap((vacation) => vacation.holidayDetails.map((detail) => `${detail.date} · ${detail.source}`))
.join("\n");
return (
{balance.remaining}d
Vacation Balance {year}
{balance.taken} of {balance.entitlement} days used
{balance.carryOver > 0 && (
+{balance.carryOver}d carried over
)}
{/* Progress bar */}
{balance.pending > 0 && (
)}
{balance.sickDays > 0 && (
{balance.sickDays} sick day{balance.sickDays !== 1 ? "s" : ""} recorded (not deducted from annual leave)
)}
{!!balance.deductionSummary && (balance.deductionSummary.approvedVacationCount > 0 || balance.deductionSummary.pendingVacationCount > 0) && (
Formula
Vacation deductions: {balance.deductionSummary.approvedDeductedDays}d approved
{balance.deductionSummary.pendingDeductedDays > 0 ? ` · ${balance.deductionSummary.pendingDeductedDays}d pending` : ""}
Requested: {balance.deductionSummary.approvedRequestedDays + balance.deductionSummary.pendingRequestedDays}d
{excludedHolidayCount > 0 && (
Excluded holidays: {excludedHolidayCount}
{excludedHolidayTooltip.length > 0 && }
)}
{holidayBasisVariants.length > 0 && (
Holiday basis: {holidayBasisVariants.join(" · ")}
)}
)}
);
}
function Stat({ label, value, color, tooltip }: { label: string; value: number; color: string; tooltip?: string }) {
return (
{value}
{label}{tooltip && }
);
}