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:
parent
3de91af13d
commit
86733d457a
|
|
@ -146,7 +146,6 @@ export const api = {
|
||||||
spendingSummary: (p?: QueryParams) => get<SpendingSummary>('/spending/summary', p),
|
spendingSummary: (p?: QueryParams) => get<SpendingSummary>('/spending/summary', p),
|
||||||
spendingTransactions:(p?: QueryParams) => get<SpendingTransactionsResponse>('/spending/transactions', p),
|
spendingTransactions:(p?: QueryParams) => get<SpendingTransactionsResponse>('/spending/transactions', p),
|
||||||
categorizeTransaction: (id: Id, d: Body) => patch(`/spending/transactions/${id}/category`, d),
|
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),
|
setSpendingBudget: (d: Body) => put('/spending/budgets', d),
|
||||||
copySpendingBudgets: (d: Body) => post<CopyBudgetsResponse>('/spending/budgets/copy', d),
|
copySpendingBudgets: (d: Body) => post<CopyBudgetsResponse>('/spending/budgets/copy', d),
|
||||||
spendingIncome: (p?: QueryParams) => get<SpendingIncomeResponse>('/spending/income', p),
|
spendingIncome: (p?: QueryParams) => get<SpendingIncomeResponse>('/spending/income', p),
|
||||||
|
|
@ -232,8 +231,6 @@ export const api = {
|
||||||
saveNotifAdmin: (data: Body) => put('/notifications/admin', data),
|
saveNotifAdmin: (data: Body) => put('/notifications/admin', data),
|
||||||
testEmail: (data: Body) => post('/notifications/test', data),
|
testEmail: (data: Body) => post('/notifications/test', data),
|
||||||
testPushNotification: () => post('/notifications/test-push', {}),
|
testPushNotification: () => post('/notifications/test-push', {}),
|
||||||
notifMe: () => get('/notifications/me'),
|
|
||||||
saveNotifMe: (data: Body) => put('/notifications/me', data),
|
|
||||||
|
|
||||||
// Profile
|
// Profile
|
||||||
profile: () => get('/profile'),
|
profile: () => get('/profile'),
|
||||||
|
|
@ -241,12 +238,9 @@ export const api = {
|
||||||
profileSettings: () => get('/profile/settings'),
|
profileSettings: () => get('/profile/settings'),
|
||||||
updateProfileSettings: (data: Body) => _fetch('PATCH', '/profile/settings', data),
|
updateProfileSettings: (data: Body) => _fetch('PATCH', '/profile/settings', data),
|
||||||
changeProfilePassword: (data: Body) => post('/profile/change-password', data),
|
changeProfilePassword: (data: Body) => post('/profile/change-password', data),
|
||||||
profileExports: () => get('/profile/exports'),
|
|
||||||
profileImportHistory: () => get('/profile/import-history'),
|
|
||||||
|
|
||||||
// Tracker
|
// Tracker
|
||||||
tracker: (y: number, m: number, params: QueryParams = {}) => get<TrackerResponse>(`/tracker${queryString({ year: y, month: m, ...params })}`),
|
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'),
|
overdueCount: () => get('/tracker/overdue-count'),
|
||||||
snoozeOverdue: (id: Id, data: Body) => put(`/bills/${id}/monthly-state`, data),
|
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),
|
createBill: (data: Body) => post<Bill>('/bills', data),
|
||||||
updateBill: (id: Id, data: Body) => put<Bill>(`/bills/${id}`, data),
|
updateBill: (id: Id, data: Body) => put<Bill>(`/bills/${id}`, data),
|
||||||
reorderBills: (order: Body) => put('/bills/reorder', order),
|
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 }),
|
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),
|
updateBillSnowball: (id: Id, data: Body) => _fetch('PATCH', `/bills/${id}/snowball`, data),
|
||||||
deleteBill: (id: Id) => del(`/bills/${id}`),
|
deleteBill: (id: Id) => del(`/bills/${id}`),
|
||||||
restoreBill: (id: Id) => post(`/bills/${id}/restore`),
|
restoreBill: (id: Id) => post(`/bills/${id}/restore`),
|
||||||
|
|
@ -299,7 +285,6 @@ export const api = {
|
||||||
merchantRuleCandidates: (id: Id) => get(`/bills/${id}/merchant-rules/candidates`),
|
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 }),
|
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 }),
|
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),
|
saveBillMonthlyState: (id: Id, data: Body) => put(`/bills/${id}/monthly-state`, data),
|
||||||
billHistoryRanges: (id: Id) => get(`/bills/${id}/history-ranges`),
|
billHistoryRanges: (id: Id) => get(`/bills/${id}/history-ranges`),
|
||||||
createBillHistoryRange: (id: Id, data: Body) => post(`/bills/${id}/history-ranges`, data),
|
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`, {}),
|
snoozeBillDrift: (id: Id) => post(`/bills/${id}/snooze-drift`, {}),
|
||||||
billTemplates: () => get('/bills/templates'),
|
billTemplates: () => get('/bills/templates'),
|
||||||
saveBillTemplate: (data: Body) => post('/bills/templates', data),
|
saveBillTemplate: (data: Body) => post('/bills/templates', data),
|
||||||
deleteBillTemplate: (id: Id) => del(`/bills/templates/${id}`),
|
|
||||||
|
|
||||||
// Subscriptions
|
// Subscriptions
|
||||||
subscriptions: () => get<SubscriptionsResponse>('/subscriptions'),
|
subscriptions: () => get<SubscriptionsResponse>('/subscriptions'),
|
||||||
|
|
@ -357,7 +341,6 @@ export const api = {
|
||||||
snowballPlans: () => get<{ plans?: unknown[] }>('/snowball/plans'),
|
snowballPlans: () => get<{ plans?: unknown[] }>('/snowball/plans'),
|
||||||
snowballActivePlan: () => get('/snowball/plans/active'),
|
snowballActivePlan: () => get('/snowball/plans/active'),
|
||||||
startSnowballPlan: (data: Body) => post('/snowball/plans', data),
|
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`, {}),
|
pauseSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/pause`, {}),
|
||||||
resumeSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/resume`, {}),
|
resumeSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/resume`, {}),
|
||||||
completeSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/complete`, {}),
|
completeSnowballPlan: (id: Id) => post(`/snowball/plans/${id}/complete`, {}),
|
||||||
|
|
@ -398,7 +381,6 @@ export const api = {
|
||||||
// Version (public)
|
// Version (public)
|
||||||
about: () => get('/about'),
|
about: () => get('/about'),
|
||||||
privacy: () => get('/privacy'),
|
privacy: () => get('/privacy'),
|
||||||
aboutAdmin: () => get('/about-admin'),
|
|
||||||
roadmap: (refresh = false) => get(`/about-admin/roadmap${refresh ? '?refresh=1' : ''}`),
|
roadmap: (refresh = false) => get(`/about-admin/roadmap${refresh ? '?refresh=1' : ''}`),
|
||||||
updateStatus: () => get('/version/update-status'),
|
updateStatus: () => get('/version/update-status'),
|
||||||
checkForUpdates: () => post('/about-admin/check-updates'),
|
checkForUpdates: () => post('/about-admin/check-updates'),
|
||||||
|
|
@ -409,7 +391,6 @@ export const api = {
|
||||||
releaseHistory: () => get('/version/history'),
|
releaseHistory: () => get('/version/history'),
|
||||||
|
|
||||||
// Export (returns a URL to navigate to, not a fetch)
|
// 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
|
// 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.
|
// 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'}`,
|
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
|
||||||
transactions: (params: QueryParams = {}) => get(`/transactions${queryString(params)}`),
|
transactions: (params: QueryParams = {}) => get(`/transactions${queryString(params)}`),
|
||||||
bankTransactionsLedger: (params: QueryParams = {}) => get<BankLedger>(`/transactions/bank-ledger${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 }),
|
matchTransaction: (id: Id, billId: Id) => post<{ transaction: BankLedgerTransaction }>(`/transactions/${id}/match`, { billId }),
|
||||||
unmatchTransaction: (id: Id) => post<{ transaction: BankLedgerTransaction }>(`/transactions/${id}/unmatch`),
|
unmatchTransaction: (id: Id) => post<{ transaction: BankLedgerTransaction }>(`/transactions/${id}/unmatch`),
|
||||||
unmatchTransactionBulk: (matches: Body) => post('/transactions/unmatch-bulk', { matches }),
|
unmatchTransactionBulk: (matches: Body) => post('/transactions/unmatch-bulk', { matches }),
|
||||||
ignoreTransaction: (id: Id) => post<BankLedgerTransaction>(`/transactions/${id}/ignore`),
|
ignoreTransaction: (id: Id) => post<BankLedgerTransaction>(`/transactions/${id}/ignore`),
|
||||||
unignoreTransaction: (id: Id) => post<BankLedgerTransaction>(`/transactions/${id}/unignore`),
|
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`),
|
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),
|
autoCategorizeTransactions: (opts: Body = {}) => post<AutoCategorizePreview>('/transactions/auto-categorize', opts),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
export default ErrorBoundary;
|
||||||
|
|
|
||||||
|
|
@ -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';
|
|
||||||
Loading…
Reference in New Issue