feat(transactions): the transaction page as a receipt with context
The amount is the headline, notes come first at full width, then the last twelve visits to the same place as a dot timeline next to the paying account's days around it with the running balance. Fields edit in place: j and k move, Enter opens and saves, Esc cancels, s c, s b, s t and s s jump to classification. A matching subscription shows as a suggestion, L links it.
This commit is contained in:
1 parent
58b356ea8b
commit
349a6a0b32
14 files changed
+2137
-683
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import type { Split } from "#lib/api/types.js";
|
||||
import {
|
||||
isEditableType,
|
||||
SHARED,
|
||||
type Side,
|
||||
} from "#lib/components/editor/model.js";
|
||||
|
||||
export type FieldName =
|
||||
| "date"
|
||||
| "description"
|
||||
| "amount"
|
||||
| "source"
|
||||
| "destination"
|
||||
| "category"
|
||||
| "budget"
|
||||
| "tags"
|
||||
| "bill"
|
||||
| "notes";
|
||||
|
||||
export interface Row {
|
||||
field: FieldName;
|
||||
index: number;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export const FIELD_LABEL: Record<FieldName, string> = {
|
||||
amount: "amount",
|
||||
bill: "subscription",
|
||||
budget: "budget",
|
||||
category: "category",
|
||||
date: "date",
|
||||
description: "description",
|
||||
destination: "destination account",
|
||||
notes: "notes",
|
||||
source: "source account",
|
||||
tags: "tags",
|
||||
};
|
||||
|
||||
const SIDES: Side[] = ["source", "destination"];
|
||||
|
||||
export const rowKey = (field: FieldName, index: number) => `${index}:${field}`;
|
||||
|
||||
export function rowsOf(splits: Split[]): Row[] {
|
||||
const type = splits[0]?.type ?? "";
|
||||
if (!isEditableType(type)) {
|
||||
return [];
|
||||
}
|
||||
const many = splits.length > 1;
|
||||
const withdrawal = type === "withdrawal";
|
||||
const locked = splits.some((split) => split.reconciled);
|
||||
const shared = SIDES.filter((side) => SHARED[type][side]);
|
||||
const top: FieldName[] = many
|
||||
? ["date", ...(locked ? [] : shared)]
|
||||
: [
|
||||
"date",
|
||||
"description",
|
||||
...(locked ? [] : (["amount", ...SIDES] as FieldName[])),
|
||||
];
|
||||
const own = (split: Split): FieldName[] => [
|
||||
...(many ? (["description"] as const) : []),
|
||||
...(many && !split.reconciled
|
||||
? ["amount" as const, ...SIDES.filter((side) => !SHARED[type][side])]
|
||||
: []),
|
||||
"category",
|
||||
...(withdrawal ? (["budget"] as const) : []),
|
||||
"tags",
|
||||
...(withdrawal ? (["bill"] as const) : []),
|
||||
"notes",
|
||||
];
|
||||
return [
|
||||
...top.map((field) => ({ field, index: 0 })),
|
||||
...splits.flatMap((split, index) =>
|
||||
own(split).map((field) => ({ field, index }))
|
||||
),
|
||||
].map((row) => ({ ...row, key: rowKey(row.field, row.index) }));
|
||||
}
|
||||
Reference in new issue
Block a user