Conversation
Introduce a currency rate fetching service with caching and TTL (24h), plus tests. New service (src/services/currencyRateFetcher.ts) fetches rates (configurable API URL), caches them in localStorage, exposes helpers (getExchangeRate, getCachedRate, getLastUpdatedTimestamp, isCachedRateValid) and is covered by unit tests. Integrate live rate fetching into PaySettingsModal: auto-fetch rates when changing currency, show fetched value, last-updated timestamp, inverse rate, refresh button, and keep amounts displayed in the original currency until save. Add related UI styles, a utility to calculate formatted inverse rates, and a storage key for cached currency rates. Also add Vite define for optional CURRENCY_CONVERSION_URL and extend budget conversion tests for round-trip precision scenarios.
Introduce a US tax estimation service and data, wire it into the setup/demo flows and tax editor, and update tax line calculations. - Add US federal/FICA/state heuristic constants (src/data/usTaxData.ts) and a taxEstimationService that produces estimated TaxSettings based on income, pay cadence, and filing status. - Integrate auto-estimate UI: TaxBreakdown editor gets an "Auto-estimate rates" button, a Federal Filing Status dropdown, and uses estimateTaxSettings to suggest rates/taxable bases; SetupWizard also uses the estimator and exposes filing status. - Change taxable-income handling: getTaxableIncomeForTaxLine now considers grossPay and label heuristics (federal/state vs Social Security vs Medicare) and calculateTaxLineAmount updated to accept grossPay; budget calculations pass grossPay through. - Update demo data generator to use estimator for realistic tax lines and adjust net pay estimation to include SS/Medicare rates from estimates. - Persist and migrate filingStatus default in fileStorage; add TaxFilingStatus to types. - Add tests for tax estimation behavior and update existing tests to reflect new taxable-income/gross-pay logic. These changes improve withholding estimation accuracy (progressive federal brackets, FICA rules, Medicare surtax and wage-base capping) and provide a UI path for automatic suggestions while preserving manual editability.
Add support for OtherIncome across the app: new types for other income and paycheck fields; new utils/otherIncome to annualize and convert other income to per-paycheck gross/taxable/net totals; integrate otherIncome into calculatePaycheckBreakdown, annualize/display helpers, and demo data. Update FileStorageService to initialize, normalize and migrate legacy otherIncome entries and include otherIncome in created budgets. Add tests for otherIncome calculations and update existing tests to expect the new fields.
Introduce a new Other Income module: adds OtherIncomeManager component, styles, tests, and index export. Integrates the new tab into PlanDashboard (tab, search-action state, snapshot upsert) and updates PlanTabs typing to use TabId. Wire budget CRUD for otherIncome in BudgetContext (add/update/delete) and allow a new other-income type in fileStorage. Update PayBreakdown to include other-income items in gross/taxable/net stages and add related tests. Add history snapshot mapping for other-income and accompanying tests. Misc: new search modules/tests, utils and type adjustments to support other income calculations and display.
Introduce a timingMode (average | payout) for OtherIncome and wire it through the UI, types, storage, labels, calculations, and tests. - UI: Added Timing Mode dropdown to OtherIncomeManager, show badge/detail and V1 note; added small CSS for the note. Defaults to 'average'. - Behavior: payout-timed entries are excluded from per-paycheck averaging (PayBreakdown filter + calculateOtherIncomePerPaycheckAmount early return). - Types/storage: Added OtherIncomeTimingMode type and optional timingMode on OtherIncome; fileStorage validates/normalizes timingMode with default 'average'. - Labels/tests: Added timing mode label/options and updated unit tests to cover exclusion of payout-timed entries and to select timingMode in OtherIncomeManager test.
Remove payout-timing exclusion and timingMode from calculations and UI so other income is always averaged across paychecks. Update OtherIncomeManager: simplify form (remove timing mode), add per-paycheck/monthly/annual preview, refactor add/edit/delete/toggle handlers, change list sorting and badges, and adjust modal fields. Update related utils and tests (otherIncome, otherIncome.test, OtherIncomeManager.test, PayBreakdown) and add calculateGrossPayPerYear. Change default tab config to hide "Other Income" by default and update tabManagement tests. Add CSS for preview row and refresh label/options in otherIncomeLabels and payroll types.
Import ErrorDialog and wire it to the budget context by destructuring errorDialog and closeErrorDialog from useBudget. Render ErrorDialog with sensible defaults (title 'Error' and empty message) and pass through actionLabel and onClose. This surfaces budget-related errors to the top-level App UI.
All relating to issue 96 #96
Replace keytar-based key storage with an Electron safeStorage-backed JSON store. Adds helpers to lazily resolve userData/keys.json, read/write the encrypted store (0600), and per-session Touch ID gating via systemPreferences.promptTouchID. Introduces a legacy keytar loader for one-time migration of existing keys into safeStorage and best-effort cleanup of legacy entries. Updates IPC handlers (save-keychain-key, get-keychain-key, delete-keychain-key) to use safeStorage, handle biometric cancellations, and fall back to keytar for migration. Also updates types to include a biometricFailed flag and adjusts imports to include safeStorage and systemPreferences.
Introduce a new Step 7 to the SetupWizard that lets users pick theme mode, color presets, and toggle visible tabs. Increment totalSteps to 7, add UI markup and CSS styles for theme buttons, preset cards and tab list, and wire up state for themeMode, appearancePreset and tab configs (using FileStorageService and getDefaultTabConfigs). Add handlers to save settings, dispatch appearance/theme change events, and enforce at least one visible tab. Also add TAB_STEP_DESCRIPTIONS and import required icons, services, constants and types.
|
✅ Version Update Detected Version has been correctly bumped from |
There was a problem hiding this comment.
Pull request overview
Release PR for v0.5.0, adding major budgeting features (Other Income, live currency conversion, smarter tax estimation) and supporting UX/infra updates (tab/search wiring, constants cleanup, release metadata).
Changes:
- Adds/updates core utilities/services for currency conversion and tax estimation, plus supporting env wiring.
- Introduces “Other Income” as a first-class tab and integrates it into dashboard tab management and plan search modules.
- Refactors shared constants (tab IDs, selectable view modes) and updates tests accordingly.
Reviewed changes
Copilot reviewed 141 out of 143 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| vite.config.ts | Adds env defines intended for release link + currency conversion configuration. |
| version | Bumps app version to 0.5.0. |
| RELEASE_NOTES.md | Updates release notes for the v0.5.0 release body. |
| src/types/viewMode.ts | Updates/derives view-mode typing from shared constants. |
| src/utils/viewModePreferences.ts | Uses shared selectable view-mode constants instead of duplicating them. |
| src/utils/taxLines.ts | Updates tax-line taxable base logic (with Medicare/SS heuristics) and supports gross-pay-aware calculations. |
| src/utils/taxLines.test.ts | Adds a unit test covering withholding vs Medicare base differences. |
| src/services/taxEstimationService.ts | Implements local IRS-backed “auto-estimate rates” logic for federal/FICA/state heuristics. |
| src/services/currencyRateFetcher.ts | Implements Frankfurter-backed exchange rate fetch + cache + offline fallback. |
| src/utils/currency.ts | Adds inverse-rate helper used by the currency editing UI. |
| src/utils/tabManagement.ts | Adds “Other Income” tab (hidden by default) and centralizes tab IDs via constants. |
| src/utils/tabManagement.test.ts | Updates ordering/visibility tests to include the new tab. |
| src/utils/searchRegistry.ts | Extends search action context with other-income actions. |
| src/utils/searchModules/payBreakdownSearchModule.ts | Switches to centralized tab IDs for navigation actions. |
| src/utils/searchModules/preTaxDeductionsSearchModule.ts | Switches to centralized tab IDs for navigation actions. |
| src/utils/searchModules/savingsSearchModule.ts | Switches to centralized tab IDs for navigation actions. |
| src/utils/searchModules/taxesSearchModule.ts | Switches to centralized tab IDs for navigation actions. |
| src/utils/searchModules/otherIncomeSearchModule.ts | Adds plan-search integration for other income results + inline actions. |
|
✅ Version Update Detected Version has been correctly bumped from |
|
✅ Version Update Detected Version has been correctly bumped from |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 142 out of 145 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (6)
src/services/currencyRateFetcher.ts:1
- The Frankfurter API query parameters in this implementation don’t match Frankfurter’s documented interface (it uses
from/toorbase+symbols, notquote). As written, requests are likely to return an error or a payload that doesn’t containrates[quote], causing conversion to fail. Update the request parameter names to the API’s supported ones and parse the response accordingly; also update the inline comment to reflect the actual format.
src/services/currencyRateFetcher.ts:1 - The Frankfurter API query parameters in this implementation don’t match Frankfurter’s documented interface (it uses
from/toorbase+symbols, notquote). As written, requests are likely to return an error or a payload that doesn’t containrates[quote], causing conversion to fail. Update the request parameter names to the API’s supported ones and parse the response accordingly; also update the inline comment to reflect the actual format.
src/utils/taxLines.ts:1 suicommonly refers to State Unemployment Insurance, not Social Security/OASDI. Including it here will misclassify SUI lines as Social Security and incorrectly base them on gross wages (and apply SS-specific logic). Removesuifrom this matcher (or add a separate matcher/handling for unemployment taxes if needed).
src/utils/otherIncome.ts:1activeMonthsis explicitly ignored in calculations, but the PDF export now displays a 'Months' column and an 'Annual Impact' value, implying month-scoped scheduling is honored. This produces objectively incorrect annual totals whenactiveMonthsis set (e.g., monthly amount with 4 active months still computes as 12 months). Either implementactiveMonthsscheduling ingetOtherIncomeScheduledOccurrencesPerYear(and related calculations), or remove/replace the 'Months' UI/export surface until scheduling is supported.
src/components/tabViews/TaxBreakdown/TaxBreakdown.tsx:1- Auto-estimate currently matches suggested rates solely by exact normalized label. This won’t update common real-world labels like 'Federal Withholding' vs 'Federal Tax', which makes the feature appear unreliable depending on user naming. Prefer a classification-based match (e.g., use the same label heuristics you introduced in
taxLines.tsfor federal/state/SS/Medicare) or map by a stable tax-line identity so estimates apply even when labels differ.
src/utils/searchModules/otherIncomeSearchModule.ts:1 - This uses a hard-coded tab id string instead of
TAB_IDS.otherIncome, and passesresetBillsAnchor: trueeven though the navigation is not to the Bills tab. Use the shared constant for consistency and remove the bills-specific option (or only pass options relevant to the target tab) to avoid unintended side effects and keep tab navigation behavior self-documenting.
Features
Improvements
Security
safeStorageAPI instead of the OS keychain viakeytar. On macOS this eliminates the repeated "Paycheck Planner wants to use your confidential information" Keychain password prompts. On Windows, keys are protected by DPAPI (bound to your Windows user account). On Linux, Chromium's secret store is used. Existing encrypted plans are migrated automatically on first launch — no action required.safeStorageprotection remains active: keys are cryptographically bound to the OS user session and inaccessible without the system login.