// Domain types for the API responses. Money fields are branded `Dollars` — the // server stores integer cents and serializes dollars over the wire (via // utils/money.fromCents), so every amount a component receives is dollars. Typing // them `Dollars` lets `fmt`/`formatUSD` accept them while rejecting raw cents // (e.g. bank-transaction `*_cents` fields), catching the cents-as-dollars bug // class at compile time. // // Shapes are typed incrementally (endpoint-by-endpoint). Complex nested payloads // that no typed consumer reads yet (trend series, autopay suggestion/stats, // sparkline) are left `unknown` and refined when a consumer needs them. import type { Dollars } from '@/lib/money'; // A bill's month status. Mirrors the server's statusService. export type TrackerStatus = | 'paid' | 'autodraft' | 'upcoming' | 'due_soon' | 'late' | 'missed' | 'skipped'; // A payment record as serialized by the server (money in dollars). serializePayment // spreads the full DB row, so extra columns pass through as `unknown`. export interface Payment { id: number; bill_id?: number; amount: Dollars; paid_date: string; payment_source?: string | null; notes?: string | null; balance_delta?: Dollars | null; interest_delta?: Dollars | null; created_at?: string; deleted_at?: string | null; [key: string]: unknown; } // One tracker row: a bill's state for a given month. Built by // statusService.buildTrackerRow and augmented by trackerService.getTracker. export interface TrackerRow { id: number; name: string; category_id: number | null; category_name: string | null; due_date: string; due_day: number | null; bucket: string; expected_amount: Dollars; notes: string | null; total_paid: Dollars; paid_toward_due: Dollars; overpaid_amount: Dollars; balance: Dollars; has_payment: boolean; is_settled: boolean; last_paid_date: string | null; last_payment_amount: Dollars | null; status: TrackerStatus; autopay_enabled: boolean; autodraft_status: string | null; auto_mark_paid: boolean; billing_cycle: string | null; cycle_type: string | null; cycle_day: string | number | null; current_balance: Dollars | null; minimum_payment: Dollars | null; interest_rate: number | null; is_subscription: boolean; has_2fa: boolean; has_merchant_rule: boolean; has_linked_transactions: boolean; website: string | null; autopay_verified_at: string | null; inactive_reason: string | null; inactivated_at: string | null; sparkline: unknown; autopay_stats: unknown; payments: Payment[]; // Augmented by getTracker: actual_amount: Dollars | null; is_skipped: boolean; snoozed_until: string | null; autopay_suggestion?: unknown; previous_month_paid: Dollars; // Bank-mode augmentation (present only when bank_tracking.enabled): pending_cleared?: boolean; bank_pending_count?: number; } export interface TrackerSummary { total_expected: Dollars; total_starting: Dollars; has_starting_amounts: boolean; total_paid: Dollars; paid_toward_due: Dollars; remaining: Dollars; total_remaining: Dollars; remaining_period: string; remaining_label: string; remaining_hint: string; overdue: Dollars; count_paid: number; count_upcoming: number; count_late: number; count_autodraft: number; previous_month_total: Dollars; trend: unknown; } // Bank tracking is off (`{ enabled: false }`) or on with the account snapshot. export type BankTracking = | { enabled: false } | { enabled: true; account_id: number; account_name: string; org_name: string; balance: Dollars; pending_payments: Dollars; pending_days: number; effective_balance: Dollars; unpaid_this_month: Dollars; remaining: Dollars; last_updated: string | null; }; export interface SafeToSpendUpcoming { id: number; name: string; due_date: string; amount: Dollars; status: TrackerStatus; } export interface CashflowTimelinePoint { date: string; balance: number; // running dollar balance; cashflowUtils treats it loosely bills: unknown[]; payday?: boolean; } export interface TrackerCashflow { next_payday: string | null; days_until_payday: number; available: Dollars; safe_to_spend: Dollars; still_due_total: Dollars; still_due_count: number; upcoming: SafeToSpendUpcoming[]; timeline: CashflowTimelinePoint[]; has_data: boolean; uses_bank_balance: boolean; period: string; period_end_label: string; period_starting: Dollars; period_bills_total: Dollars; period_paid: Dollars; period_paid_count: number; period_total_count: number; period_projected: Dollars; month_starting: Dollars; month_bills_total: Dollars; month_paid: Dollars; month_paid_count: number; month_total_count: number; month_projected: Dollars; } export interface TrackerResponse { year: number; month: number; today: string; summary: TrackerSummary; bank_tracking: BankTracking; cashflow: TrackerCashflow; rows: TrackerRow[]; }