From 6545eeabeaf973757c553d28984a6bfb4d9b08d9 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev Date: Mon, 26 Jan 2026 21:40:44 +0700 Subject: [PATCH] feat(catalog): persist bounds filter state in URL - Add urlBounds and filterByBounds computed from URL query - Add setBoundsInUrl and clearBoundsFromUrl actions - Update index.vue to use URL-based bounds state - Bounds written to URL as comma-separated values (west,south,east,north) This enables sharing links with map viewport bounds filter. --- app/composables/useCatalogSearch.ts | 31 +++++++++++++++++++++++++++++ app/pages/catalog/index.vue | 22 ++++++++++++++------ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/app/composables/useCatalogSearch.ts b/app/composables/useCatalogSearch.ts index 2394fa2..1e86674 100644 --- a/app/composables/useCatalogSearch.ts +++ b/app/composables/useCatalogSearch.ts @@ -84,6 +84,18 @@ export function useCatalogSearch() { const hubId = computed(() => route.query.hub as string | undefined) const quantity = computed(() => route.query.qty as string | undefined) + // Map bounds from URL (format: west,south,east,north) + const urlBounds = computed(() => { + const b = route.query.bounds as string | undefined + if (!b) return null + const parts = b.split(',').map(Number) + if (parts.length !== 4 || parts.some(isNaN)) return null + return { west: parts[0], south: parts[1], east: parts[2], north: parts[3] } + }) + + // Filter by bounds checkbox state from URL + const filterByBounds = computed(() => route.query.bounds !== undefined) + // Get label for a filter (from cache or fallback to ID) const getLabel = (type: string, id: string | undefined): string | null => { if (!id) return null @@ -237,6 +249,21 @@ export function useCatalogSearch() { updateQuery({ qty }) } + // Set map bounds in URL (for filter by map feature) + const setBoundsInUrl = (bounds: { west: number; south: number; east: number; north: number } | null) => { + if (bounds) { + const boundsStr = `${bounds.west.toFixed(4)},${bounds.south.toFixed(4)},${bounds.east.toFixed(4)},${bounds.north.toFixed(4)}` + updateQuery({ bounds: boundsStr }) + } else { + updateQuery({ bounds: null }) + } + } + + // Clear bounds from URL + const clearBoundsFromUrl = () => { + updateQuery({ bounds: null }) + } + const openInfo = (type: InfoEntityType, uuid: string) => { updateQuery({ info: `${type}:${uuid}`, select: null, infoTab: null, infoProduct: null }) } @@ -350,6 +377,8 @@ export function useCatalogSearch() { quantity, searchQuery, mapViewMode, + urlBounds, + filterByBounds, // Drawer state isDrawerOpen, @@ -373,6 +402,8 @@ export function useCatalogSearch() { removeFilter, editFilter, setQuantity, + setBoundsInUrl, + clearBoundsFromUrl, openInfo, closeInfo, setInfoTab, diff --git a/app/pages/catalog/index.vue b/app/pages/catalog/index.vue index 0c5ecb3..a02ce66 100644 --- a/app/pages/catalog/index.vue +++ b/app/pages/catalog/index.vue @@ -13,7 +13,7 @@ :related-points="relatedPoints" @select="onMapSelect" @bounds-change="onBoundsChange" - @update:filter-by-bounds="filterByBounds = $event" + @update:filter-by-bounds="$event ? setBoundsInUrl(currentMapBounds) : clearBoundsFromUrl()" >