diff --git a/app/composables/useCatalogInfo.ts b/app/composables/useCatalogInfo.ts index 9984c59..f33f126 100644 --- a/app/composables/useCatalogInfo.ts +++ b/app/composables/useCatalogInfo.ts @@ -23,6 +23,12 @@ export function useCatalogInfo() { const activeTab = ref('products') const isLoading = ref(false) + // Separate loading states for each tab + const isLoadingProducts = ref(false) + const isLoadingHubs = ref(false) + const isLoadingSuppliers = ref(false) + const isLoadingOffers = ref(false) + // Load hub info: hub details + products + suppliers const loadHubInfo = async (uuid: string) => { try { @@ -35,38 +41,43 @@ export function useCatalogInfo() { return } - // Load offers near hub and group by product - const offersData = await execute( - NearestOffersDocument, - { - lat: entity.value.latitude, - lon: entity.value.longitude, - radius: 500 - }, - 'public', - 'geo' - ) - - // Group offers by product - const productsMap = new Map() - offersData?.nearestOffers?.forEach((offer: any) => { - if (offer?.productUuid) { - if (!productsMap.has(offer.productUuid)) { - productsMap.set(offer.productUuid, { - uuid: offer.productUuid, - name: offer.productName, - offersCount: 0 - }) - } - productsMap.get(offer.productUuid)!.offersCount++ - } - }) - relatedProducts.value = Array.from(productsMap.values()) - // Set default active tab to offers (first step shows products) activeTab.value = 'offers' - // Note: Suppliers loaded after product selection via loadOffersForHub + // Load products (offers grouped by product) + isLoadingProducts.value = true + try { + const offersData = await execute( + NearestOffersDocument, + { + lat: entity.value.latitude, + lon: entity.value.longitude, + radius: 500 + }, + 'public', + 'geo' + ) + + // Group offers by product + const productsMap = new Map() + offersData?.nearestOffers?.forEach((offer: any) => { + if (offer?.productUuid) { + if (!productsMap.has(offer.productUuid)) { + productsMap.set(offer.productUuid, { + uuid: offer.productUuid, + name: offer.productName, + offersCount: 0 + }) + } + productsMap.get(offer.productUuid)!.offersCount++ + } + }) + relatedProducts.value = Array.from(productsMap.values()) + } finally { + isLoadingProducts.value = false + } + + // Note: Suppliers and Offers loaded after product selection via loadOffersForHub } catch (error) { console.error('Error loading hub info:', error) } @@ -99,37 +110,42 @@ export function useCatalogInfo() { return } - // Load offers near supplier and group by product - const offersData = await execute( - NearestOffersDocument, - { - lat: entity.value.latitude, - lon: entity.value.longitude, - radius: 500 - }, - 'public', - 'geo' - ) - - // Group offers by product - const productsMap = new Map() - offersData?.nearestOffers?.forEach((offer: any) => { - if (offer?.productUuid) { - if (!productsMap.has(offer.productUuid)) { - productsMap.set(offer.productUuid, { - uuid: offer.productUuid, - name: offer.productName, - offersCount: 0 - }) - } - productsMap.get(offer.productUuid)!.offersCount++ - } - }) - relatedProducts.value = Array.from(productsMap.values()) - // Set default active tab to offers (first step shows products) activeTab.value = 'offers' + // Load products (offers grouped by product) + isLoadingProducts.value = true + try { + const offersData = await execute( + NearestOffersDocument, + { + lat: entity.value.latitude, + lon: entity.value.longitude, + radius: 500 + }, + 'public', + 'geo' + ) + + // Group offers by product + const productsMap = new Map() + offersData?.nearestOffers?.forEach((offer: any) => { + if (offer?.productUuid) { + if (!productsMap.has(offer.productUuid)) { + productsMap.set(offer.productUuid, { + uuid: offer.productUuid, + name: offer.productName, + offersCount: 0 + }) + } + productsMap.get(offer.productUuid)!.offersCount++ + } + }) + relatedProducts.value = Array.from(productsMap.values()) + } finally { + isLoadingProducts.value = false + } + // Note: Hubs will be loaded after product selection } catch (error) { console.error('Error loading supplier info:', error) @@ -148,37 +164,6 @@ export function useCatalogInfo() { return } - // Load hubs near offer coordinates - const hubsData = await execute( - NearestHubsDocument, - { - lat: entity.value.latitude, - lon: entity.value.longitude, - radius: 1000, - limit: 12 - }, - 'public', - 'geo' - ) - relatedHubs.value = hubsData?.nearestHubs || [] - - // If offer has supplier UUID, load supplier profile - if (entity.value?.teamUuid) { - try { - const supplierData = await execute( - GetSupplierProfileDocument, - { uuid: entity.value.teamUuid }, - 'public', - 'exchange' - ) - relatedSuppliers.value = supplierData?.getSupplierProfile - ? [supplierData.getSupplierProfile] - : [] - } catch (e) { - // Supplier might not exist - } - } - // Set default active tab to hubs activeTab.value = 'hubs' @@ -191,6 +176,43 @@ export function useCatalogInfo() { } ] } + + // Load hubs near offer coordinates + isLoadingHubs.value = true + execute( + NearestHubsDocument, + { + lat: entity.value.latitude, + lon: entity.value.longitude, + radius: 1000, + limit: 12 + }, + 'public', + 'geo' + ).then(hubsData => { + relatedHubs.value = hubsData?.nearestHubs || [] + }).finally(() => { + isLoadingHubs.value = false + }) + + // If offer has supplier UUID, load supplier profile + if (entity.value?.teamUuid) { + isLoadingSuppliers.value = true + execute( + GetSupplierProfileDocument, + { uuid: entity.value.teamUuid }, + 'public', + 'exchange' + ).then(supplierData => { + relatedSuppliers.value = supplierData?.getSupplierProfile + ? [supplierData.getSupplierProfile] + : [] + }).catch(() => { + // Supplier might not exist + }).finally(() => { + isLoadingSuppliers.value = false + }) + } } catch (error) { console.error('Error loading offer info:', error) } @@ -205,50 +227,60 @@ export function useCatalogInfo() { return } - // Find offers near hub for this product WITH routes calculated on backend - const offersData = await execute( - NearestOffersDocument, - { - lat: hub.latitude, - lon: hub.longitude, - productUuid, - hubUuid, // Pass hubUuid to get routes calculated on backend - radius: 500, - limit: 12 - }, - 'public', - 'geo' - ) + // Load offers + isLoadingOffers.value = true + isLoadingSuppliers.value = true - // Offers already include routes from backend - relatedOffers.value = offersData?.nearestOffers || [] + try { + // Find offers near hub for this product WITH routes calculated on backend + const offersData = await execute( + NearestOffersDocument, + { + lat: hub.latitude, + lon: hub.longitude, + productUuid, + hubUuid, // Pass hubUuid to get routes calculated on backend + radius: 500, + limit: 12 + }, + 'public', + 'geo' + ) - // Extract unique suppliers from offers (use supplierUuid from offers) - const supplierUuids = new Set() - relatedOffers.value.forEach((offer: any) => { - if (offer.supplierUuid) { - supplierUuids.add(offer.supplierUuid) - } - }) + // Offers already include routes from backend + relatedOffers.value = offersData?.nearestOffers || [] + isLoadingOffers.value = false - // Load supplier profiles (limit to 12) - const suppliers: any[] = [] - for (const uuid of Array.from(supplierUuids).slice(0, 12)) { - try { - const supplierData = await execute( - GetSupplierProfileDocument, - { uuid }, - 'public', - 'exchange' - ) - if (supplierData?.getSupplierProfile) { - suppliers.push(supplierData.getSupplierProfile) + // Extract unique suppliers from offers (use supplierUuid from offers) + const supplierUuids = new Set() + relatedOffers.value.forEach((offer: any) => { + if (offer.supplierUuid) { + supplierUuids.add(offer.supplierUuid) + } + }) + + // Load supplier profiles (limit to 12) + const suppliers: any[] = [] + for (const uuid of Array.from(supplierUuids).slice(0, 12)) { + try { + const supplierData = await execute( + GetSupplierProfileDocument, + { uuid }, + 'public', + 'exchange' + ) + if (supplierData?.getSupplierProfile) { + suppliers.push(supplierData.getSupplierProfile) + } + } catch (e) { + // Supplier might not exist } - } catch (e) { - // Supplier might not exist } + relatedSuppliers.value = suppliers + } finally { + isLoadingOffers.value = false + isLoadingSuppliers.value = false } - relatedSuppliers.value = suppliers } catch (error) { console.error('Error loading offers for hub:', error) } diff --git a/app/layouts/topnav.vue b/app/layouts/topnav.vue index 5841687..d075f9b 100644 --- a/app/layouts/topnav.vue +++ b/app/layouts/topnav.vue @@ -8,9 +8,24 @@
- + +
+

+ {{ $t('hero.title', 'Оптовая торговля') }} +

+

+ {{ $t('hero.subtitle', 'Найдите лучших поставщиков и товары') }} +

+
+ + { return {} }) +// MainNavigation style - centered vertically on home page, normal on other pages +const mainNavStyle = computed(() => { + if (!isHomePage.value) return {} + // Center MainNav (100px) in container: margin-top = (height - 100) / 2 + const marginTop = Math.max(0, (heroHeight.value - 100) / 2) + return { marginTop: `${marginTop}px` } +}) + +// Hero title style - positioned above MainNav, fades on scroll +const heroTitleStyle = computed(() => { + // Position title above the centered MainNav + const navMarginTop = Math.max(0, (heroHeight.value - 100) / 2) + const titleBottom = heroHeight.value - navMarginTop + 20 // 20px gap above nav + return { + bottom: `${titleBottom}px`, + opacity: 1 - collapseProgress.value, + transform: `translateY(${collapseProgress.value * -20}px)` + } +}) + // Main content padding-top to compensate for fixed header const mainStyle = computed(() => { if (isCatalogSection.value) return { paddingTop: '0' }