fiddy/apps/web/components/recurring-entries-panel.tsx
2026-02-11 23:45:15 -08:00

74 lines
3.1 KiB
TypeScript

"use client";
import { useMemo } from "react";
import useEntries from "@/hooks/use-entries";
import { useGroupsContext } from "@/hooks/groups-context";
function monthlyMultiplier(frequency: string, intervalCount: number) {
const count = intervalCount || 1;
switch (frequency) {
case "DAILY":
return (30 / count);
case "WEEKLY":
return (52 / 12) / count;
case "BIWEEKLY":
return (26 / 12) / count;
case "MONTHLY":
return (1 / count);
case "QUARTERLY":
return (1 / 3) / count;
case "YEARLY":
return (1 / 12) / count;
default:
return 0;
}
}
export default function RecurringEntriesPanel() {
const { activeGroupId } = useGroupsContext();
const { entries, loading } = useEntries(activeGroupId);
const recurring = useMemo(() => entries.filter(entry => entry.isRecurring), [entries]);
return (
<div className="panel panel-accent p-4">
<div className="card-header">
<h2 className="card-title text-lg">Recurring entries</h2>
</div>
<div className="mt-3 space-y-2">
{!activeGroupId ? (
<div className="text-sm text-muted">Select a group to view recurring entries.</div>
) : loading ? (
<div className="space-y-2">
{[0, 1].map(row => (
<div key={row} className="rounded-lg border border-accent-weak bg-panel px-3 py-3">
<div className="animate-pulse space-y-2">
<div className="h-4 w-28 rounded bg-surface" />
<div className="h-3 w-40 rounded bg-surface" />
</div>
</div>
))}
</div>
) : recurring.length ? (
recurring.map(entry => {
const monthly = entry.frequency ? monthlyMultiplier(entry.frequency, entry.intervalCount) * entry.amountDollars : 0;
return (
<div key={entry.id} className="rounded-lg border border-accent-weak bg-panel px-3 py-3 text-sm">
<div className="flex items-center justify-between">
<div className="font-semibold">${entry.amountDollars.toFixed(2)} · {entry.tags.join(", ") || "No tags"}</div>
<div className="text-xs text-soft">{entry.entryType}</div>
</div>
<div className="mt-1 text-xs text-soft">
Next run: {entry.nextRunAt || entry.occurredAt} · Monthly est: ${monthly.toFixed(2)}
</div>
</div>
);
})
) : (
<div className="text-sm text-muted">No recurring entries yet.</div>
)}
</div>
</div>
);
}