Update TransactionsPage to match castle extension date range API changes

Synchronized TransactionsPage with castle LNbits extension API updates that introduced custom date range filtering.

**API Changes (ExpensesAPI.ts):**
- Updated `getUserTransactions()` to support `start_date` and `end_date` parameters (YYYY-MM-DD format)
- Custom date range takes precedence over preset days parameter
- Updated comment: default changed from 5 to 15 days, options now 15/30/60 (removed 5 and 90)

**UI Changes (TransactionsPage.vue):**
- **New defaults**: Changed default from 5 days to 15 days
- **New preset options**: 15, 30, 60 days (removed 5 and 90 day options)
- **Custom date range**: Added "Custom" option with date picker inputs
  - From/To date inputs using native HTML5 date picker
  - Apply button to load transactions for custom range
  - Auto-clears custom dates when switching back to preset days
- **State management**:
  - `dateRangeType` ref supports both numbers (15, 30, 60) and 'custom' string
  - `customStartDate` and `customEndDate` refs for custom date range
- **Smart loading**: Only loads transactions after user provides both dates and clicks Apply

**Priority Logic:**
- Custom date range (start_date + end_date) takes precedence
- Falls back to preset days if custom not selected
- Defaults to 15 days if custom selected but dates not provided

Matches castle extension implementation exactly (see castle extension git diff in fava_client.py, views_api.py, and static/js/index.js).

🐢 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-11-14 22:38:34 +01:00
parent 509fae1d35
commit 84596e518e
2 changed files with 109 additions and 29 deletions

View file

@ -404,7 +404,9 @@ export class ExpensesAPI extends BaseService {
options?: {
limit?: number
offset?: number
days?: number // 5, 30, 60, or 90
days?: number // 15, 30, or 60 (default: 15)
start_date?: string // ISO format: YYYY-MM-DD
end_date?: string // ISO format: YYYY-MM-DD
filter_user_id?: string
filter_account_type?: string
}
@ -415,7 +417,15 @@ export class ExpensesAPI extends BaseService {
// Add query parameters
if (options?.limit) url.searchParams.set('limit', String(options.limit))
if (options?.offset) url.searchParams.set('offset', String(options.offset))
if (options?.days) url.searchParams.set('days', String(options.days))
// Custom date range takes precedence over days
if (options?.start_date && options?.end_date) {
url.searchParams.set('start_date', options.start_date)
url.searchParams.set('end_date', options.end_date)
} else if (options?.days) {
url.searchParams.set('days', String(options.days))
}
if (options?.filter_user_id)
url.searchParams.set('filter_user_id', options.filter_user_id)
if (options?.filter_account_type)

View file

@ -10,6 +10,7 @@ import FuzzySearch from '@/components/ui/fuzzy-search/FuzzySearch.vue'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import {
CheckCircle2,
Clock,
@ -25,7 +26,9 @@ const expensesAPI = injectService<ExpensesAPI>(SERVICE_TOKENS.EXPENSES_API)
const transactions = ref<Transaction[]>([])
const isLoading = ref(false)
const selectedDays = ref(5)
const dateRangeType = ref<number | 'custom'>(15) // 15, 30, 60, or 'custom'
const customStartDate = ref<string>('')
const customEndDate = ref<string>('')
const walletKey = computed(() => user.value?.wallets?.[0]?.inkey)
@ -62,12 +65,12 @@ function handleSearchResults(results: Transaction[]) {
searchResults.value = results
}
// Day filter options
const dayOptions = [
{ label: '5 days', value: 5 },
// Date range options (matching castle LNbits extension)
const dateRangeOptions = [
{ label: '15 days', value: 15 },
{ label: '30 days', value: 30 },
{ label: '60 days', value: 60 },
{ label: '90 days', value: 90 }
{ label: 'Custom', value: 'custom' as const }
]
// Format date for display
@ -113,12 +116,27 @@ async function loadTransactions() {
isLoading.value = true
try {
const response = await expensesAPI.getUserTransactions(walletKey.value, {
// Build query params - custom date range takes precedence over preset days
const params: any = {
limit: 1000, // Load all transactions (no pagination needed)
offset: 0,
days: selectedDays.value
})
offset: 0
}
if (dateRangeType.value === 'custom') {
// Use custom date range
if (customStartDate.value && customEndDate.value) {
params.start_date = customStartDate.value
params.end_date = customEndDate.value
} else {
// Default to 15 days if custom selected but dates not provided
params.days = 15
}
} else {
// Use preset days
params.days = dateRangeType.value
}
const response = await expensesAPI.getUserTransactions(walletKey.value, params)
transactions.value = response.entries
} catch (error) {
console.error('Failed to load transactions:', error)
@ -130,10 +148,29 @@ async function loadTransactions() {
}
}
// Change day filter
function changeDayFilter(days: number) {
selectedDays.value = days
loadTransactions()
// Handle date range type change
function onDateRangeTypeChange(value: number | 'custom') {
dateRangeType.value = value
if (value !== 'custom') {
// Clear custom dates when switching to preset days
customStartDate.value = ''
customEndDate.value = ''
// Load transactions immediately with preset days
loadTransactions()
}
// If switching to custom, wait for user to provide dates
}
// Apply custom date range
function applyCustomDateRange() {
if (customStartDate.value && customEndDate.value) {
loadTransactions()
} else {
toast.error('Invalid date range', {
description: 'Please select both start and end dates'
})
}
}
onMounted(() => {
@ -163,20 +200,53 @@ onMounted(() => {
</Button>
</div>
<!-- Compact Controls Row -->
<div class="flex items-center gap-2 flex-wrap">
<Calendar class="h-4 w-4 text-muted-foreground" />
<Button
v-for="option in dayOptions"
:key="option.value"
:variant="selectedDays === option.value ? 'default' : 'outline'"
size="sm"
class="h-8 px-3 text-xs"
@click="changeDayFilter(option.value)"
:disabled="isLoading"
>
{{ option.label }}
</Button>
<!-- Date Range Controls -->
<div class="flex flex-col gap-3">
<!-- Preset Days / Custom Toggle -->
<div class="flex items-center gap-2 flex-wrap">
<Calendar class="h-4 w-4 text-muted-foreground" />
<Button
v-for="option in dateRangeOptions"
:key="option.value"
:variant="dateRangeType === option.value ? 'default' : 'outline'"
size="sm"
class="h-8 px-3 text-xs"
@click="onDateRangeTypeChange(option.value)"
:disabled="isLoading"
>
{{ option.label }}
</Button>
</div>
<!-- Custom Date Range Inputs -->
<div v-if="dateRangeType === 'custom'" class="flex items-end gap-2 flex-wrap">
<div class="flex flex-col gap-1">
<label class="text-xs text-muted-foreground">From:</label>
<Input
type="date"
v-model="customStartDate"
class="h-8 text-xs"
:disabled="isLoading"
/>
</div>
<div class="flex flex-col gap-1">
<label class="text-xs text-muted-foreground">To:</label>
<Input
type="date"
v-model="customEndDate"
class="h-8 text-xs"
:disabled="isLoading"
/>
</div>
<Button
size="sm"
class="h-8 px-3 text-xs"
@click="applyCustomDateRange"
:disabled="isLoading || !customStartDate || !customEndDate"
>
Apply
</Button>
</div>
</div>
</div>
</div>