feat(ui): add finance plan editor

Plans default to an equal split, each slice is individually editable, and
plans compose with splits. Cancelling keeps past slices.

planBalance delegates to domain slicesBalance rather than recomputing the
same total-minus-slices arithmetic, per the project's one-implementation
rule for shared math.
This commit is contained in:
Lucas Winther committed 2026-09-01 20:53:05 +02:00
1 parent 5a11941d39
commit 5b879809d2
4 files changed
+235 -1

No files matched your search

+18 -1
View File
@@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { SplitEditor } from "./SplitEditor.tsx";
import { PlanEditor } from "./PlanEditor.tsx";
import {
emptyDraft,
fromPurchase,
@@ -20,7 +21,7 @@ import {
} from "../purchaseForm.ts";
import { useDataset } from "../hooks/useDataset.ts";
import { useMutate } from "../hooks/useMutate.ts";
import { addPurchase, updatePurchase } from "../../store/actions.ts";
import { addPurchase, cancelScheduleFrom, updatePurchase } from "../../store/actions.ts";
import { CURRENCIES, type MonthId, type Purchase } from "../../domain/types.ts";
interface Props {
@@ -128,6 +129,22 @@ export function PurchaseDialog({ monthId, purchase, trigger }: Props) {
<SplitEditor draft={draft} posts={activePosts} onChange={setDraft} />
<PlanEditor draft={draft} onChange={setDraft} />
{purchase?.schedule && !purchase.schedule.cancelledFromMonth && (
<Button
type="button"
variant="outline"
size="sm"
onClick={() => {
mutate((data) => cancelScheduleFrom(data, purchase.id, monthId));
setOpen(false);
}}
>
Cancel remaining slices from {monthId}
</Button>
)}
{errors.length > 0 && (
<ul className="space-y-1 text-sm text-destructive">
{errors.map((message) => (