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:
parent
509fae1d35
commit
84596e518e
2 changed files with 109 additions and 29 deletions
|
|
@ -404,7 +404,9 @@ export class ExpensesAPI extends BaseService {
|
||||||
options?: {
|
options?: {
|
||||||
limit?: number
|
limit?: number
|
||||||
offset?: 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_user_id?: string
|
||||||
filter_account_type?: string
|
filter_account_type?: string
|
||||||
}
|
}
|
||||||
|
|
@ -415,7 +417,15 @@ export class ExpensesAPI extends BaseService {
|
||||||
// Add query parameters
|
// Add query parameters
|
||||||
if (options?.limit) url.searchParams.set('limit', String(options.limit))
|
if (options?.limit) url.searchParams.set('limit', String(options.limit))
|
||||||
if (options?.offset) url.searchParams.set('offset', String(options.offset))
|
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)
|
if (options?.filter_user_id)
|
||||||
url.searchParams.set('filter_user_id', options.filter_user_id)
|
url.searchParams.set('filter_user_id', options.filter_user_id)
|
||||||
if (options?.filter_account_type)
|
if (options?.filter_account_type)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import FuzzySearch from '@/components/ui/fuzzy-search/FuzzySearch.vue'
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Clock,
|
Clock,
|
||||||
|
|
@ -25,7 +26,9 @@ const expensesAPI = injectService<ExpensesAPI>(SERVICE_TOKENS.EXPENSES_API)
|
||||||
|
|
||||||
const transactions = ref<Transaction[]>([])
|
const transactions = ref<Transaction[]>([])
|
||||||
const isLoading = ref(false)
|
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)
|
const walletKey = computed(() => user.value?.wallets?.[0]?.inkey)
|
||||||
|
|
||||||
|
|
@ -62,12 +65,12 @@ function handleSearchResults(results: Transaction[]) {
|
||||||
searchResults.value = results
|
searchResults.value = results
|
||||||
}
|
}
|
||||||
|
|
||||||
// Day filter options
|
// Date range options (matching castle LNbits extension)
|
||||||
const dayOptions = [
|
const dateRangeOptions = [
|
||||||
{ label: '5 days', value: 5 },
|
{ label: '15 days', value: 15 },
|
||||||
{ label: '30 days', value: 30 },
|
{ label: '30 days', value: 30 },
|
||||||
{ label: '60 days', value: 60 },
|
{ label: '60 days', value: 60 },
|
||||||
{ label: '90 days', value: 90 }
|
{ label: 'Custom', value: 'custom' as const }
|
||||||
]
|
]
|
||||||
|
|
||||||
// Format date for display
|
// Format date for display
|
||||||
|
|
@ -113,12 +116,27 @@ async function loadTransactions() {
|
||||||
|
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
try {
|
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)
|
limit: 1000, // Load all transactions (no pagination needed)
|
||||||
offset: 0,
|
offset: 0
|
||||||
days: selectedDays.value
|
}
|
||||||
})
|
|
||||||
|
|
||||||
|
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
|
transactions.value = response.entries
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load transactions:', error)
|
console.error('Failed to load transactions:', error)
|
||||||
|
|
@ -130,10 +148,29 @@ async function loadTransactions() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change day filter
|
// Handle date range type change
|
||||||
function changeDayFilter(days: number) {
|
function onDateRangeTypeChange(value: number | 'custom') {
|
||||||
selectedDays.value = days
|
dateRangeType.value = value
|
||||||
loadTransactions()
|
|
||||||
|
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(() => {
|
onMounted(() => {
|
||||||
|
|
@ -163,20 +200,53 @@ onMounted(() => {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Compact Controls Row -->
|
<!-- Date Range Controls -->
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex flex-col gap-3">
|
||||||
<Calendar class="h-4 w-4 text-muted-foreground" />
|
<!-- Preset Days / Custom Toggle -->
|
||||||
<Button
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
v-for="option in dayOptions"
|
<Calendar class="h-4 w-4 text-muted-foreground" />
|
||||||
:key="option.value"
|
<Button
|
||||||
:variant="selectedDays === option.value ? 'default' : 'outline'"
|
v-for="option in dateRangeOptions"
|
||||||
size="sm"
|
:key="option.value"
|
||||||
class="h-8 px-3 text-xs"
|
:variant="dateRangeType === option.value ? 'default' : 'outline'"
|
||||||
@click="changeDayFilter(option.value)"
|
size="sm"
|
||||||
:disabled="isLoading"
|
class="h-8 px-3 text-xs"
|
||||||
>
|
@click="onDateRangeTypeChange(option.value)"
|
||||||
{{ option.label }}
|
:disabled="isLoading"
|
||||||
</Button>
|
>
|
||||||
|
{{ 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue