2026-07-04 21:42:18 -05:00
import { useState , useEffect , useRef } from 'react' ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
import { toast } from 'sonner' ;
import {
Sparkles , CheckCircle2 , Loader2 , RefreshCw , Link2 , Link2Off ,
2026-06-14 15:15:31 -05:00
XCircle , Eye , EyeOff , Search , Clock , ChevronLeft , ChevronRight ,
2026-06-08 21:07:42 -05:00
ArrowUp , ArrowDown , ArrowUpDown ,
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} from 'lucide-react' ;
2026-07-04 21:42:18 -05:00
import { api , type QueryParams } from '@/api' ;
import { cn , errMessage } from '@/lib/utils' ;
import { asDollars } from '@/lib/money' ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
import { Button } from '@/components/ui/button' ;
import { Input } from '@/components/ui/input' ;
2026-07-04 21:42:18 -05:00
import { SectionCard , type SectionCardProps } from './dataShared' ;
2026-05-29 18:06:12 -05:00
import BillModal from '@/components/BillModal' ;
2026-06-14 15:15:31 -05:00
import {
MatchBillDialog ,
transactionTitle ,
transactionDate ,
formatTransactionAmount ,
} from '@/components/transactions/MatchBillDialog' ;
2026-07-04 21:42:18 -05:00
import type { BankTransaction , Bill , Category } from '@/types' ;
interface MatchSuggestion {
id : number | string ;
score? : number ;
transactionId : number | string ;
billId : number | string ;
transaction? : BankTransaction ;
bill? : Bill ;
reasons? : string [ ] ;
}
interface SimplefinConn {
id : number | string ;
last_error? : string | null ;
last_sync_at? : string | null ;
}
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
2026-07-04 21:42:18 -05:00
const TRANSACTION_FILTERS : { id : string ; label : string ; params : Record < string , string > } [ ] = [
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
{ id : 'open' , label : 'Open' , params : { ignored : 'false' } } ,
{ id : 'unmatched' , label : 'Unmatched' , params : { match_status : 'unmatched' , ignored : 'false' } } ,
{ id : 'matched' , label : 'Matched' , params : { match_status : 'matched' , ignored : 'false' } } ,
{ id : 'ignored' , label : 'Ignored' , params : { match_status : 'ignored' , ignored : 'true' } } ,
{ id : 'all' , label : 'All' , params : { ignored : 'all' } } ,
] ;
2026-07-04 21:42:18 -05:00
function transactionStatus ( tx : BankTransaction | null | undefined ) : string {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
if ( tx ? . ignored ) return 'ignored' ;
2026-07-04 21:42:18 -05:00
return ( tx ? . match_status as string ) || 'unmatched' ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
}
2026-07-04 21:42:18 -05:00
function TransactionStatusBadge ( { tx } : { tx : BankTransaction } ) {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
const status = transactionStatus ( tx ) ;
2026-07-04 21:42:18 -05:00
const styles : Record < string , string > = {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
matched : 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600' ,
ignored : 'border-muted-foreground/30 bg-muted/40 text-muted-foreground' ,
unmatched : 'border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400' ,
} ;
return (
< span className = { cn (
'inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-semibold uppercase' ,
styles [ status ] || styles . unmatched ,
) } >
{ status }
< / span >
) ;
}
2026-07-04 21:42:18 -05:00
function matchScoreTone ( score? : number | string ) : string {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
const value = Number ( score ) || 0 ;
if ( value >= 80 ) return 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400' ;
if ( value >= 55 ) return 'border-sky-500/30 bg-sky-500/10 text-sky-600 dark:text-sky-400' ;
return 'border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400' ;
}
2026-07-04 21:42:18 -05:00
interface SuggestedMatchesPanelProps {
suggestions : MatchSuggestion [ ] ;
loading : boolean ;
actionId : string | null ;
onAccept : ( suggestion : MatchSuggestion ) = > void ;
onReject : ( suggestion : MatchSuggestion ) = > void ;
}
function SuggestedMatchesPanel ( { suggestions , loading , actionId , onAccept , onReject } : SuggestedMatchesPanelProps ) {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
return (
< div className = "rounded-lg border border-sky-500/20 bg-sky-500/[0.035]" >
< div className = "flex flex-wrap items-center justify-between gap-3 border-b border-sky-500/10 px-4 py-3" >
< div className = "flex items-center gap-2" >
< span className = "inline-flex h-8 w-8 items-center justify-center rounded-md border border-sky-500/20 bg-sky-500/10 text-sky-600 dark:text-sky-400" >
< Sparkles className = "h-4 w-4" / >
< / span >
< div >
< p className = "text-sm font-semibold" > Suggested matches < / p >
< p className = "text-xs text-muted-foreground" > { loading ? 'Checking transactions' : ` ${ suggestions . length } ready for review ` } < / p >
< / div >
< / div >
< / div >
{ loading ? (
< div className = "px-4 py-6 text-sm text-muted-foreground" >
< Loader2 className = "mr-2 inline h-4 w-4 animate-spin" / >
Finding likely bill matches . . .
< / div >
) : suggestions . length === 0 ? (
< div className = "px-4 py-6 text-sm text-muted-foreground" >
No suggested matches right now .
< / div >
) : (
< div className = "grid gap-2 p-3 xl:grid-cols-2" >
{ suggestions . map ( suggestion = > {
2026-07-04 21:42:18 -05:00
const tx = suggestion . transaction || ( { } as BankTransaction ) ;
const bill = suggestion . bill || ( { } as Bill ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
const acceptBusy = actionId === ` suggestion-match: ${ suggestion . id } ` ;
const rejectBusy = actionId === ` suggestion-reject: ${ suggestion . id } ` ;
const busy = acceptBusy || rejectBusy ;
return (
< div
key = { suggestion . id }
className = "rounded-lg border border-border/60 bg-background/80 p-3 shadow-sm transition-colors hover:border-sky-500/30"
>
< div className = "flex items-start justify-between gap-3" >
< div className = "min-w-0 space-y-1" >
< div className = "flex flex-wrap items-center gap-2" >
< span className = { cn ( 'rounded-full border px-2 py-0.5 text-[10px] font-semibold uppercase' , matchScoreTone ( suggestion . score ) ) } >
{ suggestion . score }
< / span >
< p className = "truncate text-sm font-medium" > { transactionTitle ( tx ) } < / p >
< / div >
< p className = "truncate text-xs text-muted-foreground" >
{ transactionDate ( tx ) } · { tx . source_label || tx . source_type_label || 'Transaction' }
< / p >
< / div >
< p className = { cn (
'shrink-0 text-sm font-semibold tabular-nums' ,
Number ( tx . amount ) < 0 ? 'text-destructive' : 'text-emerald-600' ,
) } >
2026-07-04 21:42:18 -05:00
{ formatTransactionAmount ( tx . amount , tx . currency ? ? undefined ) }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / p >
< / div >
< div className = "mt-3 flex items-center gap-2 rounded-md border border-emerald-500/15 bg-emerald-500/[0.045] px-2.5 py-2" >
< Link2 className = "h-3.5 w-3.5 shrink-0 text-emerald-600 dark:text-emerald-400" / >
< div className = "min-w-0" >
< p className = "truncate text-sm font-medium" > { bill . name || ` Bill ${ suggestion . billId } ` } < / p >
< p className = "text-xs text-muted-foreground" >
Expected $ { Number ( bill . expected_amount || 0 ) . toFixed ( 2 ) }
< / p >
< / div >
< / div >
2026-07-04 21:42:18 -05:00
{ ( suggestion . reasons ? . length ? ? 0 ) > 0 && (
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< div className = "mt-2 flex flex-wrap gap-1.5" >
2026-07-04 21:42:18 -05:00
{ suggestion . reasons ? . slice ( 0 , 4 ) . map ( reason = > (
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< span key = { reason } className = "rounded-full border border-border/60 bg-muted/30 px-2 py-0.5 text-[10px] text-muted-foreground" >
{ reason }
< / span >
) ) }
< / div >
) }
< div className = "mt-3 flex justify-end gap-2" >
< Button
type = "button"
size = "sm"
variant = "ghost"
disabled = { busy }
onClick = { ( ) = > onReject ( suggestion ) }
className = "h-8 text-xs text-muted-foreground hover:text-destructive"
>
{ rejectBusy ? < Loader2 className = "h-3.5 w-3.5 animate-spin" / > : < XCircle className = "h-3.5 w-3.5" / > }
< span className = "ml-1.5" > Reject < / span >
< / Button >
< Button
type = "button"
size = "sm"
disabled = { busy }
onClick = { ( ) = > onAccept ( suggestion ) }
className = "h-8 gap-1.5 text-xs"
>
{ acceptBusy ? < Loader2 className = "h-3.5 w-3.5 animate-spin" / > : < CheckCircle2 className = "h-3.5 w-3.5" / > }
Match
< / Button >
< / div >
< / div >
) ;
} ) }
< / div >
) }
< / div >
) ;
}
2026-07-04 21:42:18 -05:00
function parseUtc ( str : string | null | undefined ) : Date | null {
2026-06-07 19:41:17 -05:00
if ( ! str ) return null ;
const normalized = str . includes ( 'T' ) ? str : str.replace ( ' ' , 'T' ) + 'Z' ;
return new Date ( normalized ) ;
}
2026-07-04 21:42:18 -05:00
function timeAgo ( iso : string | null | undefined ) : string | null {
const d = parseUtc ( iso ) ;
if ( ! d ) return null ;
const secs = Math . floor ( ( Date . now ( ) - d . getTime ( ) ) / 1000 ) ;
2026-05-28 22:06:15 -05:00
if ( secs < 60 ) return 'just now' ;
if ( secs < 3600 ) return ` ${ Math . floor ( secs / 60 ) } m ago ` ;
if ( secs < 86400 ) return ` ${ Math . floor ( secs / 3600 ) } h ago ` ;
return ` ${ Math . floor ( secs / 86400 ) } d ago ` ;
}
2026-07-04 21:42:18 -05:00
interface TransactionMatchingSectionProps {
refreshKey? : number | string ;
simplefinConn? : SimplefinConn | null ;
cardProps? : Partial < SectionCardProps > ;
}
export default function TransactionMatchingSection ( { refreshKey , simplefinConn , cardProps = { } } : TransactionMatchingSectionProps ) {
const [ transactions , setTransactions ] = useState < BankTransaction [ ] > ( [ ] ) ;
const [ suggestions , setSuggestions ] = useState < MatchSuggestion [ ] > ( [ ] ) ;
const [ bills , setBills ] = useState < Bill [ ] > ( [ ] ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
const [ filter , setFilter ] = useState ( 'open' ) ;
const [ loading , setLoading ] = useState ( true ) ;
const [ suggestionsLoading , setSuggestionsLoading ] = useState ( true ) ;
const [ billsLoading , setBillsLoading ] = useState ( true ) ;
2026-07-04 21:42:18 -05:00
const [ actionId , setActionId ] = useState < string | null > ( null ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
const [ matchOpen , setMatchOpen ] = useState ( false ) ;
2026-07-04 21:42:18 -05:00
const [ matchTransaction , setMatchTransaction ] = useState < BankTransaction | null > ( null ) ;
const [ categories , setCategories ] = useState < Category [ ] > ( [ ] ) ;
const [ createBillSourceTx , setCreateBillSourceTx ] = useState < { tx : BankTransaction ; initialBill : Partial < Bill > } | null > ( null ) ;
2026-06-08 21:07:42 -05:00
const [ page , setPage ] = useState ( 1 ) ;
const [ totalCount , setTotalCount ] = useState ( 0 ) ;
const [ search , setSearch ] = useState ( '' ) ;
const [ sortBy , setSortBy ] = useState ( 'date' ) ;
const [ sortDir , setSortDir ] = useState ( 'desc' ) ;
2026-07-04 21:42:18 -05:00
const searchTimerRef = useRef < ReturnType < typeof setTimeout > | null > ( null ) ;
2026-06-08 21:07:42 -05:00
const PAGE_SIZE = 10 ;
const totalPages = Math . ceil ( totalCount / PAGE_SIZE ) || 1 ;
2026-07-04 21:42:18 -05:00
const currentFilter = TRANSACTION_FILTERS . find ( item = > item . id === filter ) || TRANSACTION_FILTERS [ 0 ] ! ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
2026-07-04 21:42:18 -05:00
const loadTransactions = async ( pageNum? : number , searchOverride? : string , sortByOverride? : string , sortDirOverride? : string ) = > {
2026-06-08 21:07:42 -05:00
const p = pageNum ? ? page ;
const q = searchOverride !== undefined ? searchOverride : search ;
const sb = sortByOverride !== undefined ? sortByOverride : sortBy ;
const sd = sortDirOverride !== undefined ? sortDirOverride : sortDir ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setLoading ( true ) ;
try {
2026-07-04 21:42:18 -05:00
const params : QueryParams = { limit : PAGE_SIZE , offset : ( p - 1 ) * PAGE_SIZE , sort_by : sb , sort_dir : sd , . . . currentFilter . params } ;
2026-06-08 21:07:42 -05:00
if ( q ) params . q = q ;
2026-07-04 21:42:18 -05:00
const resp = await api . transactions ( params ) as { transactions? : BankTransaction [ ] ; total? : number } ;
2026-06-08 21:07:42 -05:00
setTransactions ( resp . transactions || [ ] ) ;
setTotalCount ( resp . total || 0 ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Failed to load transactions.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setTransactions ( [ ] ) ;
} finally {
setLoading ( false ) ;
}
} ;
const loadSuggestions = async ( ) = > {
setSuggestionsLoading ( true ) ;
try {
2026-07-04 21:42:18 -05:00
const data = await api . matchSuggestions ( { limit : 8 } ) as MatchSuggestion [ ] ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setSuggestions ( data || [ ] ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Failed to load match suggestions.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setSuggestions ( [ ] ) ;
} finally {
setSuggestionsLoading ( false ) ;
}
} ;
const refreshTransactionWorkbench = async ( ) = > {
await Promise . all ( [ loadTransactions ( ) , loadSuggestions ( ) ] ) ;
} ;
const loadBills = async ( ) = > {
setBillsLoading ( true ) ;
try {
2026-07-04 21:42:18 -05:00
const data = await api . bills ( ) as Bill [ ] ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setBills ( data || [ ] ) ;
2026-05-29 01:06:20 -05:00
} catch ( err ) {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setBills ( [ ] ) ;
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Failed to load bills for matching.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} finally {
setBillsLoading ( false ) ;
}
} ;
2026-07-04 21:56:04 -05:00
useEffect ( ( ) = > { loadBills ( ) ; } , [ ] ) ;
2026-07-03 19:47:14 -05:00
// Intentional: reset + reload page 1 when the filter or refresh key changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
2026-06-08 21:07:42 -05:00
useEffect ( ( ) = > { setSearch ( '' ) ; setPage ( 1 ) ; loadTransactions ( 1 , '' ) ; } , [ filter , refreshKey ] ) ;
2026-07-04 21:56:04 -05:00
useEffect ( ( ) = > { loadSuggestions ( ) ; } , [ refreshKey ] ) ;
2026-05-29 18:06:12 -05:00
useEffect ( ( ) = > {
2026-07-04 21:42:18 -05:00
api . categories ( ) . then ( data = > setCategories ( ( data as Category [ ] ) || [ ] ) ) . catch ( err = > console . error ( '[TransactionMatchingSection] failed to load categories' , err ) ) ;
2026-05-29 18:06:12 -05:00
} , [ ] ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
2026-07-04 21:42:18 -05:00
const changePage = ( newPage : number ) = > {
2026-06-08 21:07:42 -05:00
setPage ( newPage ) ;
loadTransactions ( newPage ) ;
} ;
2026-07-04 21:42:18 -05:00
const handleSearchChange = ( e : React.ChangeEvent < HTMLInputElement > ) = > {
2026-06-08 21:07:42 -05:00
const value = e . target . value ;
setSearch ( value ) ;
2026-07-04 21:42:18 -05:00
if ( searchTimerRef . current ) clearTimeout ( searchTimerRef . current ) ;
2026-06-08 21:07:42 -05:00
searchTimerRef . current = setTimeout ( ( ) = > {
setPage ( 1 ) ;
loadTransactions ( 1 , value ) ;
} , 300 ) ;
} ;
2026-07-04 21:42:18 -05:00
const handleSortClick = ( column : string ) = > {
2026-06-08 21:07:42 -05:00
const newDir = sortBy === column && sortDir === 'desc' ? 'asc' : 'desc' ;
setSortBy ( column ) ;
setSortDir ( newDir ) ;
setPage ( 1 ) ;
loadTransactions ( 1 , undefined , column , newDir ) ;
} ;
2026-07-04 21:42:18 -05:00
const openMatchDialog = ( tx : BankTransaction ) = > {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setMatchTransaction ( tx ) ;
setMatchOpen ( true ) ;
if ( ! bills . length && ! billsLoading ) loadBills ( ) ;
} ;
2026-07-04 21:42:18 -05:00
const openCreateBill = ( tx : BankTransaction | null , nameOverride? : string ) = > {
if ( ! tx ) return ;
2026-05-29 18:06:12 -05:00
const amount = Math . abs ( Number ( tx . amount || 0 ) ) / 100 ;
const dateStr = transactionDate ( tx ) ;
const day = dateStr ? parseInt ( dateStr . slice ( 8 , 10 ) , 10 ) : 1 ;
setCreateBillSourceTx ( {
tx ,
initialBill : {
name : nameOverride || transactionTitle ( tx ) ,
2026-07-04 21:42:18 -05:00
expected_amount : asDollars ( amount || 0 ) ,
2026-05-29 18:06:12 -05:00
due_day : day >= 1 && day <= 31 ? day : 1 ,
billing_cycle : 'monthly' ,
cycle_type : 'monthly' ,
cycle_day : '1' ,
active : 1 ,
} ,
} ) ;
} ;
2026-07-04 21:42:18 -05:00
const handleBillCreated = async ( newBill? : Bill ) = > {
2026-05-29 18:06:12 -05:00
const tx = createBillSourceTx ? . tx ;
setCreateBillSourceTx ( null ) ;
if ( tx && newBill ? . id ) {
try {
await api . matchTransaction ( tx . id , newBill . id ) ;
toast . success ( 'Bill created and matched to transaction.' ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Bill created but match failed.' ) ) ;
2026-05-29 18:06:12 -05:00
}
}
await Promise . all ( [ loadBills ( ) , refreshTransactionWorkbench ( ) ] ) ;
} ;
2026-07-04 21:42:18 -05:00
const runTransactionAction = async ( tx : BankTransaction , action : string ) = > {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setActionId ( ` ${ action } : ${ tx . id } ` ) ;
try {
if ( action === 'unmatch' ) {
await api . unmatchTransaction ( tx . id ) ;
toast . success ( 'Transaction unmatched.' ) ;
} else if ( action === 'ignore' ) {
await api . ignoreTransaction ( tx . id ) ;
toast . success ( 'Transaction ignored.' ) ;
} else if ( action === 'unignore' ) {
await api . unignoreTransaction ( tx . id ) ;
toast . success ( 'Transaction restored.' ) ;
}
await refreshTransactionWorkbench ( ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Transaction action failed.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} finally {
setActionId ( null ) ;
}
} ;
2026-07-04 21:42:18 -05:00
const confirmMatch = async ( billId : number | string ) = > {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
if ( ! matchTransaction ) return ;
setActionId ( ` match: ${ matchTransaction . id } ` ) ;
try {
await api . matchTransaction ( matchTransaction . id , billId ) ;
toast . success ( 'Transaction matched to bill.' ) ;
setMatchOpen ( false ) ;
setMatchTransaction ( null ) ;
await refreshTransactionWorkbench ( ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Transaction match failed.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} finally {
setActionId ( null ) ;
}
} ;
2026-07-04 21:42:18 -05:00
const acceptSuggestion = async ( suggestion : MatchSuggestion ) = > {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setActionId ( ` suggestion-match: ${ suggestion . id } ` ) ;
try {
await api . matchTransaction ( suggestion . transactionId , suggestion . billId ) ;
toast . success ( 'Suggested match confirmed.' ) ;
await refreshTransactionWorkbench ( ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Suggested match failed.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} finally {
setActionId ( null ) ;
}
} ;
2026-07-04 21:42:18 -05:00
const rejectSuggestion = async ( suggestion : MatchSuggestion ) = > {
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
setActionId ( ` suggestion-reject: ${ suggestion . id } ` ) ;
try {
await api . rejectMatchSuggestion ( suggestion . id ) ;
toast . success ( 'Suggestion rejected.' ) ;
await loadSuggestions ( ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Suggestion could not be rejected.' ) ) ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
} finally {
setActionId ( null ) ;
}
} ;
2026-05-28 22:06:15 -05:00
const [ quickSyncing , setQuickSyncing ] = useState ( false ) ;
const handleQuickSync = async ( ) = > {
if ( ! simplefinConn ) return ;
setQuickSyncing ( true ) ;
try {
await api . syncDataSource ( simplefinConn . id ) ;
await refreshTransactionWorkbench ( ) ;
} catch ( err ) {
2026-07-04 21:42:18 -05:00
toast . error ( errMessage ( err , 'Sync failed' ) ) ;
2026-05-28 22:06:15 -05:00
} finally {
setQuickSyncing ( false ) ;
}
} ;
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
return (
< SectionCard
title = "Transactions"
subtitle = "Review imported or manual transactions and confirm matches to bills."
2026-05-30 21:52:02 -05:00
{ . . . cardProps }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
>
2026-05-28 22:06:15 -05:00
{ simplefinConn && (
< div className = "px-6 py-2 flex items-center justify-between gap-3 border-b border-border/50 bg-muted/20 text-xs text-muted-foreground" >
< span className = "flex items-center gap-1.5" >
< span className = { cn (
'h-1.5 w-1.5 rounded-full shrink-0' ,
simplefinConn . last_error ? 'bg-destructive' : 'bg-emerald-500' ,
) } / >
SimpleFIN
{ simplefinConn . last_sync_at && (
< span > · synced { timeAgo ( simplefinConn . last_sync_at ) } < / span >
) }
{ simplefinConn . last_error && (
< span className = "text-destructive" > · { simplefinConn . last_error } < / span >
) }
< / span >
< button
type = "button"
onClick = { handleQuickSync }
disabled = { quickSyncing }
className = "flex items-center gap-1 hover:text-foreground transition-colors disabled:opacity-50"
>
{ quickSyncing
? < > < Loader2 className = "h-3 w-3 animate-spin" / > Syncing … < / >
: < > < RefreshCw className = "h-3 w-3" / > Sync Now < / > }
< / button >
< / div >
) }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< div className = "px-6 py-5 space-y-4" >
< div className = "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between" >
< div className = "flex flex-wrap gap-2" >
{ TRANSACTION_FILTERS . map ( item = > (
< button
key = { item . id }
type = "button"
onClick = { ( ) = > setFilter ( item . id ) }
className = { cn (
'rounded-full border px-3 py-1.5 text-xs font-medium transition-colors' ,
filter === item . id
? 'border-primary/40 bg-primary/10 text-primary'
: 'border-border/60 bg-muted/20 text-muted-foreground hover:bg-muted hover:text-foreground' ,
) }
>
{ item . label }
< / button >
) ) }
< / div >
2026-06-08 21:07:42 -05:00
< div className = "flex items-center gap-2" >
< div className = "relative" >
< Search className = "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" / >
< Input
type = "search"
placeholder = "Search transactions…"
value = { search }
onChange = { handleSearchChange }
className = "h-8 w-48 pl-8 text-sm"
/ >
< / div >
< Button size = "sm" variant = "outline" type = "button" onClick = { refreshTransactionWorkbench } disabled = { loading || suggestionsLoading } >
{ loading ? < Loader2 className = "h-3.5 w-3.5 mr-1.5 animate-spin" / > : < RefreshCw className = "h-3.5 w-3.5 mr-1.5" / > }
Refresh
< / Button >
< / div >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / div >
< SuggestedMatchesPanel
suggestions = { suggestions }
loading = { suggestionsLoading }
actionId = { actionId }
onAccept = { acceptSuggestion }
onReject = { rejectSuggestion }
/ >
< div className = "overflow-x-auto rounded-lg border border-border/60" >
{ loading ? (
< div className = "px-4 py-8 text-center text-sm text-muted-foreground" > Loading transactions … < / div >
) : transactions . length === 0 ? (
< div className = "px-4 py-8 text-center text-sm text-muted-foreground" >
No transactions found for this filter .
< / div >
) : (
2026-05-29 16:51:31 -05:00
< table className = "w-full min-w-[860px] table-fixed text-sm" >
< colgroup >
2026-06-08 21:07:42 -05:00
< col className = "w-[104px]" / >
2026-05-29 16:51:31 -05:00
< col / >
< col className = "w-[200px]" / >
2026-06-08 21:07:42 -05:00
< col className = "w-[130px]" / >
2026-05-29 16:51:31 -05:00
< col className = "w-[96px]" / >
< / colgroup >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< thead >
2026-06-08 21:07:42 -05:00
< tr className = "border-b border-border/50 bg-muted/40 text-[11px] font-medium text-muted-foreground" >
< th
className = "cursor-pointer select-none px-4 py-2.5 text-left transition-colors hover:text-foreground"
onClick = { ( ) = > handleSortClick ( 'date' ) }
>
< span className = "flex items-center gap-1" >
Date
{ sortBy === 'date'
? sortDir === 'desc'
? < ArrowDown className = "h-3 w-3 text-primary" / >
: < ArrowUp className = "h-3 w-3 text-primary" / >
: < ArrowUpDown className = "h-3 w-3 opacity-30" / > }
< / span >
< / th >
< th className = "px-4 py-2.5 text-left" > Transaction < / th >
< th className = "px-4 py-2.5 text-left" > Match < / th >
< th
className = "cursor-pointer select-none px-4 py-2.5 text-right transition-colors hover:text-foreground"
onClick = { ( ) = > handleSortClick ( 'amount' ) }
>
< span className = "flex items-center justify-end gap-1" >
{ sortBy === 'amount'
? sortDir === 'desc'
? < ArrowDown className = "h-3 w-3 text-primary" / >
: < ArrowUp className = "h-3 w-3 text-primary" / >
: < ArrowUpDown className = "h-3 w-3 opacity-30" / > }
Amount
< / span >
< / th >
< th className = "px-4 py-2.5 text-right" > Actions < / th >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / tr >
< / thead >
< tbody className = "divide-y divide-border/30" >
{ transactions . map ( tx = > {
const status = transactionStatus ( tx ) ;
const busy = actionId ? . endsWith ( ` : ${ tx . id } ` ) ;
return (
< tr key = { tx . id } className = "hover:bg-muted/20" >
< td className = "px-4 py-3 text-xs text-muted-foreground whitespace-nowrap" >
{ transactionDate ( tx ) }
< / td >
2026-05-29 16:51:31 -05:00
< td className = "max-w-0 px-4 py-3" >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< div className = "min-w-0" >
< p className = "truncate text-sm font-medium" > { transactionTitle ( tx ) } < / p >
< p className = "mt-0.5 truncate text-xs text-muted-foreground" >
{ [ tx . description , tx . account_name , tx . source_label ] . filter ( Boolean ) . join ( ' · ' ) || '—' }
< / p >
< / div >
< / td >
2026-05-29 16:51:31 -05:00
< td className = "px-4 py-3" >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< div className = "flex flex-col gap-1.5" >
2026-06-07 20:07:27 -05:00
< div className = "flex items-center gap-1.5" >
< TransactionStatusBadge tx = { tx } / >
{ tx . pending ? (
< span className = "inline-flex items-center gap-1 rounded-full border border-sky-500/30 bg-sky-500/10 px-2 py-0.5 text-[10px] font-semibold uppercase text-sky-600 dark:text-sky-400" >
< Clock className = "h-2.5 w-2.5" / >
Pending
< / span >
) : null }
< / div >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
{ tx . matched_bill_name ? (
2026-07-04 21:42:18 -05:00
< span className = "truncate text-xs text-foreground" > { tx . matched_bill_name as string } < / span >
) : ( tx . advisory_filter as { confidence? : string } | undefined ) ? . confidence === 'high' ? (
2026-05-29 18:06:12 -05:00
< span className = "text-xs text-muted-foreground italic" > Probably not a bill < / span >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
) : (
< span className = "text-xs text-muted-foreground" > No bill linked < / span >
) }
< / div >
< / td >
< td className = { cn (
'px-4 py-3 text-right font-semibold tabular-nums whitespace-nowrap' ,
Number ( tx . amount ) < 0 ? 'text-destructive' : 'text-emerald-600' ,
) } >
2026-07-04 21:42:18 -05:00
{ formatTransactionAmount ( tx . amount , tx . currency ? ? undefined ) }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / td >
2026-05-29 16:51:31 -05:00
< td className = "px-3 py-3" >
< div className = "flex justify-end gap-1.5 whitespace-nowrap" >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
{ status === 'ignored' ? (
2026-05-29 16:51:31 -05:00
< Button
size = "icon"
variant = "outline"
type = "button"
disabled = { busy }
onClick = { ( ) = > runTransactionAction ( tx , 'unignore' ) }
className = "h-8 w-8 shrink-0"
aria - label = "Unignore transaction"
title = "Unignore transaction"
>
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
{ busy ? < Loader2 className = "h-3.5 w-3.5 animate-spin" / > : < Eye className = "h-3.5 w-3.5" / > }
2026-05-29 16:51:31 -05:00
< span className = "sr-only" > Unignore < / span >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / Button >
) : (
< >
{ status === 'matched' ? (
2026-05-29 16:51:31 -05:00
< Button
size = "icon"
variant = "outline"
type = "button"
disabled = { busy }
onClick = { ( ) = > runTransactionAction ( tx , 'unmatch' ) }
className = "h-8 w-8 shrink-0"
aria - label = "Unmatch transaction"
title = "Unmatch transaction"
>
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
{ busy ? < Loader2 className = "h-3.5 w-3.5 animate-spin" / > : < Link2Off className = "h-3.5 w-3.5" / > }
2026-05-29 16:51:31 -05:00
< span className = "sr-only" > Unmatch < / span >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / Button >
) : (
2026-05-29 16:51:31 -05:00
< Button
size = "icon"
type = "button"
disabled = { busy || billsLoading }
onClick = { ( ) = > openMatchDialog ( tx ) }
className = "h-8 w-8 shrink-0"
aria - label = "Match transaction"
title = "Match transaction"
>
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< Link2 className = "h-3.5 w-3.5" / >
2026-05-29 16:51:31 -05:00
< span className = "sr-only" > Match < / span >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / Button >
) }
2026-05-29 16:51:31 -05:00
< Button
size = "icon"
variant = "ghost"
type = "button"
disabled = { busy }
onClick = { ( ) = > runTransactionAction ( tx , 'ignore' ) }
className = "h-8 w-8 shrink-0"
aria - label = "Ignore transaction"
title = "Ignore transaction"
>
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
{ busy ? < Loader2 className = "h-3.5 w-3.5 animate-spin" / > : < EyeOff className = "h-3.5 w-3.5" / > }
2026-05-29 16:51:31 -05:00
< span className = "sr-only" > Ignore < / span >
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / Button >
< / >
) }
< / div >
< / td >
< / tr >
) ;
} ) }
< / tbody >
< / table >
) }
2026-06-08 21:07:42 -05:00
{ totalCount > 0 && (
< div className = "flex items-center justify-between border-t border-border/50 px-4 py-3 text-sm text-muted-foreground" >
< span > { totalCount } transaction { totalCount !== 1 ? 's' : '' } < / span >
{ totalPages > 1 && (
< div className = "flex items-center gap-2" >
< button
type = "button"
onClick = { ( ) = > changePage ( page - 1 ) }
disabled = { page === 1 }
className = "rounded border border-border/60 p-1 hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed"
aria - label = "Previous page"
>
< ChevronLeft className = "h-4 w-4" / >
< / button >
< span > Page { page } of { totalPages } < / span >
< button
type = "button"
onClick = { ( ) = > changePage ( page + 1 ) }
disabled = { page === totalPages }
className = "rounded border border-border/60 p-1 hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed"
aria - label = "Next page"
>
< ChevronRight className = "h-4 w-4" / >
< / button >
< / div >
) }
< / div >
) }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / div >
< / div >
< MatchBillDialog
open = { matchOpen }
onOpenChange = { setMatchOpen }
transaction = { matchTransaction }
bills = { bills }
loading = { actionId === ` match: ${ matchTransaction ? . id } ` }
onConfirm = { confirmMatch }
2026-05-29 18:06:12 -05:00
onCreateBill = { openCreateBill }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
/ >
2026-05-29 18:06:12 -05:00
{ createBillSourceTx && (
< BillModal
key = { ` create-from-tx- ${ createBillSourceTx . tx . id } ` }
initialBill = { createBillSourceTx . initialBill }
categories = { categories }
onClose = { ( ) = > setCreateBillSourceTx ( null ) }
onSave = { handleBillCreated }
/ >
) }
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
< / SectionCard >
) ;
}