import React, { useState } from 'react'; import { CreditCard, RefreshCw, SlidersHorizontal, } from 'lucide-react'; import { useKccStore, useOperatorRole } from '../store/kccStore'; import { StatusBadge } from '../components/StatusBadge'; import { MoneyDisplay } from '../components/MoneyDisplay'; export const BillingPage: React.FC = () => { const subscriptions = useKccStore((state) => state.subscriptions); const modules = useKccStore((state) => state.modules); const renewSubscription = useKccStore((state) => state.renewSubscription); const grantGracePeriod = useKccStore((state) => state.grantGracePeriod); const openConfirmModal = useKccStore((state) => state.openConfirmModal); const { permissions } = useOperatorRole(); const [filter, setFilter] = useState<'all' | 'expiring_7d' | 'expiring_30d' | 'overdue' | 'grace'>('all'); const [selectedSubId, setSelectedSubId] = useState(subscriptions[0]?.subscriptionId || null); const selectedSub = subscriptions.find((s) => s.subscriptionId === selectedSubId) || subscriptions[0]; // Form state const [newPeriod, setNewPeriod] = useState<'month' | 'year'>('year'); const [customModules, setCustomModules] = useState(null); const activeModulesToDisplay = customModules ?? (selectedSub ? selectedSub.activeModules : []); const filteredSubs = subscriptions.filter((sub) => { if (filter === 'all') return true; return sub.status === filter; }); // Calculate live preview amount const baseRatePerMonth = 1500000; // 15 000,00 ₽ базовый const moduleRatePerMonth = 400000; // 4 000,00 ₽ за модуль const monthlyTotal = baseRatePerMonth + activeModulesToDisplay.length * moduleRatePerMonth; const calculatedTotal = newPeriod === 'year' ? monthlyTotal * 10 : monthlyTotal; const handleRenew = () => { if (!selectedSub) return; openConfirmModal({ title: `Продление подписки: ${selectedSub.tenantName}`, actionName: `subscription.renew (${newPeriod})`, impactSummary: `Подписка будет продлена на ${newPeriod === 'year' ? '1 год' : '1 месяц'}. Состав модулей (${activeModulesToDisplay.length}): ${activeModulesToDisplay.join(', ')}. Расчетная сумма: ${calculatedTotal / 100} ₽.`, targetType: 'subscription', targetId: selectedSub.subscriptionId, targetName: selectedSub.tenantName, onConfirm: async (reason) => { await renewSubscription(selectedSub.subscriptionId, newPeriod, activeModulesToDisplay, reason); }, }); }; const handleGrantGrace = () => { if (!selectedSub) return; openConfirmModal({ title: `Предоставление Grace Period: ${selectedSub.tenantName}`, actionName: 'subscription.grace_period_granted', impactSummary: `Организации будет предоставлена отсрочка блокировки на 10 дней без остановки сервисов.`, targetType: 'subscription', targetId: selectedSub.subscriptionId, targetName: selectedSub.tenantName, onConfirm: async (reason) => { await grantGracePeriod(selectedSub.subscriptionId, 10, reason); }, }); }; return (
{/* Header */}

Подписки, лицензии и Entitlements

Управление правами доступа, сроками действия тарифов и составом включенных модулей

{/* Filters */}
{[ { id: 'all', label: 'Все' }, { id: 'expiring_7d', label: 'Истекает 7 дней' }, { id: 'expiring_30d', label: 'Истекает 30 дней' }, { id: 'grace', label: 'Grace Period' }, { id: 'suspended', label: 'Просрочено' }, ].map((tab) => ( ))}
{/* Main Grid: Subscriptions Table + Simulator Panel */}
{/* Left 2 Columns: Subscriptions List */}
{filteredSubs.map((sub) => { const isSelected = sub.subscriptionId === (selectedSub?.subscriptionId ?? ''); return ( { setSelectedSubId(sub.subscriptionId); setCustomModules(null); setNewPeriod(sub.period); }} className={`cursor-pointer transition-colors ${ isSelected ? 'bg-amber-500/10 border-l-2 border-amber-400' : 'hover:bg-zinc-800/40' }`} > ); })}
Организация / Счёт Статус Период Сумма тарифа Срок действия Grace Выбор
{sub.tenantName}
{sub.billingAccountRef}
{sub.period === 'year' ? 'Годовой' : 'Месячный'} до {new Date(sub.validUntil).toLocaleDateString('ru-RU')} {sub.graceUntil ? new Date(sub.graceUntil).toLocaleDateString('ru-RU') : '—'} { setSelectedSubId(sub.subscriptionId); setCustomModules(null); setNewPeriod(sub.period); }} className="accent-amber-400 cursor-pointer" />
{/* Right Column: Live Renewal & Module Composition Simulator */}
{selectedSub ? (
Продление и состав модулей (Preview)
Организация:
{selectedSub.tenantName}
{selectedSub.billingAccountRef}
{/* Period selection */}
{/* Modules selection */}
Включенные модули: {activeModulesToDisplay.length} выбрано
{modules.map((m) => { const isChecked = activeModulesToDisplay.includes(m.moduleId); const isCore = m.moduleId === 'krona-core'; return ( ); })}
{/* Price calculation preview */}
Расчетная сумма (Preview):
* Демо-расчет стоимости лицензии (minor units RUB). Реальные транзакции не производятся.
{/* Action Buttons */}
{/* Subscription Timeline History */}
История изменений подписки:
{selectedSub.history.map((h, i) => (
{h.actor} {new Date(h.eventAt).toLocaleDateString('ru-RU')}
{h.description}
))}
) : (
Выберите подписку в таблице слева для управления
)}
); };