Enables currency selection for expenses

Allows users to select a currency when adding an expense.

Fetches available currencies from the backend and displays them
in a dropdown menu, defaulting to EUR if the fetch fails.
The expense submission process now includes the selected currency.
This commit is contained in:
padreug 2025-11-07 21:39:16 +01:00
parent 9c8b696f06
commit 8dad92f0e5
2 changed files with 82 additions and 5 deletions

View file

@ -80,7 +80,7 @@
<!-- Amount --> <!-- Amount -->
<FormField v-slot="{ componentField }" name="amount"> <FormField v-slot="{ componentField }" name="amount">
<FormItem> <FormItem>
<FormLabel>Amount (EUR) *</FormLabel> <FormLabel>Amount *</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="number" type="number"
@ -91,7 +91,34 @@
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
Amount in Euros Amount in selected currency
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<!-- Currency -->
<FormField v-slot="{ componentField }" name="currency">
<FormItem>
<FormLabel>Currency *</FormLabel>
<Select v-bind="componentField">
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select currency" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem
v-for="currency in availableCurrencies"
:key="currency"
:value="currency"
>
{{ currency }}
</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Currency for this expense
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@ -179,7 +206,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed, onMounted } from 'vue'
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import * as z from 'zod' import * as z from 'zod'
@ -195,6 +222,13 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '@/components/ui/form' } from '@/components/ui/form'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { DollarSign, X, ChevronLeft, Loader2 } from 'lucide-vue-next' import { DollarSign, X, ChevronLeft, Loader2 } from 'lucide-vue-next'
import { injectService, SERVICE_TOKENS } from '@/core/di-container' import { injectService, SERVICE_TOKENS } from '@/core/di-container'
import { useAuth } from '@/composables/useAuthService' import { useAuth } from '@/composables/useAuthService'
@ -220,12 +254,15 @@ const toast = useToast()
const currentStep = ref(1) const currentStep = ref(1)
const selectedAccount = ref<Account | null>(null) const selectedAccount = ref<Account | null>(null)
const isSubmitting = ref(false) const isSubmitting = ref(false)
const availableCurrencies = ref<string[]>([])
const loadingCurrencies = ref(true)
// Form schema // Form schema
const formSchema = toTypedSchema( const formSchema = toTypedSchema(
z.object({ z.object({
description: z.string().min(1, 'Description is required').max(500, 'Description too long'), description: z.string().min(1, 'Description is required').max(500, 'Description too long'),
amount: z.coerce.number().min(0.01, 'Amount must be at least €0.01'), amount: z.coerce.number().min(0.01, 'Amount must be at least 0.01'),
currency: z.string().min(1, 'Currency is required'),
reference: z.string().max(100, 'Reference too long').optional(), reference: z.string().max(100, 'Reference too long').optional(),
isEquity: z.boolean().default(false) isEquity: z.boolean().default(false)
}) })
@ -237,6 +274,7 @@ const form = useForm({
initialValues: { initialValues: {
description: '', description: '',
amount: 0, amount: 0,
currency: 'EUR',
reference: '', reference: '',
isEquity: false isEquity: false
} }
@ -245,6 +283,24 @@ const form = useForm({
const { resetForm, meta } = form const { resetForm, meta } = form
const isFormValid = computed(() => meta.value.valid) const isFormValid = computed(() => meta.value.valid)
/**
* Fetch available currencies on component mount
*/
onMounted(async () => {
try {
loadingCurrencies.value = true
const currencies = await expensesAPI.getCurrencies()
availableCurrencies.value = currencies
console.log('[AddExpense] Loaded currencies:', currencies)
} catch (error) {
console.error('[AddExpense] Failed to load currencies:', error)
toast.error('Failed to load currencies', { description: 'Please check your connection and try again' })
availableCurrencies.value = []
} finally {
loadingCurrencies.value = false
}
})
/** /**
* Handle account selection * Handle account selection
*/ */
@ -280,7 +336,7 @@ const onSubmit = form.handleSubmit(async (values) => {
is_equity: values.isEquity, is_equity: values.isEquity,
user_wallet: wallet.id, user_wallet: wallet.id,
reference: values.reference, reference: values.reference,
currency: 'EUR' currency: values.currency
}) })
// Show success message // Show success message

View file

@ -207,4 +207,25 @@ export class ExpensesAPI extends BaseService {
throw error throw error
} }
} }
/**
* Get available currencies from LNbits instance
*/
async getCurrencies(): Promise<string[]> {
try {
const response = await fetch(`${this.baseUrl}/api/v1/currencies`, {
method: 'GET',
signal: AbortSignal.timeout(this.config?.apiConfig?.timeout || 30000)
})
if (!response.ok) {
throw new Error(`Failed to fetch currencies: ${response.statusText}`)
}
return await response.json()
} catch (error) {
console.error('[ExpensesAPI] Error fetching currencies:', error)
throw error
}
}
} }