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),
|
||||
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),
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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