Files
optovia/webapp/app/components/catalog/CatalogOffersSection.vue
Ruslan Bakiev 3596c3757b feat: unified catalog page with list/map toggle
- Add viewMode toggle (Список/Карта) to catalog page
- Create CatalogHubsSection with country grouping
- Create CatalogOffersSection with status badges
- Create CatalogSuppliersSection with verification badges
- Create CatalogMapView with left panel tabs (Хабы/Офферы/Компании)
- Create CatalogMapItem for clickable list items with flyTo animation
- Integrate with existing MapboxGlobe component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 19:07:01 +07:00

114 lines
3.2 KiB
Vue

<template>
<Section variant="plain" paddingY="md">
<Card>
<Stack gap="4">
<Stack direction="row" align="center" justify="between">
<Heading :level="2">Предложения</Heading>
<NuxtLink :to="localePath('/catalog/offers')">
<Button variant="secondary" size="small">Смотреть все</Button>
</NuxtLink>
</Stack>
<Grid :cols="1" :md="2" :lg="3" :gap="4">
<Card
v-for="offer in displayedOffers"
:key="offer.uuid"
padding="sm"
tone="muted"
interactive
>
<Stack gap="3">
<Stack gap="1">
<Text size="base" weight="semibold">{{ offer.title }}</Text>
<Text tone="muted">{{ offer.locationName || 'Локация не указана' }}</Text>
</Stack>
<Stack direction="row" gap="2" wrap>
<Pill
v-for="line in (offer.lines || []).slice(0, 3)"
:key="line?.uuid"
variant="outline"
>
{{ line?.productName }}
</Pill>
<Pill v-if="(offer.lines?.length || 0) > 3" variant="outline">
+{{ (offer.lines?.length || 0) - 3 }}
</Pill>
</Stack>
<Stack direction="row" align="center" justify="between">
<Badge :variant="getStatusVariant(offer.status)">
{{ getStatusLabel(offer.status) }}
</Badge>
<Text v-if="offer.validUntil" tone="muted" size="sm">
до {{ formatDate(offer.validUntil) }}
</Text>
</Stack>
</Stack>
</Card>
</Grid>
<Stack v-if="offers.length === 0" align="center" gap="2">
<Text tone="muted">Нет активных предложений</Text>
</Stack>
</Stack>
</Card>
</Section>
</template>
<script setup lang="ts">
interface OfferLine {
uuid?: string | null
productName?: string | null
}
interface Offer {
uuid?: string | null
title?: string | null
locationName?: string | null
status?: string | null
validUntil?: string | null
lines?: (OfferLine | null)[] | null
}
const props = defineProps<{
offers: Offer[]
}>()
const localePath = useLocalePath()
const displayedOffers = computed(() => props.offers.slice(0, 6))
const getStatusVariant = (status: string | null | undefined) => {
const variants: Record<string, string> = {
ACTIVE: 'success',
DRAFT: 'warning',
CANCELLED: 'error',
CLOSED: 'muted'
}
return variants[status || ''] || 'muted'
}
const getStatusLabel = (status: string | null | undefined) => {
const labels: Record<string, string> = {
ACTIVE: 'Активно',
DRAFT: 'Черновик',
CANCELLED: 'Отменено',
CLOSED: 'Закрыто'
}
return labels[status || ''] || status || 'Неизвестно'
}
const formatDate = (dateStr: string | null | undefined) => {
if (!dateStr) return ''
try {
return new Intl.DateTimeFormat('ru', {
day: 'numeric',
month: 'short'
}).format(new Date(dateStr))
} catch {
return dateStr
}
}
</script>