Add support for filtering dates

This commit is contained in:
2025-05-20 16:27:31 +02:00
parent e3618c539e
commit af15a352a3
9 changed files with 183 additions and 56 deletions

View File

@@ -1,4 +1,4 @@
import { useCallback, useMemo } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useStore } from "../../store/AppStore";
import TransactionsTable from "./TransactionsTable";
import { SkippedLines } from "./SkippedLines";
@@ -6,19 +6,48 @@ import ImportBar from "./ImportBar";
import type { ImportOptions } from "../../types/api";
import { submitTransactions } from "../../services/api.service";
import { useLoader } from "../../hooks/useLoader";
import { FilterPanel, type FilterData } from "./FilterPanel";
import dayjs from "dayjs";
export default function PrepareTransactionsPage() {
const { state, dispatch } = useStore();
const [userFilter, setUserFilter] = useState<FilterData>({});
const [deselected, setDeselected] = useState<number[]>([]);
const select = useCallback((index: number) => setDeselected(deselected.filter(x => x !== index)), [deselected, setDeselected]);
const deselect = useCallback((index: number) => setDeselected([...deselected, index]), [deselected, setDeselected]);
const desiredTransactions = useMemo(() => state.transactions.filter((_, i) => !state.filteredOut.includes(i)), [state, state.filteredOut]);
const filteredTransactions = useMemo(() => state.transactions
.filter(t => {
if (!userFilter.from) {
return true;
}
const date = dayjs(t.date);
return date.isAfter(userFilter.from, 'day') || date.isSame(userFilter.from, 'day');
})
.filter(t => {
if (!userFilter.to) {
return true;
}
const date = dayjs(t.date);
return date.isBefore(userFilter.to, 'day') || date.isSame(userFilter.to, 'day');
})
, [state.transactions, userFilter]);
const selectedTransactions = useMemo(() => filteredTransactions.filter((_, i) => !deselected.includes(i)), [filteredTransactions, deselected]);
const { fn: handleSubmit, loading } = useLoader(async (opts: ImportOptions) => {
if(!state.server) {
return;
}
const response = await submitTransactions(desiredTransactions, state.server, opts);
const response = await submitTransactions(selectedTransactions, state.server, opts);
dispatch({
type: 'SET_RESULT',
@@ -31,17 +60,9 @@ export default function PrepareTransactionsPage() {
offset: 1
}
});
}, [desiredTransactions, state]);
}, [selectedTransactions, state]);
const filterOut = useCallback((index: number) => dispatch({
type: 'FILTER_OUT',
payload: { index }
}), [dispatch]);
const removeFilter = useCallback((index: number) => dispatch({
type: 'REMOVE_FILTER',
payload: { index }
}), [dispatch]);
useEffect(() => setDeselected([]), [filteredTransactions]);
return (
<div className="columns mt-6">
@@ -50,12 +71,17 @@ export default function PrepareTransactionsPage() {
<h2 className="title is-4 has-text-centered">Prepare Transactions</h2>
<div className="content">
<h3 className="title is-5">Considered transactions ({state.transactions.length-state.filteredOut.length}/{state.transactions.length})</h3>
<h3 className="title is-5">Filters</h3>
<FilterPanel value={userFilter} setValue={setUserFilter} />
</div>
<div className="content">
<h3 className="title is-5">Considered transactions ({selectedTransactions.length}/{filteredTransactions.length})</h3>
<TransactionsTable
transactions={state.transactions}
filteredOut={state.filteredOut}
filterOut={filterOut}
removeFilter={removeFilter} />
transactions={filteredTransactions}
deselected={deselected}
deselect={deselect}
select={select} />
</div>
<div className="content">