From f97378425765c835df2e406e80ea87de1cca5c1b Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev Date: Mon, 26 Jan 2026 15:55:25 +0700 Subject: [PATCH] Add URL params for InfoPanel tab and product (infoTab, infoProduct) --- app/components/catalog/InfoPanel.vue | 42 +++++++++++++++------------- app/composables/useCatalogSearch.ts | 22 +++++++++++++-- app/pages/catalog/index.vue | 26 +++++++++++++++-- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/app/components/catalog/InfoPanel.vue b/app/components/catalog/InfoPanel.vue index 7b7ef33..86df751 100644 --- a/app/components/catalog/InfoPanel.vue +++ b/app/components/catalog/InfoPanel.vue @@ -41,8 +41,8 @@ :key="tab.id" role="tab" class="tab text-white/70" - :class="{ 'tab-active !text-white !bg-white/20': currentTab === tab.id }" - @click="currentTab = tab.id" + :class="{ 'tab-active !text-white !bg-white/20': activeTab === tab.id }" + @click="onTabClick(tab.id)" > {{ tab.label }} ({{ tab.count }}) @@ -52,7 +52,7 @@
-
+

{{ $t('catalog.empty.noProducts') }}

@@ -70,7 +70,7 @@
-
+

{{ $t('catalog.info.noHubs') }}

@@ -87,7 +87,7 @@
-
+

{{ $t('catalog.info.noSuppliers') }}

@@ -104,7 +104,7 @@
-
+
@@ -183,6 +183,7 @@ const props = defineProps<{ relatedSuppliers?: any[] relatedOffers?: any[] selectedProduct?: string | null + currentTab?: string loading?: boolean }>() @@ -191,6 +192,7 @@ const emit = defineEmits<{ 'add-to-filter': [] 'open-info': [type: InfoEntityType, uuid: string] 'select-product': [uuid: string | null] + 'update:current-tab': [tab: string] }>() const { t } = useI18n() @@ -202,9 +204,6 @@ const relatedHubs = computed(() => props.relatedHubs ?? []) const relatedSuppliers = computed(() => props.relatedSuppliers ?? []) const relatedOffers = computed(() => props.relatedOffers ?? []) -// Current active tab -const currentTab = ref('offers') - // Entity name const entityName = computed(() => { return props.entity?.name || props.entity?.productName || props.entityId.slice(0, 8) + '...' @@ -278,17 +277,20 @@ const availableTabs = computed(() => { return tabs }) -// Set default active tab when entity type changes -watch( - () => props.entityType, - () => { - const firstTab = availableTabs.value[0] - if (firstTab) { - currentTab.value = firstTab.id - } - }, - { immediate: true } -) +// Active tab - use prop or default to first available +const activeTab = computed(() => { + // If prop is set and is a valid tab, use it + if (props.currentTab && availableTabs.value.some(t => t.id === props.currentTab)) { + return props.currentTab + } + // Default to first tab + return availableTabs.value[0]?.id || 'offers' +}) + +// Handler for tab click +const onTabClick = (tabId: string) => { + emit('update:current-tab', tabId) +} // Handlers for selecting related items const onProductSelect = (product: any) => { diff --git a/app/composables/useCatalogSearch.ts b/app/composables/useCatalogSearch.ts index 60012a2..2394fa2 100644 --- a/app/composables/useCatalogSearch.ts +++ b/app/composables/useCatalogSearch.ts @@ -73,6 +73,12 @@ export function useCatalogSearch() { return null }) + // Info panel tab (stored in URL for sharing) + const infoTab = computed(() => route.query.infoTab as string | undefined) + + // Info panel selected product (stored in URL for sharing) + const infoProduct = computed(() => route.query.infoProduct as string | undefined) + const productId = computed(() => route.query.product as string | undefined) const supplierId = computed(() => route.query.supplier as string | undefined) const hubId = computed(() => route.query.hub as string | undefined) @@ -232,11 +238,19 @@ export function useCatalogSearch() { } const openInfo = (type: InfoEntityType, uuid: string) => { - updateQuery({ info: `${type}:${uuid}`, select: null }) + updateQuery({ info: `${type}:${uuid}`, select: null, infoTab: null, infoProduct: null }) } const closeInfo = () => { - updateQuery({ info: null }) + updateQuery({ info: null, infoTab: null, infoProduct: null }) + } + + const setInfoTab = (tab: string) => { + updateQuery({ infoTab: tab }) + } + + const setInfoProduct = (productUuid: string | null) => { + updateQuery({ infoProduct: productUuid }) } const clearAll = () => { @@ -326,6 +340,8 @@ export function useCatalogSearch() { // State selectMode, infoId, + infoTab, + infoProduct, displayMode, catalogMode, productId, @@ -359,6 +375,8 @@ export function useCatalogSearch() { setQuantity, openInfo, closeInfo, + setInfoTab, + setInfoProduct, clearAll, setLabel, setMapViewMode, diff --git a/app/pages/catalog/index.vue b/app/pages/catalog/index.vue index 8f419dc..37c4a21 100644 --- a/app/pages/catalog/index.vue +++ b/app/pages/catalog/index.vue @@ -43,12 +43,14 @@ :related-hubs="relatedHubs" :related-suppliers="relatedSuppliers" :related-offers="relatedOffers" - :selected-product="selectedProduct" + :selected-product="infoProduct ?? null" + :current-tab="infoTab" :loading="infoLoading" @close="onInfoClose" @add-to-filter="onInfoAddToFilter" @open-info="onInfoOpenRelated" - @select-product="(uuid: string | null) => uuid && selectInfoProduct(uuid)" + @select-product="onInfoSelectProduct" + @update:current-tab="setInfoTab" /> @@ -105,6 +107,8 @@ const { catalogMode, selectMode, infoId, + infoTab, + infoProduct, productId, supplierId, hubId, @@ -116,6 +120,8 @@ const { cancelSelect, openInfo, closeInfo, + setInfoTab, + setInfoProduct, setLabel } = useCatalogSearch() @@ -231,11 +237,22 @@ watch([filterByBounds, currentMapBounds], ([enabled, bounds]) => { watch(infoId, async (info) => { if (info) { await loadInfo(info.type, info.uuid) + // If URL has infoProduct, load offers for it + if (infoProduct.value) { + await selectInfoProduct(infoProduct.value) + } } else { clearInfo() } }, { immediate: true }) +// Watch infoProduct URL param to load offers when it changes +watch(infoProduct, async (productUuid) => { + if (productUuid && infoId.value) { + await selectInfoProduct(productUuid) + } +}) + // Related points for Info mode (shown on map) const relatedPoints = computed(() => { if (!infoId.value) return [] @@ -418,6 +435,11 @@ const onInfoOpenRelated = (type: 'hub' | 'supplier' | 'offer', uuid: string) => openInfo(type, uuid) } +// Handle product selection in InfoPanel - update URL which triggers loading +const onInfoSelectProduct = (uuid: string | null) => { + setInfoProduct(uuid) +} + // Search for offers const onSearch = async () => { if (!canSearch.value) return