satmachineadmin/templates/satmachineadmin/index.html
padreug e7b6d261f7 Removes test transaction UI button
Removes the test transaction button from the admin UI.

The test transaction endpoint is still available in the API for development and debugging purposes.
2025-10-24 00:30:34 +02:00

842 lines
32 KiB
HTML

<!--/////////////////////////////////////////////////-->
<!--//PAGE FOR THE DCA ADMIN EXTENSION IN LNBITS//////-->
<!--/////////////////////////////////////////////////-->
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block scripts %} {{ window_vars(user) }}
<script src="{{ static_url_for('satmachineadmin/static', path='js/index.js') }}"></script>
{% endblock %} {% block page %}
<div class="row q-col-gutter-md" id="dcaAdmin">
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
<!-- Deposit Management Section -->
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">DCA Deposit Management</h5>
<p class="text-caption q-my-none">Manage fiat deposits for existing DCA clients</p>
</div>
</div>
</q-card-section>
</q-card>
<!-- DCA Clients Table -->
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h6 class="text-subtitle2 q-my-none">Registered DCA Clients</h6>
<p class="text-caption q-my-none">Clients registered via the DCA client extension</p>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportClientsCSV">Export to CSV</q-btn>
</div>
</div>
<q-table
dense
flat
:rows="dcaClients"
row-key="id"
:columns="clientsTable.columns"
v-model:pagination="clientsTable.pagination"
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.field == 'username'">${ col.value || 'No username' }</div>
<div v-else-if="col.field == 'user_id'">${ col.value.substring(0, 8) }...</div>
<div v-else-if="col.field == 'wallet_id'">${ col.value.substring(0, 8) }...</div>
<div v-else-if="col.field == 'status'">
<q-badge :color="col.value === 'active' ? 'green' : 'red'">
${ col.value }
</q-badge>
</div>
<div v-else-if="col.field == 'remaining_balance'">
<span :class="col.value > 0 ? 'text-green-8 text-weight-bold' : 'text-grey-6'">
${ formatCurrency(col.value || 0) }
</span>
</div>
<div v-else-if="col.field == 'fixed_mode_daily_limit' && col.value">
${ formatCurrency(col.value) }
</div>
<div v-else>${ col.value || '-' }</div>
</q-td>
<q-td auto-width>
<q-btn
flat dense size="sm" icon="account_balance_wallet"
color="primary" class="q-mr-sm"
@click="addDepositDialog(props.row)"
>
<q-tooltip>Add Deposit</q-tooltip>
</q-btn>
<q-btn
flat dense size="sm" icon="visibility"
color="blue"
@click="viewClientDetails(props.row)"
>
<q-tooltip>View Balance & Details</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
<!-- Quick Add Deposit Section -->
<q-card>
<q-card-section>
<h6 class="text-subtitle2 q-my-none">Quick Add Deposit</h6>
<p class="text-caption q-my-none">Add a new deposit for an existing client</p>
<div v-if="dcaClients.length === 0" class="q-mt-md">
<q-banner class="bg-orange-1 text-orange-9">
<template v-slot:avatar>
<q-icon name="info" color="orange" />
</template>
No DCA clients registered yet. Clients must first install and configure the DCA client extension.
</q-banner>
</div>
<q-form v-else @submit="sendQuickDeposit" class="q-gutter-md q-mt-md">
<div class="row q-gutter-md">
<div class="col">
<q-select
filled
dense
v-model="quickDepositForm.selectedClient"
:options="clientOptions"
label="Select Client *"
option-label="label"
option-value="value"
></q-select>
</div>
<div class="col">
<q-input
filled
dense
type="number"
step="0.01"
v-model.number="quickDepositForm.amount"
label="Amount (GTQ) *"
placeholder="150.00"
hint="Enter amount in GTQ (e.g., 150.00)"
></q-input>
</div>
<div class="col-auto">
<q-btn
unelevated
color="primary"
type="submit"
:disable="!quickDepositForm.selectedClient || !quickDepositForm.amount"
>Add Deposit</q-btn
>
</div>
</div>
<div class="row">
<div class="col">
<q-input
filled
dense
type="textarea"
v-model.trim="quickDepositForm.notes"
label="Notes (Optional)"
placeholder="Optional notes about this deposit"
rows="2"
></q-input>
</div>
</div>
</q-form>
</q-card-section>
</q-card>
<!-- Deposits Management Section -->
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h6 class="text-subtitle2 q-my-none">Recent Deposits</h6>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportDepositsCSV">Export to CSV</q-btn>
</div>
</div>
<q-table
dense
flat
:rows="deposits"
row-key="id"
:columns="depositsTable.columns"
v-model:pagination="depositsTable.pagination"
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.field == 'client_id'">${ getClientUsername(col.value) }</div>
<div v-else-if="col.field == 'amount'">${ formatCurrency(col.value) }</div>
<div v-else-if="col.field == 'status'">
<q-badge :color="col.value === 'confirmed' ? 'green' : 'orange'">
${ col.value }
</q-badge>
</div>
<div v-else-if="col.field == 'created_at'">${ formatDate(col.value) }</div>
<div v-else>${ col.value }</div>
</q-td>
<q-td auto-width>
<q-btn
v-if="props.row.status === 'pending'"
flat dense size="sm" icon="check_circle"
color="green" class="q-mr-sm"
@click="confirmDeposit(props.row)"
>
<q-tooltip>Confirm Deposit</q-tooltip>
</q-btn>
<q-btn
flat dense size="sm" icon="edit"
color="orange"
@click="editDeposit(props.row)"
>
<q-tooltip>Edit Deposit</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
<!-- Lamassu Transactions Section -->
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h6 class="text-subtitle2 q-my-none">Processed Lamassu Transactions</h6>
<p class="text-caption q-my-none">ATM transactions processed through DCA distribution</p>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportLamassuTransactionsCSV">Export to CSV</q-btn>
</div>
</div>
<q-table
dense
flat
:rows="lamassuTransactions"
row-key="id"
:columns="lamassuTransactionsTable.columns"
v-model:pagination="lamassuTransactionsTable.pagination"
>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer" @click="viewTransactionDistributions(props.row)">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.field == 'lamassu_transaction_id'">${ col.value }</div>
<div v-else-if="col.field == 'fiat_amount'">${ formatCurrency(col.value) }</div>
<div v-else-if="col.field == 'crypto_amount'">${ formatSats(col.value) }</div>
<div v-else-if="col.field == 'commission_amount_sats'">${ formatSats(col.value) }</div>
<div v-else-if="col.field == 'base_amount_sats'">${ formatSats(col.value) }</div>
<div v-else-if="col.field == 'distributions_total_sats'">${ formatSats(col.value) }</div>
<div v-else-if="col.field == 'commission_percentage'">${ (col.value * 100).toFixed(1) }%</div>
<div v-else-if="col.field == 'effective_commission'">${ (col.value * 100).toFixed(1) }%</div>
<div v-else-if="col.field == 'discount'">${ col.value }%</div>
<div v-else-if="col.field == 'exchange_rate'">${ col.value.toLocaleString() }</div>
<div v-else-if="col.field == 'transaction_time'">${ formatDateTime(col.value) }</div>
<div v-else-if="col.field == 'processed_at'">${ formatDateTime(col.value) }</div>
<div v-else>${ col.value || '-' }</div>
</q-td>
<q-td auto-width>
<q-btn
flat dense size="sm" icon="visibility"
color="primary"
@click.stop="viewTransactionDistributions(props.row)"
>
<q-tooltip>View Distribution Details</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-my-none">
{{SITE_TITLE}} DCA Admin Extension
</h6>
<p>
Dollar Cost Averaging administration for Lamassu ATM integration. <br />
Manage client deposits and DCA distribution settings.
</p>
</q-card-section>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list>
<q-expansion-item
group="api"
icon="info"
label="DCA System Status"
:content-inset-level="0.5"
>
<q-card-section class="text-caption">
<div class="row">
<div class="col-6">Active Clients:</div>
<div class="col-6">${ dcaClients.filter(c => c.status === 'active').length }</div>
</div>
<div class="row">
<div class="col-6">Pending Deposits:</div>
<div class="col-6">${ deposits.filter(d => d.status === 'pending').length }</div>
</div>
<div class="row">
<div class="col-6">Total DCA Balance:</div>
<div class="col-6">${ formatCurrency(totalDcaBalance) }</div>
</div>
</q-card-section>
</q-expansion-item>
<q-separator></q-separator>
<q-expansion-item
group="api"
icon="settings"
label="Lamassu Database Config"
:content-inset-level="0.5"
>
<q-card-section class="text-caption">
<div v-if="lamassuConfig">
<p><strong>Database:</strong> ${ lamassuConfig.host }:${ lamassuConfig.port }/${ lamassuConfig.database_name }</p>
<p><strong>Status:</strong>
<q-badge v-if="lamassuConfig.test_connection_success === true" color="green">Connected</q-badge>
<q-badge v-else-if="lamassuConfig.test_connection_success === false" color="red">Failed</q-badge>
<q-badge v-else color="grey">Not tested</q-badge>
</p>
<p><strong>Last Poll:</strong> ${ lamassuConfig.last_poll_time ? formatDateTime(lamassuConfig.last_poll_time) : 'Not yet run' }</p>
<p><strong>Last Success:</strong> ${ lamassuConfig.last_successful_poll ? formatDateTime(lamassuConfig.last_successful_poll) : 'Never' }</p>
</div>
<div v-else>
<p><strong>Status:</strong> <q-badge color="orange">Not configured</q-badge></p>
</div>
<div class="q-mt-md">
<q-btn
size="sm"
color="primary"
@click="configDialog.show = true"
icon="settings"
>
Configure Database
</q-btn>
<q-btn
v-if="lamassuConfig"
size="sm"
color="accent"
@click="testDatabaseConnection"
:loading="testingConnection"
class="q-ml-sm"
>
Test Connection
</q-btn>
<q-btn
v-if="lamassuConfig"
size="sm"
color="secondary"
@click="manualPoll"
:loading="runningManualPoll"
class="q-ml-sm"
>
Manual Poll
</q-btn>
<q-btn
v-if="lamassuConfig"
size="sm"
color="deep-orange"
@click="openManualTransactionDialog"
class="q-ml-sm"
icon="build"
>
<q-tooltip>Process specific transaction by ID (bypasses dispense checks)</q-tooltip>
Manual TX
</q-btn>
</div>
</q-card-section>
</q-expansion-item>
<q-separator></q-separator>
{% include "satmachineadmin/_api_docs.html" %}
</q-list>
</q-card-section>
</q-card>
</div>
<!--/////////////////////////////////////////////////-->
<!--//////////////DEPOSIT FORM DIALOG////////////////-->
<!--/////////////////////////////////////////////////-->
<q-dialog v-model="depositFormDialog.show" position="top" @hide="closeDepositFormDialog">
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
<q-form @submit="sendDepositData" class="q-gutter-md">
<div v-if="depositFormDialog.data.client_name" class="text-h6 q-mb-md">
Deposit for: ${ depositFormDialog.data.client_name }
</div>
<q-input
filled
dense
type="number"
step="0.01"
v-model.number="depositFormDialog.data.amount"
label="Deposit Amount (GTQ) *"
placeholder="150.00"
hint="Enter amount in GTQ (e.g., 150.00)"
></q-input>
<q-select
filled
dense
emit-value
v-model="depositFormDialog.data.currency"
:options="currencyOptions"
label="Currency"
></q-select>
<q-input
filled
dense
type="textarea"
v-model.trim="depositFormDialog.data.notes"
label="Notes"
placeholder="Optional notes about this deposit"
rows="3"
></q-input>
<div class="row q-mt-lg">
<q-btn
v-if="depositFormDialog.data.id"
unelevated
color="primary"
type="submit"
>Update Deposit</q-btn
>
<q-btn
v-else
unelevated
color="primary"
:disable="!depositFormDialog.data.amount"
type="submit"
>Create Deposit</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card>
</q-dialog>
<!--/////////////////////////////////////////////////-->
<!--//////////////CLIENT DETAILS DIALOG//////////////-->
<!--/////////////////////////////////////////////////-->
<q-dialog v-model="clientDetailsDialog.show" position="top">
<q-card class="q-pa-lg" style="width: 600px; max-width: 90vw">
<div class="text-h6 q-mb-md">Client Details</div>
<div v-if="clientDetailsDialog.data">
<q-list>
<q-item v-if="clientDetailsDialog.data.username">
<q-item-section>
<q-item-label caption>Username</q-item-label>
<q-item-label>${ clientDetailsDialog.data.username }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>User ID</q-item-label>
<q-item-label>${ clientDetailsDialog.data.user_id }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Wallet ID</q-item-label>
<q-item-label>${ clientDetailsDialog.data.wallet_id }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>DCA Mode</q-item-label>
<q-item-label>${ clientDetailsDialog.data.dca_mode }</q-item-label>
</q-item-section>
</q-item>
<q-item v-if="clientDetailsDialog.data.fixed_mode_daily_limit">
<q-item-section>
<q-item-label caption>Daily Limit</q-item-label>
<q-item-label>${ formatCurrency(clientDetailsDialog.data.fixed_mode_daily_limit) }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Balance Summary</q-item-label>
<q-item-label v-if="clientDetailsDialog.balance">
Deposits: ${ formatCurrency(clientDetailsDialog.balance.total_deposits) } |
Payments: ${ formatCurrency(clientDetailsDialog.balance.total_payments) } |
Remaining: ${ formatCurrency(clientDetailsDialog.balance.remaining_balance) }
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</div>
<div class="row q-mt-lg">
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
<!--/////////////////////////////////////////////////-->
<!--//////////////LAMASSU CONFIG DIALOG//////////////-->
<!--/////////////////////////////////////////////////-->
<q-dialog v-model="configDialog.show" position="top" @hide="closeConfigDialog">
<q-card class="q-pa-lg q-pt-xl" style="width: 600px; max-width: 90vw">
<div class="text-h6 q-mb-md">Lamassu Database Configuration</div>
<q-form @submit="saveConfiguration" class="q-gutter-md">
<q-input
filled
dense
v-model.trim="configDialog.data.host"
label="Database Host *"
placeholder="e.g., localhost or 192.168.1.100"
hint="Hostname or IP address of the Lamassu Postgres server"
></q-input>
<q-input
filled
dense
type="number"
v-model.number="configDialog.data.port"
label="Database Port *"
placeholder="5432"
hint="Postgres port (usually 5432)"
></q-input>
<q-input
filled
dense
v-model.trim="configDialog.data.database_name"
label="Database Name *"
placeholder="lamassu"
hint="Name of the Lamassu database"
></q-input>
<q-input
filled
dense
v-model.trim="configDialog.data.username"
label="Username *"
placeholder="postgres"
hint="Database username with read access"
></q-input>
<q-input
filled
dense
type="password"
v-model.trim="configDialog.data.password"
label="Password *"
placeholder="Enter database password"
hint="Database password"
></q-input>
<q-separator class="q-my-md"></q-separator>
<div class="text-h6 q-mb-md">DCA Source Wallet</div>
<q-select
filled
dense
:options="g.user.wallets"
v-model="configDialog.data.selectedWallet"
label="Source Wallet for DCA Distributions *"
option-label="name"
hint="Wallet that holds Bitcoin for distribution to DCA clients"
></q-select>
<q-select
filled
dense
:options="g.user.wallets"
v-model="configDialog.data.selectedCommissionWallet"
label="Commission Wallet (Optional)"
option-label="name"
hint="Wallet where commission earnings will be sent (leave empty to keep in source wallet)"
></q-select>
<q-separator class="q-my-md"></q-separator>
<div class="text-h6 q-mb-md">DCA Client Limits</div>
<q-input
filled
dense
type="number"
v-model.number="configDialog.data.max_daily_limit_gtq"
label="Maximum Daily Limit (GTQ) *"
placeholder="2000"
hint="Maximum daily purchase limit that Fixed Mode clients can set"
:rules="[
val => val && val > 0 || 'Maximum daily limit is required',
val => val <= 10000 || 'Maximum daily limit cannot exceed 10,000 GTQ'
]"
></q-input>
<q-separator class="q-my-md"></q-separator>
<div class="text-h6 q-mb-md">SSH Tunnel (Recommended)</div>
<div class="row items-center q-mb-md">
<q-toggle
v-model="configDialog.data.use_ssh_tunnel"
color="primary"
@click.stop
/>
<span class="q-ml-sm">Use SSH Tunnel</span>
</div>
<div v-if="configDialog.data.use_ssh_tunnel" class="q-mt-md" @click.stop>
<q-input
filled
dense
v-model.trim="configDialog.data.ssh_host"
label="SSH Host *"
placeholder="e.g., your-server.com or 192.168.1.100"
hint="SSH server hostname or IP address"
@click.stop
></q-input>
<q-input
filled
dense
type="number"
v-model.number="configDialog.data.ssh_port"
label="SSH Port *"
placeholder="22"
hint="SSH port (usually 22)"
@click.stop
></q-input>
<q-input
filled
dense
v-model.trim="configDialog.data.ssh_username"
label="SSH Username *"
placeholder="ubuntu"
hint="SSH username"
@click.stop
></q-input>
<q-input
filled
dense
type="password"
v-model.trim="configDialog.data.ssh_password"
label="SSH Password"
placeholder="SSH password (if not using key)"
hint="SSH password or leave empty to use private key"
@click.stop
></q-input>
<q-input
filled
dense
type="textarea"
v-model.trim="configDialog.data.ssh_private_key"
label="SSH Private Key"
placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"
hint="SSH private key content (alternative to password)"
rows="4"
@click.stop
></q-input>
<q-banner class="bg-green-1 text-green-9 q-mt-md">
<template v-slot:avatar>
<q-icon name="security" color="green" />
</template>
SSH tunneling keeps your database secure by avoiding direct internet exposure.
The database connection will be routed through the SSH server.
</q-banner>
</div>
<q-banner v-if="!configDialog.data.id" class="bg-blue-1 text-blue-9">
<template v-slot:avatar>
<q-icon name="info" color="blue" />
</template>
This configuration will be securely stored and used for hourly polling.
Only read access to the Lamassu database is required.
</q-banner>
<div class="row q-mt-lg">
<q-btn
unelevated
color="primary"
type="submit"
:disable="!isConfigFormValid"
@click.stop
>Save Configuration</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto" @click.stop
>Cancel</q-btn
>
</div>
</q-form>
</q-card>
</q-dialog>
<!--/////////////////////////////////////////////////-->
<!--//////////////TRANSACTION DISTRIBUTIONS DIALOG////-->
<!--/////////////////////////////////////////////////-->
<q-dialog v-model="distributionDialog.show" position="top" maximized>
<q-card class="q-pa-lg">
<div class="text-h6 q-mb-md">Transaction Distribution Details</div>
<div v-if="distributionDialog.transaction" class="q-mb-lg">
<q-list>
<q-item>
<q-item-section>
<q-item-label caption>Lamassu Transaction ID</q-item-label>
<q-item-label>${ distributionDialog.transaction.lamassu_transaction_id }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Transaction Time</q-item-label>
<q-item-label>${ formatDateTime(distributionDialog.transaction.transaction_time) }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Total Amount</q-item-label>
<q-item-label>
${ formatCurrency(distributionDialog.transaction.fiat_amount) }
(${ formatSats(distributionDialog.transaction.crypto_amount) })
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Commission</q-item-label>
<q-item-label>
${ (distributionDialog.transaction.commission_percentage * 100).toFixed(1) }%
<span v-if="distributionDialog.transaction.discount > 0">
(with ${ distributionDialog.transaction.discount }% discount = ${ (distributionDialog.transaction.effective_commission * 100).toFixed(1) }% effective)
</span>
= ${ formatSats(distributionDialog.transaction.commission_amount_sats) }
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Available for Distribution</q-item-label>
<q-item-label>${ formatSats(distributionDialog.transaction.base_amount_sats) }</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>Total Distributed</q-item-label>
<q-item-label>${ formatSats(distributionDialog.transaction.distributions_total_sats) } to ${ distributionDialog.transaction.clients_count } clients</q-item-label>
</q-item-section>
</q-item>
</q-list>
</div>
<q-separator class="q-my-md"></q-separator>
<div class="text-h6 q-mb-md">Client Distributions</div>
<q-table
dense
flat
:rows="distributionDialog.distributions"
row-key="payment_id"
:columns="distributionDetailsTable.columns"
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.field == 'client_username'">${ col.value || 'No username' }</div>
<div v-else-if="col.field == 'client_user_id'">${ col.value.substring(0, 8) }...</div>
<div v-else-if="col.field == 'amount_sats'">${ formatSats(col.value) }</div>
<div v-else-if="col.field == 'amount_fiat'">${ formatCurrency(col.value) }</div>
<div v-else-if="col.field == 'exchange_rate'">${ col.value.toLocaleString() }</div>
<div v-else-if="col.field == 'status'">
<q-badge :color="col.value === 'confirmed' ? 'green' : col.value === 'failed' ? 'red' : 'orange'">
${ col.value }
</q-badge>
</div>
<div v-else-if="col.field == 'created_at'">${ formatDateTime(col.value) }</div>
<div v-else>${ col.value || '-' }</div>
</q-td>
</q-tr>
</template>
</q-table>
<div class="row q-mt-lg">
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
<!--/////////////////////////////////////////////////-->
<!--//////////////MANUAL TRANSACTION DIALOG///////////-->
<!--/////////////////////////////////////////////////-->
<q-dialog v-model="manualTransactionDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl" style="width: 500px; max-width: 90vw">
<div class="text-h6 q-mb-md">Process Specific Transaction</div>
<q-banner class="bg-orange-1 text-orange-9 q-mb-md">
<template v-slot:avatar>
<q-icon name="warning" color="orange" />
</template>
<div class="text-caption">
<strong>Use with caution:</strong> This bypasses all dispense status checks and will process the transaction even if dispense_confirmed is false. Only use this for manually settled transactions.
</div>
</q-banner>
<q-form @submit="processSpecificTransaction" class="q-gutter-md">
<q-input
filled
dense
v-model.trim="manualTransactionDialog.transactionId"
label="Lamassu Transaction ID *"
placeholder="e.g., 82746dfb-674d-4d7e-b008-7507caa02773"
hint="Enter the transaction/session ID from Lamassu database"
>
<template v-slot:prepend>
<q-icon name="receipt" />
</template>
</q-input>
<div class="text-caption text-grey-7">
This will:
<ul class="q-my-sm">
<li>Fetch the transaction from Lamassu regardless of dispense status</li>
<li>Process it through the normal DCA distribution flow</li>
<li>Credit the source wallet and distribute to clients</li>
<li>Send commission to the commission wallet (if configured)</li>
</ul>
</div>
<div class="row q-mt-lg">
<q-btn
unelevated
color="deep-orange"
type="submit"
:loading="processingSpecificTransaction"
:disable="!manualTransactionDialog.transactionId"
>
Process Transaction
</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">
Cancel
</q-btn>
</div>
</q-form>
</q-card>
</q-dialog>
</div>
{% endblock %}