Files
webapp/app/pages/catalog/hubs/index.vue
Ruslan Bakiev 2abbfd8895
All checks were successful
Build Docker Image / build (push) Successful in 3m42s
fix(catalog): remove hover/select animations from intermediate pages
Animation on map only needed on final pages (routes/sources).
Intermediate pages now navigate directly on card click.

Files changed:
- hubs/index.vue - removed hover/select
- suppliers/index.vue - removed hover/select
- suppliers/[]/[]/index.vue - removed hover
- offers/index.vue - removed hover
- offers/[]/index.vue - removed hover
2026-01-19 12:25:58 +07:00

172 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<CatalogPage
:items="displayItems"
:map-items="itemsWithCoords"
:loading="isLoading"
with-map
use-server-clustering
map-id="hubs-map"
point-color="#10b981"
:total-count="total"
>
<template #searchBar="{ displayedCount, totalCount }">
<CatalogSearchBar
v-model:search-query="searchQuery"
:active-filters="activeFilterBadges"
:displayed-count="displayedCount"
:total-count="totalCount"
@remove-filter="onRemoveFilter"
@search="onSearch"
>
<template #filters>
<div class="p-2 space-y-3">
<!-- Transport filter -->
<div>
<div class="text-xs font-semibold mb-1 text-base-content/70">{{ t('catalogHubsSection.filters.transport') }}</div>
<ul class="menu menu-compact">
<li v-for="filter in filters" :key="filter.id">
<a
:class="{ 'active': selectedFilter === filter.id }"
@click="selectedFilter = filter.id"
>{{ filter.label }}</a>
</li>
</ul>
</div>
<div class="divider my-0"></div>
<!-- Country filter -->
<div>
<div class="text-xs font-semibold mb-1 text-base-content/70">{{ t('catalogHubsSection.filters.country') }}</div>
<ul class="menu menu-compact max-h-48 overflow-y-auto">
<li v-for="filter in countryFilters" :key="filter.id">
<a
:class="{ 'active': selectedCountry === filter.id }"
@click="selectedCountry = filter.id"
>{{ filter.label }}</a>
</li>
</ul>
</div>
</div>
</template>
</CatalogSearchBar>
</template>
<template #header>
<Text v-if="!isLoading" tone="muted">Выберите хаб</Text>
</template>
<template #card="{ item }">
<HubCard :hub="item" />
</template>
<template #pagination>
<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"
/>
</template>
<template #empty>
<Text tone="muted">{{ t('catalogHubsSection.empty.no_hubs') }}</Text>
</template>
</CatalogPage>
</template>
<script setup lang="ts">
import type { MapBounds } from '~/components/catalog/CatalogMap.vue'
definePageMeta({
layout: 'topnav'
})
const { t } = useI18n()
const {
items,
total,
selectedFilter,
selectedCountry,
filters,
countryFilters,
isLoading,
isLoadingMore,
canLoadMore,
loadMore,
init
} = useCatalogHubs()
// Search bar
const searchQuery = ref('')
// Search with map checkbox
const searchWithMap = ref(false)
const currentBounds = ref<MapBounds | null>(null)
// 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,
name: item.name || '',
latitude: Number(item.latitude),
longitude: Number(item.longitude),
country: item.country
}))
)
// 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
})
})
// Active filter badges (non-default filters shown as badges)
const activeFilterBadges = computed(() => {
const badges: { id: string; label: string }[] = []
if (selectedFilter.value !== 'all') {
const filter = filters.value.find(f => f.id === selectedFilter.value)
if (filter) badges.push({ id: `transport:${filter.id}`, label: filter.label })
}
if (selectedCountry.value !== 'all') {
const filter = countryFilters.value.find(f => f.id === selectedCountry.value)
if (filter) badges.push({ id: `country:${filter.id}`, label: filter.label })
}
return badges
})
// Remove filter badge
const onRemoveFilter = (id: string) => {
if (id.startsWith('transport:')) {
selectedFilter.value = 'all'
} else if (id.startsWith('country:')) {
selectedCountry.value = 'all'
}
}
// Search handler (for future use)
const onSearch = () => {
// TODO: Implement search by hub name
}
await init()
useHead(() => ({
title: t('catalogHubsSection.header.title')
}))
</script>