Refactor MarketPage layout for improved mobile responsiveness and user experience

- Optimized the Market Header and Fuzzy Search components for better mobile display, enhancing usability on smaller screens.
- Adjusted spacing and font sizes for various elements to ensure a cohesive look across devices.
- Improved the active filters summary and category filters for better accessibility and visual clarity.

These changes enhance the overall user experience by providing a more responsive and visually appealing interface in the MarketPage.
This commit is contained in:
padreug 2025-09-26 10:33:49 +02:00
parent f2a432b6df
commit 8fe53d3d71

View file

@ -23,32 +23,36 @@
<!-- Market Content -->
<div v-else>
<!-- Market Header -->
<div class="flex items-center justify-between mb-8">
<div class="flex items-center space-x-4">
<Avatar v-if="marketStore.activeMarket?.opts?.logo">
<AvatarImage :src="marketStore.activeMarket.opts.logo" />
<AvatarFallback>M</AvatarFallback>
</Avatar>
<div>
<h1 class="text-3xl font-bold">
{{ marketStore.activeMarket?.opts?.name || 'Market' }}
</h1>
<p v-if="marketStore.activeMarket?.opts?.description" class="text-gray-600">
{{ marketStore.activeMarket.opts.description }}
</p>
<!-- Market Header - Optimized for Mobile -->
<div class="mb-4 sm:mb-6 lg:mb-8">
<!-- Market Info and Search - Responsive Layout -->
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3 sm:gap-4 lg:gap-6">
<!-- Market Info - Compact on Mobile -->
<div class="flex items-center space-x-3 sm:space-x-4">
<Avatar v-if="marketStore.activeMarket?.opts?.logo" class="h-10 w-10 sm:h-12 sm:w-12">
<AvatarImage :src="marketStore.activeMarket.opts.logo" />
<AvatarFallback>M</AvatarFallback>
</Avatar>
<div>
<h1 class="text-xl sm:text-2xl lg:text-3xl font-bold">
{{ marketStore.activeMarket?.opts?.name || 'Market' }}
</h1>
<p v-if="marketStore.activeMarket?.opts?.description" class="text-muted-foreground text-xs sm:text-sm lg:text-base line-clamp-1 sm:line-clamp-2">
{{ marketStore.activeMarket.opts.description }}
</p>
</div>
</div>
<!-- Enhanced Fuzzy Search Bar - Full Width on Mobile -->
<div class="w-full lg:flex-1 lg:max-w-md">
<MarketFuzzySearch
:data="marketStore.products"
:options="searchOptions"
@results="handleSearchResults"
@filter-category="handleCategoryFilter"
class="w-full"
/>
</div>
</div>
<!-- Enhanced Fuzzy Search Bar -->
<div class="flex-1 max-w-md ml-8">
<MarketFuzzySearch
:data="marketStore.products"
:options="searchOptions"
@results="handleSearchResults"
@filter-category="handleCategoryFilter"
class="w-full"
/>
</div>
</div>
@ -57,28 +61,28 @@
<!-- Enhanced Category Filters -->
<section
v-if="allCategories.length > 0"
class="mb-6"
class="mb-4 sm:mb-6"
aria-labelledby="category-filters-heading"
>
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-4">
<h3 id="category-filters-heading" class="text-lg font-semibold text-gray-700">
<div class="flex items-center justify-between mb-2 sm:mb-3">
<div class="flex items-center gap-2 sm:gap-4">
<h3 id="category-filters-heading" class="text-sm sm:text-lg font-semibold text-gray-700">
Browse by Category
</h3>
<!-- AND/OR Filter Mode Toggle -->
<div
v-if="selectedCategoriesCount > 1"
class="flex items-center gap-2"
class="flex items-center gap-1 sm:gap-2"
role="group"
aria-label="Filter mode selection"
>
<span class="text-xs text-muted-foreground">Match:</span>
<span class="text-xs text-muted-foreground hidden sm:inline">Match:</span>
<Button
@click="setFilterMode('any')"
:variant="filterMode === 'any' ? 'default' : 'outline'"
size="sm"
class="h-6 px-2 text-xs"
class="h-5 sm:h-6 px-1.5 sm:px-2 text-xs"
:aria-pressed="filterMode === 'any'"
aria-label="Show products with any selected category"
>
@ -88,7 +92,7 @@
@click="setFilterMode('all')"
:variant="filterMode === 'all' ? 'default' : 'outline'"
size="sm"
class="h-6 px-2 text-xs"
class="h-5 sm:h-6 px-1.5 sm:px-2 text-xs"
:aria-pressed="filterMode === 'all'"
aria-label="Show products with all selected categories"
>
@ -102,16 +106,16 @@
@click="clearAllCategoryFilters"
variant="ghost"
size="sm"
class="text-sm"
class="text-xs sm:text-sm px-2 sm:px-3 py-1 sm:py-2"
:aria-label="`Clear all ${selectedCategoriesCount} selected category filters`"
>
Clear All ({{ selectedCategoriesCount }})
<X class="w-4 h-4 ml-1" aria-hidden="true" />
<span class="hidden sm:inline">Clear All </span><span class="sm:hidden">Clear </span>({{ selectedCategoriesCount }})
<X class="w-3 h-3 sm:w-4 sm:h-4 ml-1" aria-hidden="true" />
</Button>
</div>
<div
class="flex flex-wrap gap-3"
class="flex flex-wrap gap-1.5 sm:gap-3"
role="group"
aria-label="Filter products by category"
>
@ -130,7 +134,7 @@
>
<Badge
:variant="category.selected ? 'default' : 'outline'"
class="px-4 py-2 text-sm font-medium transition-all duration-200"
class="px-2 py-1 sm:px-4 sm:py-2 text-xs sm:text-sm font-medium transition-all duration-200"
:class="{
'bg-primary text-primary-foreground shadow-md': category.selected,
'hover:bg-primary/10 hover:border-primary': !category.selected,
@ -138,10 +142,10 @@
}"
:aria-hidden="true"
>
<div class="flex items-center gap-2">
<div class="flex items-center gap-1 sm:gap-2">
<span>{{ category.category }}</span>
<div
class="px-2 py-0.5 rounded-full text-xs font-bold transition-colors"
class="px-1 py-0.5 sm:px-2 rounded-full text-xs font-bold transition-colors"
:class="category.selected
? 'bg-primary-foreground/20 text-primary-foreground'
: 'bg-secondary text-secondary-foreground'"
@ -159,9 +163,9 @@
<!-- Selection indicator -->
<div
v-if="category.selected"
class="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white shadow-sm"
class="absolute -top-0.5 -right-0.5 sm:-top-1 sm:-right-1 w-2.5 h-2.5 sm:w-3 sm:h-3 bg-green-500 rounded-full border-2 border-white shadow-sm"
>
<Check class="w-2 h-2 text-white absolute top-0.5 left-0.5" />
<Check class="w-1.5 h-1.5 sm:w-2 sm:h-2 text-white absolute top-0 left-0 sm:top-0.5 sm:left-0.5" />
</div>
</div>
</div>
@ -169,34 +173,34 @@
<!-- Active Filters Summary -->
<div
v-if="selectedCategoriesCount > 0"
class="mt-4 p-3 bg-accent/50 rounded-lg border border-border"
class="mt-2 sm:mt-4 p-2 sm:p-3 bg-accent/50 rounded-lg border border-border"
role="region"
aria-label="Active category filters"
aria-live="polite"
>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<Filter class="w-4 h-4 text-accent-foreground/80" aria-hidden="true" />
<span class="text-sm font-medium text-accent-foreground">
Active Filters
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<div class="flex items-center gap-1.5 sm:gap-2">
<Filter class="w-3 h-3 sm:w-4 sm:h-4 text-accent-foreground/80" aria-hidden="true" />
<span class="text-xs sm:text-sm font-medium text-accent-foreground">
<span class="hidden sm:inline">Active Filters</span><span class="sm:hidden">Filters</span>
<span v-if="selectedCategoriesCount > 1" class="text-xs opacity-75">
({{ filterMode === 'any' ? 'Any' : 'All' }} match):
</span>
<span v-else>:</span>
</span>
<div class="flex gap-1" role="list" aria-label="Selected category filters">
<div class="flex gap-0.5 sm:gap-1 flex-wrap" role="list" aria-label="Selected category filters">
<Badge
v-for="category in selectedCategories"
:key="category"
variant="secondary"
class="text-xs"
class="text-xs px-1.5 py-0.5"
role="listitem"
>
{{ category }}
</Badge>
</div>
</div>
<div class="text-sm text-muted-foreground" aria-live="polite">
<div class="text-xs sm:text-sm text-muted-foreground" aria-live="polite">
{{ productsToDisplay.length }} products found
</div>
</div>