Add map view toggle for fullWidthMap mode
All checks were successful
Build Docker Image / build (push) Successful in 3m25s
All checks were successful
Build Docker Image / build (push) Successful in 3m25s
- Add MapViewMode type (offers/hubs/suppliers) with cookie storage - Add view toggle button group on full-width map - Update clusterNodeType and mapPointColor based on selected view - Add translations for view options
This commit is contained in:
@@ -36,6 +36,32 @@
|
|||||||
<div v-if="fullWidthMap" class="hidden lg:flex flex-1 min-h-0 py-4">
|
<div v-if="fullWidthMap" class="hidden lg:flex flex-1 min-h-0 py-4">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="sticky rounded-xl overflow-hidden shadow-lg" :style="mapStyle">
|
<div class="sticky rounded-xl overflow-hidden shadow-lg" :style="mapStyle">
|
||||||
|
<!-- View mode toggle -->
|
||||||
|
<div class="absolute top-4 left-4 z-10">
|
||||||
|
<div class="btn-group shadow-lg bg-white/90 backdrop-blur rounded-lg">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="{ 'btn-active': mapViewMode === 'offers' }"
|
||||||
|
@click="setMapViewMode('offers')"
|
||||||
|
>
|
||||||
|
{{ $t('catalog.views.offers') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="{ 'btn-active': mapViewMode === 'hubs' }"
|
||||||
|
@click="setMapViewMode('hubs')"
|
||||||
|
>
|
||||||
|
{{ $t('catalog.views.hubs') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="{ 'btn-active': mapViewMode === 'suppliers' }"
|
||||||
|
@click="setMapViewMode('suppliers')"
|
||||||
|
>
|
||||||
|
{{ $t('catalog.views.suppliers') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<CatalogMap
|
<CatalogMap
|
||||||
ref="mapRef"
|
ref="mapRef"
|
||||||
@@ -217,6 +243,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MapBounds } from '~/components/catalog/CatalogMap.vue'
|
import type { MapBounds } from '~/components/catalog/CatalogMap.vue'
|
||||||
|
|
||||||
|
// Map view mode for full width map
|
||||||
|
const { mapViewMode, setMapViewMode } = useCatalogSearch()
|
||||||
|
|
||||||
interface MapItem {
|
interface MapItem {
|
||||||
uuid: string
|
uuid: string
|
||||||
latitude?: number | null
|
latitude?: number | null
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { LocationQuery } from 'vue-router'
|
import type { LocationQuery } from 'vue-router'
|
||||||
|
|
||||||
export type SelectMode = 'product' | 'supplier' | 'hub' | null
|
export type SelectMode = 'product' | 'supplier' | 'hub' | null
|
||||||
|
export type MapViewMode = 'offers' | 'hubs' | 'suppliers'
|
||||||
export type DisplayMode =
|
export type DisplayMode =
|
||||||
| 'map-default'
|
| 'map-default'
|
||||||
| 'grid-products'
|
| 'grid-products'
|
||||||
@@ -214,6 +215,19 @@ export function useCatalogSearch() {
|
|||||||
// Text search (for filtering within current grid) - shared via useState
|
// Text search (for filtering within current grid) - shared via useState
|
||||||
const searchQuery = useState<string>('catalog-search-query', () => '')
|
const searchQuery = useState<string>('catalog-search-query', () => '')
|
||||||
|
|
||||||
|
// Map view mode preference (stored in cookie)
|
||||||
|
const mapViewCookie = useCookie<MapViewMode>('catalog-map-view', {
|
||||||
|
default: () => 'offers',
|
||||||
|
maxAge: 60 * 60 * 24 * 365 // 1 year
|
||||||
|
})
|
||||||
|
const mapViewMode = computed({
|
||||||
|
get: () => mapViewCookie.value,
|
||||||
|
set: (val: MapViewMode) => { mapViewCookie.value = val }
|
||||||
|
})
|
||||||
|
const setMapViewMode = (mode: MapViewMode) => {
|
||||||
|
mapViewCookie.value = mode
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
selectMode,
|
selectMode,
|
||||||
@@ -223,6 +237,7 @@ export function useCatalogSearch() {
|
|||||||
hubId,
|
hubId,
|
||||||
quantity,
|
quantity,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
|
mapViewMode,
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
entityColors,
|
entityColors,
|
||||||
@@ -239,6 +254,7 @@ export function useCatalogSearch() {
|
|||||||
editFilter,
|
editFilter,
|
||||||
clearAll,
|
clearAll,
|
||||||
setLabel,
|
setLabel,
|
||||||
|
setMapViewMode,
|
||||||
|
|
||||||
// Labels cache
|
// Labels cache
|
||||||
filterLabels
|
filterLabels
|
||||||
|
|||||||
@@ -87,7 +87,9 @@ const {
|
|||||||
selectItem,
|
selectItem,
|
||||||
removeFilter,
|
removeFilter,
|
||||||
editFilter,
|
editFilter,
|
||||||
setLabel
|
setLabel,
|
||||||
|
mapViewMode,
|
||||||
|
entityColors
|
||||||
} = useCatalogSearch()
|
} = useCatalogSearch()
|
||||||
|
|
||||||
// Composables for data
|
// Composables for data
|
||||||
@@ -140,6 +142,12 @@ const useServerClustering = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const clusterNodeType = computed(() => {
|
const clusterNodeType = computed(() => {
|
||||||
|
// When in full width map mode, use mapViewMode preference
|
||||||
|
if (!selectMode.value) {
|
||||||
|
if (mapViewMode.value === 'offers') return 'offer'
|
||||||
|
if (mapViewMode.value === 'hubs') return 'logistics'
|
||||||
|
if (mapViewMode.value === 'suppliers') return 'supplier'
|
||||||
|
}
|
||||||
// For products/offers/default map show offer locations
|
// For products/offers/default map show offer locations
|
||||||
if (['map-default', 'grid-products', 'grid-offers', 'grid-products-from-supplier', 'grid-products-in-hub'].includes(displayMode.value)) {
|
if (['map-default', 'grid-products', 'grid-offers', 'grid-products-from-supplier', 'grid-products-in-hub'].includes(displayMode.value)) {
|
||||||
return 'offer'
|
return 'offer'
|
||||||
@@ -148,10 +156,13 @@ const clusterNodeType = computed(() => {
|
|||||||
return 'logistics'
|
return 'logistics'
|
||||||
})
|
})
|
||||||
|
|
||||||
// Import entity colors
|
|
||||||
const { entityColors } = useCatalogSearch()
|
|
||||||
|
|
||||||
const mapPointColor = computed(() => {
|
const mapPointColor = computed(() => {
|
||||||
|
// When in full width map mode, use mapViewMode preference
|
||||||
|
if (!selectMode.value) {
|
||||||
|
if (mapViewMode.value === 'offers') return entityColors.offer // orange
|
||||||
|
if (mapViewMode.value === 'hubs') return entityColors.hub // green
|
||||||
|
if (mapViewMode.value === 'suppliers') return entityColors.supplier // blue
|
||||||
|
}
|
||||||
if (cardType.value === 'supplier') return entityColors.supplier // blue
|
if (cardType.value === 'supplier') return entityColors.supplier // blue
|
||||||
if (cardType.value === 'hub') return entityColors.hub // green
|
if (cardType.value === 'hub') return entityColors.hub // green
|
||||||
if (cardType.value === 'offer') return entityColors.offer // orange
|
if (cardType.value === 'offer') return entityColors.offer // orange
|
||||||
|
|||||||
@@ -33,6 +33,13 @@
|
|||||||
"noOffers": "No offers found",
|
"noOffers": "No offers found",
|
||||||
"noResults": "No results found"
|
"noResults": "No results found"
|
||||||
},
|
},
|
||||||
|
"views": {
|
||||||
|
"hubs": "Hubs",
|
||||||
|
"products": "Products",
|
||||||
|
"suppliers": "Suppliers",
|
||||||
|
"offers": "Offers",
|
||||||
|
"map": "Map"
|
||||||
|
},
|
||||||
"offers": "offer | offers"
|
"offers": "offer | offers"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,13 @@
|
|||||||
"noOffers": "Предложения не найдены",
|
"noOffers": "Предложения не найдены",
|
||||||
"noResults": "Ничего не найдено"
|
"noResults": "Ничего не найдено"
|
||||||
},
|
},
|
||||||
|
"views": {
|
||||||
|
"hubs": "Хабы",
|
||||||
|
"products": "Товары",
|
||||||
|
"suppliers": "Поставщики",
|
||||||
|
"offers": "Офферы",
|
||||||
|
"map": "Карта"
|
||||||
|
},
|
||||||
"offers": "предложение | предложения | предложений"
|
"offers": "предложение | предложения | предложений"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user