Feature Request
The year selector currently lives in the sidebar navigation (global position), implying it affects all features. In reality, its primary purpose is filtering the financial summary totals on the dashboard and property detail pages. As the app has grown from an expense tracker into a full property management suite, the year selector's global placement is misleading.
Proposal: Remove the year selector from the sidebar and replace it with an inline date range filter on the dashboard, similar to the existing DateRangeFilterComponent used in the expenses list view.
Bug: Global Year Selector Silently Overrides Income Date Range Filter
This is the most urgent reason to do this refactoring.
The Income list view has its own DateRangeFilterComponent (presets: This Month, This Quarter, This Year, Custom). But the global year selector in the sidebar silently interferes with it via a reactive effect:
// income.component.ts:463-467
private yearEffect = effect(() => {
const year = this.yearService.selectedYear();
this.incomeStore.setYear(year); // forces store reload with new year
});
This causes two problems:
Problem 1: "This Year" means the globally selected year, not the calendar year
When the user picks the "This Year" preset in the income date range filter, getDateRangeFromPreset() uses the global year selector value — not the actual current calendar year. If the sidebar says 2025, "This Year" silently becomes Jan 1–Dec 31, 2025.
Problem 2: Contradicting filters return zero results silently
The backend applies BOTH the year param AND dateFrom/dateTo as cumulative AND filters:
// GetAllIncome.cs — year filter applied first
if (request.Year.HasValue)
{
query = query.Where(i => i.Date >= yearStart && i.Date <= yearEnd);
}
// Then date range further narrows
if (request.DateFrom.HasValue)
{
query = query.Where(i => i.Date >= request.DateFrom.Value);
}
If the user sets global year to 2025 but picks a custom date range of Jan–Jun 2026, the backend applies:
WHERE Date.Year == 2025 AND Date >= '2026-01-01' AND Date <= '2026-06-30'
This returns zero results with no error or explanation. The user sees an empty list and has no idea why.
Investigation: Year Selector Is Not Dead Code
A thorough audit confirms the year selector is actively consumed and wired into real API calls across the app:
| Consumer |
What year controls |
API endpoint |
Dead code? |
| Dashboard |
Expense/income totals per property |
GET /properties?year= |
No — filters financial summaries |
| Properties list |
Same financial summaries (not the property list itself) |
GET /properties?year= |
No — filters financial summaries |
| Property detail |
YTD stats + "Recent Expenses/Income" lists |
GET /properties/{id}?year= |
No — filters stats + recent activity |
| Income list |
Feeds into date range calculation AND sent as separate backend param |
GET /income?year=&dateFrom=&dateTo= |
No — actively causes confusion |
| Report dialog |
Reads global year as initial default, then fully independent |
N/A at runtime |
Semi — only seeds initial value |
| Batch report dialog |
Same — snapshots global year on open, independent after |
N/A at runtime |
Semi — only seeds initial value |
Key finding: The report dialogs already have their own year selectors. The expenses list already uses DateRangeFilterComponent without any year selector dependency. The income list is the only view where the global year selector creates an active conflict with a local date range filter.
Current State
Where the year selector lives
- Sidebar nav (
sidebar-nav.component.html) — renders the YearSelectorComponent
YearSelectorService (core/services/year-selector.service.ts) — global singleton, persists to localStorage
What currently consumes the year selector
| Consumer |
What it filters |
Actually needs global year? |
Dashboard (dashboard.component.ts) |
Summary totals (Total Expenses YTD, Total Income YTD, Net Income YTD) |
No — should use local date range |
Properties list (properties.component.ts) |
YTD expense/income per property |
No — should use local date range |
Property detail (property-detail.component.ts) |
YTD Expenses, YTD Income, Net Income cards |
No — should use local date range |
Income list (income.component.ts) |
Filters income records by year |
No — should have its own date filter (like expenses already does) |
Report dialog (report-dialog.component.ts) |
Year for Schedule E report generation |
Already has its own year selector in the dialog — independent |
Batch report dialog (batch-report-dialog.component.ts) |
Year for batch report generation |
Seeds from global year but has its own selector — independent |
Proposed Changes
1. Remove year selector from sidebar
- Remove
<app-year-selector> from sidebar-nav.component.html
- This reclaims sidebar space and eliminates the misleading "global filter" impression
2. Add date range filter to dashboard
- Place a
DateRangeFilterComponent (already exists in shared/components/) inline on the dashboard page, above or alongside the summary cards
- Presets: This Month, This Quarter, This Year (default), Last Year, Custom
- Controls the summary totals (Total Expenses, Total Income, Net Income) and the per-property running totals
- The "YTD" labels should update to reflect the selected range (e.g., "Total Expenses" instead of "Total Expenses YTD" when using a custom range)
3. Add date range filter to property detail
- Same
DateRangeFilterComponent on the property detail page, controlling the YTD Expenses / YTD Income / Net Income cards
- Could default to current year or inherit from dashboard selection
4. Fix income list — remove global year dependency
- Remove the
yearEffect that watches YearSelectorService and forces store reloads
- Income list already has
DateRangeFilterComponent — let it be the sole filter authority
- Stop sending redundant
year param to backend when dateFrom/dateTo already cover it
5. Deprecate/remove YearSelectorService
- Once all consumers migrate to local date range filters, remove:
core/services/year-selector.service.ts
shared/components/year-selector/year-selector.component.ts
- Associated spec files
- localStorage key
propertyManager.selectedYear
Existing Pattern to Follow
The expenses list already implements this pattern via:
shared/components/date-range-filter/date-range-filter.component.ts — reusable date range UI
expense-filters.component.ts — integrates the date range filter
expense-list.store.ts — manages DateRangePreset, dateFrom, dateTo state
Files Affected
Remove/modify:
frontend/src/app/core/components/sidebar-nav/sidebar-nav.component.html — remove year selector
frontend/src/app/core/components/sidebar-nav/sidebar-nav.component.ts — remove year selector import
frontend/src/app/core/components/sidebar-nav/sidebar-nav.component.scss — remove year selector styles
frontend/src/app/core/services/year-selector.service.ts — deprecate/remove
frontend/src/app/shared/components/year-selector/year-selector.component.ts — remove
frontend/src/app/features/income/income.component.ts — remove yearEffect, decouple from global year
Add date range filter to:
frontend/src/app/features/dashboard/dashboard.component.ts
frontend/src/app/features/properties/property-detail/property-detail.component.ts
frontend/src/app/features/properties/properties.component.ts
Backend may need:
- Dashboard and property summary endpoints may need to accept
dateFrom/dateTo params instead of just year
- Income endpoint: stop requiring
year when dateFrom/dateTo are provided
Acceptance Criteria
Feature Request
The year selector currently lives in the sidebar navigation (global position), implying it affects all features. In reality, its primary purpose is filtering the financial summary totals on the dashboard and property detail pages. As the app has grown from an expense tracker into a full property management suite, the year selector's global placement is misleading.
Proposal: Remove the year selector from the sidebar and replace it with an inline date range filter on the dashboard, similar to the existing
DateRangeFilterComponentused in the expenses list view.Bug: Global Year Selector Silently Overrides Income Date Range Filter
This is the most urgent reason to do this refactoring.
The Income list view has its own
DateRangeFilterComponent(presets: This Month, This Quarter, This Year, Custom). But the global year selector in the sidebar silently interferes with it via a reactive effect:This causes two problems:
Problem 1: "This Year" means the globally selected year, not the calendar year
When the user picks the "This Year" preset in the income date range filter,
getDateRangeFromPreset()uses the global year selector value — not the actual current calendar year. If the sidebar says 2025, "This Year" silently becomes Jan 1–Dec 31, 2025.Problem 2: Contradicting filters return zero results silently
The backend applies BOTH the
yearparam ANDdateFrom/dateToas cumulative AND filters:If the user sets global year to 2025 but picks a custom date range of Jan–Jun 2026, the backend applies:
This returns zero results with no error or explanation. The user sees an empty list and has no idea why.
Investigation: Year Selector Is Not Dead Code
A thorough audit confirms the year selector is actively consumed and wired into real API calls across the app:
GET /properties?year=GET /properties?year=GET /properties/{id}?year=GET /income?year=&dateFrom=&dateTo=Key finding: The report dialogs already have their own year selectors. The expenses list already uses
DateRangeFilterComponentwithout any year selector dependency. The income list is the only view where the global year selector creates an active conflict with a local date range filter.Current State
Where the year selector lives
sidebar-nav.component.html) — renders theYearSelectorComponentYearSelectorService(core/services/year-selector.service.ts) — global singleton, persists to localStorageWhat currently consumes the year selector
dashboard.component.ts)properties.component.ts)property-detail.component.ts)income.component.ts)report-dialog.component.ts)batch-report-dialog.component.ts)Proposed Changes
1. Remove year selector from sidebar
<app-year-selector>fromsidebar-nav.component.html2. Add date range filter to dashboard
DateRangeFilterComponent(already exists inshared/components/) inline on the dashboard page, above or alongside the summary cards3. Add date range filter to property detail
DateRangeFilterComponenton the property detail page, controlling the YTD Expenses / YTD Income / Net Income cards4. Fix income list — remove global year dependency
yearEffectthat watchesYearSelectorServiceand forces store reloadsDateRangeFilterComponent— let it be the sole filter authorityyearparam to backend whendateFrom/dateToalready cover it5. Deprecate/remove
YearSelectorServicecore/services/year-selector.service.tsshared/components/year-selector/year-selector.component.tspropertyManager.selectedYearExisting Pattern to Follow
The expenses list already implements this pattern via:
shared/components/date-range-filter/date-range-filter.component.ts— reusable date range UIexpense-filters.component.ts— integrates the date range filterexpense-list.store.ts— managesDateRangePreset,dateFrom,dateTostateFiles Affected
Remove/modify:
frontend/src/app/core/components/sidebar-nav/sidebar-nav.component.html— remove year selectorfrontend/src/app/core/components/sidebar-nav/sidebar-nav.component.ts— remove year selector importfrontend/src/app/core/components/sidebar-nav/sidebar-nav.component.scss— remove year selector stylesfrontend/src/app/core/services/year-selector.service.ts— deprecate/removefrontend/src/app/shared/components/year-selector/year-selector.component.ts— removefrontend/src/app/features/income/income.component.ts— removeyearEffect, decouple from global yearAdd date range filter to:
frontend/src/app/features/dashboard/dashboard.component.tsfrontend/src/app/features/properties/property-detail/property-detail.component.tsfrontend/src/app/features/properties/properties.component.tsBackend may need:
dateFrom/dateToparams instead of justyearyearwhendateFrom/dateToare providedAcceptance Criteria
yearEffectremovedYearSelectorServiceandYearSelectorComponentremoved once all consumers migrated