feat: Mobile optimization and UI improvements

- Add mobile hamburger menu in TopBar with slide-in panel
- Optimize all components for mobile (responsive fonts, spacing, touch targets)
- Add proper viewport meta tags and safe area padding
- Fix /jackpot/next API to return active cycles regardless of scheduled time
- Remove BTC display from jackpot pot (show sats only)
- Add setup/ folder to .gitignore
- Improve mobile UX: 16px inputs (no iOS zoom), 44px touch targets
- Add active states for touch feedback on buttons
This commit is contained in:
Michilis
2025-12-08 15:51:13 +00:00
parent 2fea2dc836
commit dd6b26c524
13 changed files with 432 additions and 170 deletions

View File

@@ -96,30 +96,30 @@ export default function TicketStatusPage() {
const { purchase, tickets, cycle, result } = data;
return (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl md:text-4xl font-bold mb-8 text-center text-white">
<div className="max-w-4xl mx-auto px-1">
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold mb-6 sm:mb-8 text-center text-white">
{STRINGS.ticket.title}
</h1>
{/* Save This Link */}
<div className="bg-gradient-to-r from-blue-900/30 to-purple-900/30 border border-blue-700/50 rounded-xl p-6 mb-6">
<div className="flex items-start gap-4">
<div className="text-3xl">🔖</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-white mb-2">Save This Link!</h3>
<p className="text-gray-300 text-sm mb-3">
<div className="bg-gradient-to-r from-blue-900/30 to-purple-900/30 border border-blue-700/50 rounded-xl p-4 sm:p-6 mb-5 sm:mb-6">
<div className="flex items-start gap-3 sm:gap-4">
<div className="text-2xl sm:text-3xl">🔖</div>
<div className="flex-1 min-w-0">
<h3 className="text-base sm:text-lg font-semibold text-white mb-1 sm:mb-2">Save This Link!</h3>
<p className="text-gray-300 text-xs sm:text-sm mb-3">
Bookmark or save this page to check if you've won after the draw. This is your only way to view your ticket status.
</p>
<div className="flex flex-col sm:flex-row gap-2">
<div className="flex-1 bg-gray-800/80 rounded-lg px-3 py-2 font-mono text-sm text-gray-300 break-all">
<div className="flex flex-col gap-2">
<div className="bg-gray-800/80 rounded-lg px-3 py-2 font-mono text-xs sm:text-sm text-gray-300 break-all overflow-x-auto">
{ticketUrl || `/tickets/${ticketId}`}
</div>
<button
onClick={copyLink}
className={`px-4 py-2 rounded-lg font-medium transition-all flex items-center justify-center gap-2 ${
className={`w-full sm:w-auto px-4 py-2.5 rounded-lg font-medium transition-all flex items-center justify-center gap-2 ${
copied
? 'bg-green-600 text-white'
: 'bg-blue-600 hover:bg-blue-500 text-white'
: 'bg-blue-600 hover:bg-blue-500 active:bg-blue-700 text-white'
}`}
>
{copied ? (
@@ -144,11 +144,11 @@ export default function TicketStatusPage() {
</div>
{/* Purchase Info */}
<div className="bg-gray-900 rounded-xl p-6 mb-6 border border-gray-800">
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="bg-gray-900 rounded-xl p-4 sm:p-6 mb-5 sm:mb-6 border border-gray-800">
<div className="grid grid-cols-2 gap-3 sm:gap-4 text-xs sm:text-sm">
<div>
<span className="text-gray-400">Purchase ID:</span>
<div className="text-white font-mono break-all">{purchase.id}</div>
<div className="text-white font-mono break-all text-xs sm:text-sm">{purchase.id}</div>
</div>
<div>
<span className="text-gray-400">Status:</span>
@@ -167,15 +167,15 @@ export default function TicketStatusPage() {
{/* Payment Status */}
{purchase.invoice_status === 'pending' && (
<div className="bg-yellow-900/30 text-yellow-200 px-6 py-4 rounded-lg mb-6 text-center">
<div className="bg-yellow-900/30 text-yellow-200 px-4 sm:px-6 py-3 sm:py-4 rounded-lg mb-5 sm:mb-6 text-center text-sm sm:text-base">
{STRINGS.ticket.waiting}
</div>
)}
{/* Tickets */}
{purchase.ticket_issue_status === 'issued' && (
<div className="bg-gray-900 rounded-xl p-6 mb-6 border border-gray-800">
<h2 className="text-xl font-semibold mb-4 text-gray-300">
<div className="bg-gray-900 rounded-xl p-4 sm:p-6 mb-5 sm:mb-6 border border-gray-800">
<h2 className="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 text-gray-300">
{STRINGS.ticket.ticketNumbers}
</h2>
<TicketList tickets={tickets} />
@@ -183,28 +183,30 @@ export default function TicketStatusPage() {
)}
{/* Draw Info */}
<div className="bg-gray-900 rounded-xl p-6 mb-6 border border-gray-800">
<h2 className="text-xl font-semibold mb-4 text-gray-300">
<div className="bg-gray-900 rounded-xl p-4 sm:p-6 mb-5 sm:mb-6 border border-gray-800">
<h2 className="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 text-gray-300">
Draw Information
</h2>
<div className="space-y-4">
<div className="space-y-3 sm:space-y-4">
<div>
<span className="text-gray-400">Draw Time:</span>
<div className="text-white">{formatDateTime(cycle.scheduled_at)}</div>
<span className="text-gray-400 text-sm">Draw Time:</span>
<div className="text-white text-sm sm:text-base">{formatDateTime(cycle.scheduled_at)}</div>
</div>
<div>
<span className="text-gray-400">Current Pot:</span>
<div className="text-2xl font-bold text-bitcoin-orange">
<span className="text-gray-400 text-sm">Current Pot:</span>
<div className="text-xl sm:text-2xl font-bold text-bitcoin-orange">
{cycle.pot_total_sats.toLocaleString()} sats
</div>
</div>
{cycle.status !== 'completed' && (
<div>
<span className="text-gray-400 block mb-2">Time Until Draw:</span>
<JackpotCountdown scheduledAt={cycle.scheduled_at} />
<span className="text-gray-400 text-sm block mb-2">Time Until Draw:</span>
<div className="overflow-x-auto scrollbar-hide">
<JackpotCountdown scheduledAt={cycle.scheduled_at} />
</div>
</div>
)}
</div>
@@ -212,14 +214,14 @@ export default function TicketStatusPage() {
{/* Results */}
{result.has_drawn && (
<div className="bg-gray-900 rounded-xl p-6 border border-gray-800">
<h2 className="text-xl font-semibold mb-4 text-gray-300">
<div className="bg-gray-900 rounded-xl p-4 sm:p-6 border border-gray-800">
<h2 className="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 text-gray-300">
Draw Results
</h2>
{result.is_winner ? (
<div>
<div className="bg-green-900/30 text-green-200 px-6 py-4 rounded-lg mb-4 text-center text-2xl font-bold">
<div className="bg-green-900/30 text-green-200 px-4 sm:px-6 py-3 sm:py-4 rounded-lg mb-4 text-center text-xl sm:text-2xl font-bold">
🎉 {STRINGS.ticket.congratulations}
</div>
{result.payout && (
@@ -231,10 +233,10 @@ export default function TicketStatusPage() {
</div>
) : (
<div>
<div className="bg-gray-800 px-6 py-4 rounded-lg mb-4 text-center">
<div className="text-gray-400 mb-2">{STRINGS.ticket.betterLuck}</div>
<div className="bg-gray-800 px-4 sm:px-6 py-3 sm:py-4 rounded-lg mb-4 text-center">
<div className="text-gray-400 mb-2 text-sm sm:text-base">{STRINGS.ticket.betterLuck}</div>
{cycle.winning_ticket_id && (
<div className="text-gray-300">
<div className="text-gray-300 text-sm sm:text-base">
{STRINGS.ticket.winningTicket}: <span className="font-bold text-bitcoin-orange">#{cycle.winning_ticket_id.substring(0, 8)}</span>
</div>
)}