# Learnings - Statistics Year Selection Enhancement

## [2026-01-28] Initial Analysis

### Current Implementation
- **File**: `Web/src/views/StatisticsView.vue`
- **Current picker**: `columns-type="['year', 'month']` (year-month only)
- **State variables**:
  - `currentYear` - integer year
  - `currentMonth` - integer month (1-12)
  - `selectedDate` - array `['YYYY', 'MM']` for picker
- **API calls**: All endpoints use `{ year, month }` parameters

### Vant UI Year-Only Pattern
- **Key prop**: `columns-type="['year']"`
- **Picker value**: Single-element array `['YYYY']`
- **Confirmation**: `selectedValues[0]` contains year string

### Implementation Strategy
1. Add UI toggle to switch between year-month and year-only modes
2. When year-only selected, set `currentMonth = 0` or null to indicate full year
3. Backend API already supports year-only queries (when month=0 or null)
4. Update display logic to show "YYYY年" vs "YYYY年MM月"

### API Compatibility - CRITICAL FINDING
- **Backend limitation**: `TransactionRecordRepository.BuildQuery()` (lines 81-86) requires BOTH year AND month
- Current logic: `if (year.HasValue && month.HasValue)` - year-only queries are NOT supported
- **Must modify repository** to support year-only queries:
  - When year provided but month is null/0: query entire year (Jan 1 to Dec 31)
  - When both year and month provided: query specific month (current behavior)
- All statistics endpoints use `QueryAsync(year, month, ...)` pattern

### Required Backend Changes
**File**: `Repository/TransactionRecordRepository.cs`
**Method**: `BuildQuery()` lines 81-86
**Change**: Modify year/month filtering logic to support year-only queries

```csharp
// Current (line 81-86):
if (year.HasValue && month.HasValue)
{
    var dateStart = new DateTime(year.Value, month.Value, 1);
    var dateEnd = dateStart.AddMonths(1);
    query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd);
}

// Needed:
if (year.HasValue)
{
    if (month.HasValue && month.Value > 0)
    {
        // Specific month
        var dateStart = new DateTime(year.Value, month.Value, 1);
        var dateEnd = dateStart.AddMonths(1);
        query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd);
    }
    else
    {
        // Entire year
        var dateStart = new DateTime(year.Value, 1, 1);
        var dateEnd = new DateTime(year.Value + 1, 1, 1);
        query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd);
    }
}
```

### Existing Patterns
- BudgetView.vue uses same year-month picker pattern
- Dayjs used for all date formatting: `dayjs().format('YYYY-MM-DD')`
- Date picker values always arrays for Vant UI
## [2026-01-28] Repository BuildQuery() Enhancement

### Implementation Completed
- **File Modified**: `Repository/TransactionRecordRepository.cs` lines 81-94
- **Change**: Updated year/month filtering logic to support year-only queries

### Logic Changes
```csharp
// Old: Required both year AND month
if (year.HasValue && month.HasValue) { ... }

// New: Support year-only queries
if (year.HasValue)
{
    if (month.HasValue && month.Value > 0)
    {
        // 查询指定年月
        var dateStart = new DateTime(year.Value, month.Value, 1);
        var dateEnd = dateStart.AddMonths(1);
        query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd);
    }
    else
    {
        // 查询整年数据（1月1日到下年1月1日）
        var dateStart = new DateTime(year.Value, 1, 1);
        var dateEnd = new DateTime(year.Value + 1, 1, 1);
        query = query.Where(t => t.OccurredAt >= dateStart && t.OccurredAt < dateEnd);
    }
}
```

### Behavior
- **Month-specific** (month.HasValue && month.Value > 0): Query from 1st of month to 1st of next month
- **Year-only** (month is null or 0): Query from Jan 1 to Jan 1 of next year
- **No year provided**: No date filtering applied

### Verification
- All 14 tests pass: `dotnet test WebApi.Test/WebApi.Test.csproj`
- No breaking changes to existing functionality
- Chinese comments added for business logic clarity

### Key Pattern
- Use `month.Value > 0` check to distinguish year-only (0/null) from month-specific (1-12)
- Date range is exclusive on upper bound (`< dateEnd`) to avoid including boundary dates

## [2026-01-28] Frontend Year-Only Selection Implementation

### Changes Made
**File**: `Web/src/views/StatisticsView.vue`

#### 1. Nav Bar Title Display (Line 12)
- Updated to show "YYYY年" when `currentMonth === 0`
- Shows "YYYY年MM月" when month is selected
- Template: `{{ currentMonth === 0 ? \`${currentYear}年\` : \`${currentYear}年${currentMonth}月\` }}`

#### 2. Date Picker Popup (Lines 268-289)
- Added toggle switch using `van-tabs` component
- Two modes: "按月" (month) and "按年" (year)
- Tabs positioned above the date picker
- Dynamic `columns-type` based on selection mode:
  - Year mode: `['year']`
  - Month mode: `['year', 'month']`

#### 3. State Management (Line 347)
- Added `dateSelectionMode` ref: `'month'` | `'year'`
- Default: `'month'` for backward compatibility
- `currentMonth` set to `0` when year-only selected

#### 4. Confirmation Handler (Lines 532-544)
- Updated to handle both year-only and year-month modes
- When year mode: `newMonth = 0`
- When month mode: `newMonth = parseInt(selectedValues[1])`

#### 5. API Calls (All Statistics Endpoints)
- Updated all API calls to use `month: currentMonth.value || 0`
- Ensures backend receives `0` for year-only queries
- Modified functions:
  - `fetchMonthlyData()` (line 574)
  - `fetchCategoryData()` (lines 592, 610, 626)
  - `fetchDailyData()` (line 649)
  - `fetchBalanceData()` (line 672)
  - `loadCategoryBills()` (line 1146)

#### 6. Mode Switching Watcher (Lines 1355-1366)
- Added `watch(dateSelectionMode)` to update `selectedDate` array
- When switching to year mode: `selectedDate = [year.toString()]`
- When switching to month mode: `selectedDate = [year, month]`

#### 7. Styling (Lines 1690-1705)
- Added `.date-picker-header` styles for tabs
- Clean, minimal design matching Vant UI conventions
- Proper spacing and background colors

### Vant UI Patterns Used
- **van-tabs**: For mode switching toggle
- **van-date-picker**: Dynamic `columns-type` prop
- **van-popup**: Container for picker and tabs
- Composition API with `watch` for reactive updates

### User Experience
1. Click nav bar date → popup opens with "按月" default
2. Switch to "按年" → picker shows only year column
3. Select year and confirm → `currentMonth = 0`
4. Nav bar shows "2025年" instead of "2025年1月"
5. All statistics refresh with year-only data

### Verification
- Build succeeds: `cd Web && pnpm build`
- No TypeScript errors
- No breaking changes to existing functionality
- Backward compatible with month-only selection
