Add URL params for InfoPanel tab and product (infoTab, infoProduct)
All checks were successful
Build Docker Image / build (push) Successful in 3m39s
All checks were successful
Build Docker Image / build (push) Successful in 3m39s
This commit is contained in:
@@ -41,8 +41,8 @@
|
|||||||
:key="tab.id"
|
:key="tab.id"
|
||||||
role="tab"
|
role="tab"
|
||||||
class="tab text-white/70"
|
class="tab text-white/70"
|
||||||
:class="{ 'tab-active !text-white !bg-white/20': currentTab === tab.id }"
|
:class="{ 'tab-active !text-white !bg-white/20': activeTab === tab.id }"
|
||||||
@click="currentTab = tab.id"
|
@click="onTabClick(tab.id)"
|
||||||
>
|
>
|
||||||
{{ tab.label }}
|
{{ tab.label }}
|
||||||
<span v-if="tab.count !== undefined" class="ml-1 opacity-70">({{ tab.count }})</span>
|
<span v-if="tab.count !== undefined" class="ml-1 opacity-70">({{ tab.count }})</span>
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<!-- Tab content -->
|
<!-- Tab content -->
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<!-- Products tab (only for Offer entity) -->
|
<!-- Products tab (only for Offer entity) -->
|
||||||
<div v-if="currentTab === 'products' && entityType === 'offer'">
|
<div v-if="activeTab === 'products' && entityType === 'offer'">
|
||||||
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
||||||
<Icon name="lucide:package" size="32" class="mb-2 opacity-50" />
|
<Icon name="lucide:package" size="32" class="mb-2 opacity-50" />
|
||||||
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hubs tab -->
|
<!-- Hubs tab -->
|
||||||
<div v-if="currentTab === 'hubs'">
|
<div v-if="activeTab === 'hubs'">
|
||||||
<div v-if="relatedHubs.length === 0" class="text-center py-8 text-white/60">
|
<div v-if="relatedHubs.length === 0" class="text-center py-8 text-white/60">
|
||||||
<Icon name="lucide:warehouse" size="32" class="mb-2 opacity-50" />
|
<Icon name="lucide:warehouse" size="32" class="mb-2 opacity-50" />
|
||||||
<p>{{ $t('catalog.info.noHubs') }}</p>
|
<p>{{ $t('catalog.info.noHubs') }}</p>
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Suppliers tab -->
|
<!-- Suppliers tab -->
|
||||||
<div v-if="currentTab === 'suppliers'">
|
<div v-if="activeTab === 'suppliers'">
|
||||||
<div v-if="relatedSuppliers.length === 0" class="text-center py-8 text-white/60">
|
<div v-if="relatedSuppliers.length === 0" class="text-center py-8 text-white/60">
|
||||||
<Icon name="lucide:factory" size="32" class="mb-2 opacity-50" />
|
<Icon name="lucide:factory" size="32" class="mb-2 opacity-50" />
|
||||||
<p>{{ $t('catalog.info.noSuppliers') }}</p>
|
<p>{{ $t('catalog.info.noSuppliers') }}</p>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Offers tab (two-step for Hub/Supplier) -->
|
<!-- Offers tab (two-step for Hub/Supplier) -->
|
||||||
<div v-if="currentTab === 'offers'">
|
<div v-if="activeTab === 'offers'">
|
||||||
<!-- Step 1: Select product (for Hub/Supplier) -->
|
<!-- Step 1: Select product (for Hub/Supplier) -->
|
||||||
<div v-if="!selectedProduct && (entityType === 'hub' || entityType === 'supplier')">
|
<div v-if="!selectedProduct && (entityType === 'hub' || entityType === 'supplier')">
|
||||||
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
<div v-if="relatedProducts.length === 0" class="text-center py-8 text-white/60">
|
||||||
@@ -183,6 +183,7 @@ const props = defineProps<{
|
|||||||
relatedSuppliers?: any[]
|
relatedSuppliers?: any[]
|
||||||
relatedOffers?: any[]
|
relatedOffers?: any[]
|
||||||
selectedProduct?: string | null
|
selectedProduct?: string | null
|
||||||
|
currentTab?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -191,6 +192,7 @@ const emit = defineEmits<{
|
|||||||
'add-to-filter': []
|
'add-to-filter': []
|
||||||
'open-info': [type: InfoEntityType, uuid: string]
|
'open-info': [type: InfoEntityType, uuid: string]
|
||||||
'select-product': [uuid: string | null]
|
'select-product': [uuid: string | null]
|
||||||
|
'update:current-tab': [tab: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -202,9 +204,6 @@ const relatedHubs = computed(() => props.relatedHubs ?? [])
|
|||||||
const relatedSuppliers = computed(() => props.relatedSuppliers ?? [])
|
const relatedSuppliers = computed(() => props.relatedSuppliers ?? [])
|
||||||
const relatedOffers = computed(() => props.relatedOffers ?? [])
|
const relatedOffers = computed(() => props.relatedOffers ?? [])
|
||||||
|
|
||||||
// Current active tab
|
|
||||||
const currentTab = ref<string>('offers')
|
|
||||||
|
|
||||||
// Entity name
|
// Entity name
|
||||||
const entityName = computed(() => {
|
const entityName = computed(() => {
|
||||||
return props.entity?.name || props.entity?.productName || props.entityId.slice(0, 8) + '...'
|
return props.entity?.name || props.entity?.productName || props.entityId.slice(0, 8) + '...'
|
||||||
@@ -278,17 +277,20 @@ const availableTabs = computed(() => {
|
|||||||
return tabs
|
return tabs
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set default active tab when entity type changes
|
// Active tab - use prop or default to first available
|
||||||
watch(
|
const activeTab = computed(() => {
|
||||||
() => props.entityType,
|
// If prop is set and is a valid tab, use it
|
||||||
() => {
|
if (props.currentTab && availableTabs.value.some(t => t.id === props.currentTab)) {
|
||||||
const firstTab = availableTabs.value[0]
|
return props.currentTab
|
||||||
if (firstTab) {
|
}
|
||||||
currentTab.value = firstTab.id
|
// Default to first tab
|
||||||
|
return availableTabs.value[0]?.id || 'offers'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handler for tab click
|
||||||
|
const onTabClick = (tabId: string) => {
|
||||||
|
emit('update:current-tab', tabId)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handlers for selecting related items
|
// Handlers for selecting related items
|
||||||
const onProductSelect = (product: any) => {
|
const onProductSelect = (product: any) => {
|
||||||
|
|||||||
@@ -73,6 +73,12 @@ export function useCatalogSearch() {
|
|||||||
return null
|
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 productId = computed(() => route.query.product as string | undefined)
|
||||||
const supplierId = computed(() => route.query.supplier as string | undefined)
|
const supplierId = computed(() => route.query.supplier as string | undefined)
|
||||||
const hubId = computed(() => route.query.hub 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) => {
|
const openInfo = (type: InfoEntityType, uuid: string) => {
|
||||||
updateQuery({ info: `${type}:${uuid}`, select: null })
|
updateQuery({ info: `${type}:${uuid}`, select: null, infoTab: null, infoProduct: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeInfo = () => {
|
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 = () => {
|
const clearAll = () => {
|
||||||
@@ -326,6 +340,8 @@ export function useCatalogSearch() {
|
|||||||
// State
|
// State
|
||||||
selectMode,
|
selectMode,
|
||||||
infoId,
|
infoId,
|
||||||
|
infoTab,
|
||||||
|
infoProduct,
|
||||||
displayMode,
|
displayMode,
|
||||||
catalogMode,
|
catalogMode,
|
||||||
productId,
|
productId,
|
||||||
@@ -359,6 +375,8 @@ export function useCatalogSearch() {
|
|||||||
setQuantity,
|
setQuantity,
|
||||||
openInfo,
|
openInfo,
|
||||||
closeInfo,
|
closeInfo,
|
||||||
|
setInfoTab,
|
||||||
|
setInfoProduct,
|
||||||
clearAll,
|
clearAll,
|
||||||
setLabel,
|
setLabel,
|
||||||
setMapViewMode,
|
setMapViewMode,
|
||||||
|
|||||||
@@ -43,12 +43,14 @@
|
|||||||
:related-hubs="relatedHubs"
|
:related-hubs="relatedHubs"
|
||||||
:related-suppliers="relatedSuppliers"
|
:related-suppliers="relatedSuppliers"
|
||||||
:related-offers="relatedOffers"
|
:related-offers="relatedOffers"
|
||||||
:selected-product="selectedProduct"
|
:selected-product="infoProduct ?? null"
|
||||||
|
:current-tab="infoTab"
|
||||||
:loading="infoLoading"
|
:loading="infoLoading"
|
||||||
@close="onInfoClose"
|
@close="onInfoClose"
|
||||||
@add-to-filter="onInfoAddToFilter"
|
@add-to-filter="onInfoAddToFilter"
|
||||||
@open-info="onInfoOpenRelated"
|
@open-info="onInfoOpenRelated"
|
||||||
@select-product="(uuid: string | null) => uuid && selectInfoProduct(uuid)"
|
@select-product="onInfoSelectProduct"
|
||||||
|
@update:current-tab="setInfoTab"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Quote results: show offers after search -->
|
<!-- Quote results: show offers after search -->
|
||||||
@@ -105,6 +107,8 @@ const {
|
|||||||
catalogMode,
|
catalogMode,
|
||||||
selectMode,
|
selectMode,
|
||||||
infoId,
|
infoId,
|
||||||
|
infoTab,
|
||||||
|
infoProduct,
|
||||||
productId,
|
productId,
|
||||||
supplierId,
|
supplierId,
|
||||||
hubId,
|
hubId,
|
||||||
@@ -116,6 +120,8 @@ const {
|
|||||||
cancelSelect,
|
cancelSelect,
|
||||||
openInfo,
|
openInfo,
|
||||||
closeInfo,
|
closeInfo,
|
||||||
|
setInfoTab,
|
||||||
|
setInfoProduct,
|
||||||
setLabel
|
setLabel
|
||||||
} = useCatalogSearch()
|
} = useCatalogSearch()
|
||||||
|
|
||||||
@@ -231,11 +237,22 @@ watch([filterByBounds, currentMapBounds], ([enabled, bounds]) => {
|
|||||||
watch(infoId, async (info) => {
|
watch(infoId, async (info) => {
|
||||||
if (info) {
|
if (info) {
|
||||||
await loadInfo(info.type, info.uuid)
|
await loadInfo(info.type, info.uuid)
|
||||||
|
// If URL has infoProduct, load offers for it
|
||||||
|
if (infoProduct.value) {
|
||||||
|
await selectInfoProduct(infoProduct.value)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
clearInfo()
|
clearInfo()
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { 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)
|
// Related points for Info mode (shown on map)
|
||||||
const relatedPoints = computed(() => {
|
const relatedPoints = computed(() => {
|
||||||
if (!infoId.value) return []
|
if (!infoId.value) return []
|
||||||
@@ -418,6 +435,11 @@ const onInfoOpenRelated = (type: 'hub' | 'supplier' | 'offer', uuid: string) =>
|
|||||||
openInfo(type, uuid)
|
openInfo(type, uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle product selection in InfoPanel - update URL which triggers loading
|
||||||
|
const onInfoSelectProduct = (uuid: string | null) => {
|
||||||
|
setInfoProduct(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
// Search for offers
|
// Search for offers
|
||||||
const onSearch = async () => {
|
const onSearch = async () => {
|
||||||
if (!canSearch.value) return
|
if (!canSearch.value) return
|
||||||
|
|||||||
Reference in New Issue
Block a user