window.app = Vue.createApp({ el: '#vue', mixins: [windowMixin], delimiters: ['${', '}'], data: function () { return { // DCA Admin Data dcaClients: [], deposits: [], totalDcaBalance: 0, // Table configurations clientsTable: { columns: [ { name: 'user_id', align: 'left', label: 'User ID', field: 'user_id' }, { name: 'wallet_id', align: 'left', label: 'Wallet ID', field: 'wallet_id' }, { name: 'dca_mode', align: 'left', label: 'DCA Mode', field: 'dca_mode' }, { name: 'fixed_mode_daily_limit', align: 'left', label: 'Daily Limit', field: 'fixed_mode_daily_limit' }, { name: 'status', align: 'left', label: 'Status', field: 'status' } ], pagination: { rowsPerPage: 10 } }, depositsTable: { columns: [ { name: 'client_id', align: 'left', label: 'Client', field: 'client_id' }, { name: 'amount', align: 'left', label: 'Amount', field: 'amount' }, { name: 'currency', align: 'left', label: 'Currency', field: 'currency' }, { name: 'status', align: 'left', label: 'Status', field: 'status' }, { name: 'created_at', align: 'left', label: 'Created', field: 'created_at' }, { name: 'notes', align: 'left', label: 'Notes', field: 'notes' } ], pagination: { rowsPerPage: 10 } }, // Dialog states depositFormDialog: { show: false, data: { currency: 'GTQ' } }, clientDetailsDialog: { show: false, data: null, balance: null }, // Quick deposit form quickDepositForm: { client_id: '', amount: null, notes: '' }, // Options currencyOptions: [ { label: 'GTQ', value: 'GTQ' }, { label: 'USD', value: 'USD' } ], // Legacy data (keep for backward compatibility) invoiceAmount: 10, qrValue: 'lnurlpay', myex: [], myexTable: { columns: [ { name: 'id', align: 'left', label: 'ID', field: 'id' }, { name: 'name', align: 'left', label: 'Name', field: 'name' }, { name: 'wallet', align: 'left', label: 'Wallet', field: 'wallet' }, { name: 'total', align: 'left', label: 'Total sent/received', field: 'total' } ], pagination: { rowsPerPage: 10 } }, formDialog: { show: false, data: {}, advanced: {} }, urlDialog: { show: false, data: {} } } }, /////////////////////////////////////////////////// ////////////////METHODS FUNCTIONS////////////////// /////////////////////////////////////////////////// methods: { // Utility Methods formatCurrency(amount) { if (!amount) return 'Q 0.00'; return new Intl.NumberFormat('es-GT', { style: 'currency', currency: 'GTQ', }).format(amount); }, formatDate(dateString) { if (!dateString) return '' return new Date(dateString).toLocaleDateString() }, // DCA Client Methods async getDcaClients() { try { const { data } = await LNbits.api.request( 'GET', '/myextension/api/v1/dca/clients', this.g.user.wallets[0].inkey ) this.dcaClients = data } catch (error) { LNbits.utils.notifyApiError(error) } }, // Test Client Creation (temporary for testing) async createTestClient() { try { const testData = { user_id: this.g.user.id, wallet_id: this.g.user.wallets[0].id, dca_mode: 'flow' } const { data: newClient } = await LNbits.api.request( 'POST', '/myextension/api/v1/dca/clients', this.g.user.wallets[0].adminkey, testData ) this.dcaClients.push(newClient) this.$q.notify({ type: 'positive', message: 'Test client created successfully!', timeout: 5000 }) } catch (error) { LNbits.utils.notifyApiError(error) } }, // Quick Deposit Methods async sendQuickDeposit() { try { const data = { client_id: this.quickDepositForm.client_id, amount: this.quickDepositForm.amount, currency: 'GTQ', notes: this.quickDepositForm.notes } const { data: newDeposit } = await LNbits.api.request( 'POST', '/myextension/api/v1/dca/deposits', this.g.user.wallets[0].adminkey, data ) this.deposits.unshift(newDeposit) // Reset form this.quickDepositForm = { client_id: '', amount: null, notes: '' } this.$q.notify({ type: 'positive', message: 'Deposit created successfully', timeout: 5000 }) } catch (error) { LNbits.utils.notifyApiError(error) } }, async viewClientDetails(client) { try { const { data: balance } = await LNbits.api.request( 'GET', `/myextension/api/v1/dca/clients/${client.id}/balance`, this.g.user.wallets[0].inkey ) this.clientDetailsDialog.data = client this.clientDetailsDialog.balance = balance this.clientDetailsDialog.show = true } catch (error) { LNbits.utils.notifyApiError(error) } }, // Deposit Methods async getDeposits() { try { const { data } = await LNbits.api.request( 'GET', '/myextension/api/v1/dca/deposits', this.g.user.wallets[0].inkey ) this.deposits = data } catch (error) { LNbits.utils.notifyApiError(error) } }, addDepositDialog(client) { this.depositFormDialog.data = { client_id: client.id, client_name: `${client.user_id.substring(0, 8)}...`, currency: 'GTQ' } this.depositFormDialog.show = true }, async sendDepositData() { try { const data = { client_id: this.depositFormDialog.data.client_id, amount: this.depositFormDialog.data.amount, currency: this.depositFormDialog.data.currency, notes: this.depositFormDialog.data.notes } if (this.depositFormDialog.data.id) { // Update existing deposit (mainly for notes/status) const { data: updatedDeposit } = await LNbits.api.request( 'PUT', `/myextension/api/v1/dca/deposits/${this.depositFormDialog.data.id}`, this.g.user.wallets[0].adminkey, { status: this.depositFormDialog.data.status, notes: data.notes } ) const index = this.deposits.findIndex(d => d.id === updatedDeposit.id) if (index !== -1) { this.deposits.splice(index, 1, updatedDeposit) } } else { // Create new deposit const { data: newDeposit } = await LNbits.api.request( 'POST', '/myextension/api/v1/dca/deposits', this.g.user.wallets[0].adminkey, data ) this.deposits.unshift(newDeposit) } this.closeDepositFormDialog() this.$q.notify({ type: 'positive', message: this.depositFormDialog.data.id ? 'Deposit updated successfully' : 'Deposit created successfully', timeout: 5000 }) } catch (error) { LNbits.utils.notifyApiError(error) } }, closeDepositFormDialog() { this.depositFormDialog.show = false this.depositFormDialog.data = { currency: 'GTQ' } }, async confirmDeposit(deposit) { try { await LNbits.utils .confirmDialog('Confirm that this deposit has been physically placed in the ATM machine?') .onOk(async () => { const { data: updatedDeposit } = await LNbits.api.request( 'PUT', `/myextension/api/v1/dca/deposits/${deposit.id}/status`, this.g.user.wallets[0].adminkey, { status: 'confirmed', notes: 'Confirmed by admin - money placed in machine' } ) const index = this.deposits.findIndex(d => d.id === deposit.id) if (index !== -1) { this.deposits.splice(index, 1, updatedDeposit) } this.$q.notify({ type: 'positive', message: 'Deposit confirmed! DCA is now active for this client.', timeout: 5000 }) }) } catch (error) { LNbits.utils.notifyApiError(error) } }, editDeposit(deposit) { this.depositFormDialog.data = { ...deposit } this.depositFormDialog.show = true }, // Export Methods async exportClientsCSV() { await LNbits.utils.exportCSV(this.clientsTable.columns, this.dcaClients) }, async exportDepositsCSV() { await LNbits.utils.exportCSV(this.depositsTable.columns, this.deposits) }, // Legacy Methods (keep for backward compatibility) async closeFormDialog() { this.formDialog.show = false this.formDialog.data = {} }, async getMyExtensions() { await LNbits.api .request( 'GET', '/myextension/api/v1/myex', this.g.user.wallets[0].inkey ) .then(response => { this.myex = response.data }) .catch(err => { LNbits.utils.notifyApiError(err) }) }, async sendMyExtensionData() { const data = { name: this.formDialog.data.name, lnurlwithdrawamount: this.formDialog.data.lnurlwithdrawamount, lnurlpayamount: this.formDialog.data.lnurlpayamount } const wallet = _.findWhere(this.g.user.wallets, { id: this.formDialog.data.wallet }) if (this.formDialog.data.id) { data.id = this.formDialog.data.id data.total = this.formDialog.data.total await this.updateMyExtension(wallet, data) } else { await this.createMyExtension(wallet, data) } }, async updateMyExtensionForm(tempId) { const myextension = _.findWhere(this.myex, { id: tempId }) this.formDialog.data = { ...myextension } if (this.formDialog.data.tip_wallet != '') { this.formDialog.advanced.tips = true } if (this.formDialog.data.withdrawlimit >= 1) { this.formDialog.advanced.otc = true } this.formDialog.show = true }, async createMyExtension(wallet, data) { data.wallet = wallet.id await LNbits.api .request('POST', '/myextension/api/v1/myex', wallet.adminkey, data) .then(response => { this.myex.push(response.data) this.closeFormDialog() }) .catch(error => { LNbits.utils.notifyApiError(error) }) }, async updateMyExtension(wallet, data) { data.wallet = wallet.id await LNbits.api .request( 'PUT', `/myextension/api/v1/myex/${data.id}`, wallet.adminkey, data ) .then(response => { this.myex = _.reject(this.myex, obj => obj.id == data.id) this.myex.push(response.data) this.closeFormDialog() }) .catch(error => { LNbits.utils.notifyApiError(error) }) }, async deleteMyExtension(tempId) { var myextension = _.findWhere(this.myex, { id: tempId }) const wallet = _.findWhere(this.g.user.wallets, { id: myextension.wallet }) await LNbits.utils .confirmDialog('Are you sure you want to delete this MyExtension?') .onOk(function () { LNbits.api .request( 'DELETE', '/myextension/api/v1/myex/' + tempId, wallet.adminkey ) .then(() => { this.myex = _.reject(this.myex, function (obj) { return obj.id === myextension.id }) }) .catch(error => { LNbits.utils.notifyApiError(error) }) }) }, async exportCSV() { await LNbits.utils.exportCSV(this.myexTable.columns, this.myex) }, async itemsArray(tempId) { const myextension = _.findWhere(this.myex, { id: tempId }) return [...myextension.itemsMap.values()] }, async openformDialog(id) { const [tempId, itemId] = id.split(':') const myextension = _.findWhere(this.myex, { id: tempId }) if (itemId) { const item = myextension.itemsMap.get(id) this.formDialog.data = { ...item, myextension: tempId } } else { this.formDialog.data.myextension = tempId } this.formDialog.data.currency = myextension.currency this.formDialog.show = true }, async openUrlDialog(tempid) { this.urlDialog.data = _.findWhere(this.myex, { id: tempid }) this.qrValue = this.urlDialog.data.lnurlpay // Connecting to our websocket fired in tasks.py this.connectWebocket(this.urlDialog.data.id) this.urlDialog.show = true }, async closeformDialog() { this.formDialog.show = false this.formDialog.data = {} }, async createInvoice(tempid) { /////////////////////////////////////////////////// ///Simple call to the api to create an invoice///// /////////////////////////////////////////////////// myex = _.findWhere(this.myex, { id: tempid }) const wallet = _.findWhere(this.g.user.wallets, { id: myex.wallet }) const data = { myextension_id: tempid, amount: this.invoiceAmount, memo: 'MyExtension - ' + myex.name } await LNbits.api .request('POST', `/myextension/api/v1/myex/payment`, wallet.inkey, data) .then(response => { this.qrValue = response.data.payment_request this.connectWebocket(wallet.inkey) }) .catch(error => { LNbits.utils.notifyApiError(error) }) }, connectWebocket(myextension_id) { ////////////////////////////////////////////////// ///wait for pay action to happen and do a thing//// /////////////////////////////////////////////////// if (location.protocol !== 'http:') { localUrl = 'wss://' + document.domain + ':' + location.port + '/api/v1/ws/' + myextension_id } else { localUrl = 'ws://' + document.domain + ':' + location.port + '/api/v1/ws/' + myextension_id } this.connection = new WebSocket(localUrl) this.connection.onmessage = () => { this.urlDialog.show = false } } }, /////////////////////////////////////////////////// //////LIFECYCLE FUNCTIONS RUNNING ON PAGE LOAD///// /////////////////////////////////////////////////// async created() { // Load DCA admin data await Promise.all([ this.getDcaClients(), this.getDeposits() ]) // Calculate total DCA balance this.calculateTotalDcaBalance() // Legacy data loading await this.getMyExtensions() }, watch: { deposits() { this.calculateTotalDcaBalance() } }, computed: { clientOptions() { return this.dcaClients.map(client => ({ label: `${client.user_id.substring(0, 8)}... (${client.dca_mode})`, value: client.id })) }, calculateTotalDcaBalance() { this.totalDcaBalance = this.deposits .filter(d => d.status === 'confirmed') .reduce((total, deposit) => total + deposit.amount, 0) } } })