refactor: remove verified dead code — orphan file, dead HOC, unused api wrappers

From the repo-wide dead-code audit (High-confidence, re-verified against the live
tree; 0 references each):
- Delete client/components/SummaryCard.tsx — orphan superseded by the tracker's
  components/tracker/SummaryCards.tsx (the one actually imported).
- Remove the unused withErrorBoundary HOC from ErrorBoundary.tsx (the
  ErrorBoundary component itself stays; it's used app-wide).
- Remove 17 dead api.ts client wrappers with no caller (spendingBudgets,
  upcomingBills, billAmortization, billMonthlyState, archiveBill,
  deleteBillTemplate, updateSnowballPlan, aboutAdmin, exportUrl, notifMe,
  saveNotifMe, profileExports, profileImportHistory, createManualTransaction,
  updateTransaction, deleteTransaction, transactionMerchantMatch).

Deliberately left: StatusBadge.tsx orphan (has uncommitted parallel edits — will
remove once that lands) and the WebAuthn feature (a build-or-bin product decision,
not dead-code cleanup).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
null 2026-07-05 17:21:31 -05:00
parent 3de91af13d
commit 86733d457a
3 changed files with 0 additions and 148 deletions

View File

@ -146,7 +146,6 @@ export const api = {
spendingSummary: (p?: QueryParams) => get<SpendingSummary>('/spending/summary', p),
spendingTransactions:(p?: QueryParams) => get<SpendingTransactionsResponse>('/spending/transactions', p),
categorizeTransaction: (id: Id, d: Body) => patch(`/spending/transactions/${id}/category`, d),
spendingBudgets: (p?: QueryParams) => get('/spending/budgets', p),
setSpendingBudget: (d: Body) => put('/spending/budgets', d),
copySpendingBudgets: (d: Body) => post<CopyBudgetsResponse>('/spending/budgets/copy', d),
spendingIncome: (p?: QueryParams) => get<SpendingIncomeResponse>('/spending/income', p),
@ -232,8 +231,6 @@ export const api = {
saveNotifAdmin: (data: Body) => put('/notifications/admin', data),
testEmail: (data: Body) => post('/notifications/test', data),
testPushNotification: () => post('/notifications/test-push', {}),
notifMe: () => get('/notifications/me'),
saveNotifMe: (data: Body) => put('/notifications/me', data),
// Profile
profile: () => get('/profile'),
@ -241,12 +238,9 @@ export const api = {
profileSettings: () => get('/profile/settings'),
updateProfileSettings: (data: Body) => _fetch('PATCH', '/profile/settings', data),
changeProfilePassword: (data: Body) => post('/profile/change-password', data),
profileExports: () => get('/profile/exports'),
profileImportHistory: () => get('/profile/import-history'),
// Tracker
tracker: (y: number, m: number, params: QueryParams = {}) => get<TrackerResponse>(`/tracker${queryString({ year: y, month: m, ...params })}`),
upcomingBills: (days = 30) => get(`/tracker/upcoming?days=${days}`),
overdueCount: () => get('/tracker/overdue-count'),
snoozeOverdue: (id: Id, data: Body) => put(`/bills/${id}/monthly-state`, data),
@ -273,15 +267,7 @@ export const api = {
createBill: (data: Body) => post<Bill>('/bills', data),
updateBill: (id: Id, data: Body) => put<Bill>(`/bills/${id}`, data),
reorderBills: (order: Body) => put('/bills/reorder', order),
archiveBill: (id: Id, archived = true) => put(`/bills/${id}/archived`, { archived }),
updateBillBalance: (id: Id, bal: unknown) => _fetch('PATCH', `/bills/${id}/balance`, { current_balance: bal }),
billAmortization: (id: Id, opts: { payment?: number | string; max_months?: number | string } = {}) => {
const params = new URLSearchParams();
if (opts.payment) params.set('payment', String(opts.payment));
if (opts.max_months) params.set('max_months', String(opts.max_months));
const qs = params.toString();
return get(`/bills/${id}/amortization${qs ? `?${qs}` : ''}`);
},
updateBillSnowball: (id: Id, data: Body) => _fetch('PATCH', `/bills/${id}/snowball`, data),
deleteBill: (id: Id) => del(`/bills/${id}`),
restoreBill: (id: Id) => post(`/bills/${id}/restore`),
@ -299,7 +285,6 @@ export const api = {
merchantRuleCandidates: (id: Id) => get(`/bills/${id}/merchant-rules/candidates`),
toggleRuleAutoAttribute: (id: Id, ruleId: Id, on: unknown) => _fetch('PATCH', `/bills/${id}/merchant-rules/${ruleId}/auto-attribute`, { enabled: on }),
importHistoricalPayments: (id: Id, ids: unknown) => post(`/bills/${id}/merchant-rules/import-historical`, { transaction_ids: ids }),
billMonthlyState: (id: Id, y: number, m: number) => get(`/bills/${id}/monthly-state?year=${y}&month=${m}`),
saveBillMonthlyState: (id: Id, data: Body) => put(`/bills/${id}/monthly-state`, data),
billHistoryRanges: (id: Id) => get(`/bills/${id}/history-ranges`),
createBillHistoryRange: (id: Id, data: Body) => post(`/bills/${id}/history-ranges`, data),
@ -309,7 +294,6 @@ export const api = {
snoozeBillDrift: (id: Id) => post(`/bills/${id}/snooze-drift`, {}),
billTemplates: () => get('/bills/templates'),
saveBillTemplate: (data: Body) => post('/bills/templates', data),
deleteBillTemplate: (id: Id) => del(`/bills/templates/${id}`),
// Subscriptions
subscriptions: () => get<SubscriptionsResponse>('/subscriptions'),
@ -357,7 +341,6 @@ export const api = {
snowballPlans: () => get<{ plans?: unknown[] }>('/snowball/plans'),
snowballActivePlan: () => get('/snowball/plans/active'),
startSnowballPlan: (data: Body) => post('/snowball/plans', data),
updateSnowballPlan: (id: Id, d: Body) => _fetch('PATCH', `/snowball/plans/${id}`, d),
pauseSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/pause`, {}),
resumeSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/resume`, {}),
completeSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/complete`, {}),
@ -398,7 +381,6 @@ export const api = {
// Version (public)
about: () => get('/about'),
privacy: () => get('/privacy'),
aboutAdmin: () => get('/about-admin'),
roadmap: (refresh = false) => get(`/about-admin/roadmap${refresh ? '?refresh=1' : ''}`),
updateStatus: () => get('/version/update-status'),
checkForUpdates: () => post('/about-admin/check-updates'),
@ -409,7 +391,6 @@ export const api = {
releaseHistory: () => get('/version/history'),
// Export (returns a URL to navigate to, not a fetch)
exportUrl: (year: Id, fmt?: string): string => `/api/export?year=${year}&format=${fmt||'csv'}`,
// Export a specific date range (both bounds inclusive) — used to export one
// tracker month. The /api/export route accepts ?from&to in addition to ?year.
exportRangeUrl: (from: string, to: string, fmt?: string): string => `/api/export?from=${from}&to=${to}&format=${fmt||'csv'}`,
@ -497,15 +478,11 @@ export const api = {
// Transactions
transactions: (params: QueryParams = {}) => get(`/transactions${queryString(params)}`),
bankTransactionsLedger: (params: QueryParams = {}) => get<BankLedger>(`/transactions/bank-ledger${queryString(params)}`),
createManualTransaction: (data: Body) => post('/transactions/manual', data),
updateTransaction: (id: Id, data: Body) => put(`/transactions/${id}`, data),
deleteTransaction: (id: Id) => del(`/transactions/${id}`),
matchTransaction: (id: Id, billId: Id) => post<{ transaction: BankLedgerTransaction }>(`/transactions/${id}/match`, { billId }),
unmatchTransaction: (id: Id) => post<{ transaction: BankLedgerTransaction }>(`/transactions/${id}/unmatch`),
unmatchTransactionBulk: (matches: Body) => post('/transactions/unmatch-bulk', { matches }),
ignoreTransaction: (id: Id) => post<BankLedgerTransaction>(`/transactions/${id}/ignore`),
unignoreTransaction: (id: Id) => post<BankLedgerTransaction>(`/transactions/${id}/unignore`),
transactionMerchantMatch: (id: Id) => get(`/transactions/${id}/merchant-match`),
applyTransactionMerchantMatch: (id: Id) => post<{ matched?: boolean; category?: { id: number; name: string } }>(`/transactions/${id}/apply-merchant-match`),
autoCategorizeTransactions: (opts: Body = {}) => post<AutoCategorizePreview>('/transactions/auto-categorize', opts),

View File

@ -108,23 +108,4 @@ class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundarySta
}
}
// ────────────────────────────────────────────────────────────────────────────
// withErrorBoundary HOC
// ────────────────────────────────────────────────────────────────────────────
export function withErrorBoundary<P extends object>(
Component: React.ComponentType<P>,
displayName: string = Component.displayName || Component.name || 'Component',
) {
function WrappedComponent(props: P) {
return (
<ErrorBoundary>
<Component {...props} />
</ErrorBoundary>
);
}
WrappedComponent.displayName = `withErrorBoundary(${displayName})`;
return WrappedComponent;
}
export default ErrorBoundary;

View File

@ -1,106 +0,0 @@
import React, { useMemo } from 'react';
import { cn, fmt } from '@/lib/utils';
import { AlertCircle, CheckCircle2, Clock, TrendingUp, Settings2, type LucideIcon } from 'lucide-react';
import type { Dollars } from '@/lib/money';
type CardType = 'starting' | 'paid' | 'remaining' | 'overdue';
interface CardDef {
label: string;
icon: LucideIcon;
bar: string;
glow: string;
borderActive?: string;
valueClass: string;
activateWhen: (v: number) => boolean;
}
const CARD_DEFS: Record<CardType, CardDef> = {
starting: {
label: 'Starting',
icon: TrendingUp,
bar: 'from-slate-400 to-slate-300',
glow: '',
valueClass: 'text-foreground',
activateWhen: () => true,
},
paid: {
label: 'Total Paid',
icon: CheckCircle2,
bar: 'from-emerald-500 to-emerald-300',
glow: 'shadow-[0_4px_20px_rgba(16,185,129,0.15)]',
borderActive: 'border-emerald-400/40',
valueClass: 'text-emerald-600 dark:text-emerald-100',
activateWhen: (v) => v > 0,
},
remaining: {
label: 'Remaining',
icon: Clock,
bar: 'from-blue-400 to-indigo-300',
glow: '',
valueClass: 'text-foreground',
activateWhen: () => true,
},
overdue: {
label: 'Overdue',
icon: AlertCircle,
bar: 'from-rose-400 to-orange-300',
glow: 'shadow-[0_4px_20px_rgba(251,113,133,0.10)]',
borderActive: 'border-rose-400/35',
valueClass: 'text-red-500 dark:text-rose-100',
activateWhen: (v) => v > 0,
},
};
interface SummaryCardProps {
type: CardType;
value?: Dollars;
onEdit?: () => void;
hint?: string;
}
export const SummaryCard = React.memo(function SummaryCard({ type, value, onEdit, hint }: SummaryCardProps) {
const def = useMemo(() => CARD_DEFS[type], [type]);
const isActive = useMemo(() => def.activateWhen(value || 0), [def, value]);
const Icon = useMemo(() => def.icon, [def]);
return (
<div className={cn(
'flex-1 min-w-0 relative overflow-hidden rounded-xl border border-border/80',
'bg-card/95 px-5 py-4 shadow-sm shadow-black/15 transition-all duration-300',
isActive && def.glow,
isActive && def.borderActive,
)}>
<div className={cn(
'absolute top-0 left-0 right-0 h-[3px] bg-gradient-to-r',
def.bar,
!isActive && (type === 'paid' || type === 'overdue') && 'opacity-30',
)} />
<div className="flex items-center gap-2 mb-3">
<Icon className={cn('h-4 w-4', isActive ? def.valueClass : 'text-muted-foreground')} />
<p className="text-xs font-semibold uppercase tracking-normal text-muted-foreground">
{def.label}
</p>
{type === 'starting' && onEdit && (
<button
onClick={onEdit}
className="ml-auto h-4 w-4 text-muted-foreground hover:text-foreground transition-colors"
title="Edit monthly starting amounts"
aria-label="Edit monthly starting amounts"
>
<Settings2 className="h-4 w-4" />
</button>
)}
</div>
<p className={cn(
'tracker-number text-[1.75rem] font-bold leading-none',
isActive ? def.valueClass : 'text-foreground',
)}>
{fmt(value)}
</p>
{hint && <p className="mt-2 text-xs font-medium text-muted-foreground">{hint}</p>}
</div>
);
});
SummaryCard.displayName = 'SummaryCard';