Files
webapp/app/pages/catalog/suppliers/index.vue
Ruslan Bakiev 7ea96a97b3
All checks were successful
Build Docker Image / build (push) Successful in 4m1s
Refactor catalog layout: replace Teleport with provide/inject
- Create useCatalogLayout composable for data transfer from pages to layout
- Layout now owns SearchBar and CatalogMap components directly
- Pages provide data via provideCatalogLayout()
- Fixes navigation glitches (multiple SearchBars) when switching tabs
- Support custom subNavItems for clientarea pages
- Unify 6 pages to use catalog layout:
  - catalog/offers, suppliers, hubs
  - clientarea/orders, addresses, offers
2026-01-15 10:49:40 +07:00

161 lines
4.3 KiB
Vue

<template>
<div>
<!-- List content -->
<div v-if="isLoading" class="flex items-center justify-center py-8">
<Card padding="lg">
<Stack align="center" justify="center" gap="3">
<Spinner />
<Text tone="muted">{{ t('catalogLanding.states.loading') }}</Text>
</Stack>
</Card>
</div>
<template v-else>
<Stack gap="3">
<div
v-for="item in displayItems"
:key="item.uuid || item.teamUuid"
:class="{ 'ring-2 ring-primary rounded-lg': (item.uuid || item.teamUuid) === selectedSupplierId }"
@click="onSelectSupplier(item)"
@mouseenter="hoveredSupplierId = item.uuid || item.teamUuid"
@mouseleave="hoveredSupplierId = undefined"
>
<SupplierCard :supplier="item" />
</div>
</Stack>
<PaginationLoadMore
v-if="displayItems.length > 0"
:shown="displayItems.length"
:total="total"
:can-load-more="canLoadMore"
:loading="isLoadingMore"
hide-counter
@load-more="loadMore"
class="mt-4"
/>
<Stack v-if="displayItems.length === 0" align="center" gap="2" class="py-8">
<Text tone="muted">{{ t('catalogSuppliersSection.empty.no_suppliers') }}</Text>
</Stack>
</template>
</div>
</template>
<script setup lang="ts">
import type { MapBounds } from '~/components/catalog/CatalogMap.vue'
import { provideCatalogLayout } from '~/composables/useCatalogLayout'
definePageMeta({
layout: 'catalog'
})
const { t } = useI18n()
const {
items,
total,
isLoading,
isLoadingMore,
canLoadMore,
loadMore,
init
} = useCatalogSuppliers()
// Selected/hovered supplier for map highlighting
const selectedSupplierId = ref<string>()
const hoveredSupplierId = ref<string>()
// Search bar
const searchQuery = ref('')
// Search with map checkbox
const searchWithMap = ref(false)
const currentBounds = ref<MapBounds | null>(null)
const onBoundsChange = (bounds: MapBounds) => {
currentBounds.value = bounds
}
// Filter items with valid coordinates for map
const itemsWithCoords = computed(() =>
items.value.filter(item =>
item.latitude != null &&
item.longitude != null &&
!isNaN(Number(item.latitude)) &&
!isNaN(Number(item.longitude))
).map(item => ({
uuid: item.uuid || item.teamUuid,
name: item.name || '',
latitude: Number(item.latitude),
longitude: Number(item.longitude)
}))
)
// Filtered items when searchWithMap is enabled
const displayItems = computed(() => {
if (!searchWithMap.value || !currentBounds.value) return items.value
return items.value.filter(item => {
if (item.latitude == null || item.longitude == null) return false
const { west, east, north, south } = currentBounds.value!
const lng = Number(item.longitude)
const lat = Number(item.latitude)
return lng >= west && lng <= east && lat >= south && lat <= north
})
})
// Hovered item with coordinates for map highlight
const hoveredItem = computed(() => {
if (!hoveredSupplierId.value) return null
const item = items.value.find(i => (i.uuid || i.teamUuid) === hoveredSupplierId.value)
if (!item?.latitude || !item?.longitude) return null
return { latitude: Number(item.latitude), longitude: Number(item.longitude) }
})
// Search handler (for future use)
const onSearch = () => {
// TODO: Implement search
}
// No filters for suppliers
const onRemoveFilter = (_id: string) => {
// No filters to remove
}
const onSelectSupplier = (supplier: any) => {
selectedSupplierId.value = supplier.uuid || supplier.teamUuid
}
// Handle selection from map
const onMapSelect = (uuid: string) => {
const supplier = items.value.find(i => (i.uuid || i.teamUuid) === uuid)
if (supplier) {
selectedSupplierId.value = uuid
}
}
// Provide data to layout (no filter component for suppliers)
provideCatalogLayout({
searchQuery,
activeFilters: ref([]),
displayedCount: computed(() => displayItems.value.length),
totalCount: computed(() => total.value),
onSearch,
onRemoveFilter,
mapItems: itemsWithCoords,
mapId: 'suppliers-map',
pointColor: '#3b82f6',
hoveredItemId: hoveredSupplierId,
hoveredItem,
onMapSelect,
onBoundsChange,
searchWithMap
})
await init()
useHead(() => ({
title: t('catalogSuppliersSection.header.title')
}))
</script>