feat(filters): smart query bar with amount operators and multi-select dropdowns
- Query bar parses >500, >=500, <500, <=500, 500-1500 into amount_min/max filters - Parsed tokens shown as dismissable chips below the query bar - Category, Bank, Tag, Type filters upgraded from single-select to multi-select - MultiSelect dropdown component with checkbox list and active-state border - Backend: TransactionFilters uses string[] for categories/bank_names/tag_ids/transaction_types - SQL: ANY($n::text[]) / ANY($n::int[]) for array filters
This commit is contained in:
+12
-4
@@ -14,21 +14,29 @@ interface TransactionsResponse {
|
||||
interface TransactionFilters {
|
||||
from?: string;
|
||||
to?: string;
|
||||
category?: string;
|
||||
bank_name?: string;
|
||||
categories?: string[];
|
||||
bank_names?: string[];
|
||||
tag_ids?: string[];
|
||||
transaction_types?: string[];
|
||||
search?: string;
|
||||
statement_id?: string;
|
||||
tag_id?: string;
|
||||
sort_by?: string;
|
||||
sort_dir?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
amount_min?: number;
|
||||
amount_max?: number;
|
||||
}
|
||||
|
||||
function buildParams(filters: TransactionFilters): string {
|
||||
const params = new URLSearchParams();
|
||||
Object.entries(filters).forEach(([key, val]) => {
|
||||
if (val !== undefined && val !== "") params.set(key, String(val));
|
||||
if (val === undefined || val === "") return;
|
||||
if (Array.isArray(val)) {
|
||||
if (val.length > 0) params.set(key, val.join(","));
|
||||
} else {
|
||||
params.set(key, String(val));
|
||||
}
|
||||
});
|
||||
return params.toString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user