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