Files
webapp/app/components/catalog/CatalogMapPanel.vue
2026-01-07 09:10:35 +07:00

156 lines
4.3 KiB
Vue

<template>
<Card class="h-full flex flex-col overflow-hidden">
<!-- Tabs -->
<div class="flex border-b border-base-300 flex-shrink-0 bg-base-200/70 rounded-t-xl">
<button
v-for="tab in tabs"
:key="tab.id"
class="flex-1 px-4 py-3 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring focus-visible:ring-primary/30"
:class="[
activeTab === tab.id
? 'text-primary border-b-2 border-primary bg-primary/10'
: 'text-base-content/70 hover:text-base-content hover:bg-base-200'
]"
@click="activeTab = tab.id"
>
{{ t(tab.label) }}
<span class="ml-1 text-xs text-base-content/60">({{ tab.count }})</span>
</button>
</div>
<!-- List -->
<div class="flex-1 overflow-y-auto p-3 space-y-2 bg-base-100">
<!-- Hubs Tab -->
<template v-if="activeTab === 'hubs'">
<HubCard
v-for="hub in hubs"
:key="hub.uuid"
:hub="hub"
selectable
:is-selected="selectedId === hub.uuid"
@select="selectHub(hub)"
/>
<Text v-if="hubs.length === 0" tone="muted" size="sm" class="text-center py-4">
{{ t('catalogMap.empty.hubs') }}
</Text>
</template>
<!-- Offers Tab -->
<template v-if="activeTab === 'offers'">
<OfferCard
v-for="offer in offers"
:key="offer.uuid"
:offer="offer"
selectable
compact
:is-selected="selectedId === offer.uuid"
@select="selectOffer(offer)"
/>
<Text v-if="offers.length === 0" tone="muted" size="sm" class="text-center py-4">
{{ t('catalogMap.empty.offers') }}
</Text>
</template>
<!-- Suppliers Tab -->
<template v-if="activeTab === 'suppliers'">
<SupplierCard
v-for="supplier in suppliers"
:key="supplier.uuid"
:supplier="supplier"
selectable
:is-selected="selectedId === supplier.uuid"
@select="selectSupplier(supplier)"
/>
<Text v-if="suppliers.length === 0" tone="muted" size="sm" class="text-center py-4">
{{ t('catalogMap.empty.suppliers') }}
</Text>
</template>
</div>
</Card>
</template>
<script setup lang="ts">
interface Hub {
uuid?: string | null
name?: string | null
country?: string | null
latitude?: number | null
longitude?: number | null
distance?: string
}
interface Offer {
uuid?: string | null
title?: string | null
locationName?: string | null
status?: string | null
latitude?: number | null
longitude?: number | null
lines?: any[] | null
}
interface Supplier {
uuid?: string | null
name?: string | null
country?: string | null
offersCount?: number | null
isVerified?: boolean | null
}
const props = defineProps<{
hubs: Hub[]
offers: Offer[]
suppliers: Supplier[]
}>()
const emit = defineEmits<{
(e: 'fly-to', location: { uuid: string; name: string; latitude: number; longitude: number; country?: string }): void
}>()
type TabId = 'hubs' | 'offers' | 'suppliers'
const activeTab = ref<TabId>('hubs')
const selectedId = ref<string | null>(null)
const { t } = useI18n()
const tabs = computed(() => [
{ id: 'hubs' as const, label: 'catalogMap.tabs.hubs', count: props.hubs.length },
{ id: 'offers' as const, label: 'catalogMap.tabs.offers', count: props.offers.length },
{ id: 'suppliers' as const, label: 'catalogMap.tabs.suppliers', count: props.suppliers.length }
])
const selectHub = (hub: Hub) => {
selectedId.value = hub.uuid || null
if (hub.latitude && hub.longitude) {
emit('fly-to', {
uuid: hub.uuid!,
name: hub.name || '',
latitude: hub.latitude,
longitude: hub.longitude,
country: hub.country || undefined
})
}
}
const selectOffer = (offer: Offer) => {
selectedId.value = offer.uuid || null
if (offer.latitude && offer.longitude) {
emit('fly-to', {
uuid: offer.uuid!,
name: offer.title || '',
latitude: offer.latitude,
longitude: offer.longitude,
country: offer.locationName || undefined
})
}
}
const selectSupplier = (supplier: Supplier) => {
selectedId.value = supplier.uuid || null
// Suppliers don't have coordinates currently
}
</script>