Optimize TransactionsPage for mobile view

Dramatically reduced wasted space and improved mobile UX by:

- **Compact header**: Moved refresh button inline with title, similar to NostrFeed
- **Compact controls**: All day filter buttons now on one row with Calendar icon
- **Removed nested cards**: Eliminated Card wrapper around transactions list
- **Full-width layout**: Transactions now use full screen width on mobile (border-b) and rounded cards on desktop (md:border md:rounded-lg)
- **Consistent padding**: Uses px-0 on mobile, px-4 on desktop, matching NostrFeed patterns
- **Reduced vertical space**: Compacted header section to about half the original height
- **Cleaner imports**: Removed unused Card, CardContent, CardHeader, CardTitle, CardDescription, and Separator components

Layout now follows NostrFeed's mobile-optimized patterns with max-w-3xl container and responsive spacing.

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-11-14 16:39:32 +01:00
parent 9c4b14f382
commit 557d7ecacc

View file

@ -8,10 +8,8 @@ import type { Transaction } from '../types'
import type { FuzzySearchOptions } from '@/composables/useFuzzySearch'
import FuzzySearch from '@/components/ui/fuzzy-search/FuzzySearch.vue'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import {
CheckCircle2,
Clock,
@ -177,39 +175,36 @@ onMounted(() => {
</script>
<template>
<div class="container mx-auto p-4 max-w-4xl">
<!-- Header -->
<div class="mb-6">
<h1 class="text-2xl sm:text-3xl font-bold mb-2">My Transactions</h1>
<p class="text-muted-foreground">View your recent transaction history</p>
<div class="flex flex-col">
<!-- Compact Header -->
<div class="flex flex-col gap-3 p-4 md:p-6 border-b md:bg-card/50 md:backdrop-blur-sm">
<div class="w-full max-w-3xl mx-auto">
<div class="flex items-center justify-between mb-3">
<div>
<h1 class="text-lg md:text-xl font-bold">My Transactions</h1>
<p class="text-xs md:text-sm text-muted-foreground">View your recent transaction history</p>
</div>
<Button
variant="outline"
size="sm"
@click="loadTransactions"
:disabled="isLoading"
class="gap-2 md:h-10 md:px-4 hover:bg-accent transition-colors"
>
<RefreshCw :class="{ 'animate-spin': isLoading }" class="h-4 w-4" />
<span class="hidden md:inline">Refresh</span>
</Button>
</div>
<!-- Search Bar -->
<div class="mb-4">
<FuzzySearch
:data="transactions"
:options="searchOptions"
placeholder="Search transactions by description, payee, reference..."
@results="handleSearchResults"
/>
</div>
<!-- Controls -->
<Card class="mb-4">
<CardContent class="pt-6">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<!-- Day Filter -->
<div class="space-y-2">
<div class="text-sm text-muted-foreground flex items-center gap-2">
<Calendar class="h-4 w-4" />
<span>Show transactions from:</span>
</div>
<div class="flex flex-wrap gap-2">
<!-- Compact Controls Row -->
<div class="flex items-center gap-2 flex-wrap">
<Calendar class="h-4 w-4 text-muted-foreground" />
<Button
v-for="option in dayOptions"
:key="option.value"
:variant="selectedDays === option.value ? 'default' : 'outline'"
size="sm"
class="h-8 px-3 text-xs"
@click="changeDayFilter(option.value)"
:disabled="isLoading"
>
@ -217,51 +212,52 @@ onMounted(() => {
</Button>
</div>
</div>
<!-- Refresh Button -->
<Button variant="outline" size="sm" @click="loadTransactions" :disabled="isLoading">
<RefreshCw class="h-4 w-4" :class="{ 'animate-spin': isLoading }" />
</Button>
</div>
</CardContent>
</Card>
<!-- Transactions List -->
<Card>
<CardHeader>
<CardTitle>Recent Transactions</CardTitle>
<CardDescription>
<!-- Content Container -->
<div class="w-full max-w-3xl mx-auto px-0 md:px-4">
<!-- Search Bar -->
<div class="px-4 md:px-0 py-3">
<FuzzySearch
:data="transactions"
:options="searchOptions"
placeholder="Search transactions..."
@results="handleSearchResults"
/>
</div>
<!-- Results Count -->
<div class="px-4 md:px-0 py-2 text-xs md:text-sm text-muted-foreground">
<span v-if="searchResults.length > 0">
Found {{ transactionsToDisplay.length }} matching transaction{{ transactionsToDisplay.length === 1 ? '' : 's' }}
</span>
<span v-else>
Showing {{ pagination.offset + 1 }} -
{{ Math.min(pagination.offset + pagination.limit, pagination.total) }} of
{{ pagination.total }} transactions
Showing {{ pagination.offset + 1 }} - {{ Math.min(pagination.offset + pagination.limit, pagination.total) }} of {{ pagination.total }} transactions
</span>
</CardDescription>
</CardHeader>
<CardContent>
</div>
<!-- Loading State -->
<div v-if="isLoading" class="text-center py-12">
<RefreshCw class="h-8 w-8 animate-spin mx-auto mb-4 text-muted-foreground" />
<p class="text-muted-foreground">Loading transactions...</p>
<div v-if="isLoading" class="flex items-center justify-center py-12">
<div class="flex items-center gap-2">
<RefreshCw class="h-4 w-4 animate-spin" />
<span class="text-muted-foreground">Loading transactions...</span>
</div>
</div>
<!-- Empty State -->
<div v-else-if="transactionsToDisplay.length === 0" class="text-center py-12">
<div v-else-if="transactionsToDisplay.length === 0" class="text-center py-12 px-4">
<p class="text-muted-foreground">No transactions found</p>
<p class="text-sm text-muted-foreground mt-2">
{{ searchResults.length > 0 ? 'Try a different search term' : 'Try selecting a different time period' }}
</p>
</div>
<!-- Transaction Items (Mobile-Optimized) -->
<div v-else class="space-y-3">
<!-- Transaction Items (Full-width on mobile, no nested cards) -->
<div v-else class="md:space-y-3 md:py-4">
<div
v-for="(transaction, index) in transactionsToDisplay"
v-for="transaction in transactionsToDisplay"
:key="transaction.id"
class="border rounded-lg p-4 hover:bg-muted/50 transition-colors"
class="border-b md:border md:rounded-lg p-4 hover:bg-muted/50 transition-colors"
>
<!-- Transaction Header -->
<div class="flex items-start justify-between gap-3 mb-2">
@ -296,7 +292,7 @@ onMounted(() => {
</div>
</div>
<!-- Transaction Details (Collapsible on mobile) -->
<!-- Transaction Details -->
<div class="space-y-1 text-xs sm:text-sm">
<!-- Payee -->
<div v-if="transaction.payee" class="text-muted-foreground">
@ -325,16 +321,12 @@ onMounted(() => {
<span class="text-xs">Source: {{ transaction.meta.source }}</span>
</div>
</div>
<!-- Separator between items (except last) -->
<Separator v-if="index < transactionsToDisplay.length - 1" class="mt-3" />
</div>
</div>
<!-- Pagination (hide when searching) -->
<div
v-if="!isLoading && transactions.length > 0 && searchResults.length === 0 && (pagination.has_next || pagination.has_prev)"
class="flex items-center justify-between mt-6 pt-4 border-t"
class="flex items-center justify-between px-4 md:px-0 py-6 border-t"
>
<Button
variant="outline"
@ -360,7 +352,12 @@ onMounted(() => {
<ChevronRight class="h-4 w-4 ml-1" />
</Button>
</div>
</CardContent>
</Card>
<!-- End of list indicator -->
<div v-if="transactionsToDisplay.length > 0" class="text-center py-6 text-md text-muted-foreground">
<p>🐢</p>
</div>
</div>
</div>
</div>
</template>