Fix Info panel - translations, two-step offers flow, icon, add to filter
All checks were successful
Build Docker Image / build (push) Successful in 3m36s
All checks were successful
Build Docker Image / build (push) Successful in 3m36s
- Add i18n translations for entities, tabs, and info sections (EN/RU) - Refactor offers tab to two-step flow (products → offers) for Hub/Supplier - Replace entity badge with circular icon in header - Fix "Add to filter" button with name fallback and proper cleanup - Update selectItem() to clear info param when adding to filter
This commit is contained in:
@@ -3,9 +3,12 @@
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="badge badge-sm" :style="{ backgroundColor: badgeColor }">
|
||||
{{ badgeLabel }}
|
||||
</span>
|
||||
<div
|
||||
class="flex items-center justify-center w-6 h-6 rounded-full"
|
||||
:style="{ backgroundColor: badgeColor }"
|
||||
>
|
||||
<Icon :name="entityIcon" size="14" class="text-white" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-base text-base-content">{{ entityName }}</h3>
|
||||
</div>
|
||||
<button
|
||||
@@ -48,11 +51,11 @@
|
||||
|
||||
<!-- Tab content -->
|
||||
<div class="flex flex-col gap-2 min-h-[200px]">
|
||||
<!-- Products tab -->
|
||||
<div v-if="currentTab === 'products'">
|
||||
<!-- Products tab (only for Offer entity) -->
|
||||
<div v-if="currentTab === 'products' && entityType === 'offer'">
|
||||
<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" />
|
||||
<p>{{ $t('catalog.info.noProducts') }}</p>
|
||||
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<ProductCard
|
||||
@@ -100,26 +103,58 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Offers tab -->
|
||||
<!-- Offers tab (two-step for Hub/Supplier) -->
|
||||
<div v-if="currentTab === 'offers'">
|
||||
<div v-if="!selectedProduct" class="text-center py-8 text-white/60">
|
||||
<!-- Step 1: Select product (for Hub/Supplier) -->
|
||||
<div v-if="!selectedProduct && (entityType === 'hub' || entityType === 'supplier')">
|
||||
<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" />
|
||||
<p>{{ $t('catalog.empty.noProducts') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<ProductCard
|
||||
v-for="product in relatedProducts"
|
||||
:key="product.uuid"
|
||||
:product="product"
|
||||
selectable
|
||||
compact
|
||||
@select="onProductSelect(product)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Show offers for selected product -->
|
||||
<div v-else-if="selectedProduct">
|
||||
<!-- Back button to products -->
|
||||
<button
|
||||
class="btn btn-sm btn-ghost mb-2 text-white/80 hover:text-white"
|
||||
@click="emit('select-product', null)"
|
||||
>
|
||||
<Icon name="lucide:arrow-left" size="16" />
|
||||
{{ $t('common.back') }}
|
||||
</button>
|
||||
|
||||
<div v-if="relatedOffers.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:shopping-bag" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.empty.noOffers') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<OfferCard
|
||||
v-for="offer in relatedOffers"
|
||||
:key="offer.uuid"
|
||||
:offer="offer"
|
||||
selectable
|
||||
compact
|
||||
@select="onOfferSelect(offer)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- For Offer entity type - just show message -->
|
||||
<div v-else class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:info" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.selectProductFirst') }}</p>
|
||||
</div>
|
||||
<div v-else-if="relatedOffers.length === 0" class="text-center py-8 text-white/60">
|
||||
<Icon name="lucide:shopping-bag" size="32" class="mb-2 opacity-50" />
|
||||
<p>{{ $t('catalog.info.noOffers') }}</p>
|
||||
</div>
|
||||
<div v-else class="flex flex-col gap-2">
|
||||
<OfferCard
|
||||
v-for="offer in relatedOffers"
|
||||
:key="offer.uuid"
|
||||
:offer="offer"
|
||||
selectable
|
||||
compact
|
||||
@select="onOfferSelect(offer)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -158,7 +193,7 @@ const { t } = useI18n()
|
||||
const { entityColors } = useCatalogSearch()
|
||||
|
||||
// Current active tab
|
||||
const currentTab = ref<string>('products')
|
||||
const currentTab = ref<string>('offers')
|
||||
|
||||
// Entity name
|
||||
const entityName = computed(() => {
|
||||
@@ -180,20 +215,24 @@ const badgeLabel = computed(() => {
|
||||
return ''
|
||||
})
|
||||
|
||||
const entityIcon = computed(() => {
|
||||
if (props.entityType === 'hub') return 'lucide:warehouse'
|
||||
if (props.entityType === 'supplier') return 'lucide:factory'
|
||||
if (props.entityType === 'offer') return 'lucide:shopping-bag'
|
||||
return 'lucide:info'
|
||||
})
|
||||
|
||||
// Available tabs based on entity type and data
|
||||
const availableTabs = computed(() => {
|
||||
const tabs: Array<{ id: string; label: string; count?: number }> = []
|
||||
|
||||
if (props.entityType === 'hub') {
|
||||
tabs.push({
|
||||
id: 'products',
|
||||
label: t('catalog.tabs.products'),
|
||||
count: props.relatedProducts?.length || 0
|
||||
})
|
||||
tabs.push({
|
||||
id: 'offers',
|
||||
label: t('catalog.tabs.offers'),
|
||||
count: props.relatedOffers?.length || 0
|
||||
count: props.selectedProduct
|
||||
? props.relatedOffers?.length || 0
|
||||
: props.relatedProducts?.length || 0
|
||||
})
|
||||
tabs.push({
|
||||
id: 'suppliers',
|
||||
@@ -201,15 +240,12 @@ const availableTabs = computed(() => {
|
||||
count: props.relatedSuppliers?.length || 0
|
||||
})
|
||||
} else if (props.entityType === 'supplier') {
|
||||
tabs.push({
|
||||
id: 'products',
|
||||
label: t('catalog.tabs.products'),
|
||||
count: props.relatedProducts?.length || 0
|
||||
})
|
||||
tabs.push({
|
||||
id: 'offers',
|
||||
label: t('catalog.tabs.offers'),
|
||||
count: props.relatedOffers?.length || 0
|
||||
count: props.selectedProduct
|
||||
? props.relatedOffers?.length || 0
|
||||
: props.relatedProducts?.length || 0
|
||||
})
|
||||
tabs.push({
|
||||
id: 'hubs',
|
||||
|
||||
@@ -210,7 +210,8 @@ export function useCatalogSearch() {
|
||||
setLabel(type, id, label)
|
||||
updateQuery({
|
||||
[type]: id,
|
||||
select: null // Exit selection mode
|
||||
select: null, // Exit selection mode
|
||||
info: null // Exit info mode
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -395,9 +395,11 @@ const onInfoClose = () => {
|
||||
const onInfoAddToFilter = () => {
|
||||
if (!infoId.value || !entity.value) return
|
||||
const { type, uuid } = infoId.value
|
||||
const name = entity.value.name
|
||||
// Fallback for name - offer entities might use productName
|
||||
const name = entity.value.name || entity.value.productName || uuid.slice(0, 8) + '...'
|
||||
selectItem(type, uuid, name)
|
||||
closeInfo()
|
||||
clearInfo()
|
||||
}
|
||||
|
||||
const onInfoOpenRelated = (type: 'hub' | 'supplier' | 'offer', uuid: string) => {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"catalog": {
|
||||
"entities": {
|
||||
"hub": "Hub",
|
||||
"supplier": "Supplier",
|
||||
"offer": "Offer"
|
||||
},
|
||||
"filters": {
|
||||
"product": "Product",
|
||||
"supplier": "Supplier",
|
||||
@@ -41,6 +46,18 @@
|
||||
"offers": "Offers",
|
||||
"map": "Map"
|
||||
},
|
||||
"tabs": {
|
||||
"products": "Products",
|
||||
"offers": "Offers",
|
||||
"suppliers": "Suppliers",
|
||||
"hubs": "Hubs",
|
||||
"product": "Product",
|
||||
"supplier": "Supplier"
|
||||
},
|
||||
"info": {
|
||||
"selectProductFirst": "Select a product first",
|
||||
"addToFilter": "Add to filter"
|
||||
},
|
||||
"modes": {
|
||||
"explore": "Explore",
|
||||
"quote": "Get Quote"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"catalog": {
|
||||
"entities": {
|
||||
"hub": "Хаб",
|
||||
"supplier": "Поставщик",
|
||||
"offer": "Оффер"
|
||||
},
|
||||
"filters": {
|
||||
"product": "Товар",
|
||||
"supplier": "Поставщик",
|
||||
@@ -41,6 +46,18 @@
|
||||
"offers": "Офферы",
|
||||
"map": "Карта"
|
||||
},
|
||||
"tabs": {
|
||||
"products": "Товары",
|
||||
"offers": "Офферы",
|
||||
"suppliers": "Поставщики",
|
||||
"hubs": "Хабы",
|
||||
"product": "Товар",
|
||||
"supplier": "Поставщик"
|
||||
},
|
||||
"info": {
|
||||
"selectProductFirst": "Сначала выберите товар",
|
||||
"addToFilter": "Добавить в фильтр"
|
||||
},
|
||||
"modes": {
|
||||
"explore": "Исследовать",
|
||||
"quote": "Найти офферы"
|
||||
|
||||
Reference in New Issue
Block a user