void;
+ horizonLabel: string;
+ total: number;
+ rows: SpendBreakdownRow[];
+}
+
+/** Opened from the overall-spend card's "Задаргаа" affordance: how the
+ * horizon's total spend is composed, category by category, biggest first —
+ * "how did this number get so big?". Ports SpendBreakdownView.swift; each row
+ * links to the same `/planner/[category]` transactions page the category-limit
+ * list already uses. */
+export function SpendBreakdown({ open, onOpenChange, horizonLabel, total, rows }: SpendBreakdownProps) {
+ const spent = rows
+ .filter((r) => dec(r.spent) > 0)
+ .slice()
+ .sort((a, b) => dec(b.spent) - dec(a.spent));
+
+ return (
+
+
+
+
+
+ {s.spendBreakdown.title(horizonLabel)}
+
+
+
+
+ {tugrik(total)}
+
+
+ {spent.length === 0 ? (
+
+ ) : (
+
+ {spent.map((row) => {
+ const style = categoryStyle(row.category);
+ const value = dec(row.spent);
+ const percent = total > 0 ? Math.min(100, (value / total) * 100) : 0;
+ const pct = total > 0 ? Math.round((value / total) * 100) : 0;
+ return (
+ -
+ onOpenChange(false)}
+ className="flex items-center gap-3"
+ style={{ color: "inherit", textDecoration: "none" }}
+ >
+
+
+
+
+ {style.name}
+
+ {tugrik(value)}
+
+
+
+
+
+ );
+ })}
+
+ )}
+
+
+
+ onOpenChange(false)}>
+ {s.spendBreakdown.close}
+
+
+
+
+
+ );
+}
diff --git a/src/features/planner/strings.ts b/src/features/planner/strings.ts
index 4228fc5..8b1b748 100644
--- a/src/features/planner/strings.ts
+++ b/src/features/planner/strings.ts
@@ -25,6 +25,14 @@ export const plannerStrings = {
},
editTitle: (horizonLabel: string) => `${horizonLabel} — нийт лимит`,
unsetHint: "Лимит тохируулаагүй",
+ breakdown: "Задаргаа",
+ },
+ // Spend-breakdown dialog, ported from SpendBreakdownView.swift: how a
+ // horizon's spend total is composed, category by category.
+ spendBreakdown: {
+ title: (horizonLabel: string) => `${horizonLabel} — задаргаа`,
+ empty: "Гүйлгээ алга",
+ close: "Хаах",
},
categories: {
title: "Ангиллын лимит",