+
+
{s.review.title}
+
+
+
+ {isLoading && queue === null ? (
+
+ ) : current ? (
+ <>
+
+ {s.review.remaining(queue!.length)}
+
+
+
+
+ {current.merchant}
+
+ {s.review.transactionCount(current.count)} · {hidden ? MASKED : tugrikRaw(current.total)}
+
+
+
+
{s.review.question}
+
+
+ {mainCategories.map((cat) => {
+ const style = categoryStyle(cat.name, cat.kind === "income");
+ return (
+
+ );
+ })}
+
+
+
skip(current)}>
+ {s.review.skip}
+
+ >
+ ) : (
+ <>
+
+
+ {s.review.close}
+
+ >
+ )}
+
+ );
+}
diff --git a/src/features/accounting/CategoryChips.tsx b/src/features/accounting/CategoryChips.tsx
new file mode 100644
index 0000000..4ad8ea8
--- /dev/null
+++ b/src/features/accounting/CategoryChips.tsx
@@ -0,0 +1,89 @@
+"use client";
+
+import { categoryStyle } from "@/ds/categoryStyle";
+import { Icon } from "@/ds/icons";
+import { accountingStrings as s } from "./strings";
+
+export interface CategoryChipOption {
+ /** Raw backend category name (as stored on `Txn.category`). */
+ name: string;
+ /** Direction of a representative transaction in that category — decides
+ * the fallback style for an unrecognized category name. */
+ income: boolean;
+}
+
+export interface CategoryChipsProps {
+ categories: CategoryChipOption[];
+ /** `null` = "Бүгд" (all, no filter). */
+ selected: string | null;
+ onSelect: (category: string | null) => void;
+}
+
+/** Horizontal, scrollable category filter row above the Тооцоо list: "Бүгд"
+ * plus every category present in the loaded month, tinted with
+ * `categoryStyle` and filled when active. Tap to filter the visible rows. */
+export function CategoryChips({ categories, selected, onSelect }: CategoryChipsProps) {
+ if (categories.length === 0) return null;
+ return (
+