const mapJournalEntry = obj => { return obj } window.app = Vue.createApp({ el: '#vue', mixins: [windowMixin], data() { return { balance: null, transactions: [], accounts: [], currencies: [], expenseDialog: { show: false, description: '', amount: null, expenseAccount: '', isEquity: false, reference: '', currency: null, loading: false }, payDialog: { show: false, amount: null, loading: false } } }, computed: { expenseAccounts() { return this.accounts.filter(a => a.account_type === 'expense') }, amountLabel() { if (this.expenseDialog.currency) { return `Amount (${this.expenseDialog.currency}) *` } return 'Amount (sats) *' }, currencyOptions() { const options = [{label: 'Satoshis (default)', value: null}] this.currencies.forEach(curr => { options.push({label: curr, value: curr}) }) return options } }, methods: { async loadBalance() { try { const response = await LNbits.api.request( 'GET', '/castle/api/v1/balance', this.g.user.wallets[0].inkey ) this.balance = response.data } catch (error) { LNbits.utils.notifyApiError(error) } }, async loadTransactions() { try { const response = await LNbits.api.request( 'GET', '/castle/api/v1/entries/user', this.g.user.wallets[0].inkey ) this.transactions = response.data } catch (error) { LNbits.utils.notifyApiError(error) } }, async loadAccounts() { try { const response = await LNbits.api.request( 'GET', '/castle/api/v1/accounts', this.g.user.wallets[0].inkey ) this.accounts = response.data } catch (error) { LNbits.utils.notifyApiError(error) } }, async loadCurrencies() { try { const response = await LNbits.api.request( 'GET', '/castle/api/v1/currencies', this.g.user.wallets[0].inkey ) this.currencies = response.data } catch (error) { LNbits.utils.notifyApiError(error) } }, async submitExpense() { this.expenseDialog.loading = true try { await LNbits.api.request( 'POST', '/castle/api/v1/entries/expense', this.g.user.wallets[0].inkey, { description: this.expenseDialog.description, amount: this.expenseDialog.amount, expense_account: this.expenseDialog.expenseAccount, is_equity: this.expenseDialog.isEquity, user_wallet: this.g.user.wallets[0].id, reference: this.expenseDialog.reference || null, currency: this.expenseDialog.currency || null } ) this.$q.notify({ type: 'positive', message: 'Expense added successfully' }) this.expenseDialog.show = false this.resetExpenseDialog() await this.loadBalance() await this.loadTransactions() } catch (error) { LNbits.utils.notifyApiError(error) } finally { this.expenseDialog.loading = false } }, async submitPayment() { this.payDialog.loading = true try { // First, generate an invoice for the payment const invoiceResponse = await LNbits.api.request( 'POST', '/api/v1/payments', this.g.user.wallets[0].inkey, { out: false, amount: this.payDialog.amount, memo: `Payment to Castle - ${this.payDialog.amount} sats`, unit: 'sat' } ) // Show the invoice to the user this.$q.notify({ type: 'positive', message: 'Invoice generated! Pay it to settle your balance.', timeout: 5000 }) // TODO: After payment, call /castle/api/v1/pay-balance to record it // This would typically be done via a webhook or payment verification this.payDialog.show = false this.payDialog.amount = null } catch (error) { LNbits.utils.notifyApiError(error) } finally { this.payDialog.loading = false } }, showPayBalanceDialog() { this.payDialog.amount = Math.abs(this.balance.balance) this.payDialog.show = true }, resetExpenseDialog() { this.expenseDialog.description = '' this.expenseDialog.amount = null this.expenseDialog.expenseAccount = '' this.expenseDialog.isEquity = false this.expenseDialog.reference = '' this.expenseDialog.currency = null }, formatSats(amount) { return new Intl.NumberFormat().format(amount) }, formatDate(dateString) { return new Date(dateString).toLocaleDateString() }, getTotalAmount(entry) { if (!entry.lines || entry.lines.length === 0) return 0 return entry.lines.reduce((sum, line) => sum + line.debit + line.credit, 0) / 2 } }, async created() { await this.loadBalance() await this.loadTransactions() await this.loadAccounts() await this.loadCurrencies() } })