Files
webapp/app/pages/catalog/offers/index.vue
Ruslan Bakiev 1e87a14065
All checks were successful
Build Docker Image / build (push) Successful in 4m49s
feat(catalog): implement step-by-step navigation for offers and suppliers
- Transform offers/index.vue to show products list with sparkline charts
- Create nested routes for offers: /offers → /offers/[productId] → /offers/[productId]/[hubId]
- Create nested routes for suppliers: /suppliers → /suppliers/[supplierId] → /suppliers/[supplierId]/[productId] → /suppliers/[supplierId]/[productId]/[hubId]
- Add OffersBreadcrumbs and SuppliersBreadcrumbs components for navigation
- Update HubCard to accept custom linkTo prop
- Key difference: Suppliers calculation uses FindRoutes (single source), Offers uses FindProductRoutes (all sources)
2026-01-16 00:52:47 +07:00

80 lines
2.4 KiB
Vue

<template>
<Stack gap="0">
<!-- Loading -->
<Section v-if="isLoading" variant="plain" paddingY="lg">
<Stack align="center" justify="center" gap="4">
<Spinner />
<Text tone="muted">{{ t('catalogProducts.states.loading') }}</Text>
</Stack>
</Section>
<!-- Empty -->
<Section v-else-if="products.length === 0" variant="plain" paddingY="lg">
<Card padding="lg">
<Stack align="center" gap="4">
<IconCircle tone="primary">
<Icon name="lucide:package" size="24" />
</IconCircle>
<Heading :level="2">{{ t('catalogProducts.empty.title') }}</Heading>
<Text tone="muted">{{ t('catalogProducts.empty.subtitle') }}</Text>
</Stack>
</Card>
</Section>
<!-- Content -->
<Section v-else variant="plain" paddingY="lg">
<Stack gap="4">
<!-- Header -->
<div>
<Heading :level="1">{{ t('catalogProducts.header.title') }}</Heading>
<Text tone="muted" size="sm">{{ t('catalogProducts.header.subtitle', { count: products.length }) }}</Text>
</div>
<!-- Products grid -->
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
<HubProductCard
v-for="product in products"
:key="product.uuid"
:name="product.name || ''"
:price-history="getMockPriceHistory(product.uuid)"
@select="goToProduct(product.uuid)"
/>
</div>
</Stack>
</Section>
</Stack>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'topnav'
})
const localePath = useLocalePath()
const { t } = useI18n()
const { items: products, isLoading, init } = useCatalogProducts()
// Navigate to product detail
const goToProduct = (productId: string) => {
navigateTo(localePath(`/catalog/offers/${productId}`))
}
// Mock price history generator (seeded by uuid for consistent results)
const getMockPriceHistory = (uuid: string): number[] => {
const seed = uuid.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
const basePrice = 100 + (seed % 200)
return Array.from({ length: 7 }, (_, i) => {
const variation = Math.sin(seed + i * 0.5) * 20 + Math.cos(seed * 0.3 + i) * 10
return Math.round(basePrice + variation)
})
}
// Initialize
await init()
useHead(() => ({
title: t('catalogProducts.header.title')
}))
</script>