diff --git a/src/modules/expenses/components/AccountSelector.vue b/src/modules/expenses/components/AccountSelector.vue index ec49ed3..18d6c3b 100644 --- a/src/modules/expenses/components/AccountSelector.vue +++ b/src/modules/expenses/components/AccountSelector.vue @@ -151,6 +151,7 @@ import { Check } from 'lucide-vue-next' import { injectService, SERVICE_TOKENS } from '@/core/di-container' +import { useAuth } from '@/composables/useAuthService' import type { ExpensesAPI } from '../services/ExpensesAPI' import type { Account, AccountNode } from '../types' @@ -167,8 +168,9 @@ interface Emits { const props = defineProps() const emit = defineEmits() -// Inject services +// Inject services and composables const expensesAPI = injectService(SERVICE_TOKENS.EXPENSES_API) +const { user } = useAuth() // Component state const isLoading = ref(false) @@ -204,7 +206,14 @@ async function loadAccounts() { error.value = null try { + // Get wallet key from first wallet (invoice key for read operations) + const wallet = user.value?.wallets?.[0] + if (!wallet || !wallet.inkey) { + throw new Error('No wallet available. Please log in.') + } + accountHierarchy.value = await expensesAPI.getAccountHierarchy( + wallet.inkey, props.rootAccount ) } catch (err) { diff --git a/src/modules/expenses/components/AddExpense.vue b/src/modules/expenses/components/AddExpense.vue index 6b24130..e6e09c2 100644 --- a/src/modules/expenses/components/AddExpense.vue +++ b/src/modules/expenses/components/AddExpense.vue @@ -197,6 +197,8 @@ import { } from '@/components/ui/form' import { DollarSign, X, ChevronLeft, Loader2 } from 'lucide-vue-next' import { injectService, SERVICE_TOKENS } from '@/core/di-container' +import { useAuth } from '@/composables/useAuthService' +import { useToast } from '@/core/composables/useToast' import type { ExpensesAPI } from '../services/ExpensesAPI' import type { Account } from '../types' import AccountSelector from './AccountSelector.vue' @@ -209,8 +211,10 @@ interface Emits { const emit = defineEmits() -// Inject services +// Inject services and composables const expensesAPI = injectService(SERVICE_TOKENS.EXPENSES_API) +const { user } = useAuth() +const toast = useToast() // Component state const currentStep = ref(1) @@ -255,22 +259,33 @@ function handleAccountSelected(account: Account) { const onSubmit = form.handleSubmit(async (values) => { if (!selectedAccount.value) { console.error('[AddExpense] No account selected') + toast.error('No account selected', { description: 'Please select an account first' }) + return + } + + // Get wallet key from first wallet (invoice key for submission) + const wallet = user.value?.wallets?.[0] + if (!wallet || !wallet.inkey) { + toast.error('No wallet available', { description: 'Please log in to submit expenses' }) return } isSubmitting.value = true try { - await expensesAPI.submitExpense({ + await expensesAPI.submitExpense(wallet.inkey, { description: values.description, amount: values.amount, expense_account: selectedAccount.value.name, is_equity: values.isEquity, - user_wallet: '', // Will be filled by backend from auth + user_wallet: wallet.id, reference: values.reference, currency: 'sats' }) + // Show success message + toast.success('Expense submitted', { description: 'Your expense is pending admin approval' }) + // Reset form and close resetForm() selectedAccount.value = null @@ -281,6 +296,8 @@ const onSubmit = form.handleSubmit(async (values) => { emit('close') } catch (error) { console.error('[AddExpense] Error submitting expense:', error) + const errorMessage = error instanceof Error ? error.message : 'Unknown error' + toast.error('Submission failed', { description: errorMessage }) } finally { isSubmitting.value = false } diff --git a/src/modules/expenses/services/ExpensesAPI.ts b/src/modules/expenses/services/ExpensesAPI.ts index 0646bc7..e03d36e 100644 --- a/src/modules/expenses/services/ExpensesAPI.ts +++ b/src/modules/expenses/services/ExpensesAPI.ts @@ -15,7 +15,7 @@ export class ExpensesAPI extends BaseService { protected readonly metadata = { name: 'ExpensesAPI', version: '1.0.0', - dependencies: ['AuthService', 'ToastService'] + dependencies: [] // No dependencies - wallet key is passed as parameter } private get config() { @@ -34,18 +34,9 @@ export class ExpensesAPI extends BaseService { } /** - * Get authentication headers + * Get authentication headers with provided wallet key */ - private getHeaders(): HeadersInit { - if (!this.authService) { - throw new Error('AuthService not available') - } - - const walletKey = this.authService.walletKey - if (!walletKey) { - throw new Error('Wallet key not available. Please log in.') - } - + private getHeaders(walletKey: string): HeadersInit { return { 'Content-Type': 'application/json', 'X-Api-Key': walletKey @@ -58,11 +49,11 @@ export class ExpensesAPI extends BaseService { * Note: Currently returns all accounts. Once castle API implements * user permissions, use filter_by_user=true parameter. */ - async getAccounts(): Promise { + async getAccounts(walletKey: string): Promise { try { - const response = await fetch(`${this.baseUrl}/api/v1/accounts`, { + const response = await fetch(`${this.baseUrl}/castle/api/v1/accounts`, { method: 'GET', - headers: this.getHeaders(), + headers: this.getHeaders(walletKey), signal: AbortSignal.timeout(this.config?.apiConfig?.timeout || 30000) }) @@ -74,11 +65,6 @@ export class ExpensesAPI extends BaseService { return accounts as Account[] } catch (error) { console.error('[ExpensesAPI] Error fetching accounts:', error) - if (this.toastService) { - this.toastService.error('Failed to load accounts', { - description: error instanceof Error ? error.message : 'Unknown error' - }) - } throw error } } @@ -89,8 +75,8 @@ export class ExpensesAPI extends BaseService { * Converts flat account list to nested tree based on colon-separated names * e.g., "Expenses:Groceries:Organic" becomes nested structure */ - async getAccountHierarchy(rootAccount?: string): Promise { - const accounts = await this.getAccounts() + async getAccountHierarchy(walletKey: string, rootAccount?: string): Promise { + const accounts = await this.getAccounts(walletKey) // Filter by root account if specified let filteredAccounts = accounts @@ -151,11 +137,11 @@ export class ExpensesAPI extends BaseService { /** * Submit expense entry to castle */ - async submitExpense(request: ExpenseEntryRequest): Promise { + async submitExpense(walletKey: string, request: ExpenseEntryRequest): Promise { try { - const response = await fetch(`${this.baseUrl}/api/v1/entries/expense`, { + const response = await fetch(`${this.baseUrl}/castle/api/v1/entries/expense`, { method: 'POST', - headers: this.getHeaders(), + headers: this.getHeaders(walletKey), body: JSON.stringify(request), signal: AbortSignal.timeout(this.config?.apiConfig?.timeout || 30000) }) @@ -168,28 +154,9 @@ export class ExpensesAPI extends BaseService { } const entry = await response.json() - - if (this.toastService) { - this.toastService.success('Expense submitted', { - description: 'Your expense is pending admin approval' - }) - } - return entry as ExpenseEntry } catch (error) { console.error('[ExpensesAPI] Error submitting expense:', error) - - let errorMessage = 'Failed to submit expense' - if (error instanceof Error) { - errorMessage = error.message - } - - if (this.toastService) { - this.toastService.error('Submission failed', { - description: errorMessage - }) - } - throw error } } @@ -197,11 +164,11 @@ export class ExpensesAPI extends BaseService { /** * Get user's expense entries */ - async getUserExpenses(): Promise { + async getUserExpenses(walletKey: string): Promise { try { - const response = await fetch(`${this.baseUrl}/api/v1/entries/user`, { + const response = await fetch(`${this.baseUrl}/castle/api/v1/entries/user`, { method: 'GET', - headers: this.getHeaders(), + headers: this.getHeaders(walletKey), signal: AbortSignal.timeout(this.config?.apiConfig?.timeout || 30000) }) @@ -215,11 +182,6 @@ export class ExpensesAPI extends BaseService { return entries as ExpenseEntry[] } catch (error) { console.error('[ExpensesAPI] Error fetching user expenses:', error) - if (this.toastService) { - this.toastService.error('Failed to load expenses', { - description: error instanceof Error ? error.message : 'Unknown error' - }) - } throw error } } @@ -227,11 +189,11 @@ export class ExpensesAPI extends BaseService { /** * Get user's balance with castle */ - async getUserBalance(): Promise<{ balance: number; currency: string }> { + async getUserBalance(walletKey: string): Promise<{ balance: number; currency: string }> { try { - const response = await fetch(`${this.baseUrl}/api/v1/balance`, { + const response = await fetch(`${this.baseUrl}/castle/api/v1/balance`, { method: 'GET', - headers: this.getHeaders(), + headers: this.getHeaders(walletKey), signal: AbortSignal.timeout(this.config?.apiConfig?.timeout || 30000) })