mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat: complete Phase 2 - refactor all user pages to Bento UI
This commit is contained in:
@@ -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`
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user