130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
import React, { useState } from 'react'
|
||
import { PricingCardProps } from '@/app/types'
|
||
import Button from '@/components/ui/Button'
|
||
import showToast from '@/components/ui/Toast'
|
||
import useUserStore from '@/app/store/userStore'
|
||
|
||
const PricingCard: React.FC<PricingCardProps> = ({
|
||
plan,
|
||
price,
|
||
features,
|
||
isPopular,
|
||
isActive,
|
||
onPlanChange,
|
||
}) => {
|
||
const [isLoading, setIsLoading] = useState(false)
|
||
const { user, setUser } = useUserStore()
|
||
|
||
const handlePlanChange = async () => {
|
||
try {
|
||
setIsLoading(true)
|
||
|
||
const requestData = {
|
||
plan: plan,
|
||
}
|
||
|
||
const response = await fetch('/api/account/membership', {
|
||
method: 'PATCH',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(requestData),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
console.error('PricingCard - Server error:', error)
|
||
throw new Error(error.error || 'Ошибка при создании маршрута')
|
||
}
|
||
|
||
// обновляем данные в сторе
|
||
if (user) {
|
||
setUser({
|
||
...user,
|
||
account_type: plan,
|
||
})
|
||
}
|
||
|
||
// обновляем данные на странице
|
||
if (onPlanChange) {
|
||
onPlanChange()
|
||
}
|
||
|
||
showToast({
|
||
type: 'success',
|
||
duration: 1000,
|
||
message: `Тариф успешно изменен на ${plan.toUpperCase()}!`,
|
||
})
|
||
} catch (error) {
|
||
console.error('Error changing plan:', error)
|
||
showToast({
|
||
type: 'error',
|
||
message: 'Не удалось изменить тариф. Попробуйте позже.',
|
||
})
|
||
} finally {
|
||
setIsLoading(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div
|
||
className={`flex h-full translate-y-5 animate-[fadeIn_0.8s_ease-out_forwards] flex-col rounded-xl p-6 shadow-lg ${
|
||
isPopular ? 'border-orange border-2' : 'border border-gray-200'
|
||
} transition-all duration-300 hover:translate-y-[-8px]`}
|
||
>
|
||
<div className="flex-1">
|
||
{isPopular && (
|
||
<div className="bg-orange mb-2 inline-block rounded-2xl px-2 py-1 text-xs text-white">
|
||
Популярный выбор
|
||
</div>
|
||
)}
|
||
{isActive && (
|
||
<div className="mb-2 inline-block rounded-2xl bg-blue-500 px-2 py-1 text-xs text-white">
|
||
Активный план
|
||
</div>
|
||
)}
|
||
<h3 className="mb-2 text-xl font-bold capitalize">{plan}</h3>
|
||
<div className="mb-4">
|
||
<span className="text-3xl font-bold">{price}₸</span>
|
||
{price > 0 && <span className="text-gray-600"> / месяц</span>}
|
||
</div>
|
||
<ul className="space-y-2">
|
||
{features.map((feature, index) => (
|
||
<li key={index} className="flex items-start gap-2">
|
||
<svg
|
||
className="mt-1 h-[16px] w-[16px] flex-shrink-0 text-green-500"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M5 13l4 4L19 7"
|
||
/>
|
||
</svg>
|
||
<span className="leading-6">{feature}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
|
||
<div className="mt-6">
|
||
<Button
|
||
className={`flex w-full items-center justify-center rounded-2xl px-4 py-2 ${
|
||
isPopular
|
||
? 'bg-orange hover:bg-orange/80 text-white'
|
||
: 'bg-gray-100 hover:bg-blue-500 hover:text-white'
|
||
} ${isActive ? 'hidden' : ''} ${isLoading ? 'cursor-not-allowed opacity-50' : ''}`}
|
||
text={isLoading ? 'Обновление...' : 'Выбрать план'}
|
||
onClick={handlePlanChange}
|
||
disabled={isLoading}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default PricingCard
|