Refactor catalog layout: mode toggle to top right, view toggle to top left
All checks were successful
Build Docker Image / build (push) Successful in 3m35s

- Move Explore/Quote mode toggle to top right corner
- Add view toggle (Offers/Hubs/Suppliers) to top left in Explore mode
- Panel shows only in Quote mode when showPanel prop is true
- Simplified panel slot structure
This commit is contained in:
Ruslan Bakiev
2026-01-22 19:20:11 +07:00
parent 850ab3f252
commit ddf691c83b
2 changed files with 82 additions and 47 deletions

View File

@@ -28,8 +28,8 @@
</ClientOnly> </ClientOnly>
</div> </div>
<!-- Mode tabs (top left overlay) --> <!-- Mode tabs (top RIGHT overlay) -->
<div class="absolute top-4 left-4 z-20"> <div class="absolute top-4 right-4 z-20 hidden lg:block">
<div class="tabs tabs-boxed bg-white/95 backdrop-blur shadow-lg"> <div class="tabs tabs-boxed bg-white/95 backdrop-blur shadow-lg">
<button <button
class="tab" class="tab"
@@ -50,43 +50,85 @@
</div> </div>
</div> </div>
<!-- Left overlay panel --> <!-- View toggle (top LEFT overlay) - for Explore mode -->
<div <div v-if="catalogMode === 'explore'" class="absolute top-4 left-4 z-20 hidden lg:block">
class="absolute top-20 left-4 bottom-4 z-10 w-80 max-w-[calc(100vw-2rem)] hidden lg:block" <div class="join bg-white/95 backdrop-blur shadow-lg rounded-lg">
> <button
<div class="bg-white/95 backdrop-blur rounded-xl shadow-lg p-4 h-full overflow-hidden flex flex-col"> class="btn btn-sm join-item"
<!-- Explore mode panel --> :class="{ 'btn-active': mapViewMode === 'offers' }"
<template v-if="catalogMode === 'explore'"> @click="setMapViewMode('offers')"
<slot name="explore-panel"> >
<ExplorePanel {{ $t('catalog.views.offers') }}
:selected-item="selectedMapItem" </button>
@close-selected="selectedMapItem = null" <button
@view-details="onViewDetails" class="btn btn-sm join-item"
/> :class="{ 'btn-active': mapViewMode === 'hubs' }"
</slot> @click="setMapViewMode('hubs')"
</template> >
{{ $t('catalog.views.hubs') }}
<!-- Quote mode panel --> </button>
<template v-else> <button
<slot name="quote-panel" /> class="btn btn-sm join-item"
</template> :class="{ 'btn-active': mapViewMode === 'suppliers' }"
@click="setMapViewMode('suppliers')"
>
{{ $t('catalog.views.suppliers') }}
</button>
</div> </div>
</div> </div>
<!-- Mobile bottom sheet (simplified) --> <!-- Left overlay panel (shown when showPanel is true) -->
<div
v-if="showPanel"
class="absolute top-4 left-4 bottom-4 z-10 w-96 max-w-[calc(100vw-2rem)] hidden lg:block"
:class="catalogMode === 'explore' ? 'top-16' : 'top-4'"
>
<div class="bg-white/95 backdrop-blur rounded-xl shadow-lg p-4 h-full overflow-hidden flex flex-col">
<slot name="panel" />
</div>
</div>
<!-- Mobile bottom sheet -->
<div class="lg:hidden absolute bottom-0 left-0 right-0 z-20"> <div class="lg:hidden absolute bottom-0 left-0 right-0 z-20">
<!-- Mobile mode toggle --> <!-- Mobile toggles -->
<div class="flex justify-center mb-2"> <div class="flex justify-between items-center px-4 mb-2">
<!-- View toggle (Explore mode only) -->
<div v-if="catalogMode === 'explore'" class="join bg-white/95 backdrop-blur shadow-lg rounded-lg">
<button
class="btn btn-xs join-item"
:class="{ 'btn-active': mapViewMode === 'offers' }"
@click="setMapViewMode('offers')"
>
{{ $t('catalog.views.offers') }}
</button>
<button
class="btn btn-xs join-item"
:class="{ 'btn-active': mapViewMode === 'hubs' }"
@click="setMapViewMode('hubs')"
>
{{ $t('catalog.views.hubs') }}
</button>
<button
class="btn btn-xs join-item"
:class="{ 'btn-active': mapViewMode === 'suppliers' }"
@click="setMapViewMode('suppliers')"
>
{{ $t('catalog.views.suppliers') }}
</button>
</div>
<div v-else />
<!-- Mode toggle -->
<div class="tabs tabs-boxed bg-white/95 backdrop-blur shadow-lg tabs-sm"> <div class="tabs tabs-boxed bg-white/95 backdrop-blur shadow-lg tabs-sm">
<button <button
class="tab" class="tab tab-sm"
:class="{ 'tab-active': catalogMode === 'explore' }" :class="{ 'tab-active': catalogMode === 'explore' }"
@click="setCatalogMode('explore')" @click="setCatalogMode('explore')"
> >
{{ $t('catalog.modes.explore') }} {{ $t('catalog.modes.explore') }}
</button> </button>
<button <button
class="tab" class="tab tab-sm"
:class="{ 'tab-active': catalogMode === 'quote' }" :class="{ 'tab-active': catalogMode === 'quote' }"
@click="setCatalogMode('quote')" @click="setCatalogMode('quote')"
> >
@@ -95,8 +137,9 @@
</div> </div>
</div> </div>
<!-- Mobile panel (collapsible) --> <!-- Mobile panel (collapsible) - only when showPanel is true -->
<div <div
v-if="showPanel"
class="bg-white/95 backdrop-blur rounded-t-xl shadow-lg transition-all duration-300" class="bg-white/95 backdrop-blur rounded-t-xl shadow-lg transition-all duration-300"
:class="mobilePanelExpanded ? 'h-[60vh]' : 'h-auto'" :class="mobilePanelExpanded ? 'h-[60vh]' : 'h-auto'"
> >
@@ -109,21 +152,7 @@
</div> </div>
<div class="px-4 pb-4 overflow-y-auto" :class="mobilePanelExpanded ? 'h-[calc(60vh-2rem)]' : 'max-h-48'"> <div class="px-4 pb-4 overflow-y-auto" :class="mobilePanelExpanded ? 'h-[calc(60vh-2rem)]' : 'max-h-48'">
<!-- Explore mode panel --> <slot name="panel" />
<template v-if="catalogMode === 'explore'">
<slot name="explore-panel">
<ExplorePanel
:selected-item="selectedMapItem"
@close-selected="selectedMapItem = null"
@view-details="onViewDetails"
/>
</slot>
</template>
<!-- Quote mode panel -->
<template v-else>
<slot name="quote-panel" />
</template>
</div> </div>
</div> </div>
</div> </div>
@@ -133,7 +162,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { MapBounds } from '~/components/catalog/CatalogMap.vue' import type { MapBounds } from '~/components/catalog/CatalogMap.vue'
const { mapViewMode, catalogMode, setCatalogMode } = useCatalogSearch() const { mapViewMode, setMapViewMode, catalogMode, setCatalogMode } = useCatalogSearch()
interface MapItem { interface MapItem {
uuid: string uuid: string
@@ -152,13 +181,15 @@ const props = withDefaults(defineProps<{
pointColor?: string pointColor?: string
hoveredId?: string hoveredId?: string
items?: MapItem[] items?: MapItem[]
showPanel?: boolean
}>(), { }>(), {
loading: false, loading: false,
useServerClustering: true, useServerClustering: true,
clusterNodeType: 'offer', clusterNodeType: 'offer',
mapId: 'catalog-map', mapId: 'catalog-map',
pointColor: '#f97316', pointColor: '#f97316',
items: () => [] items: () => [],
showPanel: false
}) })
const emit = defineEmits<{ const emit = defineEmits<{

View File

@@ -6,10 +6,11 @@
map-id="unified-catalog-map" map-id="unified-catalog-map"
:point-color="mapPointColor" :point-color="mapPointColor"
:items="[]" :items="[]"
:show-panel="showPanel"
@select="onMapSelect" @select="onMapSelect"
> >
<!-- Quote panel slot --> <!-- Panel slot - shows Quote form or selection list -->
<template #quote-panel> <template #panel>
<QuotePanel <QuotePanel
:product-id="productId" :product-id="productId"
:product-label="getFilterLabel('product', productId)" :product-label="getFilterLabel('product', productId)"
@@ -140,6 +141,9 @@ const showQuoteResults = ref(false)
// Loading state // Loading state
const isLoading = computed(() => offersLoading.value) const isLoading = computed(() => offersLoading.value)
// Show panel when in Quote mode
const showPanel = computed(() => catalogMode.value === 'quote')
// Get filter label from cache // Get filter label from cache
const getFilterLabel = (type: string, id: string | undefined): string | undefined => { const getFilterLabel = (type: string, id: string | undefined): string | undefined => {
if (!id) return undefined if (!id) return undefined