fix: remove duplicate back buttons, improve multi-subscription UX

- Remove custom back buttons from Subscription detail and SubscriptionPurchase
  pages — native Telegram mini-app back button already handles navigation
- SubscriptionListCard: distinct trial badge with star icon and amber styling,
  trial cards get amber border/background for visual differentiation
- SubscriptionPurchase: change "Купить тариф" to "Новый тариф" when adding
  a new tariff in multi-subscription mode
- Subscription detail: show tariff name as title in multi-tariff mode
This commit is contained in:
c0mrade
2026-03-23 21:38:04 +03:00
parent 376e1bb56d
commit 9d3fb37d60
3 changed files with 50 additions and 103 deletions

View File

@@ -19,15 +19,28 @@ function formatDate(iso: string | null, locale?: string): string {
function StatusBadge({ function StatusBadge({
status, status,
isTrial,
t, t,
}: { }: {
status: string; status: string;
isTrial: boolean;
t: (key: string, fallback: string) => string; t: (key: string, fallback: string) => string;
}) { }) {
const isActive = status === 'active' || status === 'trial'; const isActive = status === 'active' || status === 'trial';
const isLimited = status === 'limited'; const isLimited = status === 'limited';
const isExpired = status === 'expired' || status === 'disabled'; const isExpired = status === 'expired' || status === 'disabled';
if (isTrial) {
return (
<span className="inline-flex items-center gap-1 rounded-full border border-amber-400/25 bg-amber-400/10 px-2 py-0.5 text-[10px] font-semibold text-amber-400">
<svg className="h-2.5 w-2.5" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z" />
</svg>
{t('subscription.statusTrial', 'Тестовая')}
</span>
);
}
const color = isActive const color = isActive
? 'bg-emerald-400/15 text-emerald-400 border-emerald-400/20' ? 'bg-emerald-400/15 text-emerald-400 border-emerald-400/20'
: isLimited : isLimited
@@ -35,9 +48,7 @@ function StatusBadge({
: 'bg-red-400/15 text-red-400 border-red-400/20'; : 'bg-red-400/15 text-red-400 border-red-400/20';
const label = isActive const label = isActive
? status === 'trial' ? t('subscription.statusActive', 'Активна')
? t('subscription.statusTrial', 'Тестовая')
: t('subscription.statusActive', 'Активна')
: isLimited : isLimited
? t('subscription.statusLimited', 'Ограничена') ? t('subscription.statusLimited', 'Ограничена')
: isExpired : isExpired
@@ -70,6 +81,7 @@ export default function SubscriptionListCard({
onClick(); onClick();
}; };
const isTrial = subscription.is_trial;
const isActive = subscription.status === 'active' || subscription.status === 'trial'; const isActive = subscription.status === 'active' || subscription.status === 'trial';
const isExpired = subscription.status === 'expired' || subscription.status === 'disabled'; const isExpired = subscription.status === 'expired' || subscription.status === 'disabled';
const trafficLimit = subscription.traffic_limit_gb; const trafficLimit = subscription.traffic_limit_gb;
@@ -83,18 +95,27 @@ export default function SubscriptionListCard({
const trafficColor = const trafficColor =
trafficPercent >= 90 ? 'bg-red-400' : trafficPercent >= 70 ? 'bg-amber-400' : 'bg-emerald-400'; trafficPercent >= 90 ? 'bg-red-400' : trafficPercent >= 70 ? 'bg-amber-400' : 'bg-emerald-400';
const borderColor = isTrial
? 'rgba(251,191,36,0.2)'
: isExpired
? 'rgba(255,59,92,0.15)'
: g.cardBorder;
const bgColor = isTrial
? isDark
? 'rgba(251,191,36,0.04)'
: 'rgba(251,191,36,0.03)'
: isExpired
? isDark
? 'rgba(255,59,92,0.04)'
: 'rgba(255,59,92,0.03)'
: g.cardBg;
return ( return (
<button <button
onClick={handleClick} onClick={handleClick}
className="w-full rounded-2xl border p-4 text-left transition-all duration-200 hover:scale-[1.01] active:scale-[0.99]" className="w-full rounded-2xl border p-4 text-left transition-all duration-200 hover:scale-[1.01] active:scale-[0.99]"
style={{ style={{ background: bgColor, borderColor }}
background: isExpired
? isDark
? 'rgba(255,59,92,0.04)'
: 'rgba(255,59,92,0.03)'
: g.cardBg,
borderColor: isExpired ? 'rgba(255,59,92,0.15)' : g.cardBorder,
}}
> >
{/* Header: tariff name + status badge + chevron */} {/* Header: tariff name + status badge + chevron */}
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
@@ -102,7 +123,7 @@ export default function SubscriptionListCard({
<span className="truncate text-base font-semibold" style={{ color: g.text }}> <span className="truncate text-base font-semibold" style={{ color: g.text }}>
{subscription.tariff_name || t('subscription.defaultName', 'Подписка')} {subscription.tariff_name || t('subscription.defaultName', 'Подписка')}
</span> </span>
<StatusBadge status={subscription.status} t={t} /> <StatusBadge status={subscription.status} isTrial={isTrial} t={t} />
</div> </div>
<svg <svg
className="h-4 w-4 shrink-0 opacity-30" className="h-4 w-4 shrink-0 opacity-30"

View File

@@ -479,34 +479,12 @@ export default function Subscription() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Multi-tariff: back navigation + tariff name */} {/* Page title — native Telegram back button handles navigation */}
{isMultiTariff && subscriptionId ? ( <h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">
<div> {isMultiTariff && subscription?.tariff_name
<button ? subscription.tariff_name
onClick={() => navigate('/subscriptions')} : t('subscription.title')}
className="mb-2 flex items-center gap-1.5 text-sm text-dark-50/50 transition-colors hover:text-dark-50/80" </h1>
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M15 18l-6-6 6-6" />
</svg>
{t('subscription.backToList', 'Мои подписки')}
</button>
<h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">
{subscription?.tariff_name || t('subscription.title')}
</h1>
</div>
) : (
<h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">{t('subscription.title')}</h1>
)}
{/* Current Subscription */} {/* Current Subscription */}
{subscription ? ( {subscription ? (

View File

@@ -383,34 +383,7 @@ export default function SubscriptionPurchase() {
if (optionsError || (!purchaseOptions && !optionsLoading)) { if (optionsError || (!purchaseOptions && !optionsLoading)) {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center gap-3"> <h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">{t('subscription.extend')}</h1>
<button
onClick={() => navigate('/subscription')}
aria-label="Back"
className="flex h-9 w-9 items-center justify-center rounded-xl transition-colors"
style={{
background: g.innerBg,
border: `1px solid ${g.innerBorder}`,
}}
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-dark-50/60"
>
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
</button>
<h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">
{t('subscription.extend')}
</h1>
</div>
<div <div
className="rounded-3xl p-6 text-center" className="rounded-3xl p-6 text-center"
style={{ style={{
@@ -434,41 +407,16 @@ export default function SubscriptionPurchase() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Header with back link */} {/* Header — native Telegram back button handles navigation */}
<div className="flex items-center gap-3"> <h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">
<button {isMultiTariff && !subscriptionId
onClick={() => navigate('/subscriptions')} ? t('subscription.newTariff', 'Новый тариф')
aria-label="Back" : subscription?.is_daily && !subscription?.is_trial
className="flex h-9 w-9 items-center justify-center rounded-xl transition-colors" ? t('subscription.switchTariff.title')
style={{ : subscription && !subscription.is_trial
background: g.innerBg, ? t('subscription.extend')
border: `1px solid ${g.innerBorder}`, : t('subscription.getSubscription')}
}} </h1>
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-dark-50/60"
>
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
</button>
<h1 className="text-2xl font-bold text-dark-50 sm:text-3xl">
{isMultiTariff && !subscriptionId
? t('subscription.buyTariff', 'Купить тариф')
: subscription?.is_daily && !subscription?.is_trial
? t('subscription.switchTariff.title')
: subscription && !subscription.is_trial
? t('subscription.extend')
: t('subscription.getSubscription')}
</h1>
</div>
{/* Tariffs Section */} {/* Tariffs Section */}
{isTariffsMode && tariffs.length > 0 && ( {isTariffsMode && tariffs.length > 0 && (