Performance profile of AquaDealers: caching architecture, code splitting strategy, known bottlenecks, and what's working well.
All API data flows through TanStack React Query with persistence to IndexedDB via idb-keyval. This provides both in-memory caching and cross-session persistence.
| Setting | Value | Effect |
|---|---|---|
staleTime | 5 minutes | Data considered fresh for 5 min; no refetch during this window |
gcTime | 24 hours | Unused cache entries garbage-collected after 24 hours |
networkMode | offlineFirst | Serve from cache immediately, refetch in background when online |
| Persistence | IndexedDB (idb-keyval) | Cache survives browser restarts; restored on app launch |
offlineFirst network mode combined with IndexedDB persistence means the app remains functional (read-only) even without network connectivity. Mutations queue and replay when online.React.lazy() with dynamic imports| Severity | Issue | Location | Impact |
|---|---|---|---|
| High | Monthly report unbounded fetch | /reports page |
10+ queries with no row limits or pagination. Fetches ALL bills, payments, and expenses for the selected period. Will cause timeouts and memory issues for high-volume dealers (1000+ bills/month). |
| High | getDuesSummary fetches 10,000 farmer rows | Dues summary calculation | Pulls up to 10,000 farmer rows client-side to calculate dues summary. All data transferred over the network and processed in the browser. Should be a server-side aggregation. |
| High | Bill PDF html2canvas freezes mobile | PDF generation (Bill details) | html2canvas renders the bill DOM to a canvas element, which freezes the main thread for 3–5 seconds on mobile devices. UI is completely unresponsive during this time. |
| Medium | Dashboard 15+ parallel queries on cold load | Dashboard page | Cold load fires 15+ Supabase queries simultaneously. On slow connections, this can saturate the connection pool and cause waterfall delays. Subsequent loads are fast due to caching. |
| Medium | 35+ queries use select('*') | Various service files | Selecting all columns transfers unnecessary data over the network. Impact compounds on large tables (bills, bill_items, inventory_lots). Should select only needed columns. |
| Medium | Missing loading="lazy" on images | Product and farmer images | All images load eagerly, increasing initial page weight. Particularly noticeable on product listing and farmer list pages with many entries. |
| Low | Cashbook no pagination | Cashbook page | Loads all cashbook entries for the selected period without pagination. Acceptable for typical usage but will degrade with very high transaction volumes. |
Solid offline experience. Cache persists across sessions. Background refetches keep data fresh without blocking UI. offlineFirst mode means instant renders from cache.
64 pages all use React.lazy(). Initial bundle is small. Only the current page's code is loaded. Combined with service worker precaching for fast subsequent visits.
The app uses polling (via React Query's refetch intervals) instead of Supabase Realtime. This avoids WebSocket connection overhead and simplifies the data layer. Acceptable because AquaDealers is primarily single-user (one dealer, one device at a time).
Large dataset searches (farmers, products) use server-side search RPCs rather than loading all data client-side. This keeps search responsive even with thousands of records.
The Dues page uses useWindowVirtualizer (TanStack Virtual) to render only visible farmer rows. Handles thousands of farmers without DOM bloat.
The Daily Book prefetches adjacent dates (previous day, next day) so date navigation feels instant. Data is ready before the user taps the arrow.