Files
optovia/webapp/app/pages/dashboard/cabinet/offers.vue
Ruslan Bakiev 19c5a1e60f Add personal cabinet with team type (buyer/seller)
- Add cabinet layout with 1/6 sidebar
- Header: rename "Мои заказы" to "Личный кабинет"
- Add cabinet pages: orders, offers (seller only), new offer
- TeamCreateForm: add team type selection (BUYER/SELLER)
- Sidebar shows "Мои предложения" only for SELLER teams

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

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

151 lines
4.6 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>
<Stack gap="6">
<Stack direction="row" justify="between" align="center">
<Heading :level="1">Мои предложения</Heading>
<NuxtLink :to="localePath('/dashboard/cabinet/offers/new')">
<Button>
<Icon name="lucide:plus" size="16" class="mr-2" />
Добавить
</Button>
</NuxtLink>
</Stack>
<Alert v-if="hasError" variant="error">
<Stack gap="2">
<Heading :level="4" weight="semibold">Ошибка</Heading>
<Text tone="muted">{{ error }}</Text>
<Button @click="loadOffers">Попробовать снова</Button>
</Stack>
</Alert>
<Stack v-else-if="isLoading" align="center" justify="center" gap="3">
<Spinner />
<Text tone="muted">Загружаем предложения...</Text>
</Stack>
<template v-else>
<Stack v-if="offers.length" gap="4">
<Card v-for="offer in offers" :key="offer.uuid" padding="lg">
<Stack gap="3">
<Stack direction="row" justify="between" align="center">
<Heading :level="3">{{ offer.title || 'Без названия' }}</Heading>
<Badge :variant="getStatusVariant(offer.status)">
{{ getStatusText(offer.status) }}
</Badge>
</Stack>
<Text v-if="offer.description" tone="muted">{{ offer.description }}</Text>
<Grid :cols="1" :md="3" :gap="3">
<Stack gap="1">
<Text size="base" weight="semibold">Локация</Text>
<Text tone="muted">{{ offer.locationName || 'Не указана' }}</Text>
</Stack>
<Stack gap="1">
<Text size="base" weight="semibold">Товары</Text>
<Text tone="muted">
{{ offer.lines?.[0]?.productName || 'Нет товаров' }}
<template v-if="offer.lines?.length > 1">
+{{ offer.lines.length - 1 }} ещё
</template>
</Text>
</Stack>
<Stack gap="1">
<Text size="base" weight="semibold">Действует до</Text>
<Text tone="muted">{{ formatDate(offer.validUntil) }}</Text>
</Stack>
</Grid>
</Stack>
</Card>
</Stack>
<Stack v-else align="center" gap="3">
<IconCircle tone="primary">
<Icon name="lucide:package" size="24" />
</IconCircle>
<Heading :level="3">Нет предложений</Heading>
<Text tone="muted">Создайте своё первое предложение</Text>
<NuxtLink :to="localePath('/dashboard/cabinet/offers/new')">
<Button>
<Icon name="lucide:plus" size="16" class="mr-2" />
Добавить предложение
</Button>
</NuxtLink>
</Stack>
</template>
</Stack>
</template>
<script setup lang="ts">
import { GetOffersDocument } from '~/composables/graphql/team/exchange-generated'
definePageMeta({
layout: 'cabinet',
middleware: ['auth-oidc']
})
const localePath = useLocalePath()
const { execute } = useGraphQL()
const offers = ref<any[]>([])
const isLoading = ref(true)
const hasError = ref(false)
const error = ref('')
const loadOffers = async () => {
try {
isLoading.value = true
hasError.value = false
const result = await execute(GetOffersDocument, {}, 'team', 'exchange')
offers.value = result.getOffers || []
} catch (err: any) {
hasError.value = true
error.value = err.message || 'Не удалось загрузить предложения'
offers.value = []
} finally {
isLoading.value = false
}
}
const getStatusVariant = (status: string) => {
const variants: Record<string, string> = {
active: 'success',
draft: 'warning',
expired: 'error',
sold: 'muted'
}
return variants[status] || 'muted'
}
const getStatusText = (status: string) => {
const texts: Record<string, string> = {
active: 'Активно',
draft: 'Черновик',
expired: 'Истекло',
sold: 'Продано'
}
return texts[status] || status || 'Неизвестно'
}
const formatDate = (date: string) => {
if (!date) return 'Не указано'
try {
const dateObj = new Date(date)
if (isNaN(dateObj.getTime())) return 'Невалидная дата'
return new Intl.DateTimeFormat('ru-RU', {
day: 'numeric',
month: 'long',
year: 'numeric'
}).format(dateObj)
} catch {
return 'Невалидная дата'
}
}
onMounted(async () => {
await loadOffers()
})
</script>