Skip to content

feat: Stream the transactions spreadsheet and speed up reports#92

Merged
nfebe merged 1 commit into
devfrom
feat/issue-88-followups
Jun 21, 2026
Merged

feat: Stream the transactions spreadsheet and speed up reports#92
nfebe merged 1 commit into
devfrom
feat/issue-88-followups

Conversation

@nfebe

@nfebe nfebe commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Performance and UX follow-ups from issue #88, focused on making reports and the transactions spreadsheet fast and reliable.

Reports performance

  • Transaction pages for a report are fetched concurrently instead of one at a time, so wide date ranges render much sooner.
  • Per-category spending trends are computed in a single pass instead of rescanning every transaction once per category.

Reports correctness

  • Charts and other report tabs stay visible whenever their data exists, with a loading state instead of a flash of the empty screen. Previously the whole view was hidden while stats loaded or whenever overview totals were zero, so charts often never appeared.
  • A notice shows when a period has more transactions than can be charted at once, so partial figures are not mistaken for complete ones.
  • Amounts that could not be converted to the default currency are surfaced, so cross-currency totals are no longer silently understated.

Transactions spreadsheet

  • Rows stream in chunks: the first rows and running totals appear immediately and grow as the rest load, instead of blocking on the full set.
  • Columns are sortable.
  • The current filtered and sorted view exports to a CSV file.
  • Search is debounced so typing stays smooth on large sets.

Backend test coverage for the currency behavior is in a companion PR on trakli/webservice.

Refs #88

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 21, 2026

Copy link
Copy Markdown

Deploying trakli-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0238442
Status:⚡️  Build in progress...

View logs

@sourceant

sourceant Bot commented Jun 21, 2026

Copy link
Copy Markdown

Code Review Summary

The PR introduces significant performance improvements for both the transactions spreadsheet and reports. It transitions from sequential to concurrent fetching and optimizes data processing for charts. It also adds a robust CSV export feature and sortable columns.

🚀 Key Improvements

  • Implemented concurrent transaction fetching in useReportData.ts using a worker queue pattern.
  • Optimized category trend calculations in useReportData.ts to a single-pass O(N) operation.
  • Added streaming support to TransactionsSpreadsheet.vue, allowing UI updates as data chunks arrive.
  • Improved UI stability in reports.vue by refining the hasData logic to prevent empty state flashes.

💡 Minor Suggestions

  • Sanitize CSV data against formula injection characters.
  • Use ref for timers as noted in previous review comments.

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

const searchInput = ref('');
const search = ref('');

let searchTimer: ReturnType<typeof setTimeout> | null = null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The debouncing logic uses a local variable searchTimer. This could lead to memory leaks or multiple timers if the component re-renders frequently or if the watch is triggered rapidly. It is safer to use a ref for the timer or a utility like useDebounce from VueUse.

Suggested change
let searchTimer: ReturnType<typeof setTimeout> | null = null;
const searchTimer = ref<ReturnType<typeof setTimeout> | null>(null);
watch(searchInput, (value) => {
if (searchTimer.value) clearTimeout(searchTimer.value);
searchTimer.value = setTimeout(() => {
search.value = value;
}, 200);
});

const queue: number[] = [];
for (let p = 2; p <= targetPages; p++) queue.push(p);

let cursor = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrency control: The worker pattern here is good, but cursor++ is not atomic in a multi-threaded environment. While JS is single-threaded, if fetchPage contained an await before the increment, it could cause issues. It's fine here because of JS event loop, but a cleaner pattern is queue.shift().

Suggested change
let cursor = 0;
const worker = async () => {
while (queue.length > 0) {
const p = queue.shift()!;
const resp = await fetchPage(p);
pages[p - 1] = resp.data || [];
}
};

Reports now load noticeably faster and no longer hide charts while data
is still arriving.

- Report transaction pages are fetched concurrently instead of one at a
  time, so wide date ranges render much sooner.
- Per-category spending trends are built in a single pass rather than
  rescanning every transaction once per category.
- The charts and other report tabs stay visible whenever chart data
  exists, with a loading state instead of a flash of the empty screen.
- A notice appears when a period has more transactions than can be
  charted at once, so partial figures are not mistaken for complete ones.
- Amounts that could not be converted to the default currency are now
  flagged, so cross-currency totals are no longer silently understated.

The transactions spreadsheet streams rows in chunks: the first rows and
running totals appear immediately and grow as the rest load. Columns can
be sorted, the current view can be exported to a CSV file, and search is
debounced so typing stays smooth on large sets.
@nfebe nfebe force-pushed the feat/issue-88-followups branch from b37e2e6 to 0238442 Compare June 21, 2026 14:17

@sourceant sourceant Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. No specific code suggestions were generated. See the overview comment for a summary.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying webui with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0238442
Status: ✅  Deploy successful!
Preview URL: https://5b6e29ad.webui-9fh.pages.dev
Branch Preview URL: https://feat-issue-88-followups.webui-9fh.pages.dev

View logs

@nfebe nfebe merged commit c6fae2f into dev Jun 21, 2026
5 of 6 checks passed
@nfebe nfebe deleted the feat/issue-88-followups branch June 21, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant