/**
* Регулятор — Минтранс, ФАВТ, Ространснадзор
* Доступ: favt_inspector или admin.
* Показывает ТОЛЬКО агрегированные данные согласно:
* - ВК РФ ст. 8, 24.1, 28, 33, 36, 37, 67, 68
* - ФАП-246, ФАП-285, ФГИС РЭВС
* - ICAO Annex 6, 7, 8, 19; Doc 9734, 9760, 9859
* - EASA Part-M, Part-CAMO, Part-145, Part-ARO
*
* Разработчик: АО «REFLY»
*/
'use client';
import { useState, useEffect, useCallback } from 'react';
import { PageLayout, DataTable, StatusBadge, EmptyState } from '@/components/ui';
import { useAuth } from '@/lib/auth-context';
import { useI18n } from '@/hooks/useI18n';
type Tab = 'overview' | 'aircraft' | 'certifications' | 'safety' | 'audits' | 'personnel';
interface OverviewData {
aircraft: { total: number; airworthy: number; in_maintenance: number; grounded: number; decommissioned: number };
organizations: { total: number };
certification: { total_applications: number; pending: number; approved: number; rejected: number };
safety: { total_risks: number; critical: number; high: number; unresolved: number };
audits_last_30d: number;
legal_basis: string[];
}
const TABS: { id: Tab; label: string; icon: string }[] = [
{ id: 'overview', label: 'Сводка', icon: '📊' },
{ id: 'aircraft', label: 'Реестр ВС', icon: '✈️' },
{ id: 'certifications', label: 'Сертификация', icon: '📋' },
{ id: 'safety', label: 'Безопасность', icon: '🛡️' },
{ id: 'audits', label: 'Аудиты', icon: '🔍' },
{ id: 'personnel' as Tab, label: 'Персонал ПЛГ', icon: '🎓' },
];
function AccessDenied() {
return (
🔒
Доступ ограничен
Панель доступна уполномоченным сотрудникам Минтранса, ФАВТ и Ространснадзора.
Основание: ВК РФ ст. 8 — Федеральные правила использования воздушного пространства.
Для получения доступа обратитесь к администратору системы.
);
}
function StatCard({ label, value, sub, color = 'primary' }: { label: string; value: number; sub?: string; color?: string }) {
const colors: Record = {
primary: 'bg-blue-50 text-blue-700 border-blue-200',
green: 'bg-green-50 text-green-700 border-green-200',
yellow: 'bg-yellow-50 text-yellow-700 border-yellow-200',
red: 'bg-red-50 text-red-700 border-red-200',
gray: 'bg-gray-50 text-gray-700 border-gray-200',
};
return (
{value}
{label}
{sub &&
{sub}
}
);
}
function LegalBasisBadge({ items }: { items: string[] }) {
return (
📜 Правовые основания ({items.length})
{items.map((b, i) => {b} )}
);
}
export default function RegulatorPanel() {
const { user } = useAuth();
const [tab, setTab] = useState('overview');
const [overview, setOverview] = useState(null);
const [aircraftData, setAircraftData] = useState(null);
const [certData, setCertData] = useState(null);
const [safetyData, setSafetyData] = useState(null);
const [auditData, setAuditData] = useState(null);
const [personnelData, setPersonnelData] = useState(null);
const [loading, setLoading] = useState(false);
const [days, setDays] = useState(90);
const [agency, setAgency] = useState<'mintrans' | 'favt' | 'rostransnadzor'>('favt');
const DEMO_OVERVIEW: OverviewData = {
aircraft: { total: 142, airworthy: 118, in_maintenance: 12, grounded: 8, decommissioned: 4 },
organizations: { total: 28 },
certification: { total_applications: 15, pending: 3, approved: 10, rejected: 2 },
safety: { total_risks: 45, critical: 2, high: 8, unresolved: 5 },
audits_last_30d: 7,
legal_basis: ['ВК РФ ст. 8, 24.1, 28, 33, 36, 37', 'ФАП-246, ФАП-148', 'ICAO Annex 6/8/19'],
};
// Access control: only favt_inspector or admin
const hasAccess = user?.role === 'favt_inspector' || user?.role === 'admin';
const fetchData = useCallback(async (endpoint: string) => {
try {
const res = await fetch(`/api/v1/regulator/${endpoint}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (e) {
console.error(`Regulator API error:`, e);
return null;
}
}, []);
useEffect(() => {
if (!hasAccess) return;
setLoading(true);
fetchData('overview').then(d => { setOverview(d || DEMO_OVERVIEW); setLoading(false); });
}, [hasAccess, fetchData]);
useEffect(() => {
if (!hasAccess) return;
if (tab === 'aircraft' && !aircraftData) fetchData('aircraft-register').then(setAircraftData);
if (tab === 'certifications' && !certData) fetchData('certifications').then(setCertData);
if (tab === 'safety' && !safetyData) fetchData(`safety-indicators?days=${days}`).then(setSafetyData);
if (tab === 'audits' && !auditData) fetchData(`audits?days=${days}`).then(setAuditData);
if (tab === 'personnel' && !personnelData) fetchData('personnel-summary').then(setPersonnelData);
}, [tab, hasAccess, days, fetchData, aircraftData, certData, safetyData, auditData]);
if (!hasAccess) return ;
const handlePdfExport = async () => {
try {
const res = await fetch('/api/v1/regulator/report/pdf');
if (!res.ok) throw new Error('PDF generation failed');
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `favt_report_${new Date().toISOString().slice(0, 10)}.pdf`;
a.click();
URL.revokeObjectURL(url);
} catch (e) { console.error(e); }
};
const handleExport = async () => {
const data = await fetchData('report');
if (data) {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `favt_report_${new Date().toISOString().slice(0, 10)}.json`;
a.click();
URL.revokeObjectURL(url);
}
};
return (
📄 JSON отчёт
📑 PDF отчёт
}
>
{/* Disclaimer */}
⚠️ Ограниченный доступ. Данные предоставляются в агрегированном виде согласно
ВК РФ (60-ФЗ), ФАП-21/128/145/147/148/149/246, ФЗ-488, ICAO Annex 6/8/19, EASA Part-M/CAMO/145/ARO, Поручение Президента Пр-1379, ТЗ АСУ ТК.
Персональные данные и коммерческая тайна не раскрываются.
{/* Ведомства */}
{(['mintrans', 'favt', 'rostransnadzor'] as const).map(a => (
setAgency(a)}
className={`px-4 py-2 text-sm font-medium border-b-2 ${agency === a ? 'border-blue-600 text-blue-600' : 'border-transparent text-gray-500'}`}>
{a === 'mintrans' ? 'Минтранс' : a === 'favt' ? 'ФАВТ' : 'Ространснадзор'}
))}
{/* Tabs */}
{TABS.map(t => (
setTab(t.id)}
className={`px-4 py-2.5 text-sm font-medium whitespace-nowrap border-b-2 transition-colors
${tab === t.id ? 'border-blue-600 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700'}`}>
{t.icon} {t.label}
))}
{loading && !overview ? (
⏳ Загрузка данных...
) : (
<>
{/* === OVERVIEW TAB === */}
{tab === 'overview' && overview && (
{/* Aircraft section — ВК РФ ст. 33, ICAO Annex 7 */}
✈️ Состояние парка ВС (ВК РФ ст. 33; ICAO Annex 7, 8)
{/* Certification — ФАП-246, ICAO Annex 6 */}
📋 Сертификация эксплуатантов (ФАП-246; ICAO Annex 6)
{/* Safety — ВК РФ ст. 24.1, ICAO Annex 19 */}
🛡️ Показатели безопасности (ГПБП; ICAO Annex 19; Doc 9859)
{/* Audits + Orgs */}
🔍 Надзорная деятельность (ВК РФ ст. 28; ICAO Doc 9734)
)}
{/* === AIRCRAFT REGISTER TAB === */}
{tab === 'aircraft' && (
Данные аналогичны ФГИС РЭВС (приказ Росавиации № 180-П от 09.03.2017)
{aircraftData?.items?.length ? (
(
)},
{ key: 'organization', label: 'Эксплуатант' },
{ key: 'cert_expiry', label: 'СЛГ до', render: (v: string) => v ? new Date(v).toLocaleDateString('ru-RU') : '—' },
]}
data={aircraftData.items}
/>
) : }
)}
{/* === CERTIFICATIONS TAB === */}
{tab === 'certifications' && (
Процедуры подтверждения соответствия по ФАП-246
{certData?.items?.length ? (
v?.slice(0, 8) },
{ key: 'type', label: 'Тип' },
{ key: 'status', label: 'Статус', render: (v: string) => (
)},
{ key: 'organization', label: 'Организация' },
{ key: 'submitted_at', label: 'Дата подачи', render: (v: string) => v ? new Date(v).toLocaleDateString('ru-RU') : '—' },
]}
data={certData.items}
/>
) : }
)}
{/* === SAFETY TAB === */}
{tab === 'safety' && (
Период:
{[30, 90, 180, 365].map(d => (
{ setDays(d); setSafetyData(null); }}
className={`px-2.5 py-1 rounded text-xs ${days === d ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-600'}`}>
{d}д
))}
{safetyData ? (
<>
{/* Severity distribution */}
Распределение рисков по степени серьёзности
{Object.entries(safetyData.severity_distribution || {}).map(([sev, cnt]) => (
))}
{/* Monthly trend */}
{safetyData.monthly_trend?.length > 0 && (
Динамика рисков по месяцам
{safetyData.monthly_trend.map((m: any, i: number) => {
const maxC = Math.max(...safetyData.monthly_trend.map((t: any) => t.count), 1);
return (
{m.count}
{m.month ? new Date(m.month).toLocaleDateString('ru-RU', { month: 'short' }) : '?'}
);
})}
)}
⚠️
{safetyData.critical_unresolved}
Критических неустранённых рисков
>
) :
Загрузка...
}
)}
{/* === AUDITS TAB === */}
{tab === 'audits' && (
Результаты инспекционного контроля (ВК РФ ст. 28)
{auditData?.items?.length ? (
v?.slice(0, 8) },
{ key: 'type', label: 'Тип проверки' },
{ key: 'status', label: 'Результат', render: (v: string) => (
)},
{ key: 'aircraft_reg', label: 'Рег. знак ВС' },
{ key: 'conducted_at', label: 'Дата', render: (v: string) => v ? new Date(v).toLocaleDateString('ru-RU') : '—' },
]}
data={auditData.items}
/>
) : }
)}
>
)}
{/* === PERSONNEL PLG TAB === */}
{tab === 'personnel' && (
Агрегированные данные о персонале ПЛГ (ВК РФ ст. 52-54; ФАП-147; ICAO Annex 1)
{personnelData ? (
<>
{personnelData.total_specialists}
Всего специалистов
{personnelData.compliant}
Квалификация в порядке
{personnelData.non_compliant}
Нарушения
{personnelData.compliance_rate}%
Compliance rate
{Object.keys(personnelData.by_category || {}).length > 0 && (
Распределение по категориям (EASA Part-66 / ФАП-147)
{Object.entries(personnelData.by_category).map(([cat, cnt]) => (
{cnt as number}
Кат. {cat}
))}
)}
Персональные данные (ФИО, табельные номера) не раскрываются (ФЗ-152)
>
) :
Загрузка...
}
)}
{/* Footer */}
Данные предоставлены из АСУ ТК КЛГ согласно ТЗ (утв. 24.07.2022) и Поручению Президента Пр-1379. Агрегированные показатели — коммерческая тайна и ПДн не раскрываются.
Система соответствует требованиям ФЗ-152 «О персональных данных» и ФЗ-149 «Об информации».
© {new Date().getFullYear()} АО «REFLY» — Разработчик АСУ ТК
);
}