feat: complete Phase 2 - refactor all user pages to Bento UI

This commit is contained in:
Dev
2026-01-20 22:26:13 +05:00
parent b9b889b0bf
commit c92a4e7704
8 changed files with 78 additions and 53 deletions

View File

@@ -126,6 +126,7 @@ interface BentoCardProps {
Этап 2: ██████████ 100%
Этап 3: ██████████ 100%
Этап 4: ██████████ 100%
Этап 5: ██████████ 100%
─────────────────────
Общий: ██████████ 100%
```
@@ -156,14 +157,14 @@ interface BentoCardProps {
### Этап 5: Остальные страницы пользователя
> Применить bento-стили к основным страницам
| # | Страница | Приоритет | Объём |
|---|----------|-----------|-------|
| 5.1 | Subscription.tsx | High | Большая — формы, тарифы, карточки |
| 5.2 | Balance.tsx | High | Средняя — баланс, транзакции, методы оплаты |
| 5.3 | Referral.tsx | Medium | Средняя — статистика, ссылка, условия |
| 5.4 | Support.tsx | Medium | Малая — тикеты |
| 5.5 | Profile.tsx | Low | Малая — настройки |
| 5.6 | Info.tsx | Low | Малая — статичный контент |
| # | Страница | Приоритет | Объём | Статус |
|---|----------|-----------|-------|--------|
| 5.1 | Subscription.tsx | High | Большая — формы, тарифы, карточки | `[x]` |
| 5.2 | Balance.tsx | High | Средняя — баланс, транзакции, методы оплаты | `[x]` |
| 5.3 | Referral.tsx | Medium | Средняя — статистика, ссылка, условия | `[x]` |
| 5.4 | Support.tsx | Medium | Малая — тикеты | `[x]` |
| 5.5 | Profile.tsx | Low | Малая — настройки | `[x]` |
| 5.6 | Info.tsx | Low | Малая — статичный контент | `[x]` |
### Этап 6: Модалки
> Обновить модальные окна в bento-стиле
@@ -203,3 +204,23 @@ interface BentoCardProps {
- ✅ Floating TabBar
- ✅ Dashboard в bento-стиле
- ✅ Commit: `bf0bcfb`
### 2026-01-20 — Subscription.tsx Refactor
-Все секции `card``bento-card` (6 шт):
- Current Subscription (line 429)
- Daily Pause (line 634)
- Additional Options (line 733)
- My Devices (line 1153)
- Tariffs section (line 1223)
- Classic mode purchase (line 1925)
- ✅ Tariff cards: `bento-card-hover` + `bento-card-glow` для выбранного
- ✅ Period selection cards: `bento-card-hover` + `bento-card-glow`
- ✅ Traffic selection cards: `bento-card-hover` + `bento-card-glow`
- ✅ Исправлен `.bento-grid` — добавлен breakpoint для xs (<375px)
### 2026-01-20 — Phase 2 Complete (Все страницы пользователя)
- **Balance.tsx**: 4 карточки `bento-card`, методы оплаты `bento-card-hover`
- **Referral.tsx**: stats grid `bento-grid` + `bento-card-hover`, 5 секций `bento-card`
- **Support.tsx**: 3 карточки `bento-card`, tickets list items `rounded-bento`
- **Profile.tsx**: 3 карточки `bento-card`
- **Info.tsx**: FAQ items, rules, privacy, offer `bento-card`

View File

@@ -122,7 +122,7 @@ export default function Balance() {
<h1 className="text-2xl sm:text-3xl font-bold text-dark-50">{t('balance.title')}</h1>
{/* Balance Card */}
<div className="card bg-gradient-to-br from-accent-500/10 to-transparent border-accent-500/20">
<div className="bento-card bento-card-glow bg-gradient-to-br from-accent-500/10 to-transparent">
<div className="text-sm text-dark-400 mb-2">{t('balance.currentBalance')}</div>
<div className="text-4xl sm:text-5xl font-bold text-dark-50">
{formatAmount(balanceData?.balance_rubles || 0)}
@@ -131,7 +131,7 @@ export default function Balance() {
</div>
{/* Promo Code Section */}
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-4">{t('balance.promocode.title')}</h2>
<div className="flex gap-3">
<input
@@ -168,7 +168,7 @@ export default function Balance() {
{/* Payment Methods */}
{paymentMethods && paymentMethods.length > 0 && (
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-4">{t('balance.topUpBalance')}</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
{paymentMethods.map((method) => {
@@ -181,10 +181,10 @@ export default function Balance() {
key={method.id}
disabled={!method.is_available}
onClick={() => method.is_available && setSelectedMethod(method)}
className={`p-4 rounded-xl border text-left transition-all ${
className={`bento-card-hover p-4 text-left transition-all ${
method.is_available
? 'border-dark-700/50 hover:border-accent-500/50 bg-dark-800/30 cursor-pointer'
: 'border-dark-800/30 bg-dark-900/30 opacity-50 cursor-not-allowed'
? 'cursor-pointer'
: 'opacity-50 cursor-not-allowed'
}`}
>
<div className="font-semibold text-dark-100">{translatedName || method.name}</div>
@@ -202,7 +202,7 @@ export default function Balance() {
)}
{/* Transaction History */}
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-4">{t('balance.transactionHistory')}</h2>
{isLoading ? (

View File

@@ -166,7 +166,7 @@ export default function Info() {
return (
<div className="space-y-2">
{faqPages.map((faq: FaqPage) => (
<div key={faq.id} className="card p-0 overflow-hidden">
<div key={faq.id} className="bento-card p-0 overflow-hidden">
<button
onClick={() => toggleFaq(faq.id)}
className="w-full px-4 py-3 flex items-center justify-between text-left hover:bg-dark-800/50 transition-colors"
@@ -203,7 +203,7 @@ export default function Info() {
}
return (
<div className="card prose prose-invert max-w-none">
<div className="bento-card prose prose-invert max-w-none">
<div dangerouslySetInnerHTML={{ __html: formatContent(rules.content) }} />
{rules.updated_at && (
<p className="text-sm text-dark-400 mt-6 pt-4 border-t border-dark-700">
@@ -232,7 +232,7 @@ export default function Info() {
}
return (
<div className="card prose prose-invert max-w-none">
<div className="bento-card prose prose-invert max-w-none">
<div dangerouslySetInnerHTML={{ __html: formatContent(privacy.content) }} />
{privacy.updated_at && (
<p className="text-sm text-dark-400 mt-6 pt-4 border-t border-dark-700">
@@ -261,7 +261,7 @@ export default function Info() {
}
return (
<div className="card prose prose-invert max-w-none">
<div className="bento-card prose prose-invert max-w-none">
<div dangerouslySetInnerHTML={{ __html: formatContent(offer.content) }} />
{offer.updated_at && (
<p className="text-sm text-dark-400 mt-6 pt-4 border-t border-dark-700">

View File

@@ -97,7 +97,7 @@ export default function Profile() {
<h1 className="text-2xl sm:text-3xl font-bold text-dark-50">{t('profile.title')}</h1>
{/* User Info Card */}
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-6">{t('profile.accountInfo')}</h2>
<div className="space-y-4">
<div className="flex justify-between items-center py-3 border-b border-dark-800/50">
@@ -126,7 +126,7 @@ export default function Profile() {
</div>
{/* Email Section */}
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-6">{t('profile.emailAuth')}</h2>
{user?.email ? (
@@ -250,7 +250,7 @@ export default function Profile() {
</div>
{/* Notification Settings */}
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-6">{t('profile.notifications.title')}</h2>
{notificationsLoading ? (

View File

@@ -144,8 +144,8 @@ export default function Referral() {
</h1>
{/* Stats Cards */}
<div className='grid grid-cols-2 sm:grid-cols-3 gap-4'>
<div className='card'>
<div className='bento-grid'>
<div className='bento-card-hover'>
<div className='text-sm text-dark-400'>
{t('referral.stats.totalReferrals')}
</div>
@@ -155,7 +155,7 @@ export default function Referral() {
{t('referral.stats.activeReferrals').toLowerCase()}
</div>
</div>
<div className='card'>
<div className='bento-card-hover'>
<div className='text-sm text-dark-400'>
{t('referral.stats.totalEarnings')}
</div>
@@ -163,7 +163,7 @@ export default function Referral() {
{formatPositive(info?.total_earnings_rubles || 0)}
</div>
</div>
<div className='card col-span-2 sm:col-span-1'>
<div className='bento-card-hover col-span-2 sm:col-span-1'>
<div className='text-sm text-dark-400'>
{t('referral.stats.commissionRate')}
</div>
@@ -174,7 +174,7 @@ export default function Referral() {
</div>
{/* Referral Link */}
<div className='card'>
<div className='bento-card'>
<h2 className='text-lg font-semibold text-dark-100 mb-4'>
{t('referral.yourLink')}
</h2>
@@ -217,7 +217,7 @@ export default function Referral() {
{/* Program Terms */}
{terms && (
<div className='card'>
<div className='bento-card'>
<h2 className='text-lg font-semibold text-dark-100 mb-4'>
{t('referral.terms.title')}
</h2>
@@ -259,7 +259,7 @@ export default function Referral() {
)}
{/* Referrals List */}
<div className='card'>
<div className='bento-card'>
<h2 className='text-lg font-semibold text-dark-100 mb-4'>
{t('referral.yourReferrals')}
</h2>
@@ -314,7 +314,7 @@ export default function Referral() {
{/* Earnings History */}
{earnings?.items && earnings.items.length > 0 && (
<div className='card'>
<div className='bento-card'>
<h2 className='text-lg font-semibold text-dark-100 mb-4'>
{t('referral.earningsHistory')}
</h2>

View File

@@ -426,7 +426,7 @@ export default function Subscription() {
{/* Current Subscription */}
{subscription ? (
<div className="card">
<div className="bento-card">
<div className="flex items-center justify-between mb-6">
<div>
<h2 className="text-lg font-semibold text-dark-100">{t('subscription.currentPlan')}</h2>
@@ -631,7 +631,7 @@ export default function Subscription() {
{/* Daily Subscription Pause */}
{subscription && subscription.is_daily && !subscription.is_trial && (
<div className="card">
<div className="bento-card">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold text-dark-100">{t('subscription.pause.title')}</h2>
@@ -730,7 +730,7 @@ export default function Subscription() {
{/* Additional Options (Buy Devices) */}
{subscription && subscription.is_active && !subscription.is_trial && (
<div className="card">
<div className="bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-4">Дополнительные опции</h2>
{/* Buy Devices */}
@@ -1150,7 +1150,7 @@ export default function Subscription() {
{/* My Devices Section */}
{subscription && (
<div className="card">
<div className="bento-card">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-dark-100">{t('subscription.myDevices')}</h2>
{devicesData && devicesData.devices.length > 0 && (
@@ -1220,7 +1220,7 @@ export default function Subscription() {
{/* Tariffs Section - Combined Purchase/Extend/Switch like MiniApp */}
{isTariffsMode && tariffs.length > 0 && (
<div ref={tariffsCardRef} className="card">
<div ref={tariffsCardRef} className="bento-card">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-dark-100">
{subscription?.is_daily && !subscription?.is_trial
@@ -1375,10 +1375,10 @@ export default function Subscription() {
return (
<div
key={tariff.id}
className={`p-5 rounded-xl border text-left transition-all ${
className={`bento-card-hover p-5 text-left transition-all ${
isCurrentTariff
? 'border-accent-500 bg-accent-500/10'
: 'border-dark-700/50 bg-dark-800/30'
? 'bento-card-glow border-accent-500'
: ''
}`}
>
<div className="flex items-start justify-between mb-3">
@@ -1922,7 +1922,7 @@ export default function Subscription() {
{/* Purchase/Extend Section - Classic Mode */}
{classicOptions && classicOptions.periods.length > 0 && (
<div ref={tariffsCardRef} className="card">
<div ref={tariffsCardRef} className="bento-card">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-dark-100">
{subscription && !subscription.is_trial ? t('subscription.extend') : t('subscription.getSubscription')}
@@ -1984,10 +1984,10 @@ export default function Subscription() {
setSelectedDevices(period.devices.current)
}
}}
className={`p-4 rounded-xl border text-left transition-all relative ${
className={`bento-card-hover p-4 text-left transition-all relative ${
selectedPeriod?.id === period.id
? 'border-accent-500 bg-accent-500/10'
: 'border-dark-700/50 hover:border-dark-600 bg-dark-800/30'
? 'bento-card-glow border-accent-500'
: ''
}`}
>
{displayDiscount && displayDiscount > 0 && (
@@ -2022,13 +2022,11 @@ export default function Subscription() {
key={option.value}
onClick={() => setSelectedTraffic(option.value)}
disabled={!option.is_available}
className={`p-4 rounded-xl border text-center transition-all relative ${
className={`bento-card-hover p-4 text-center transition-all relative ${
selectedTraffic === option.value
? 'border-accent-500 bg-accent-500/10'
: option.is_available
? 'border-dark-700/50 hover:border-dark-600 bg-dark-800/30'
: 'border-dark-800/30 bg-dark-900/30 opacity-50 cursor-not-allowed'
}`}
? 'bento-card-glow border-accent-500'
: ''
} ${!option.is_available ? 'opacity-50 cursor-not-allowed' : ''}`}
>
{promoTraffic.percent && (
<div className="absolute -top-2 -right-2 px-2 py-0.5 bg-orange-500 text-white text-xs font-medium rounded-full">

View File

@@ -385,7 +385,7 @@ export default function Support() {
return (
<div className="max-w-md mx-auto mt-12">
<div className="card text-center">
<div className="bento-card text-center">
<div className="w-16 h-16 mx-auto mb-4 rounded-2xl bg-dark-800 flex items-center justify-center">
<svg className="w-8 h-8 text-dark-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" />
@@ -454,7 +454,7 @@ export default function Support() {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Tickets List */}
<div className="lg:col-span-1 card">
<div className="lg:col-span-1 bento-card">
<h2 className="text-lg font-semibold text-dark-100 mb-4">{t('support.yourTickets')}</h2>
{isLoading ? (
@@ -471,7 +471,7 @@ export default function Support() {
setShowCreateForm(false)
setReplyAttachment(null)
}}
className={`w-full text-left p-4 rounded-xl border transition-all ${
className={`w-full text-left p-4 rounded-bento border transition-all ${
selectedTicket?.id === ticket.id
? 'border-accent-500 bg-accent-500/10'
: 'border-dark-700/50 hover:border-dark-600 bg-dark-800/30'
@@ -502,7 +502,7 @@ export default function Support() {
</div>
{/* Ticket Detail / Create Form */}
<div className="lg:col-span-2 card">
<div className="lg:col-span-2 bento-card">
{showCreateForm ? (
<div>
<h2 className="text-lg font-semibold text-dark-100 mb-6">{t('support.createTicket')}</h2>

View File

@@ -363,10 +363,16 @@
.bento-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-columns: 1fr;
gap: var(--bento-gap);
}
@media (min-width: 375px) {
.bento-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 768px) {
.bento-grid {
grid-template-columns: repeat(4, 1fr);