Compare commits

..

3 commits

Author SHA1 Message Date
eefadb6c20 Merge pull request 'fix(ui): make admin dialogs mobile-friendly + add fullscreen QR zoom' (#40) from fix/mobile-friendly-dialogs-and-QR-zoom into main
Some checks failed
ci.yml / Merge pull request 'fix(ui): make admin dialogs mobile-friendly + add fullscreen QR zoom' (#40) from fix/mobile-friendly-dialogs-and-QR-zoom into main (push) Failing after 0s
Reviewed-on: #40
2026-07-02 18:56:04 +00:00
282d8e75c5 fix(ui): make the zoom QR actually fill the screen
Some checks failed
ci.yml / fix(ui): make the zoom QR actually fill the screen (pull_request) Failing after 0s
lnbits-qrcode has no `options` prop — it renders width:100% of its
parent, capped by a `max-width` PROP that defaults to 450px. So the
previous `:options="{width: qrZoom.size}"` was dead: the QR only ever
grew to its container, and inside the zoom dialog's items-center flex
column the container collapsed to the QR's natural width, so "enlarge"
barely changed anything.

Wrap the zoom QR in an explicit full-width div (capped 96vw), feed the
real `max-width` prop, hide the component's own copy/download buttons
for a clean scan target, and size it to ~full shorter-viewport-edge.
Also swap the inline pair-dialog QR's dead `:options` for `max-width`.
2026-07-02 19:58:05 +02:00
015208500a fix(ui): make spirekeeper dialogs mobile-friendly + add fullscreen QR zoom
Some checks failed
ci.yml / fix(ui): make spirekeeper dialogs mobile-friendly + add fullscreen QR zoom (pull_request) Failing after 0s
The `min-width: 480px` on dialog cards overrode `max-width: 95vw` on
narrow viewports (CSS min-width wins on conflict), so add-machine / pair
/ etc. dialogs overflowed off-screen on phones. Switch every dialog card
from minWidth to width so maxWidth can shrink it to fit.

Also cap the tall add-machine form section at 65vh with a scroll
container so all fields stay reachable on short screens.

For pairing, the seed-URL QR is now tappable and has an "Enlarge for
scanning" button opening a fullscreen maximized QR sized to the shorter
viewport edge — maximises visibility to the spire's scan camera.
2026-07-02 19:45:11 +02:00
2 changed files with 70 additions and 16 deletions

View file

@ -200,6 +200,13 @@ window.app = Vue.createApp({
durationHours: null, durationHours: null,
result: null result: null
}, },
// Fullscreen QR overlay — maximises a QR's on-screen size so a spire
// camera can lock onto it. openQrZoom() sizes it to the viewport.
qrZoom: {
show: false,
value: '',
size: 280
},
machineDetail: { machineDetail: {
show: false, show: false,
loading: false, loading: false,
@ -1603,6 +1610,18 @@ window.app = Vue.createApp({
}) })
}, },
// Open a fullscreen QR sized to nearly the full shorter viewport edge
// so it renders as large as possible for a spire's camera. The QR's
// wrapper caps at 96vw, so an over-estimate here is fine — it just
// means "fill the width". Feeds the lnbits-qrcode `max-width` prop.
openQrZoom(value) {
if (!value) return
const edge = Math.min(window.innerWidth, window.innerHeight)
this.qrZoom.size = Math.max(240, Math.floor(edge - 16))
this.qrZoom.value = value
this.qrZoom.show = true
},
_emptyDepositForm() { _emptyDepositForm() {
// currency is server-resolved from the selected client's machine // currency is server-resolved from the selected client's machine
// fiat_code (see #26); not stored on the form, just displayed in // fiat_code (see #26); not stored on the form, just displayed in

View file

@ -763,13 +763,13 @@
<!-- ADD MACHINE DIALOG --> <!-- ADD MACHINE DIALOG -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="addMachineDialog.show" persistent> <q-dialog v-model="addMachineDialog.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}"> <q-card :style="{width: '480px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Add bitSpire machine</div> <div class="text-h6">Add bitSpire machine</div>
<q-space ></q-space> <q-space ></q-space>
<q-btn icon="close" flat round dense v-close-popup></q-btn> <q-btn icon="close" flat round dense v-close-popup></q-btn>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section class="scroll" :style="{maxHeight: '65vh'}">
<p class="text-caption q-mb-md" :style="{opacity: 0.7}"> <p class="text-caption q-mb-md" :style="{opacity: 0.7}">
Register an ATM by its Nostr public key. Choose the LNbits wallet that Register an ATM by its Nostr public key. Choose the LNbits wallet that
will receive cash-out payments from this machine — settlements there will receive cash-out payments from this machine — settlements there
@ -847,7 +847,7 @@
<!-- PAIR SPIRE DIALOG — mint bunker key + one-shot seed URL (S0/#9) --> <!-- PAIR SPIRE DIALOG — mint bunker key + one-shot seed URL (S0/#9) -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="pairDialog.show" persistent> <q-dialog v-model="pairDialog.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}"> <q-card :style="{width: '480px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Pair spire</div> <div class="text-h6">Pair spire</div>
<q-space></q-space> <q-space></q-space>
@ -901,10 +901,16 @@
into <code>provision-atm</code>. Shown once — copy it now. into <code>provision-atm</code>. Shown once — copy it now.
</q-banner> </q-banner>
<div class="row justify-center q-mb-md"> <div class="column items-center q-mb-md">
<div class="cursor-pointer" :style="{width: '280px', maxWidth: '90vw'}"
@click="openQrZoom(pairDialog.result.seed_url)">
<lnbits-qrcode <lnbits-qrcode
:value="pairDialog.result.seed_url" :value="pairDialog.result.seed_url"
:options="{width: 280}"></lnbits-qrcode> :max-width="280"></lnbits-qrcode>
</div>
<q-btn flat dense color="primary" icon="zoom_out_map"
label="Enlarge for scanning" size="sm" class="q-mt-sm"
@click="openQrZoom(pairDialog.result.seed_url)"></q-btn>
</div> </div>
<q-input <q-input
@ -938,6 +944,35 @@
</q-card> </q-card>
</q-dialog> </q-dialog>
<!-- =============================================================== -->
<!-- QR ZOOM DIALOG — fullscreen QR to maximise scan visibility -->
<!-- =============================================================== -->
<q-dialog v-model="qrZoom.show" maximized>
<q-card class="bg-white text-grey-9 column no-wrap">
<q-bar class="bg-white">
<q-space></q-space>
<q-btn dense flat icon="close" color="grey-9" v-close-popup>
<q-tooltip>Close</q-tooltip>
</q-btn>
</q-bar>
<q-card-section
class="col column items-center justify-center q-pt-none">
<!-- lnbits-qrcode renders width:100% of its parent capped by the
max-width prop (NOT an `options` prop). Give it an explicit
full-width wrapper so it actually fills the screen. -->
<div :style="{width: qrZoom.size + 'px', maxWidth: '96vw'}">
<lnbits-qrcode
:value="qrZoom.value"
:max-width="qrZoom.size"
:show-buttons="false"></lnbits-qrcode>
</div>
<div class="text-caption text-center q-mt-md" :style="{opacity: 0.7}">
Point the spire's camera here — tap Close when scanned.
</div>
</q-card-section>
</q-card>
</q-dialog>
<!-- =============================================================== --> <!-- =============================================================== -->
<!-- MACHINE DETAIL DIALOG — settlements list + per-row actions --> <!-- MACHINE DETAIL DIALOG — settlements list + per-row actions -->
<!-- =============================================================== --> <!-- =============================================================== -->
@ -1212,7 +1247,7 @@
<!-- CASSETTE PUBLISH CONFIRM DIALOG --> <!-- CASSETTE PUBLISH CONFIRM DIALOG -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="cassettePublishConfirm.show" persistent> <q-dialog v-model="cassettePublishConfirm.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}"> <q-card :style="{width: '480px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Publish cassette config to ATM</div> <div class="text-h6">Publish cassette config to ATM</div>
<q-space ></q-space> <q-space ></q-space>
@ -1264,7 +1299,7 @@
<!-- PARTIAL-DISPENSE DIALOG --> <!-- PARTIAL-DISPENSE DIALOG -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="partialDispenseDialog.show" persistent> <q-dialog v-model="partialDispenseDialog.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}"> <q-card :style="{width: '480px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Apply partial dispense</div> <div class="text-h6">Apply partial dispense</div>
<q-space ></q-space> <q-space ></q-space>
@ -1323,7 +1358,7 @@
<!-- ADD-NOTE DIALOG --> <!-- ADD-NOTE DIALOG -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="noteDialog.show" persistent> <q-dialog v-model="noteDialog.show" persistent>
<q-card :style="{minWidth: '420px', maxWidth: '95vw'}"> <q-card :style="{width: '420px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Add note to settlement</div> <div class="text-h6">Add note to settlement</div>
<q-space ></q-space> <q-space ></q-space>
@ -1353,7 +1388,7 @@
<!-- SUPER-FEE EDIT DIALOG (super-only) --> <!-- SUPER-FEE EDIT DIALOG (super-only) -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="superFeeDialog.show" persistent> <q-dialog v-model="superFeeDialog.show" persistent>
<q-card :style="{minWidth: '460px', maxWidth: '95vw'}"> <q-card :style="{width: '460px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Platform fee (super-only)</div> <div class="text-h6">Platform fee (super-only)</div>
<q-space ></q-space> <q-space ></q-space>
@ -1400,7 +1435,7 @@
<!-- ADD / EDIT DEPOSIT DIALOG --> <!-- ADD / EDIT DEPOSIT DIALOG -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="depositDialog.show" persistent> <q-dialog v-model="depositDialog.show" persistent>
<q-card :style="{minWidth: '460px', maxWidth: '95vw'}"> <q-card :style="{width: '460px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6" <div class="text-h6"
v-text="depositDialog.mode === 'add' ? 'Record deposit' : 'Edit deposit'"></div> v-text="depositDialog.mode === 'add' ? 'Record deposit' : 'Edit deposit'"></div>
@ -1466,7 +1501,7 @@
<!-- REJECT DEPOSIT DIALOG (status update with notes) --> <!-- REJECT DEPOSIT DIALOG (status update with notes) -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="rejectDepositDialog.show" persistent> <q-dialog v-model="rejectDepositDialog.show" persistent>
<q-card :style="{minWidth: '420px', maxWidth: '95vw'}"> <q-card :style="{width: '420px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Reject deposit</div> <div class="text-h6">Reject deposit</div>
<q-space ></q-space> <q-space ></q-space>
@ -1495,7 +1530,7 @@
<!-- ADD / EDIT CLIENT DIALOGS --> <!-- ADD / EDIT CLIENT DIALOGS -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="clientDialog.show" persistent> <q-dialog v-model="clientDialog.show" persistent>
<q-card :style="{minWidth: '520px', maxWidth: '95vw'}"> <q-card :style="{width: '520px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6" v-text="clientDialog.mode === 'add' ? 'Register liquidity provider' : 'Edit LP'"></div> <div class="text-h6" v-text="clientDialog.mode === 'add' ? 'Register liquidity provider' : 'Edit LP'"></div>
<q-space ></q-space> <q-space ></q-space>
@ -1552,7 +1587,7 @@
<!-- SETTLE BALANCE DIALOG (closes spirekeeper#2) --> <!-- SETTLE BALANCE DIALOG (closes spirekeeper#2) -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="settleBalanceDialog.show" persistent> <q-dialog v-model="settleBalanceDialog.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}"> <q-card :style="{width: '480px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Settle LP balance</div> <div class="text-h6">Settle LP balance</div>
<q-space ></q-space> <q-space ></q-space>
@ -1611,7 +1646,7 @@
<!-- EDIT MACHINE DIALOG (same fields; PUT instead of POST) --> <!-- EDIT MACHINE DIALOG (same fields; PUT instead of POST) -->
<!-- =============================================================== --> <!-- =============================================================== -->
<q-dialog v-model="editMachineDialog.show" persistent> <q-dialog v-model="editMachineDialog.show" persistent>
<q-card :style="{minWidth: '480px', maxWidth: '95vw'}"> <q-card :style="{width: '480px', maxWidth: '95vw'}">
<q-card-section class="row items-center q-pb-none"> <q-card-section class="row items-center q-pb-none">
<div class="text-h6">Edit machine</div> <div class="text-h6">Edit machine</div>
<q-space ></q-space> <q-space ></q-space>