mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
fix: platform-aware delete confirmation + trial CTA to purchase
Delete subscription: - Telegram mini app: native destructive popup (red button) - Web (mobile + desktop): Sheet bottom panel with full-width buttons - Both: proper loading state on delete button Trial CTA: leads to /subscription/purchase (trial cannot be renewed)
This commit is contained in:
@@ -38,9 +38,12 @@ export default function PurchaseCTAButton({
|
|||||||
? t('subscription.cta.renewHint', 'Продление подписки')
|
? t('subscription.cta.renewHint', 'Продление подписки')
|
||||||
: t('subscription.cta.activeHint');
|
: t('subscription.cta.activeHint');
|
||||||
|
|
||||||
// In multi-tariff mode: renew goes to per-subscription renew page
|
// Trial → purchase page (buy a real tariff, trial can't be renewed)
|
||||||
const linkTo =
|
// Multi-tariff active → per-subscription renew page
|
||||||
isMultiTariff && subscription?.id
|
// Otherwise → purchase page
|
||||||
|
const linkTo = isTrial
|
||||||
|
? '/subscription/purchase'
|
||||||
|
: isMultiTariff && subscription?.id
|
||||||
? `/subscriptions/${subscription.id}/renew`
|
? `/subscriptions/${subscription.id}/renew`
|
||||||
: '/subscription/purchase';
|
: '/subscription/purchase';
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ import { useNavigate, useParams } from 'react-router';
|
|||||||
import { subscriptionApi } from '../api/subscription';
|
import { subscriptionApi } from '../api/subscription';
|
||||||
import { WebBackButton } from '../components/WebBackButton';
|
import { WebBackButton } from '../components/WebBackButton';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Sheet,
|
||||||
DialogContent,
|
SheetContent,
|
||||||
DialogDescription,
|
SheetDescription,
|
||||||
DialogFooter,
|
SheetFooter,
|
||||||
DialogHeader,
|
SheetHeader,
|
||||||
DialogTitle,
|
SheetTitle,
|
||||||
} from '../components/primitives/Dialog';
|
} from '../components/primitives/Sheet';
|
||||||
|
import { useDestructiveConfirm } from '../platform/hooks/useNativeDialog';
|
||||||
|
import { usePlatform } from '../platform';
|
||||||
import TrafficProgressBar from '../components/dashboard/TrafficProgressBar';
|
import TrafficProgressBar from '../components/dashboard/TrafficProgressBar';
|
||||||
import { HoverBorderGradient } from '../components/ui/hover-border-gradient';
|
import { HoverBorderGradient } from '../components/ui/hover-border-gradient';
|
||||||
import { useTrafficZone } from '../hooks/useTrafficZone';
|
import { useTrafficZone } from '../hooks/useTrafficZone';
|
||||||
@@ -183,8 +185,10 @@ export default function Subscription() {
|
|||||||
const g = getGlassColors(isDark);
|
const g = getGlassColors(isDark);
|
||||||
const haptic = useHaptic();
|
const haptic = useHaptic();
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteSheet, setShowDeleteSheet] = useState(false);
|
||||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||||
|
const { platform } = usePlatform();
|
||||||
|
const destructiveConfirm = useDestructiveConfirm();
|
||||||
|
|
||||||
// Helper to format price from kopeks
|
// Helper to format price from kopeks
|
||||||
const formatPrice = (kopeks: number) => `${formatAmount(kopeks / 100)} ${currencySymbol}`;
|
const formatPrice = (kopeks: number) => `${formatAmount(kopeks / 100)} ${currencySymbol}`;
|
||||||
@@ -1278,8 +1282,33 @@ export default function Subscription() {
|
|||||||
{isMultiTariff && subscription && !subscription.is_active && !subscription.is_trial && (
|
{isMultiTariff && subscription && !subscription.is_active && !subscription.is_trial && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDeleteConfirm(true)}
|
onClick={async () => {
|
||||||
className="flex w-full items-center justify-center gap-2 rounded-2xl border border-red-400/20 bg-red-400/5 p-3.5 text-sm font-medium text-red-400 transition-colors hover:bg-red-400/10"
|
if (platform === 'telegram') {
|
||||||
|
// Telegram: native destructive popup
|
||||||
|
const confirmed = await destructiveConfirm(
|
||||||
|
t(
|
||||||
|
'subscription.deleteWarning',
|
||||||
|
'Подписка будет удалена безвозвратно. Все данные, устройства и настройки будут потеряны.',
|
||||||
|
),
|
||||||
|
t('subscription.confirmDelete', 'Да, удалить'),
|
||||||
|
t('subscription.deleteTitle', 'Удалить подписку?'),
|
||||||
|
);
|
||||||
|
if (!confirmed) return;
|
||||||
|
setDeleteLoading(true);
|
||||||
|
try {
|
||||||
|
await subscriptionApi.deleteSubscription(subscription.id);
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['subscriptions-list'] });
|
||||||
|
navigate('/subscriptions', { replace: true });
|
||||||
|
} catch {
|
||||||
|
setDeleteLoading(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Web: show Sheet
|
||||||
|
setShowDeleteSheet(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={deleteLoading}
|
||||||
|
className="flex w-full items-center justify-center gap-2 rounded-2xl border border-red-400/20 bg-red-400/5 p-3.5 text-sm font-medium text-red-400 transition-colors hover:bg-red-400/10 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
className="h-4 w-4"
|
className="h-4 w-4"
|
||||||
@@ -1294,27 +1323,24 @@ export default function Subscription() {
|
|||||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{t('subscription.delete', 'Удалить подписку')}
|
{deleteLoading
|
||||||
|
? t('common.processing', 'Удаление...')
|
||||||
|
: t('subscription.delete', 'Удалить подписку')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
|
{/* Web: Sheet confirmation */}
|
||||||
<DialogContent className="max-w-sm">
|
<Sheet open={showDeleteSheet} onOpenChange={setShowDeleteSheet}>
|
||||||
<DialogHeader>
|
<SheetContent>
|
||||||
<DialogTitle>{t('subscription.deleteTitle', 'Удалить подписку?')}</DialogTitle>
|
<SheetHeader>
|
||||||
<DialogDescription>
|
<SheetTitle>{t('subscription.deleteTitle', 'Удалить подписку?')}</SheetTitle>
|
||||||
|
<SheetDescription>
|
||||||
{t(
|
{t(
|
||||||
'subscription.deleteWarning',
|
'subscription.deleteWarning',
|
||||||
'Подписка будет удалена безвозвратно. Все данные, устройства и настройки будут потеряны. Это действие нельзя отменить.',
|
'Подписка будет удалена безвозвратно. Все данные, устройства и настройки будут потеряны. Это действие нельзя отменить.',
|
||||||
)}
|
)}
|
||||||
</DialogDescription>
|
</SheetDescription>
|
||||||
</DialogHeader>
|
</SheetHeader>
|
||||||
<DialogFooter className="gap-2 sm:gap-0">
|
<SheetFooter>
|
||||||
<button
|
|
||||||
onClick={() => setShowDeleteConfirm(false)}
|
|
||||||
className="rounded-xl border border-dark-700 bg-dark-800 px-4 py-2.5 text-sm font-medium text-dark-300 transition-colors hover:bg-dark-700"
|
|
||||||
>
|
|
||||||
{t('common.cancel', 'Отмена')}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setDeleteLoading(true);
|
setDeleteLoading(true);
|
||||||
@@ -1324,19 +1350,25 @@ export default function Subscription() {
|
|||||||
navigate('/subscriptions', { replace: true });
|
navigate('/subscriptions', { replace: true });
|
||||||
} catch {
|
} catch {
|
||||||
setDeleteLoading(false);
|
setDeleteLoading(false);
|
||||||
setShowDeleteConfirm(false);
|
setShowDeleteSheet(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={deleteLoading}
|
disabled={deleteLoading}
|
||||||
className="rounded-xl bg-red-500 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-red-600 disabled:opacity-50"
|
className="w-full rounded-xl bg-red-500 py-3 text-sm font-semibold text-white transition-colors hover:bg-red-600 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{deleteLoading
|
{deleteLoading
|
||||||
? t('common.processing', 'Удаление...')
|
? t('common.processing', 'Удаление...')
|
||||||
: t('subscription.confirmDelete', 'Да, удалить')}
|
: t('subscription.confirmDelete', 'Да, удалить')}
|
||||||
</button>
|
</button>
|
||||||
</DialogFooter>
|
<button
|
||||||
</DialogContent>
|
onClick={() => setShowDeleteSheet(false)}
|
||||||
</Dialog>
|
className="w-full rounded-xl border border-dark-700 bg-dark-800 py-3 text-sm font-medium text-dark-300 transition-colors hover:bg-dark-700"
|
||||||
|
>
|
||||||
|
{t('common.cancel', 'Отмена')}
|
||||||
|
</button>
|
||||||
|
</SheetFooter>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user