fix(machine): responsive mobile layout for cash-in/cash-out views

- Push header below fixed status badges on mobile (pt-12)
- Use w-full max-w-* instead of fixed widths so cards fit mobile screens
- Make main content scrollable with overflow-auto
- Reduce padding on mobile (p-4 vs p-8)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-02-20 10:31:22 -05:00
commit b6b544d45a
2 changed files with 56 additions and 25 deletions

View file

@ -189,16 +189,18 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
<template> <template>
<div class="flex flex-1 flex-col"> <div class="flex flex-1 flex-col">
<!-- Header --> <!-- Header -->
<header class="flex items-center bg-black/30 px-8 py-4"> <header class="flex items-center bg-black/30 px-4 pb-3 pt-12 sm:px-8 sm:py-4">
<Button variant="ghost" class="text-muted-foreground" @click="cancel">← Cancel</Button> <Button variant="ghost" class="text-muted-foreground" @click="cancel">← Cancel</Button>
<h2 class="flex-1 text-center text-xl font-semibold">Buy Bitcoin</h2> <h2 class="flex-1 text-center text-lg font-semibold sm:text-xl">Buy Bitcoin</h2>
<div class="w-20"></div> <div class="w-20"></div>
</header> </header>
<!-- Content --> <!-- Content -->
<main class="flex flex-1 items-center justify-center p-8"> <main
class="flex min-h-0 flex-1 items-start justify-center overflow-auto p-4 sm:items-center sm:p-8"
>
<!-- Fetching Rate --> <!-- Fetching Rate -->
<Card v-if="nestedState === 'fetchingRate'" class="w-80 border-0 bg-white/5"> <Card v-if="nestedState === 'fetchingRate'" class="w-full max-w-80 border-0 bg-white/5">
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<Skeleton class="h-10 w-10 rounded-full" /> <Skeleton class="h-10 w-10 rounded-full" />
<p class="text-muted-foreground">Fetching exchange rate...</p> <p class="text-muted-foreground">Fetching exchange rate...</p>
@ -206,7 +208,10 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Inserting Bills --> <!-- Inserting Bills -->
<Card v-else-if="nestedState === 'insertingBills'" class="w-96 border-0 bg-white/5"> <Card
v-else-if="nestedState === 'insertingBills'"
class="w-full max-w-96 border-0 bg-white/5"
>
<CardHeader class="text-center"> <CardHeader class="text-center">
<CardTitle>Insert Cash</CardTitle> <CardTitle>Insert Cash</CardTitle>
<CardDescription v-if="context"> <CardDescription v-if="context">
@ -282,7 +287,10 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Generating NDebit --> <!-- Generating NDebit -->
<Card v-else-if="nestedState === 'generatingNdebit'" class="w-80 border-0 bg-white/5"> <Card
v-else-if="nestedState === 'generatingNdebit'"
class="w-full max-w-80 border-0 bg-white/5"
>
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<Skeleton class="h-10 w-10 rounded-full" /> <Skeleton class="h-10 w-10 rounded-full" />
<p class="text-muted-foreground">Preparing payment code...</p> <p class="text-muted-foreground">Preparing payment code...</p>
@ -290,7 +298,10 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Display QR (unified lightning + clink) --> <!-- Display QR (unified lightning + clink) -->
<Card v-else-if="nestedState === 'displayingQR'" class="w-[420px] border-0 bg-white/5"> <Card
v-else-if="nestedState === 'displayingQR'"
class="w-full max-w-[420px] border-0 bg-white/5"
>
<CardContent class="pt-6 space-y-5"> <CardContent class="pt-6 space-y-5">
<!-- Amount Display --> <!-- Amount Display -->
<div class="text-center"> <div class="text-center">
@ -455,7 +466,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Ask for Receipt --> <!-- Ask for Receipt -->
<Card v-else-if="nestedState === 'askForReceipt'" class="w-80 border-0 bg-white/5"> <Card v-else-if="nestedState === 'askForReceipt'" class="w-full max-w-80 border-0 bg-white/5">
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<div class="text-6xl text-green-400">✅</div> <div class="text-6xl text-green-400">✅</div>
<CardTitle>Payment Sent!</CardTitle> <CardTitle>Payment Sent!</CardTitle>
@ -468,7 +479,10 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Sending Receipt --> <!-- Sending Receipt -->
<Card v-else-if="nestedState === 'sendingReceipt'" class="w-80 border-0 bg-white/5"> <Card
v-else-if="nestedState === 'sendingReceipt'"
class="w-full max-w-80 border-0 bg-white/5"
>
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<Skeleton class="h-10 w-10 rounded-full" /> <Skeleton class="h-10 w-10 rounded-full" />
<p class="text-muted-foreground">Sending receipt...</p> <p class="text-muted-foreground">Sending receipt...</p>
@ -476,7 +490,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Complete --> <!-- Complete -->
<Card v-else-if="nestedState === 'complete'" class="w-80 border-0 bg-green-900/30"> <Card v-else-if="nestedState === 'complete'" class="w-full max-w-80 border-0 bg-green-900/30">
<CardContent class="flex flex-col items-center gap-4 p-8 text-center"> <CardContent class="flex flex-col items-center gap-4 p-8 text-center">
<div class="text-6xl">🎉</div> <div class="text-6xl">🎉</div>
<CardTitle class="text-green-400">Transaction Complete!</CardTitle> <CardTitle class="text-green-400">Transaction Complete!</CardTitle>
@ -489,7 +503,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Error --> <!-- Error -->
<Card v-else-if="nestedState === 'error'" class="w-80 border-0 bg-red-900/30"> <Card v-else-if="nestedState === 'error'" class="w-full max-w-80 border-0 bg-red-900/30">
<CardContent class="flex flex-col items-center gap-4 p-8 text-center"> <CardContent class="flex flex-col items-center gap-4 p-8 text-center">
<div class="text-6xl">⚠️</div> <div class="text-6xl">⚠️</div>
<CardTitle class="text-red-400">Error</CardTitle> <CardTitle class="text-red-400">Error</CardTitle>
@ -501,7 +515,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice || atmStore.isReque
</Card> </Card>
<!-- Fallback --> <!-- Fallback -->
<Card v-else class="w-80 border-0 bg-white/5"> <Card v-else class="w-full max-w-80 border-0 bg-white/5">
<CardContent class="p-8 text-center"> <CardContent class="p-8 text-center">
<p class="text-muted-foreground">State: {{ nestedState }}</p> <p class="text-muted-foreground">State: {{ nestedState }}</p>
</CardContent> </CardContent>

View file

@ -98,16 +98,18 @@ function formatFiat(cents: number): string {
<template> <template>
<div class="flex flex-1 flex-col"> <div class="flex flex-1 flex-col">
<!-- Header --> <!-- Header -->
<header class="flex items-center bg-black/30 px-8 py-4"> <header class="flex items-center bg-black/30 px-4 pb-3 pt-12 sm:px-8 sm:py-4">
<Button variant="ghost" class="text-muted-foreground" @click="cancel">← Cancel</Button> <Button variant="ghost" class="text-muted-foreground" @click="cancel">← Cancel</Button>
<h2 class="flex-1 text-center text-xl font-semibold">Sell Bitcoin</h2> <h2 class="flex-1 text-center text-lg font-semibold sm:text-xl">Sell Bitcoin</h2>
<div class="w-20"></div> <div class="w-20"></div>
</header> </header>
<!-- Content --> <!-- Content -->
<main class="flex flex-1 items-center justify-center p-8"> <main
class="flex min-h-0 flex-1 items-start justify-center overflow-auto p-4 sm:items-center sm:p-8"
>
<!-- Fetching Rate --> <!-- Fetching Rate -->
<Card v-if="nestedState === 'fetchingRate'" class="w-80 border-0 bg-white/5"> <Card v-if="nestedState === 'fetchingRate'" class="w-full max-w-80 border-0 bg-white/5">
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<Skeleton class="h-10 w-10 rounded-full" /> <Skeleton class="h-10 w-10 rounded-full" />
<p class="text-muted-foreground">Fetching exchange rate...</p> <p class="text-muted-foreground">Fetching exchange rate...</p>
@ -115,7 +117,10 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Selecting Amount - Main cash-out screen --> <!-- Selecting Amount - Main cash-out screen -->
<Card v-else-if="nestedState === 'selectingAmount'" class="w-[520px] border-0 bg-white/5"> <Card
v-else-if="nestedState === 'selectingAmount'"
class="w-full max-w-[520px] border-0 bg-white/5"
>
<CardHeader class="text-center"> <CardHeader class="text-center">
<CardTitle>Select Cash Amount</CardTitle> <CardTitle>Select Cash Amount</CardTitle>
<CardDescription v-if="context"> <CardDescription v-if="context">
@ -198,7 +203,10 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Generating Invoice --> <!-- Generating Invoice -->
<Card v-else-if="nestedState === 'generatingInvoice'" class="w-80 border-0 bg-white/5"> <Card
v-else-if="nestedState === 'generatingInvoice'"
class="w-full max-w-80 border-0 bg-white/5"
>
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<Skeleton class="h-10 w-10 rounded-full" /> <Skeleton class="h-10 w-10 rounded-full" />
<p class="text-muted-foreground">Creating invoice...</p> <p class="text-muted-foreground">Creating invoice...</p>
@ -206,7 +214,10 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Displaying Invoice - Show QR for payment --> <!-- Displaying Invoice - Show QR for payment -->
<Card v-else-if="nestedState === 'displayingInvoice'" class="w-[480px] border-0 bg-white/5"> <Card
v-else-if="nestedState === 'displayingInvoice'"
class="w-full max-w-[480px] border-0 bg-white/5"
>
<CardHeader class="text-center"> <CardHeader class="text-center">
<CardTitle>Scan to Pay</CardTitle> <CardTitle>Scan to Pay</CardTitle>
<CardDescription v-if="context"> <CardDescription v-if="context">
@ -260,7 +271,10 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Dispensing Cash --> <!-- Dispensing Cash -->
<Card v-else-if="nestedState === 'dispensingCash'" class="w-80 border-0 bg-white/5"> <Card
v-else-if="nestedState === 'dispensingCash'"
class="w-full max-w-80 border-0 bg-white/5"
>
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<Skeleton class="h-10 w-10 rounded-full" /> <Skeleton class="h-10 w-10 rounded-full" />
<p class="text-muted-foreground">Dispensing cash...</p> <p class="text-muted-foreground">Dispensing cash...</p>
@ -269,7 +283,10 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Cash Ready --> <!-- Cash Ready -->
<Card v-else-if="nestedState === 'waitingForCashTaken'" class="w-80 border-0 bg-green-900/30"> <Card
v-else-if="nestedState === 'waitingForCashTaken'"
class="w-full max-w-80 border-0 bg-green-900/30"
>
<CardContent class="flex flex-col items-center gap-4 p-8 text-center"> <CardContent class="flex flex-col items-center gap-4 p-8 text-center">
<div class="text-6xl text-green-400">✅</div> <div class="text-6xl text-green-400">✅</div>
<CardTitle class="text-green-400">Cash Ready!</CardTitle> <CardTitle class="text-green-400">Cash Ready!</CardTitle>
@ -288,7 +305,7 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Ask for Receipt --> <!-- Ask for Receipt -->
<Card v-else-if="nestedState === 'askForReceipt'" class="w-80 border-0 bg-white/5"> <Card v-else-if="nestedState === 'askForReceipt'" class="w-full max-w-80 border-0 bg-white/5">
<CardContent class="flex flex-col items-center gap-4 p-8"> <CardContent class="flex flex-col items-center gap-4 p-8">
<CardTitle>Would you like a receipt?</CardTitle> <CardTitle>Would you like a receipt?</CardTitle>
<p class="text-sm text-muted-foreground">Scan your npub to receive a Nostr DM receipt</p> <p class="text-sm text-muted-foreground">Scan your npub to receive a Nostr DM receipt</p>
@ -300,7 +317,7 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Complete --> <!-- Complete -->
<Card v-else-if="nestedState === 'complete'" class="w-80 border-0 bg-green-900/30"> <Card v-else-if="nestedState === 'complete'" class="w-full max-w-80 border-0 bg-green-900/30">
<CardContent class="flex flex-col items-center gap-4 p-8 text-center"> <CardContent class="flex flex-col items-center gap-4 p-8 text-center">
<div class="text-6xl">🎉</div> <div class="text-6xl">🎉</div>
<CardTitle class="text-green-400">Transaction Complete!</CardTitle> <CardTitle class="text-green-400">Transaction Complete!</CardTitle>
@ -311,7 +328,7 @@ function formatFiat(cents: number): string {
<!-- Error --> <!-- Error -->
<Card <Card
v-else-if="nestedState === 'error' || nestedState === 'dispenseError'" v-else-if="nestedState === 'error' || nestedState === 'dispenseError'"
class="w-80 border-0 bg-red-900/30" class="w-full max-w-80 border-0 bg-red-900/30"
> >
<CardContent class="flex flex-col items-center gap-4 p-8 text-center"> <CardContent class="flex flex-col items-center gap-4 p-8 text-center">
<div class="text-6xl">⚠️</div> <div class="text-6xl">⚠️</div>
@ -324,7 +341,7 @@ function formatFiat(cents: number): string {
</Card> </Card>
<!-- Fallback --> <!-- Fallback -->
<Card v-else class="w-80 border-0 bg-white/5"> <Card v-else class="w-full max-w-80 border-0 bg-white/5">
<CardContent class="p-8 text-center"> <CardContent class="p-8 text-center">
<p class="text-muted-foreground">State: {{ nestedState }}</p> <p class="text-muted-foreground">State: {{ nestedState }}</p>
</CardContent> </CardContent>