Simplify catalog UI - remove chips, add drawer for list
All checks were successful
Build Docker Image / build (push) Successful in 3m59s
All checks were successful
Build Docker Image / build (push) Successful in 3m59s
- Remove product/hub chips from QuoteForm.vue (duplicate of toggle) - Add drawer state to useCatalogSearch.ts (isDrawerOpen, selectDrawerItem, applyDrawerFilter) - Convert SelectionPanel to drawer with header, scrollable content, and footer - Add "Список" button to CatalogPage.vue to open drawer - Add "Применить фильтр" button in drawer footer - Add slide animations for drawer (left on desktop, up on mobile) - Update translations: catalog.list, catalog.applyFilter
This commit is contained in:
@@ -2,64 +2,6 @@
|
|||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<h3 class="font-semibold text-lg">{{ $t('catalog.quote.title') }}</h3>
|
<h3 class="font-semibold text-lg">{{ $t('catalog.quote.title') }}</h3>
|
||||||
|
|
||||||
<!-- Product chip -->
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label py-1">
|
|
||||||
<span class="label-text text-xs text-base-content/70">{{ $t('catalog.filters.product') }}</span>
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
v-if="productLabel"
|
|
||||||
class="btn btn-outline btn-sm justify-start gap-2"
|
|
||||||
@click="emit('edit-filter', 'product')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:package" size="16" />
|
|
||||||
<span class="flex-1 text-left truncate">{{ productLabel }}</span>
|
|
||||||
<span
|
|
||||||
class="btn btn-ghost btn-xs btn-circle"
|
|
||||||
@click.stop="emit('remove-filter', 'product')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:x" size="14" />
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
class="btn btn-ghost btn-sm justify-start gap-2 border border-dashed border-base-content/30"
|
|
||||||
@click="emit('edit-filter', 'product')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:plus" size="16" class="text-base-content/50" />
|
|
||||||
<span class="text-base-content/50">{{ $t('catalog.quote.selectProduct') }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hub chip -->
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label py-1">
|
|
||||||
<span class="label-text text-xs text-base-content/70">{{ $t('catalog.filters.hub') }}</span>
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
v-if="hubLabel"
|
|
||||||
class="btn btn-outline btn-sm justify-start gap-2"
|
|
||||||
@click="emit('edit-filter', 'hub')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:map-pin" size="16" />
|
|
||||||
<span class="flex-1 text-left truncate">{{ hubLabel }}</span>
|
|
||||||
<span
|
|
||||||
class="btn btn-ghost btn-xs btn-circle"
|
|
||||||
@click.stop="emit('remove-filter', 'hub')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:x" size="14" />
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
class="btn btn-ghost btn-sm justify-start gap-2 border border-dashed border-base-content/30"
|
|
||||||
@click="emit('edit-filter', 'hub')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:plus" size="16" class="text-base-content/50" />
|
|
||||||
<span class="text-base-content/50">{{ $t('catalog.quote.selectHub') }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Quantity input -->
|
<!-- Quantity input -->
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<label class="label py-1">
|
<label class="label py-1">
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<MapPanel>
|
<div class="flex flex-col h-full">
|
||||||
<template #header>
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex-shrink-0 p-4 border-b border-white/10">
|
||||||
<h3 class="font-semibold text-base text-base-content">{{ title }}</h3>
|
<div class="flex items-center justify-between mb-3">
|
||||||
<button class="btn btn-ghost btn-xs btn-circle text-base-content/60 hover:text-base-content" @click="emit('close')">
|
<h3 class="font-semibold text-base text-white">{{ title }}</h3>
|
||||||
|
<button class="btn btn-ghost btn-xs btn-circle text-white/60 hover:text-white" @click="closeDrawer">
|
||||||
<Icon name="lucide:x" size="16" />
|
<Icon name="lucide:x" size="16" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -11,83 +12,96 @@
|
|||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="searchPlaceholder"
|
:placeholder="searchPlaceholder"
|
||||||
class="input input-sm w-full bg-white/50 border-base-300/50 text-base-content placeholder:text-base-content/50"
|
class="input input-sm w-full bg-white/10 border-white/20 text-white placeholder:text-white/50"
|
||||||
/>
|
/>
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Content -->
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
|
||||||
<span class="loading loading-spinner loading-md" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="filteredItems.length === 0" class="text-center py-8 text-white/60">
|
<!-- Content (scrollable) -->
|
||||||
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
<div class="flex-1 overflow-y-auto p-4">
|
||||||
<p>{{ $t('catalog.empty.noResults') }}</p>
|
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||||
</div>
|
<span class="loading loading-spinner loading-md text-white" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else class="flex flex-col gap-2">
|
<div v-else-if="filteredItems.length === 0" class="text-center py-8 text-white/60">
|
||||||
<!-- Products -->
|
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
||||||
<template v-if="selectMode === 'product'">
|
<p>{{ $t('catalog.empty.noResults') }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="flex flex-col gap-2">
|
||||||
|
<!-- Products -->
|
||||||
|
<template v-if="selectMode === 'product'">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in filteredItems"
|
||||||
|
:key="item.uuid ?? index"
|
||||||
|
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||||
|
@mouseleave="emit('hover', null)"
|
||||||
|
>
|
||||||
|
<ProductCard
|
||||||
|
:product="item"
|
||||||
|
selectable
|
||||||
|
compact
|
||||||
|
:is-selected="drawerSelectedItem?.uuid === item.uuid"
|
||||||
|
@select="onDrawerSelect(item)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Hubs -->
|
||||||
|
<template v-else-if="selectMode === 'hub'">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in filteredItems"
|
||||||
|
:key="item.uuid ?? index"
|
||||||
|
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||||
|
@mouseleave="emit('hover', null)"
|
||||||
|
>
|
||||||
|
<HubCard
|
||||||
|
:hub="item"
|
||||||
|
selectable
|
||||||
|
:is-selected="drawerSelectedItem?.uuid === item.uuid"
|
||||||
|
@select="onDrawerSelect(item)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Suppliers -->
|
||||||
|
<template v-else-if="selectMode === 'supplier'">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in filteredItems"
|
||||||
|
:key="item.uuid ?? index"
|
||||||
|
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||||
|
@mouseleave="emit('hover', null)"
|
||||||
|
>
|
||||||
|
<SupplierCard
|
||||||
|
:supplier="item"
|
||||||
|
selectable
|
||||||
|
:is-selected="drawerSelectedItem?.uuid === item.uuid"
|
||||||
|
@select="onDrawerSelect(item)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Infinite scroll sentinel -->
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in filteredItems"
|
v-if="hasMore && !searchQuery"
|
||||||
:key="item.uuid ?? index"
|
ref="loadMoreSentinel"
|
||||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
class="flex items-center justify-center py-4"
|
||||||
@mouseleave="emit('hover', null)"
|
|
||||||
>
|
>
|
||||||
<ProductCard
|
<span v-if="loadingMore" class="loading loading-spinner loading-sm text-white/60" />
|
||||||
:product="item"
|
|
||||||
selectable
|
|
||||||
compact
|
|
||||||
:is-selected="selectedId === item.uuid"
|
|
||||||
@select="onSelect(item)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Hubs -->
|
|
||||||
<template v-else-if="selectMode === 'hub'">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in filteredItems"
|
|
||||||
:key="item.uuid ?? index"
|
|
||||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
|
||||||
@mouseleave="emit('hover', null)"
|
|
||||||
>
|
|
||||||
<HubCard
|
|
||||||
:hub="item"
|
|
||||||
selectable
|
|
||||||
:is-selected="selectedId === item.uuid"
|
|
||||||
@select="onSelect(item)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Suppliers -->
|
|
||||||
<template v-else-if="selectMode === 'supplier'">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in filteredItems"
|
|
||||||
:key="item.uuid ?? index"
|
|
||||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
|
||||||
@mouseleave="emit('hover', null)"
|
|
||||||
>
|
|
||||||
<SupplierCard
|
|
||||||
:supplier="item"
|
|
||||||
selectable
|
|
||||||
:is-selected="selectedId === item.uuid"
|
|
||||||
@select="onSelect(item)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Infinite scroll sentinel -->
|
|
||||||
<div
|
|
||||||
v-if="hasMore && !searchQuery"
|
|
||||||
ref="loadMoreSentinel"
|
|
||||||
class="flex items-center justify-center py-4"
|
|
||||||
>
|
|
||||||
<span v-if="loadingMore" class="loading loading-spinner loading-sm text-white/60" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MapPanel>
|
|
||||||
|
<!-- Footer with Apply button -->
|
||||||
|
<div class="flex-shrink-0 p-4 border-t border-white/10">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary w-full"
|
||||||
|
:disabled="!drawerSelectedItem"
|
||||||
|
@click="onApplyFilter"
|
||||||
|
>
|
||||||
|
{{ $t('catalog.applyFilter') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -104,21 +118,26 @@ const props = defineProps<{
|
|||||||
products?: Item[]
|
products?: Item[]
|
||||||
hubs?: Item[]
|
hubs?: Item[]
|
||||||
suppliers?: Item[]
|
suppliers?: Item[]
|
||||||
selectedId?: string
|
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
loadingMore?: boolean
|
loadingMore?: boolean
|
||||||
hasMore?: boolean
|
hasMore?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'select': [type: string, item: Item]
|
|
||||||
'close': []
|
|
||||||
'load-more': []
|
'load-more': []
|
||||||
'hover': [uuid: string | null]
|
'hover': [uuid: string | null]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// Get drawer functions from useCatalogSearch
|
||||||
|
const {
|
||||||
|
drawerSelectedItem,
|
||||||
|
selectDrawerItem,
|
||||||
|
applyDrawerFilter,
|
||||||
|
closeDrawer
|
||||||
|
} = useCatalogSearch()
|
||||||
|
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
const loadMoreSentinel = ref<HTMLElement | null>(null)
|
const loadMoreSentinel = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
@@ -187,9 +206,15 @@ const filteredItems = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const onSelect = (item: Item) => {
|
// Select item in drawer (doesn't apply filter yet)
|
||||||
if (props.selectMode && item.uuid) {
|
const onDrawerSelect = (item: Item) => {
|
||||||
emit('select', props.selectMode, item)
|
if (item.uuid && item.name) {
|
||||||
|
selectDrawerItem(item.uuid, item.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply filter and close drawer
|
||||||
|
const onApplyFilter = () => {
|
||||||
|
applyDrawerFilter()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -39,10 +39,18 @@
|
|||||||
<span class="text-white text-sm">{{ $t('common.loading') }}</span>
|
<span class="text-white text-sm">{{ $t('common.loading') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter by bounds checkbox (LEFT, next to panel) -->
|
<!-- List button (LEFT, opens drawer) -->
|
||||||
|
<button
|
||||||
|
class="absolute top-[116px] left-4 z-20 hidden lg:flex items-center gap-2 bg-black/30 backdrop-blur-md rounded-lg px-3 py-1.5 border border-white/10 text-white text-sm hover:bg-black/40 transition-colors"
|
||||||
|
@click="openDrawer"
|
||||||
|
>
|
||||||
|
<Icon name="lucide:menu" size="16" />
|
||||||
|
<span>{{ $t('catalog.list') }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Filter by bounds checkbox (LEFT, next to list button) -->
|
||||||
<label
|
<label
|
||||||
v-if="showPanel"
|
class="absolute top-[116px] left-32 z-20 hidden lg:flex items-center gap-2 bg-black/30 backdrop-blur-md rounded-lg px-3 py-1.5 border border-white/10 cursor-pointer text-white text-sm hover:bg-black/40 transition-colors"
|
||||||
class="absolute top-[116px] left-[26rem] z-20 hidden lg:flex items-center gap-2 bg-black/30 backdrop-blur-md rounded-lg px-3 py-1.5 border border-white/10 cursor-pointer text-white text-sm hover:bg-black/40 transition-colors"
|
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -90,20 +98,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Left overlay panel (shown when showPanel is true, below header) -->
|
<!-- Left drawer (slides from left when isDrawerOpen is true) -->
|
||||||
<div
|
<Transition name="slide-left">
|
||||||
v-if="showPanel"
|
<div
|
||||||
class="absolute top-[116px] left-4 bottom-4 z-10 w-96 max-w-[calc(100vw-2rem)] hidden lg:block"
|
v-if="isDrawerOpen"
|
||||||
>
|
class="absolute top-[116px] left-4 bottom-4 z-30 w-96 max-w-[calc(100vw-2rem)] hidden lg:block"
|
||||||
<div class="bg-black/30 backdrop-blur-md rounded-xl shadow-lg border border-white/10 p-4 h-full overflow-y-auto text-white">
|
>
|
||||||
<slot name="panel" />
|
<div class="bg-black/50 backdrop-blur-md rounded-xl shadow-lg border border-white/10 h-full flex flex-col text-white">
|
||||||
|
<slot name="panel" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Transition>
|
||||||
|
|
||||||
<!-- Mobile bottom sheet -->
|
<!-- 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 view toggle -->
|
<!-- Mobile controls: List button + view toggle -->
|
||||||
<div class="flex justify-end px-4 mb-2">
|
<div class="flex justify-between px-4 mb-2">
|
||||||
|
<!-- List button (mobile) -->
|
||||||
|
<button
|
||||||
|
class="flex items-center gap-2 bg-black/30 backdrop-blur-md rounded-lg px-3 py-2 border border-white/10 text-white text-sm"
|
||||||
|
@click="openDrawer"
|
||||||
|
>
|
||||||
|
<Icon name="lucide:menu" size="16" />
|
||||||
|
<span>{{ $t('catalog.list') }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Mobile view toggle -->
|
||||||
<div class="flex gap-1 bg-black/30 backdrop-blur-md rounded-lg p-1 border border-white/10">
|
<div class="flex gap-1 bg-black/30 backdrop-blur-md rounded-lg p-1 border border-white/10">
|
||||||
<button
|
<button
|
||||||
class="flex items-center justify-center w-8 h-8 rounded-md transition-colors"
|
class="flex items-center justify-center w-8 h-8 rounded-md transition-colors"
|
||||||
@@ -135,24 +155,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile panel (collapsible) - only when showPanel is true -->
|
<!-- Mobile panel (collapsible) - only when drawer is open -->
|
||||||
<div
|
<Transition name="slide-up">
|
||||||
v-if="showPanel"
|
|
||||||
class="bg-black/30 backdrop-blur-md rounded-t-xl shadow-lg border border-white/10 transition-all duration-300 text-white"
|
|
||||||
:class="mobilePanelExpanded ? 'h-[60vh]' : 'h-auto'"
|
|
||||||
>
|
|
||||||
<!-- Drag handle -->
|
|
||||||
<div
|
<div
|
||||||
class="flex justify-center py-2 cursor-pointer"
|
v-if="isDrawerOpen"
|
||||||
@click="mobilePanelExpanded = !mobilePanelExpanded"
|
class="bg-black/50 backdrop-blur-md rounded-t-xl shadow-lg border border-white/10 transition-all duration-300 text-white h-[60vh]"
|
||||||
>
|
>
|
||||||
<div class="w-10 h-1 bg-white/30 rounded-full" />
|
<!-- Drag handle / close -->
|
||||||
</div>
|
<div
|
||||||
|
class="flex justify-center py-2 cursor-pointer"
|
||||||
|
@click="closeDrawer"
|
||||||
|
>
|
||||||
|
<div class="w-10 h-1 bg-white/30 rounded-full" />
|
||||||
|
</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 h-[calc(60vh-2rem)]">
|
||||||
<slot name="panel" />
|
<slot name="panel" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -160,7 +181,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, setMapViewMode } = useCatalogSearch()
|
const { mapViewMode, setMapViewMode, isDrawerOpen, openDrawer, closeDrawer } = useCatalogSearch()
|
||||||
|
|
||||||
// Point color based on map view mode
|
// Point color based on map view mode
|
||||||
const VIEW_MODE_COLORS = {
|
const VIEW_MODE_COLORS = {
|
||||||
@@ -320,3 +341,29 @@ const flyTo = (lat: number, lng: number, zoom = 8) => {
|
|||||||
|
|
||||||
defineExpose({ flyTo, currentBounds })
|
defineExpose({ flyTo, currentBounds })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Drawer slide animation (desktop - left) */
|
||||||
|
.slide-left-enter-active,
|
||||||
|
.slide-left-leave-active {
|
||||||
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-left-enter-from,
|
||||||
|
.slide-left-leave-to {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drawer slide animation (mobile - up) */
|
||||||
|
.slide-up-enter-active,
|
||||||
|
.slide-up-leave-active {
|
||||||
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-up-enter-from,
|
||||||
|
.slide-up-leave-to {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -260,6 +260,47 @@ export function useCatalogSearch() {
|
|||||||
mapViewCookie.value = mode
|
mapViewCookie.value = mode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drawer state for list view
|
||||||
|
const isDrawerOpen = ref(false)
|
||||||
|
const drawerSelectedItem = ref<{ uuid: string; name: string } | null>(null)
|
||||||
|
|
||||||
|
const openDrawer = () => {
|
||||||
|
isDrawerOpen.value = true
|
||||||
|
drawerSelectedItem.value = null
|
||||||
|
|
||||||
|
// Set selectMode based on mapViewMode so SelectionPanel shows the right list
|
||||||
|
const newSelectMode: SelectMode = mapViewMode.value === 'hubs' ? 'hub'
|
||||||
|
: mapViewMode.value === 'suppliers' ? 'supplier'
|
||||||
|
: 'product'
|
||||||
|
startSelect(newSelectMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDrawer = () => {
|
||||||
|
isDrawerOpen.value = false
|
||||||
|
drawerSelectedItem.value = null
|
||||||
|
cancelSelect() // Also exit selection mode
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectDrawerItem = (uuid: string, name: string) => {
|
||||||
|
drawerSelectedItem.value = { uuid, name }
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyDrawerFilter = () => {
|
||||||
|
if (!drawerSelectedItem.value) {
|
||||||
|
closeDrawer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine filter type based on mapViewMode
|
||||||
|
const type = mapViewMode.value === 'hubs' ? 'hub'
|
||||||
|
: mapViewMode.value === 'suppliers' ? 'supplier'
|
||||||
|
: 'product' // offers -> select product from offer
|
||||||
|
|
||||||
|
const { uuid, name } = drawerSelectedItem.value
|
||||||
|
selectItem(type, uuid, name)
|
||||||
|
closeDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
// Catalog mode: explore (map browsing) or quote (search for offers)
|
// Catalog mode: explore (map browsing) or quote (search for offers)
|
||||||
const catalogMode = computed<CatalogMode>(() => {
|
const catalogMode = computed<CatalogMode>(() => {
|
||||||
return route.query.mode === 'quote' ? 'quote' : 'explore'
|
return route.query.mode === 'quote' ? 'quote' : 'explore'
|
||||||
@@ -292,6 +333,10 @@ export function useCatalogSearch() {
|
|||||||
searchQuery,
|
searchQuery,
|
||||||
mapViewMode,
|
mapViewMode,
|
||||||
|
|
||||||
|
// Drawer state
|
||||||
|
isDrawerOpen,
|
||||||
|
drawerSelectedItem,
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
entityColors,
|
entityColors,
|
||||||
|
|
||||||
@@ -317,6 +362,12 @@ export function useCatalogSearch() {
|
|||||||
setMapViewMode,
|
setMapViewMode,
|
||||||
setCatalogMode,
|
setCatalogMode,
|
||||||
|
|
||||||
|
// Drawer actions
|
||||||
|
openDrawer,
|
||||||
|
closeDrawer,
|
||||||
|
selectDrawerItem,
|
||||||
|
applyDrawerFilter,
|
||||||
|
|
||||||
// Labels cache
|
// Labels cache
|
||||||
filterLabels
|
filterLabels
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@
|
|||||||
:loading="selectionLoading"
|
:loading="selectionLoading"
|
||||||
:loading-more="selectionLoadingMore"
|
:loading-more="selectionLoadingMore"
|
||||||
:has-more="selectionHasMore && !filterByBounds"
|
:has-more="selectionHasMore && !filterByBounds"
|
||||||
@select="onSelectItem"
|
|
||||||
@close="cancelSelect"
|
|
||||||
@load-more="onLoadMore"
|
@load-more="onLoadMore"
|
||||||
@hover="onHoverItem"
|
@hover="onHoverItem"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -75,6 +75,8 @@
|
|||||||
"title": "Исследуйте рынок",
|
"title": "Исследуйте рынок",
|
||||||
"subtitle": "Переключайтесь между офферами, хабами и поставщиками"
|
"subtitle": "Переключайтесь между офферами, хабами и поставщиками"
|
||||||
},
|
},
|
||||||
"offers": "предложение | предложения | предложений"
|
"offers": "предложение | предложения | предложений",
|
||||||
|
"list": "Список",
|
||||||
|
"applyFilter": "Применить фильтр"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user