diff --git a/src/app/providers.tsx b/src/app/providers.tsx
index 4ec9bc0..bbeb587 100644
--- a/src/app/providers.tsx
+++ b/src/app/providers.tsx
@@ -7,7 +7,7 @@
import { type ReactNode, useEffect, useState } from 'react';
import { ensureFirstLaunch } from '@db/seed';
-import { CelebrationProvider, ToastProvider } from '@components/ui';
+import { Button, CelebrationProvider, ToastProvider } from '@components/ui';
import { ensureDailyRecoverySnapshot } from '@/backup/recovery';
import { ThemeSync } from '@shared/settings/ThemeSync';
import { PwaUpdatePrompt } from './pwa/PwaUpdatePrompt';
@@ -50,13 +50,20 @@ export function AppProviders({ children }: AppProvidersProps) {
if (error) {
return (
-
-
-
Database error
-
{error}
-
- Try clearing site data in your browser settings.
+
+
+
AfterSum could not open local data
+
+ Reload the app first. If the problem continues, do not clear site data unless you already
+ have a portable backup—clearing site data permanently removes local AfterSum records.
)}
diff --git a/src/routes/overview/SearchPage.tsx b/src/routes/overview/SearchPage.tsx
index 57ecf07..b79bbf0 100644
--- a/src/routes/overview/SearchPage.tsx
+++ b/src/routes/overview/SearchPage.tsx
@@ -2,9 +2,8 @@
import { useMemo, useState } from 'react';
import { useNavigate } from '@tanstack/react-router';
-import { useLiveQuery } from 'dexie-react-hooks';
-import { getDB } from '@db/database';
import { Card, EmptyState, Input, Spinner } from '@components/ui';
+import { useGlobalSearchData } from '@overview/queries/useGlobalSearchData';
import { Search as SearchIcon, Receipt, Users, HandCoins, UserCircle } from 'lucide-react';
import { formatHumanDate } from '@shared/dates';
import type { LendEntryType } from '@db/schema';
@@ -30,18 +29,7 @@ function lendEntryLabel(type: LendEntryType): string {
export function SearchPage() {
const [q, setQ] = useState('');
const navigate = useNavigate();
- const all = useLiveQuery(async () => {
- const db = getDB();
- const [track, expenses, groups, people, lendEntries, ledgers] = await Promise.all([
- db.trackTransactions.toArray(),
- db.splitExpenses.toArray(),
- db.splitGroups.toArray(),
- db.people.toArray(),
- db.lendEntries.toArray(),
- db.lendLedgers.toArray(),
- ]);
- return { track, expenses, groups, people, lendEntries, ledgers };
- }, []);
+ const all = useGlobalSearchData();
const results = useMemo(() => {
if (!all || !q.trim()) return [];
@@ -50,9 +38,14 @@ export function SearchPage() {
const visibleGroups = new Map(
all.groups.filter((group) => !group.deletedAt).map((group) => [group.id, group]),
);
+ const visiblePeople = new Map(
+ all.people.filter((person) => !person.deletedAt).map((person) => [person.id, person]),
+ );
+ const visibleLedgers = new Map(
+ all.ledgers.filter((ledger) => !ledger.deletedAt).map((ledger) => [ledger.id, ledger]),
+ );
- for (const person of all.people) {
- if (person.deletedAt) continue;
+ for (const person of visiblePeople.values()) {
const nameMatches = person.name.toLocaleLowerCase().includes(needle);
const noteMatches = person.note?.toLocaleLowerCase().includes(needle) ?? false;
if (nameMatches || noteMatches) {
@@ -97,9 +90,9 @@ export function SearchPage() {
for (const entry of all.lendEntries) {
if (entry.deletedAt) continue;
- const ledger = all.ledgers.find((item) => item.id === entry.ledgerId && !item.deletedAt);
+ const ledger = visibleLedgers.get(entry.ledgerId);
if (!ledger) continue;
- const person = all.people.find((item) => item.id === ledger.personId && !item.deletedAt);
+ const person = visiblePeople.get(ledger.personId);
if (!person) continue;
const note = entry.note?.trim() ?? '';
if (!note.toLocaleLowerCase().includes(needle) && !person.name.toLocaleLowerCase().includes(needle)) continue;
diff --git a/src/routes/settings/BackupCenter.tsx b/src/routes/settings/BackupCenter.tsx
index ff86abd..865fcb8 100644
--- a/src/routes/settings/BackupCenter.tsx
+++ b/src/routes/settings/BackupCenter.tsx
@@ -23,7 +23,8 @@ import {
import { createPortableBackupFile, shareOrDownloadBackup } from '@/backup/portable';
import { buildFullZip } from '@/export/zip/builder';
import { csvOfTrackTransactions } from '@/export/csv/serializer';
-import { getDB } from '@db/database';
+import { trackCategoryRepository } from '@modules/track/repositories/trackCategoryRepository';
+import { trackTransactionRepository } from '@modules/track/repositories/trackTransactionRepository';
import type { RecoverySnapshot } from '@db/schema';
import { formatHumanDateTime, nowISO, toMonthKey } from '@shared/dates';
import { fileFromBlob, shareOrDownloadFile } from '@shared/files/shareFile';
@@ -166,32 +167,24 @@ export function BackupCenter() {
setBusy('month');
try {
const month = toMonthKey();
- const db = getDB();
const [transactions, categories] = await Promise.all([
- db.trackTransactions.toArray(),
- db.trackCategories.toArray(),
+ trackTransactionRepository.listByMonth(month),
+ trackCategoryRepository.listAll(),
]);
- const filtered = transactions.filter(
- (transaction) => !transaction.deletedAt && transaction.date.startsWith(month),
- );
- const csv = csvOfTrackTransactions(
- filtered,
- categories,
- settings?.defaultCurrency ?? 'INR',
- );
+ const csv = csvOfTrackTransactions(transactions, categories);
const file = new File([csv], `track-${month}.csv`, {
type: 'text/csv;charset=utf-8',
});
const result = await shareOrDownloadFile(file, {
title: `AfterSum Track export — ${month}`,
- text: `${filtered.length} Track transactions from ${month}`,
+ text: `${transactions.length} Track transactions from ${month}`,
});
if (result === 'cancelled') return;
toast.show(
result === 'shared'
- ? `Shared ${filtered.length} transactions for ${month}.`
- : `Exported ${filtered.length} transactions for ${month}.`,
+ ? `Shared ${transactions.length} transactions for ${month}.`
+ : `Exported ${transactions.length} transactions for ${month}.`,
);
} catch (error) {
toast.show('Export failed: ' + errorMessage(error), { variant: 'error' });
@@ -214,7 +207,7 @@ export function BackupCenter() {
Data & Storage
- No AfterSum account or backend. Data stays on this device unless you explicitly save a backup or export.
+ AfterSum keeps your data on this device unless you explicitly save a backup or export a file.
@@ -278,7 +271,7 @@ export function BackupCenter() {
)}
- Browser usage and quota are reported for this web origin, so they include AfterSum's database and offline cache and may include other data stored on the same host. “AfterSum backup data” is the approximate serialized size of your app records, not IndexedDB's exact on-disk size.
+ Browser usage and quota cover this web origin, including AfterSum's database and offline cache. “AfterSum backup data” is the approximate size of your app records, not exact IndexedDB disk usage.
- This browser does not expose the persistent-storage API. AfterSum will still use local IndexedDB, but the browser controls eviction.
+ This browser does not expose persistent storage. AfterSum still uses local IndexedDB, but the browser controls eviction.
)}
@@ -307,7 +300,7 @@ export function BackupCenter() {
Portable backup
- Save one complete AfterSum file somewhere outside this device. On supported devices, the system share sheet lets you choose Drive, Files, WhatsApp, Dropbox, or another installed destination without connecting an account to AfterSum.
+ Save one complete, restorable AfterSum file somewhere outside this device. On supported devices, the system share sheet lets you choose where it goes without connecting an account to AfterSum.
@@ -320,7 +313,7 @@ export function BackupCenter() {
{busy === 'portable' ? : } Save portable backup
- This portable format is readable JSON. Keep the file somewhere private.
+ This backup is readable JSON and contains your financial records. Keep it private.
@@ -330,7 +323,7 @@ export function BackupCenter() {
Automatic recovery
- Keeps one rolling daily checkpoint, replacing the previous automatic copy, plus up to three pre-restore safety checkpoints. These help with mistakes but cannot recover a lost or reset device.
+ Keeps one rolling daily checkpoint plus up to three pre-restore safety checkpoints. These help undo local mistakes but cannot recover a lost or reset device.
@@ -364,7 +357,7 @@ export function BackupCenter() {
Restore portable backup
- Choose an AfterSum backup file. Restore is explicit and creates a local safety checkpoint first.
+ Choose an AfterSum backup file. Restore replaces current financial records and creates a local safety checkpoint first.