mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat: wheel subscription picker for multi-tariff mode
- API: spin() accepts subscriptionId, WheelConfig has eligible_subscriptions - Auto-select subscription when only one eligible - Show subscription picker when multiple eligible (days payment) - Disable spin button until subscription selected (when >1 eligible) - Daily tariffs excluded from eligible list
This commit is contained in:
@@ -8,6 +8,12 @@ export interface WheelPrize {
|
||||
prize_type: string;
|
||||
}
|
||||
|
||||
export interface EligibleSubscription {
|
||||
id: number;
|
||||
tariff_name: string | null;
|
||||
days_left: number;
|
||||
}
|
||||
|
||||
export interface WheelConfig {
|
||||
is_enabled: boolean;
|
||||
name: string;
|
||||
@@ -25,6 +31,7 @@ export interface WheelConfig {
|
||||
user_balance_kopeks: number;
|
||||
required_balance_kopeks: number;
|
||||
has_subscription: boolean;
|
||||
eligible_subscriptions: EligibleSubscription[] | null;
|
||||
}
|
||||
|
||||
export interface SpinAvailability {
|
||||
@@ -187,9 +194,13 @@ export const wheelApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
spin: async (paymentType: 'telegram_stars' | 'subscription_days'): Promise<SpinResult> => {
|
||||
spin: async (
|
||||
paymentType: 'telegram_stars' | 'subscription_days',
|
||||
subscriptionId?: number,
|
||||
): Promise<SpinResult> => {
|
||||
const response = await apiClient.post<SpinResult>('/cabinet/wheel/spin', {
|
||||
payment_type: paymentType,
|
||||
...(subscriptionId != null && { subscription_id: subscriptionId }),
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -107,6 +107,7 @@ export default function Wheel() {
|
||||
const [isPayingStars, setIsPayingStars] = useState(false);
|
||||
const [historyExpanded, setHistoryExpanded] = useState(false);
|
||||
const [showStarsConfirm, setShowStarsConfirm] = useState(false);
|
||||
const [selectedSubscriptionId, setSelectedSubscriptionId] = useState<number | null>(null);
|
||||
const paymentTypeInitialized = useRef(false);
|
||||
|
||||
const {
|
||||
@@ -136,6 +137,11 @@ export default function Wheel() {
|
||||
} else if (daysEnabled) {
|
||||
setPaymentType('subscription_days');
|
||||
}
|
||||
|
||||
// Auto-select subscription if only one eligible
|
||||
if (config.eligible_subscriptions?.length === 1) {
|
||||
setSelectedSubscriptionId(config.eligible_subscriptions[0].id);
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
// Function to poll for new spin result after Stars payment
|
||||
@@ -368,7 +374,7 @@ export default function Wheel() {
|
||||
};
|
||||
|
||||
const spinMutation = useMutation({
|
||||
mutationFn: () => wheelApi.spin(paymentType),
|
||||
mutationFn: () => wheelApi.spin(paymentType, selectedSubscriptionId ?? undefined),
|
||||
onSuccess: (result) => {
|
||||
if (result.success) {
|
||||
setTargetRotation(result.rotation_degrees);
|
||||
@@ -506,11 +512,18 @@ export default function Wheel() {
|
||||
// Stars via Telegram invoice don't require ruble balance, so only check daily limit
|
||||
const dailyLimitReached = config.daily_limit > 0 && config.user_spins_today >= config.daily_limit;
|
||||
const noSubscription = !config.has_subscription;
|
||||
const needsSubscriptionPick =
|
||||
paymentType === 'subscription_days' &&
|
||||
config.eligible_subscriptions &&
|
||||
config.eligible_subscriptions.length > 1 &&
|
||||
!selectedSubscriptionId;
|
||||
|
||||
const spinDisabled =
|
||||
isSpinning ||
|
||||
isPayingStars ||
|
||||
dailyLimitReached ||
|
||||
noSubscription ||
|
||||
needsSubscriptionPick ||
|
||||
(paymentType === 'telegram_stars' ? !starsEnabled : !config.can_spin);
|
||||
|
||||
return (
|
||||
@@ -582,6 +595,38 @@ export default function Wheel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Subscription selector for days payment in multi-tariff */}
|
||||
{paymentType === 'subscription_days' &&
|
||||
config.eligible_subscriptions &&
|
||||
config.eligible_subscriptions.length > 1 && (
|
||||
<div className="rounded-xl border border-dark-700/30 bg-dark-800/30 p-3">
|
||||
<p className="mb-2 text-center text-xs text-dark-400">
|
||||
{t('wheel.selectSubscription', 'Выберите подписку')}
|
||||
</p>
|
||||
<div className="space-y-1.5">
|
||||
{config.eligible_subscriptions.map((sub) => (
|
||||
<button
|
||||
key={sub.id}
|
||||
onClick={() => setSelectedSubscriptionId(sub.id)}
|
||||
disabled={isSpinning}
|
||||
className={`flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm transition-all ${
|
||||
selectedSubscriptionId === sub.id
|
||||
? 'bg-accent-500/15 text-accent-400'
|
||||
: 'text-dark-400 hover:text-dark-200'
|
||||
}`}
|
||||
>
|
||||
<span className="font-medium">
|
||||
{sub.tariff_name || t('subscription.defaultName', 'Подписка')}
|
||||
</span>
|
||||
<span className="text-xs opacity-60">
|
||||
{sub.days_left} {t('common.units.days', 'дней')}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stars confirmation panel */}
|
||||
{showStarsConfirm && !isSpinning && !isPayingStars ? (
|
||||
<div className="space-y-3 rounded-xl border border-accent-500/30 bg-accent-500/5 p-4">
|
||||
|
||||
Reference in New Issue
Block a user