feat(catalog): add loading states for InfoPanel tabs and filter map by active tab
All checks were successful
Build Docker Image / build (push) Successful in 3m35s

- Add separate loading states for products, hubs, suppliers, offers
- Show spinner on tabs while loading, disable tab during load
- Filter relatedPoints on map by current active tab
This commit is contained in:
Ruslan Bakiev
2026-01-26 17:49:59 +07:00
parent f680740f52
commit 3f56a2f117
3 changed files with 156 additions and 93 deletions

View File

@@ -41,11 +41,17 @@
:key="tab.id"
role="tab"
class="tab text-white/70"
:class="{ 'tab-active !text-white !bg-white/20': activeTab === tab.id }"
@click="onTabClick(tab.id)"
:class="{
'tab-active !text-white !bg-white/20': activeTab === tab.id,
'pointer-events-none opacity-50': tab.loading
}"
@click="!tab.loading && onTabClick(tab.id)"
>
{{ tab.label }}
<span v-if="tab.count !== undefined" class="ml-1 opacity-70">({{ tab.count }})</span>
<span v-if="tab.loading" class="ml-1">
<span class="loading loading-spinner loading-xs" />
</span>
<span v-else-if="tab.count !== undefined" class="ml-1 opacity-70">({{ tab.count }})</span>
</a>
</div>
@@ -185,6 +191,10 @@ const props = defineProps<{
selectedProduct?: string | null
currentTab?: string
loading?: boolean
loadingProducts?: boolean
loadingHubs?: boolean
loadingSuppliers?: boolean
loadingOffers?: boolean
}>()
const emit = defineEmits<{
@@ -226,33 +236,45 @@ const entityIcon = computed(() => {
// Available tabs based on entity type and data
const availableTabs = computed(() => {
const tabs: Array<{ id: string; label: string; count?: number }> = []
const tabs: Array<{ id: string; label: string; count?: number; loading?: boolean }> = []
if (props.entityType === 'hub') {
// For hub: offers tab shows products first, then offers after product selection
const offersLoading = props.selectedProduct ? props.loadingOffers : props.loadingProducts
const offersCount = props.selectedProduct
? props.relatedOffers?.length
: props.relatedProducts?.length
tabs.push({
id: 'offers',
label: t('catalog.tabs.offers'),
count: props.selectedProduct
? props.relatedOffers?.length || 0
: props.relatedProducts?.length || 0
count: offersLoading ? undefined : (offersCount || 0),
loading: offersLoading
})
tabs.push({
id: 'suppliers',
label: t('catalog.tabs.suppliers'),
count: props.relatedSuppliers?.length || 0
count: props.loadingSuppliers ? undefined : (props.relatedSuppliers?.length || 0),
loading: props.loadingSuppliers
})
} else if (props.entityType === 'supplier') {
// For supplier: offers tab shows products first, then offers after product selection
const offersLoading = props.selectedProduct ? props.loadingOffers : props.loadingProducts
const offersCount = props.selectedProduct
? props.relatedOffers?.length
: props.relatedProducts?.length
tabs.push({
id: 'offers',
label: t('catalog.tabs.offers'),
count: props.selectedProduct
? props.relatedOffers?.length || 0
: props.relatedProducts?.length || 0
count: offersLoading ? undefined : (offersCount || 0),
loading: offersLoading
})
tabs.push({
id: 'hubs',
label: t('catalog.tabs.hubs'),
count: props.relatedHubs?.length || 0
count: props.loadingHubs ? undefined : (props.relatedHubs?.length || 0),
loading: props.loadingHubs
})
} else if (props.entityType === 'offer') {
if (props.relatedProducts && props.relatedProducts.length > 0) {
@@ -264,12 +286,15 @@ const availableTabs = computed(() => {
tabs.push({
id: 'hubs',
label: t('catalog.tabs.hubs'),
count: props.relatedHubs?.length || 0
count: props.loadingHubs ? undefined : (props.relatedHubs?.length || 0),
loading: props.loadingHubs
})
if (props.relatedSuppliers && props.relatedSuppliers.length > 0) {
tabs.push({
id: 'suppliers',
label: t('catalog.tabs.supplier')
label: t('catalog.tabs.supplier'),
count: props.loadingSuppliers ? undefined : (props.relatedSuppliers?.length || 0),
loading: props.loadingSuppliers
})
}
}