/** * Ядро системы ПЛГ — Контроль лётной годности * 5 подсистем: ДЛГ (AD), Бюллетени (SB), Ресурсы, Программы ТО, Компоненты * * ВК РФ ст. 36, 37, 37.2; ФАП-148; ФАП-145; EASA Part-M; ICAO Annex 6/8 */ 'use client'; import { useState, useEffect, useCallback } from 'react'; import { PageLayout, DataTable, StatusBadge, Modal, EmptyState } from '@/components/ui'; type Tab = 'control' | 'directives' | 'bulletins' | 'life-limits' | 'maint-programs' | 'components'; interface ControlRecord { id: string; registration: string; aircraft_type: string; last_check_date: string; status: string; valid_until: string; responsible: string; notes?: string; history?: { date: string; type: string; result: string }[]; } const DEMO_CONTROL: ControlRecord[] = [ { id: '1', registration: 'RA-73001', aircraft_type: 'SSJ-100', last_check_date: '2024-11-15', status: 'Годен', valid_until: '2025-11-15', responsible: 'Иванов И.И.', history: [{ date: '2024-11-15', type: 'Периодический осмотр', result: 'Годен' }, { date: '2023-11-10', type: 'Периодический осмотр', result: 'Годен' }] }, { id: '2', registration: 'RA-73002', aircraft_type: 'MC-21', last_check_date: '2024-10-20', status: 'Годен', valid_until: '2025-10-20', responsible: 'Петров П.П.' }, { id: '3', registration: 'RA-73003', aircraft_type: 'Ан-148', last_check_date: '2024-09-05', status: 'Ограниченно годен', valid_until: '2025-01-05', responsible: 'Сидорова А.С.', notes: 'Ограничение по наработке двигателя' }, { id: '4', registration: 'VQ-BAB', aircraft_type: 'Boeing 737-800', last_check_date: '2024-12-01', status: 'Годен', valid_until: '2025-12-01', responsible: 'Козлов М.А.' }, { id: '5', registration: 'RA-73005', aircraft_type: 'Airbus A320', last_check_date: '2024-08-12', status: 'Годен', valid_until: '2025-08-12', responsible: 'Новикова Е.В.' }, { id: '6', registration: 'RA-73006', aircraft_type: 'SSJ-100', last_check_date: '2024-11-28', status: 'Годен', valid_until: '2025-11-28', responsible: 'Иванов И.И.' }, { id: '7', registration: 'RA-73007', aircraft_type: 'MC-21', last_check_date: '2024-10-10', status: 'На проверке', valid_until: '—', responsible: 'Петров П.П.' }, ]; const TABS: { id: Tab; label: string; icon: string; basis: string }[] = [ { id: 'control', label: 'Контроль ЛГ', icon: '✈️', basis: 'ВК РФ ст. 36; ФАП-148; Контроль лётной годности ВС' }, { id: 'directives', label: 'ДЛГ / AD', icon: '⚠️', basis: 'ВК РФ ст. 37; ФАП-148 п.4.3' }, { id: 'bulletins', label: 'Бюллетени SB', icon: '📢', basis: 'ФАП-148 п.4.5; EASA Part-21' }, { id: 'life-limits', label: 'Ресурсы', icon: '⏱️', basis: 'ФАП-148 п.4.2; EASA Part-M.A.302' }, { id: 'maint-programs', label: 'Программы ТО', icon: '📋', basis: 'ФАП-148 п.3; ICAO Annex 6' }, { id: 'components', label: 'Компоненты', icon: '🔩', basis: 'ФАП-145 п.A.42; EASA Part-M.A.501' }, ]; export default function AirworthinessCorePage() { const [tab, setTab] = useState('control'); const [data, setData] = useState>({}); const [loading, setLoading] = useState(false); const [showAddModal, setShowAddModal] = useState(false); const [controlRecords, setControlRecords] = useState(DEMO_CONTROL); const [selectedControl, setSelectedControl] = useState(null); const [controlFilter, setControlFilter] = useState(''); const [controlSort, setControlSort] = useState<'registration' | 'last_check_date' | 'status'>('registration'); const api = useCallback(async (endpoint: string, opts?: RequestInit) => { const res = await fetch(`/api/v1/airworthiness-core/${endpoint}`, opts); return res.json(); }, []); useEffect(() => { if (tab === 'control') { setLoading(false); return; } setLoading(true); const endpoint = tab === 'life-limits' ? 'life-limits' : tab === 'maint-programs' ? 'maintenance-programs' : tab; api(endpoint).then(d => { setData(prev => ({ ...prev, [tab]: d })); setLoading(false); }); }, [tab, api]); const currentTab = TABS.find(t => t.id === tab)!; const items = tab === 'control' ? [] : (data[tab]?.items || []); const filteredControl = controlRecords .filter(r => !controlFilter || r.registration.toLowerCase().includes(controlFilter.toLowerCase()) || r.aircraft_type.toLowerCase().includes(controlFilter.toLowerCase()) || r.status.toLowerCase().includes(controlFilter.toLowerCase())) .sort((a, b) => { const va = a[controlSort], vb = b[controlSort]; return String(va).localeCompare(String(vb), undefined, { numeric: true }); }); const downloadCertificate = (r: ControlRecord) => { const text = [ 'СЕРТИФИКАТ ЛЁТНОЙ ГОДНОСТИ (выписка)', `Бортовой номер: ${r.registration}`, `Тип ВС: ${r.aircraft_type}`, `Дата последней проверки: ${r.last_check_date}`, `Статус ЛГ: ${r.status}`, `Срок действия: ${r.valid_until}`, `Ответственный: ${r.responsible}`, r.notes ? `Примечания: ${r.notes}` : '', '', 'Документ сформирован системой КЛГ АСУ ТК. © АО «REFLY»', ].filter(Boolean).join('\n'); const blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `certificate_${r.registration}.txt`; a.click(); URL.revokeObjectURL(url); }; const statusColors: Record = { open: 'bg-red-500', complied: 'bg-green-500', incorporated: 'bg-green-500', not_applicable: 'bg-gray-400', deferred: 'bg-yellow-500', serviceable: 'bg-green-500', unserviceable: 'bg-red-500', overhauled: 'bg-blue-500', scrapped: 'bg-gray-400', mandatory: 'bg-red-500', alert: 'bg-orange-500', recommended: 'bg-blue-500', info: 'bg-gray-400', }; const statusLabels: Record = { open: 'Открыта', complied: 'Выполнена', incorporated: 'Внедрён', not_applicable: 'Неприменимо', deferred: 'Отложена', serviceable: 'Исправен', unserviceable: 'Неисправен', overhauled: 'После ремонта', scrapped: 'Списан', mandatory: 'Обязат.', alert: 'Важный', recommended: 'Рекоменд.', info: 'Информ.', }; return ( setShowAddModal(true)} className="btn-primary text-sm px-4 py-2 rounded">+ Добавить}>
Ядро системы ПЛГ. ВК РФ ст. 36, 37, 37.2; ФАП-148; ФАП-145; EASA Part-M; ICAO Annex 6/8. Модуль обеспечивает непрерывный контроль лётной годности ВС.
{/* Tabs */}
{TABS.map(t => ( ))}
{loading ?
⏳ Загрузка...
: ( <> {/* CONTROL LG */} {tab === 'control' && ( <>
setControlFilter(e.target.value)} className="input-field w-64" /> Сортировка: {(['registration', 'last_check_date', 'status'] as const).map(k => ( ))}
{filteredControl.map(r => ( setSelectedControl(r)} className="border-b border-gray-100 hover:bg-blue-50 cursor-pointer"> ))}
Бортовой номерТип ВСДата последней проверки Статус ЛГСрок действияОтветственный
{r.registration} {r.aircraft_type} {r.last_check_date} {r.status} {r.valid_until} {r.responsible}
{filteredControl.length === 0 && } )} {/* DIRECTIVES (AD/ДЛГ) */} {tab === 'directives' && ( items.length > 0 ? ( v?.join(', ') || '—' }, { key: 'compliance_type', label: 'Тип', render: (v: string) => }, { key: 'effective_date', label: 'Дата', render: (v: string) => v ? new Date(v).toLocaleDateString('ru-RU') : '—' }, { key: 'status', label: 'Статус', render: (v: string) => }, ]} data={items} /> ) : )} {/* BULLETINS (SB) */} {tab === 'bulletins' && ( items.length > 0 ? ( }, { key: 'estimated_manhours', label: 'Трудоёмк. (ч)', render: (v: number) => v || '—' }, { key: 'status', label: 'Статус', render: (v: string) => }, ]} data={items} /> ) : )} {/* LIFE LIMITS */} {tab === 'life-limits' && ( items.length > 0 ? ( { if (!v) return '—'; const parts = []; if (v.hours !== undefined) parts.push(`${v.hours}ч`); if (v.cycles !== undefined) parts.push(`${v.cycles}цикл`); if (v.days !== undefined) parts.push(`${v.days}дн`); const isLow = Object.values(v).some((val: any) => typeof val === 'number' && val < 100); return {parts.join(' / ') || '—'}; }}, { key: 'critical', label: '⚠️', render: (v: boolean) => v ? КРИТИЧ. : '✅' }, ]} data={items} /> ) : )} {/* MAINTENANCE PROGRAMS */} {tab === 'maint-programs' && ( items.length > 0 ? (
{items.map((m: any) => (
{m.name}
{m.aircraft_type} · {m.revision}
{m.approved_by &&
Утв.: {m.approved_by}
}
{m.tasks?.length || 0} задач
))}
) : )} {/* COMPONENTS */} {tab === 'components' && ( items.length > 0 ? ( }, { key: 'certificate_type', label: 'Сертификат' }, ]} data={items} /> ) : )} )} {/* Control LG detail modal */} setSelectedControl(null)} title={selectedControl ? `Контроль ЛГ — ${selectedControl.registration}` : ''} size="md" footer={selectedControl ? (
) : undefined}> {selectedControl && (
Бортовой номер
{selectedControl.registration}
Тип ВС
{selectedControl.aircraft_type}
Дата последней проверки
{selectedControl.last_check_date}
Статус ЛГ
{selectedControl.status}
Срок действия
{selectedControl.valid_until}
Ответственный
{selectedControl.responsible}
{selectedControl.notes &&
Примечания

{selectedControl.notes}

}

История проверок

    {(selectedControl.history || []).map((h, i) =>
  • {h.date} — {h.type}: {h.result}
  • )} {(!selectedControl.history || selectedControl.history.length === 0) &&
  • Нет данных
  • }
)} {/* Legal basis footer */}
{currentTab.basis} · © АО «REFLY»
); }