redisign tokens paytment

This commit is contained in:
smanylov
2026-03-11 12:21:56 +07:00
parent fa60a79241
commit e8f4f9939f
4 changed files with 151 additions and 36 deletions
+91
View File
@@ -298,6 +298,97 @@
}
}
}
.token-package {
background: #f8fafc;
border: 2px solid #e2e8f0;
border-radius: 16px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
&:hover {
transform: translateY(-4px) scale(1.02);
border-color: #6366f1;
box-shadow: 0 12px 30px rgba(99, 102, 241, 0.2);
&::before {
left: 100%;
}
}
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.1), transparent);
transition: left 0.5s ease;
}
&.selected {
border-color: #10b981;
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
transform: scale(1.05);
}
.token-package-amount {
font-size: 1.8rem;
font-weight: 700;
color: #1e293b;
margin-bottom: 6px;
}
.token-package-text {
font-size: 0.85rem;
color: #64748b;
margin-bottom: 8px;
}
.token-package-price {
font-size: 1.3rem;
font-weight: 600;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 8px;
}
.token-package-detail {
color: #64748b;
font-size: 0.8rem;
margin-top: 6px;
}
}
.plan-button {
width: 100%;
max-width: 280px;
padding: 12px 18px;
border: none;
border-radius: 12px;
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
display: block;
margin: 0 auto;
}
.selected-token-info {
margin-top: 8px;
color: #64748b;
font-size: 0.85rem;
text-align: center;
}
}
.secure-payments-wrapper {
+53 -33
View File
@@ -54,6 +54,7 @@ export default function PaymentTubBuyTokens() {
const [showWidget, setShowWidget] = useState<boolean>(false);
const [yooMoneyConfig, setYooMoneyConfig] = useState<YooMoneyCheckoutWidgetConfig | null>(null);
const queryClient = useQueryClient();
const [selectedTokenPackage, setSelectedTokenPackage] = useState<Tariff | null>(null);
async function createPayment(value: number, tokensBundle: number) {
if (isProcessing) return;
@@ -175,50 +176,69 @@ export default function PaymentTubBuyTokens() {
setShowWidget(false);
}
function selectHandler(item: Tariff) {
console.log(item);
setSelectedTokenPackage(item);
console.log(selectedTokenPackage);
}
return (
<>
<ModalWindow state={openWindow} callBack={closeModal}>
<PaymentConfirmationModal />
</ModalWindow>
<div className="tab-content">
<div className="billing-toggle" style={{ display: 'none' }}>
<div>
<button className="billing-btn active">💳 Помесячно</button>
<button className="billing-btn">
🔥 Годовая
<span>ВЫГОДНО</span>
</button>
</div>
</div>
<div className="tab-content block-wrapper">
<div className="subscription-grid">
{tokensPaymentData?.map((item: {
id: number;
name: string;
price: number;
tokens: number;
maxFilesCount: number;
diskSize: number;
}) => (
<div className="plan-card animate-card" key={item.id}>
<div className="plan-header">
<div className="plan-price">{item.price ?? 0} </div>
<div className="plan-tokens header">
{item.tokens ?? 0}
<br />
токенов
</div>
</div>
<div className="flex justify-center">
<button
className="btn btn-primary"
onClick={() => openPaymentWindow(item.price, item.name, item.tokens, item.id)}
{tokensPaymentData?.map((item: Tariff) => (
<div
className={`token-package ${selectedTokenPackage?.id === item.id ? 'selected' : ''}`}
key={item.id}
onClick={(() => {
selectHandler(item);
})}
>
{t('buy')}
</button>
<div
className="token-package-amount"
>
{item.tokens ?? 0}
</div>
<div
className="token-package-text"
>
{t('tokens')}
</div>
<div className="token-package-price">{item.price ?? 0} </div>
<div className="token-package-detail">
~{Math.ceil((item.price / item.tokens) * 10) / 10 } за токен
</div>
</div>
))}
</div>
<div>
<button
className="plan-button btn-primary"
onClick={() => {
if (selectedTokenPackage) {
openPaymentWindow(selectedTokenPackage.price, selectedTokenPackage.name, selectedTokenPackage.tokens, selectedTokenPackage.id)
}
}}
disabled={selectedTokenPackage ? false : true}
>
{selectedTokenPackage ? `Купить за ${selectedTokenPackage.price}` : t('select-a-token-package')}
</button>
{selectedTokenPackage && (
<div
className="selected-token-info"
>
{t('package-selected-tokens-price', {
tokens: selectedTokenPackage.tokens,
price: selectedTokenPackage.price
})}
</div>
)}
</div>
</div>
{/* Модальное окно с виджетом */}
+3 -1
View File
@@ -294,7 +294,9 @@
"there-are-not-enough-funds-to-protect-file": "There are not enough funds to protect file. {count} tokens required",
"duplicate-files": "Duplicate files",
"monitoring-is-not-supported": "Monitoring is not supported",
"frequently-asked-questions": "Frequently Asked Questions"
"frequently-asked-questions": "Frequently Asked Questions",
"select-a-token-package": "Select a token package",
"package-selected-tokens-price": "Package selected: {tokens} tokens for {price}₽"
},
"Login-register-form": {
"and": "and",
+3 -1
View File
@@ -294,7 +294,9 @@
"there-are-not-enough-funds-to-protect-file": "Для защиты файла недостаточно средств. Необходимо {count} токен(ов/а)",
"duplicate-files": "Дубликаты файлов",
"monitoring-is-not-supported": "Мониторинг не поддерживается",
"frequently-asked-questions": "Часто задаваемые вопросы"
"frequently-asked-questions": "Часто задаваемые вопросы",
"select-a-token-package": "Выберите пакет токенов",
"package-selected-tokens-price": "Выбран пакет: {tokens} токенов за {price}₽"
},
"Login-register-form": {
"and": "и",