update pricing to new api

This commit is contained in:
austnv
2026-06-14 22:38:12 +03:00
parent fa88b2ff84
commit 7b7d58f61f
+106 -64
View File
@@ -4,8 +4,20 @@
Прозрачные тарифы Прозрачные тарифы
</h2> </h2>
<!-- Состояние загрузки -->
<div v-if="loading" class="flex justify-center py-12">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
<!-- Состояние ошибки -->
<div v-else-if="error" class="text-center text-red-500 py-12">
{{ error }}
</div>
<!-- Основной контент -->
<template v-else>
<!-- Переключатель периода --> <!-- Переключатель периода -->
<div class="flex justify-center gap-2 mb-10"> <div class="flex justify-center gap-2 mb-10 flex-wrap">
<button <button
v-for="period in availablePeriods" v-for="period in availablePeriods"
:key="period.value" :key="period.value"
@@ -20,7 +32,10 @@
</div> </div>
<!-- Сетка тарифов --> <!-- Сетка тарифов -->
<div class="grid md:grid-cols-2 gap-12"> <div
v-if="tariffCards.length"
class="grid md:grid-cols-2 gap-12"
>
<div <div
v-for="tariff in tariffCards" v-for="tariff in tariffCards"
:key="tariff.key" :key="tariff.key"
@@ -59,88 +74,115 @@
</button> </button>
</div> </div>
</div> </div>
<div v-else class="text-center py-12 text-gray-500">
Нет доступных тарифов для выбранного периода
</div>
</template>
</section> </section>
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed, onMounted } from 'vue'
import { Check } from '@lucide/vue' import { Check } from '@lucide/vue'
// Исходный конфиг // Состояния
const pricingConfig = { const loading = ref(true)
default_tariff: "premium", const error = ref(null)
tariffs: [ const tariffsData = ref([]) // исходные тарифы (из API)
{ const defaultTariffKey = ref('') // ключ популярного тарифа
key: "standard", const selectedPeriod = ref(1) // выбранный период (месяцев)
names: { ru: "Standard", en: "Standard" },
descriptions: { ru: "До 3 устройств", en: "up to 3 devices" }, // Доступные периоды (вычисляются динамически)
prices_rub: { "1": 179.0, "3": 387.0, "6": 594.0, "12": 996.0 }, const availablePeriods = computed(() => {
hwid_device_limit: 2, const periodsSet = new Set()
monthly_gb: 0.0 for (const tariff of tariffsData.value) {
}, if (tariff.enabled && tariff.enabled_periods) {
{ tariff.enabled_periods.forEach(p => periodsSet.add(p))
key: "premium",
names: { ru: "Premium", en: "Premium"},
descriptions: { ru: "До 10 устройств, сервера РФ и США", en: "Up to 10 devices, RU, USA servers" },
prices_rub: { "1": 349.0, "3": 987.0, "6": 1734.0, "12": 1992.0 },
hwid_device_limit: 10,
monthly_gb: 0.0
} }
]
} }
const sorted = Array.from(periodsSet).sort((a, b) => a - b)
return sorted.map(value => ({
value,
label: value === 1 ? '1 мес' : `${value} мес`
}))
})
// Доступные периоды // Активные (включённые) тарифы, отфильтрованные по выбранному периоду
const availablePeriods = [ const activeTariffs = computed(() => {
{ value: 1, label: '1 мес' }, return tariffsData.value.filter(tariff =>
{ value: 3, label: '3 мес' }, tariff.enabled && tariff.enabled_periods?.includes(selectedPeriod.value)
{ value: 6, label: '6 мес' }, )
{ value: 12, label: '12 мес' } })
]
// Выбранный период (по умолчанию 1 месяц) // Карточки для отображения (с ценами, фичами и флагом популярности)
const selectedPeriod = ref(1)
// Функция для получения цены за месяц
const getMonthlyPrice = (tariff, period) => {
const total = tariff.prices_rub[String(period)]
if (!total) return '—'
const monthly = total / period
return Number.isInteger(monthly) ? monthly : monthly.toFixed(2)
}
// Подготовка данных для карточек
const tariffCards = computed(() => { const tariffCards = computed(() => {
const tariffs = pricingConfig.tariffs return activeTariffs.value.map(tariff => {
const defaultKey = pricingConfig.default_tariff const totalPrice = tariff.prices_rub?.[String(selectedPeriod.value)]
let monthlyPrice = '—'
if (totalPrice && totalPrice > 0) {
const monthly = totalPrice / selectedPeriod.value
monthlyPrice = Number.isInteger(monthly) ? monthly : monthly.toFixed(2)
}
// Разделяем тарифы: популярный (премиум) и остальные // Формируем список особенностей (features)
const premiumTariff = tariffs.find(t => t.key === defaultKey) const features = []
const otherTariffs = tariffs.filter(t => t.key !== defaultKey)
// Порядок: первый обычный, премиум, второй обычный // 1. Описание из JSON (если есть)
const ordered = [ const description = tariff.descriptions?.ru?.trim()
{ ...otherTariffs[0], isPopular: false }, if (description) {
{ ...premiumTariff, isPopular: true }, features.push(description)
] }
return ordered.map(tariff => { // 2. Лимит устройств
const price = getMonthlyPrice(tariff, selectedPeriod.value) if (tariff.hwid_device_limit) {
features.push(`До ${tariff.hwid_device_limit} устройств`)
}
// Фичи: разбиваем описание по запятой и добавляем общие пункты // 3. Безлимитный трафик (всегда)
const descriptionFeatures = tariff.descriptions.ru features.push('Безлимитный трафик')
.split(',')
.map(s => s.trim()) // 4. Поддержка 24/7
.filter(Boolean) features.push('Поддержка 24/7')
const staticFeatures = ['Безлимитный трафик', 'Поддержка 24/7']
const features = [...new Set([...descriptionFeatures, ...staticFeatures])]
return { return {
key: tariff.key, key: tariff.key,
name: tariff.names.ru, name: tariff.names?.ru || tariff.key,
price: `${price}`, price: `${monthlyPrice}`,
features, features,
isPopular: tariff.isPopular isPopular: tariff.key === defaultTariffKey.value
} }
}) })
}) })
// Загрузка данных с API
const fetchTariffs = async () => {
loading.value = true
error.value = null
try {
const response = await fetch('/api/v1/tariffs')
if (!response.ok) {
throw new Error(`Ошибка загрузки: ${response.status}`)
}
const data = await response.json()
tariffsData.value = data.tariffs || []
defaultTariffKey.value = data.default_tariff || ''
// Установка начального периода (первый доступный)
if (availablePeriods.value.length > 0) {
const exists = availablePeriods.value.some(p => p.value === selectedPeriod.value)
if (!exists) {
selectedPeriod.value = availablePeriods.value[0].value
}
}
} catch (err) {
console.error('Failed to load tariffs:', err)
error.value = 'Не удалось загрузить тарифы. Попробуйте позже.'
} finally {
loading.value = false
}
}
onMounted(() => {
fetchTariffs()
})
</script> </script>