uchill/front_material/components/profile/ProfilePaymentTab.tsx

149 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState, useEffect } from 'react';
import apiClient from '@/lib/api-client';
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
import Link from 'next/link';
/** Подписи преимуществ из plan.features (API) */
const FEATURE_LABELS: Record<string, string> = {
video_calls: 'Видеозвонки',
screen_sharing: 'Демонстрация экрана',
whiteboard: 'Интерактивная доска',
homework: 'Домашние задания',
materials: 'Материалы',
analytics: 'Аналитика',
telegram_bot: 'Telegram-бот',
api_access: 'API',
};
/** Стандартные преимущества по типу тарифа (если API не вернул features) */
const DEFAULT_FEATURES: string[] = [
'Видеозвонки',
'Демонстрация экрана',
'Интерактивная доска',
'Домашние задания',
'Материалы',
'Аналитика',
'Telegram-бот',
];
function getBenefitList(plan: any): string[] {
const fromApi = plan.features && typeof plan.features === 'object'
? Object.entries(plan.features)
.filter(([, enabled]) => enabled)
.map(([key]) => FEATURE_LABELS[key] || key)
: [];
if (fromApi.length > 0) return fromApi;
// Fallback: все стандартные функции для месячных, для "за ученика" добавляем "Гибкая оплата"
if (plan.subscription_type === 'per_student') {
return ['Гибкая оплата за каждого ученика', ...DEFAULT_FEATURES];
}
return ['Безлимит учеников', ...DEFAULT_FEATURES];
}
function getPlanDescription(plan: any): string {
if (plan.description) return plan.description;
if (plan.subscription_type === 'per_student') {
return 'Оплата за каждого ученика. Гибкая система в зависимости от количества.';
}
return 'Ежемесячная подписка без ограничений по количеству учеников. Все функции доступны.';
}
const CheckIcon = () => (
<svg width="18" height="18" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
<path d="M16.7071 5.29289C17.0976 5.68342 17.0976 6.31658 16.7071 6.70711L8.70711 14.7071C8.31658 15.0976 7.68342 15.0976 7.29289 14.7071L3.29289 10.7071C2.90237 10.3166 2.90237 9.68342 3.29289 9.29289C3.68342 8.90237 4.31658 8.90237 4.70711 9.29289L8 12.5858L15.2929 5.29289C15.6834 4.90237 16.3166 4.90237 16.7071 5.29289Z" fill="currentColor" />
</svg>
);
export function ProfilePaymentTab() {
const [plans, setPlans] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [subscription, setSubscription] = useState<any>(null);
useEffect(() => {
const load = async () => {
try {
const [plansRes, subRes] = await Promise.all([
apiClient.get<any>('/subscriptions/plans/').then((r) => r.data?.results || r.data || []),
apiClient.get<any>('/subscriptions/subscriptions/active/').then((r) => r.data).catch(() => null),
]);
setPlans(Array.isArray(plansRes) ? plansRes : []);
setSubscription(subRes);
} catch {
setPlans([]);
} finally {
setLoading(false);
}
};
load();
}, []);
if (loading) {
return <LoadingSpinner size="medium" />;
}
if (plans.length === 0) {
return (
<p style={{ color: 'var(--md-sys-color-on-surface-variant)', fontSize: 14 }}>
Нет доступных тарифов
</p>
);
}
return (
<div className="ios26-payment-tab">
{subscription && (
<div className="ios26-plan-card ios26-plan-card--current">
<span className="ios26-plan-card__overline">Текущая подписка</span>
<h3 className="ios26-plan-card__headline">{subscription.plan?.name || 'Активна'}</h3>
{subscription.end_date && (
<p className="ios26-plan-card__supporting">
До {new Date(subscription.end_date).toLocaleDateString('ru-RU')}
</p>
)}
</div>
)}
<span className="ios26-payment-tab__label">ТАРИФНЫЕ ПЛАНЫ</span>
<div className="ios26-plan-card-grid">
{plans.slice(0, 5).map((plan: any) => {
const benefits = getBenefitList(plan);
const description = getPlanDescription(plan);
const priceText = plan.price_per_student
? `${Math.round(plan.price_per_student || 0).toLocaleString('ru-RU')} ₽/уч.`
: `${Math.round(plan.price || 0).toLocaleString('ru-RU')} ₽/мес`;
return (
<article key={plan.id} className="ios26-plan-card ios26-plan-card--elevated">
<span className="ios26-plan-card__badge">{plan.name}</span>
<div className="ios26-plan-card__price-block">
<span className="ios26-plan-card__price-value">
{plan.price_per_student
? Math.round(plan.price_per_student || 0).toLocaleString('ru-RU')
: Math.round(plan.price || 0).toLocaleString('ru-RU')}
</span>
<span className="ios26-plan-card__price-period">
{plan.price_per_student ? 'за ученика' : '/мес'}
</span>
</div>
<p className="ios26-plan-card__supporting">{description}</p>
<ul className="ios26-plan-card__benefits" aria-label="Преимущества">
{benefits.slice(0, 5).map((label) => (
<li key={label} className="ios26-plan-card__benefit">
<span className="ios26-plan-card__benefit-icon"><CheckIcon /></span>
{label}
</li>
))}
</ul>
<div className="ios26-plan-card__actions">
<Link href="/payment" className="ios26-plan-card__action">
Подробнее и оплатить
</Link>
</div>
</article>
);
})}
</div>
</div>
);
}