- | {a.registration_number || a.registrationNumber} |
- {a.aircraft_type || a.aircraftType} |
- {a.model || '—'} |
- {a.operator || a.operator_name || '—'} |
- {a.flight_hours || a.flightHours || '—'} |
- |
+ {a.registration_number ?? a.registrationNumber ?? '—'} |
+ {typeLabel(a)} |
+ {a.operator_name ?? a.operator ?? '—'} |
+ {a.serial_number ?? '—'} |
+ {a.total_time ?? a.flight_hours ?? a.flightHours ?? '—'} |
+ |
diff --git a/app/checklists/page.tsx b/app/checklists/page.tsx
index 314b863..4456258 100644
--- a/app/checklists/page.tsx
+++ b/app/checklists/page.tsx
@@ -10,7 +10,7 @@ import { RequireRole } from '@/lib/auth-context';
export default function ChecklistsPage() {
const [domain, setDomain] = useState();
- const { data, isLoading, mutate } = useChecklistsData({ domain });
+ const { data, isLoading, mutate } = useChecklistsData({ domain: domain === 'part_m_ru' ? undefined : domain });
const templates = data?.items || [];
const [exp, setExp] = useState(null);
const [editTemplate, setEditTemplate] = useState(null);
@@ -18,7 +18,7 @@ export default function ChecklistsPage() {
const gen = async (src: string) => { const n = prompt('Название:'); if (!n) return; await checklistsApi.generate(src, n); mutate(); };
return (
-
@@ -26,14 +26,7 @@ export default function ChecklistsPage() {
{!isLoading && templates.length > 0 ? (
diff --git a/app/documents/page.tsx b/app/documents/page.tsx
index 13d33f9..d71ad89 100644
--- a/app/documents/page.tsx
+++ b/app/documents/page.tsx
@@ -7,7 +7,7 @@ export default function DocumentsPage() {
{ title: 'Входящие документы', desc: 'PDF и DOCX файлы', href: '/inbox', icon: '📥' },
{ title: 'Вложения аудитов', desc: 'Фото и протоколы', href: '/audits', icon: '🔍' },
{ title: 'Сертификаты', desc: 'Сертификаты ЛГ', href: '/airworthiness', icon: '📜' },
- { title: 'Нормативные документы', desc: 'ФАП, ICAO, EASA', href: '/regulations', icon: '📚' },
+ { title: 'Нормативные документы', desc: 'Part-M RU · ФАП', href: '/regulations', icon: '📚' },
{ title: 'Чек-листы', desc: 'Шаблоны проверок', href: '/checklists', icon: '✅' },
{ title: 'Шаблоны документов', desc: 'Заявки, акты, письма, формы', href: '/templates', icon: '📋' },
];
diff --git a/app/settings/page.tsx b/app/settings/page.tsx
index d3f11b4..0f20a23 100644
--- a/app/settings/page.tsx
+++ b/app/settings/page.tsx
@@ -2,16 +2,21 @@
import { useState, useEffect } from 'react';
import { PageLayout } from '@/components/ui';
import { apiFetch } from '@/lib/api/api-client';
+import { useAuth } from '@/lib/auth-context';
+
+const ROLE_LABELS: Record = { admin: 'Администратор', authority_inspector: 'Инспектор', operator_manager: 'Менеджер оператора', operator_user: 'Оператор', mro_manager: 'Менеджер ТОиР', mro_specialist: 'Специалист ТОиР', mro_user: 'Специалист ТОиР' };
export default function SettingsPage() {
+ const { user } = useAuth();
const [prefs, setPrefs] = useState(null);
const [saving, setSaving] = useState(false);
useEffect(() => {
- apiFetch('/notification-preferences').then(setPrefs);
+ apiFetch('/notification-preferences').catch(() => null).then(setPrefs);
}, []);
const save = async () => {
+ if (!prefs) return;
setSaving(true);
await apiFetch('/notification-preferences', { method: 'PUT', body: JSON.stringify(prefs) });
setSaving(false);
@@ -27,11 +32,49 @@ export default function SettingsPage() {
);
- if (!prefs) return ⏳ ;
-
return (
-
+
+
+ 👤 Профиль пользователя
+
+ Имя{user?.display_name ?? 'Dev User'}
+ Email{user?.email ?? 'dev@local'}
+ Роль{user?.role ? ROLE_LABELS[user.role] ?? user.role : 'Администратор'}
+ Организация{user?.organization_name ?? '—'}
+
+
+
+
+ 🖥️ Настройки системы
+
+ Название системыREFLY АСУ ТК
+ Версия2.0.0-beta
+ Нормативная базаPart-M RU
+ ЯзыкРусский
+ Часовой поясEurope/Moscow (UTC+3)
+
+
+
+
+ 🔗 Интеграции
+
+ AI-помощник (Claude)⚠️ Настройка
+ ФГИС ЕС ОрВДНе подключено
+ Keycloak SSOПодключено (dev)
+ MinIO (документы)Подключено
+
+
+
+
+ ℹ️ О системе
+ REFLY АСУ ТК v2.0.0-beta
+ Part-M RU · Гармонизировано с ICAO/EASA
+ © 2025–2026 REFLY Aviation Technologies
+
+
+ {prefs && (
+ <>
📢 Типы уведомлений
@@ -78,6 +121,8 @@ export default function SettingsPage() {
className="btn-primary px-6 py-2 rounded text-sm disabled:opacity-50">
{saving ? '⏳ Сохранение...' : '💾 Сохранить настройки'}
+ >
+ )}
);
diff --git a/app/users/page.tsx b/app/users/page.tsx
index cb78bdd..810d12e 100644
--- a/app/users/page.tsx
+++ b/app/users/page.tsx
@@ -4,8 +4,8 @@ import UserEditModal from '@/components/UserEditModal';
import { PageLayout, DataTable, FilterBar, StatusBadge } from '@/components/ui';
import { useUsersData } from '@/hooks/useSWRData';
-const RL: Record = { admin:'Администратор', authority_inspector:'Инспектор', operator_manager:'Менеджер оператора', operator_user:'Оператор', mro_manager:'Менеджер ТОиР', mro_user:'Специалист ТОиР' };
-const RC: Record = { admin:'bg-green-500', authority_inspector:'bg-blue-500', operator_manager:'bg-orange-500', operator_user:'bg-orange-400', mro_manager:'bg-purple-500', mro_user:'bg-purple-400' };
+const RL: Record = { admin:'Администратор', authority_inspector:'Инспектор', operator_manager:'Менеджер оператора', operator_user:'Оператор', mro_manager:'Менеджер ТОиР', mro_specialist:'Специалист ТОиР', mro_user:'Специалист ТОиР' };
+const RC: Record = { admin:'bg-green-500', authority_inspector:'bg-blue-500', operator_manager:'bg-orange-500', operator_user:'bg-orange-400', mro_manager:'bg-purple-500', mro_specialist:'bg-purple-400', mro_user:'bg-purple-400' };
export default function UsersPage() {
const [rf, setRf] = useState();
@@ -14,7 +14,7 @@ export default function UsersPage() {
return (
({ value: v, label: l }))]} className="mb-4" />
- {u.display_name} },
{ key: 'email', header: 'Email', render: (u: any) => {u.email || '—'} },
diff --git a/backend/app/api/routes/aircraft.py b/backend/app/api/routes/aircraft.py
index 840c9aa..c534c82 100644
--- a/backend/app/api/routes/aircraft.py
+++ b/backend/app/api/routes/aircraft.py
@@ -28,11 +28,17 @@ def _serialize_aircraft(a: Aircraft, db: Session) -> AircraftOut:
return AircraftOut.model_validate({
"id": a.id,
"registration_number": a.registration_number,
- "aircraft_type": {
- "id": a.aircraft_type.id, "manufacturer": a.aircraft_type.manufacturer,
- "model": a.aircraft_type.model, "created_at": a.aircraft_type.created_at,
- "updated_at": a.aircraft_type.updated_at,
- } if a.aircraft_type else None,
+ "aircraft_type": (
+ {
+ "id": a.aircraft_type.id,
+ "manufacturer": a.aircraft_type.manufacturer,
+ "model": a.aircraft_type.model,
+ "created_at": a.aircraft_type.created_at,
+ "updated_at": a.aircraft_type.updated_at,
+ }
+ if a.aircraft_type
+ else None
+ ),
"operator_id": a.operator_id, "operator_name": operator_name,
"serial_number": a.serial_number,
"manufacture_date": getattr(a, 'manufacture_date', None),
@@ -88,7 +94,7 @@ def list_aircraft(
items = []
for a in items_raw:
try:
- if a.aircraft_type: items.append(_serialize_aircraft(a, db))
+ items.append(_serialize_aircraft(a, db))
except Exception as e:
logger.error(f"Serialization error for aircraft {a.id}: {e}")
return {"items": items, "total": total, "page": page, "per_page": per_page,
diff --git a/backend/app/api/routes/airworthiness_core.py b/backend/app/api/routes/airworthiness_core.py
index f803341..cbfdf38 100644
--- a/backend/app/api/routes/airworthiness_core.py
+++ b/backend/app/api/routes/airworthiness_core.py
@@ -34,6 +34,37 @@ _maint_programs: dict = {}
_components: dict = {}
+def seed_airworthiness_core_demo(aircraft_id: Optional[str] = None) -> None:
+ """Заполнить демо-данными ДЛГ и Life Limits при первом запуске (если пусто)."""
+ if _directives:
+ return
+ now = datetime.now(timezone.utc).isoformat()
+ demo_ads = [
+ {"number": "AD-2025-0142-R1", "title": "Замена болтов крепления двигателя", "status": "open", "compliance_deadline": "2026-04-15", "aircraft_types": ["B737", "SSJ100"], "effective_date": "2025-06-01", "compliance_type": "mandatory"},
+ {"number": "AD-2025-0089", "title": "Инспекция лонжерона крыла зона 3", "status": "complied", "compliance_date": "2025-11-20", "aircraft_types": ["B737"], "effective_date": "2025-03-01", "compliance_type": "mandatory"},
+ {"number": "AD-2026-0012", "title": "Модификация системы предупреждения TCAS", "status": "open", "compliance_deadline": "2026-06-01", "aircraft_types": ["B737", "A320"], "effective_date": "2026-01-15", "compliance_type": "mandatory"},
+ {"number": "AD-2025-0201", "title": "Замена топливного насоса", "status": "complied", "compliance_date": "2026-01-15", "aircraft_types": ["SSJ100"], "effective_date": "2025-08-01", "compliance_type": "mandatory"},
+ {"number": "AD-2025-0178", "title": "Инспекция фюзеляжа секции 41-43", "status": "open", "compliance_deadline": "2026-03-30", "aircraft_types": ["B737"], "effective_date": "2025-07-01", "compliance_type": "mandatory"},
+ {"number": "AD-2024-0315-R2", "title": "Обновление ПО FADEC двигателя", "status": "overdue", "compliance_deadline": "2025-12-01", "aircraft_types": ["CFM56"], "effective_date": "2024-10-01", "compliance_type": "mandatory"},
+ ]
+ for d in demo_ads:
+ did = str(uuid.uuid4())
+ _directives[did] = {"id": did, "created_at": now, "issuing_authority": "FATA", "ata_chapter": None, "repetitive": False, "description": "", "affected_parts": [], **d}
+ logger.info("seed_airworthiness_core: %s directives", len(demo_ads))
+
+ if not _life_limits and aircraft_id:
+ demo_ll = [
+ {"component_name": "Двигатель CFM56-5B", "part_number": "CFM-56-5B", "serial_number": "SN-001", "limit_type": "combined", "flight_hours_limit": 30000.0, "cycles_limit": 20000, "current_hours": 24580.0, "current_cycles": 15200, "aircraft_id": aircraft_id},
+ {"component_name": "Шасси основное левое", "part_number": "LG-32-001", "serial_number": "SN-102", "limit_type": "cycles", "cycles_limit": 40000, "current_cycles": 38800, "aircraft_id": aircraft_id},
+ {"component_name": "APU GTCP131-9A", "part_number": "APU-131-9A", "serial_number": "SN-201", "limit_type": "flight_hours", "flight_hours_limit": 15000.0, "current_hours": 6500.0, "aircraft_id": aircraft_id},
+ {"component_name": "Лопатки турбины ВД", "part_number": "HPT-7680", "serial_number": "SN-305", "limit_type": "cycles", "cycles_limit": 8000, "current_cycles": 7680, "aircraft_id": aircraft_id},
+ ]
+ for ll in demo_ll:
+ lid = str(uuid.uuid4())
+ _life_limits[lid] = {"id": lid, "created_at": now, "calendar_limit_months": None, "install_date": None, "last_overhaul_date": None, "notes": None, **ll}
+ logger.info("seed_airworthiness_core: %s life limits", len(demo_ll))
+
+
# ===================================================================
# 1. ДИРЕКТИВЫ ЛЁТНОЙ ГОДНОСТИ (AD / ДЛГ)
# ===================================================================
diff --git a/backend/app/demo/seed_document_templates.py b/backend/app/demo/seed_document_templates.py
index 27c71fa..a525dba 100644
--- a/backend/app/demo/seed_document_templates.py
+++ b/backend/app/demo/seed_document_templates.py
@@ -544,7 +544,15 @@ def _templates_data() -> list[dict]:
def seed_document_templates():
db = SessionLocal()
try:
+ # Удалить шаблоны ICAO / EASA / FAA — оставляем только российские (Part-M RU, ФАП)
+ deleted = db.query(DocumentTemplate).filter(DocumentTemplate.standard.in_(["EASA", "FAA", "ICAO"])).delete(synchronize_session=False)
+ if deleted:
+ logger.info("Removed %s non-Russian document templates (EASA/FAA/ICAO)", deleted)
+ db.commit()
+
for d in _templates_data():
+ if d.get("standard") in ("EASA", "FAA", "ICAO"):
+ continue
if db.query(DocumentTemplate).filter(DocumentTemplate.code == d["code"]).first():
continue
db.add(
diff --git a/backend/app/demo/seed_full_demo.py b/backend/app/demo/seed_full_demo.py
index 9e02324..ca3134b 100644
--- a/backend/app/demo/seed_full_demo.py
+++ b/backend/app/demo/seed_full_demo.py
@@ -15,6 +15,7 @@ from app.models import (
CertApplication,
CertApplicationStatus,
RiskAlert,
+ AirworthinessCertificate,
)
from app.models.aircraft_db import Aircraft
from app.models.personnel_plg import PLGSpecialist, PLGQualification
@@ -212,9 +213,11 @@ def seed_full_demo():
if plg_org:
specialists_data = [
("Петрова Елена В.", "ПЕТРОВА-001", "Инспектор ЛГ", "I", "LIC-2024-001", "2026-12-01"),
- ("Волков Алексей Н.", "ВОЛКОВ-001", "Инженер ТОиР", "B2", "LIC-2024-002", "2026-08-15"),
+ ("Волков Алексей Н.", "ВОЛКОВ-001", "Инженер по ТО", "B2", "LIC-2024-002", "2026-08-15"),
("Морозова Ольга С.", "МОРОЗОВА-001", "Авиатехник B1", "B1", "LIC-2024-003", "2026-03-20"),
- ("Козлов Дмитрий И.", "КОЗЛОВ-001", "Пилот CAT.A", "CAT-A", "LIC-2024-004", "2027-01-10"),
+ ("Козлов Дмитрий И.", "КОЗЛОВ-001", "Инженер CAT.A", "CAT-A", "LIC-2024-004", "2027-01-10"),
+ ("Николаев Павел Р.", "НИКОЛАЕВ-001", "Инспектор Росавиации", "I", "LIC-2024-005", "2026-09-30"),
+ ("Сидорова Анна М.", "СИДОРОВА-001", "Диспетчер ТОиР", "D", "LIC-2024-006", "2026-11-15"),
]
for full_name, personnel_number, position, category, license_no, expires in specialists_data:
if db.query(PLGSpecialist).filter(PLGSpecialist.personnel_number == personnel_number).first():
@@ -248,6 +251,69 @@ def seed_full_demo():
db.commit()
logger.info("seed_full_demo: personnel PLG checked/created")
+ # ─── 7. Сертификаты лётной годности (СЛГ) ─────────────────────────
+ issuer = db.query(User).filter(User.role.in_(["admin", "authority_inspector"])).first()
+ issuer_id = str(issuer.id) if issuer else None
+ certs_data = [
+ ("KLG-2025-001", "RA-89060", "2026-08-15", "valid"),
+ ("KLG-2025-002", "RA-89061", "2026-05-20", "valid"),
+ ("KLG-2024-015", "RA-73801", "2026-02-28", "expiring_soon"),
+ ("KLG-2025-003", "RA-12345", "2027-01-10", "valid"),
+ ]
+ for cert_num, reg, valid_until, status in certs_data:
+ if db.query(AirworthinessCertificate).filter(AirworthinessCertificate.certificate_number == cert_num).first():
+ continue
+ ac_id = _get_aircraft_id_by_reg(db, reg)
+ if not ac_id:
+ ac_id = _get_first_aircraft_id(db)
+ if not ac_id or not issuer_id:
+ continue
+ exp_d = datetime.strptime(valid_until, "%Y-%m-%d").date()
+ issue_d = exp_d - timedelta(days=365)
+ db.add(
+ AirworthinessCertificate(
+ aircraft_id=ac_id,
+ certificate_number=cert_num,
+ certificate_type="standard",
+ issue_date=datetime(issue_d.year, issue_d.month, issue_d.day, tzinfo=timezone.utc),
+ expiry_date=datetime(exp_d.year, exp_d.month, exp_d.day, tzinfo=timezone.utc),
+ issuing_authority="Росавиация",
+ issued_by_user_id=issuer_id,
+ status=status,
+ )
+ )
+ db.commit()
+ logger.info("seed_full_demo: airworthiness certificates checked/created")
+
+ # ─── 8. Наряды на работу (Work Orders, in-memory) ──────────────────
+ try:
+ from app.api.routes.work_orders import _work_orders
+ aircraft_regs = [r[0] for r in db.query(Aircraft.registration_number).limit(5).all()]
+ reg1 = aircraft_regs[0] if aircraft_regs else "RA-89060"
+ reg2 = aircraft_regs[1] if len(aircraft_regs) > 1 else "RA-89061"
+ reg3 = aircraft_regs[2] if len(aircraft_regs) > 2 else "RA-73801"
+ reg4 = aircraft_regs[3] if len(aircraft_regs) > 3 else "RA-12345"
+ wos_demo = [
+ ("WO-2026-001", reg1, "Периодическое ТО A-Check", "in_progress", "urgent"),
+ ("WO-2026-002", reg2, "Замена колеса основной стойки", "open", "normal"),
+ ("WO-2026-003", reg3, "Устранение течи гидросистемы", "completed", "urgent"),
+ ("WO-2026-004", reg1, "Плановая замена фильтров двигателя", "open", "normal"),
+ ("WO-2026-005", reg4, "Внеплановое ТО после bird strike", "in_progress", "urgent"),
+ ]
+ for wo_num, reg, title, status, priority in wos_demo:
+ if any(w.get("wo_number") == wo_num for w in _work_orders.values()):
+ continue
+ wid = str(uuid.uuid4())
+ _work_orders[wid] = {
+ "id": wid, "wo_number": wo_num, "aircraft_reg": reg, "title": title,
+ "wo_type": "scheduled" if "Планов" in title else "unscheduled",
+ "description": title, "status": status, "priority": priority,
+ "created_at": datetime.now(timezone.utc).isoformat(),
+ }
+ logger.info("seed_full_demo: work orders (in-memory) populated")
+ except Exception as e:
+ logger.warning("seed_full_demo: work orders skip %s", e)
+
except Exception as e:
db.rollback()
logger.exception("seed_full_demo failed: %s", e)
diff --git a/backend/app/main.py b/backend/app/main.py
index 8c69f4b..921e976 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -78,6 +78,18 @@ async def lifespan(app: FastAPI):
except Exception as e:
import logging
logging.getLogger(__name__).warning("Full demo seed skipped: %s", e)
+ try:
+ from app.db.session import SessionLocal
+ from app.models.aircraft_db import Aircraft
+ from app.api.routes.airworthiness_core import seed_airworthiness_core_demo
+ db = SessionLocal()
+ ac = db.query(Aircraft).filter(Aircraft.registration_number == "RA-89060").first() or db.query(Aircraft).first()
+ aircraft_id = str(ac.id) if ac else None
+ db.close()
+ seed_airworthiness_core_demo(aircraft_id)
+ except Exception as e:
+ import logging
+ logging.getLogger(__name__).warning("Airworthiness core demo seed skipped: %s", e)
# Планировщик рисков (передаём app для shutdown hook)
setup_scheduler(app)
yield
diff --git a/backend/app/schemas/aircraft.py b/backend/app/schemas/aircraft.py
index f54d6c3..c1bc3dc 100644
--- a/backend/app/schemas/aircraft.py
+++ b/backend/app/schemas/aircraft.py
@@ -35,7 +35,7 @@ class AircraftUpdate(BaseModel):
class AircraftOut(TimestampOut):
id: str
registration_number: str
- aircraft_type: AircraftTypeOut
+ aircraft_type: AircraftTypeOut | None = None
operator_id: str
operator_name: str | None = None # Название организации-оператора
serial_number: str | None = None # Серийный номер ВС
diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx
index 45e3f9e..de29db5 100644
--- a/components/Sidebar.tsx
+++ b/components/Sidebar.tsx
@@ -15,6 +15,8 @@ import { sidebarIcons, commonIcons } from '@/icons/refly-icons';
import type { SidebarKey } from '@/icons/refly-icons';
import { Icon } from '@/components/Icon';
+const HEADER_ICON_KEY: SidebarKey = 'aircraft';
+
interface MenuItem { name: string; path: string; iconKey: SidebarKey; roles?: UserRole[]; }
const menuItems: MenuItem[] = [
@@ -73,7 +75,9 @@ export default function Sidebar() {
{/* Header */}
КОНТРОЛЬ ЛЁТНОЙ ГОДНОСТИ
@@ -92,7 +96,7 @@ export default function Sidebar() {
{visibleItems.map((item) => {
const active = pathname === item.path;
return (
- setMobileOpen(false)}
className={`flex items-center px-6 py-3 text-white no-underline transition-colors
${active ? 'bg-white/[0.15] border-l-[3px] border-accent-blue' : 'border-l-[3px] border-transparent hover:bg-white/[0.07]'}`}>
diff --git a/components/ui/StatusBadge.tsx b/components/ui/StatusBadge.tsx
index aad6c43..9dca8e1 100644
--- a/components/ui/StatusBadge.tsx
+++ b/components/ui/StatusBadge.tsx
@@ -1,9 +1,13 @@
'use client';
+import { statusIcons, type StatusKey } from '@/icons/refly-icons';
+import { Icon } from '@/components/Icon';
+
interface Props {
status: string;
colorMap?: Record ;
labelMap?: Record;
+ showIcon?: boolean;
}
const defaults: Record = {
@@ -17,8 +21,16 @@ const defaults: Record = {
overdue: 'bg-red-600', deferred: 'bg-yellow-600',
};
-export default function StatusBadge({ status, colorMap, labelMap }: Props) {
+export default function StatusBadge({ status, colorMap, labelMap, showIcon = true }: Props) {
const color = colorMap?.[status] || defaults[status] || 'bg-gray-400';
const label = labelMap?.[status] || status;
- return {label};
+ const StatusIcon = statusIcons[status as StatusKey];
+ return (
+
+ {showIcon && StatusIcon && (
+
+ )}
+ {label}
+
+ );
}
diff --git a/docs/ops/AI_PROXY_SETUP.md b/docs/ops/AI_PROXY_SETUP.md
new file mode 100644
index 0000000..1aef22f
--- /dev/null
+++ b/docs/ops/AI_PROXY_SETUP.md
@@ -0,0 +1,48 @@
+# Настройка прокси Anthropic через papa-app (Railway)
+
+KLG backend в Yandex Cloud (РФ) не может вызывать Anthropic API из-за блокировки по IP.
+Запросы идут через papa-app на Railway (вне РФ).
+
+## Что сделано в коде
+
+- **papa-app:** `POST /api/proxy/anthropic` — принимает тело запроса и заголовок `x-proxy-secret`, подставляет `ANTHROPIC_API_KEY` и вызывает `https://api.anthropic.com/v1/messages`.
+- **KLG backend:** при заданных `AI_PROXY_URL` и `AI_PROXY_SECRET` использует прокси вместо прямого вызова Anthropic.
+
+## 1. Railway (papa-app)
+
+В проекте papa-app в Railway Dashboard → Variables:
+
+| Переменная | Значение | Секретность |
+|------------|----------|-------------|
+| `ANTHROPIC_API_KEY` | Ваш ключ Anthropic (sk-ant-...) | Secret |
+| `PROXY_SECRET` | `klg-refly-proxy-2026` (или свой) | Secret |
+
+После сохранения Railway пересоберёт и задеплоит приложение.
+
+## 2. Сервер KLG (158.160.22.166)
+
+```bash
+cd ~/klg-asutk-app
+git pull origin main
+
+# Добавить в .env (если ещё не добавлено)
+grep -q AI_PROXY_URL .env || echo 'AI_PROXY_URL=https://papa-app-production.up.railway.app/api/proxy/anthropic' >> .env
+grep -q AI_PROXY_SECRET .env || echo 'AI_PROXY_SECRET=klg-refly-proxy-2026' >> .env
+
+docker compose build backend && docker compose up -d backend
+```
+
+Значение `AI_PROXY_SECRET` должно совпадать с `PROXY_SECRET` в Railway.
+
+## 3. Проверка
+
+```bash
+# Прокси (после деплоя papa-app и установки переменных)
+curl -s -X POST https://papa-app-production.up.railway.app/api/proxy/anthropic \
+ -H "Content-Type: application/json" \
+ -H "x-proxy-secret: klg-refly-proxy-2026" \
+ -d '{"model":"claude-3-5-sonnet-20241022","max_tokens":50,"messages":[{"role":"user","content":"Hi"}]}'
+```
+
+Ожидается JSON с полем `content` (ответ модели) или сообщение об ошибке от Anthropic.
+401 — неверный `x-proxy-secret`, 500 — не задан `ANTHROPIC_API_KEY` в Railway.
diff --git a/icons/refly-icons.tsx b/icons/refly-icons.tsx
index ee3968b..664c386 100644
--- a/icons/refly-icons.tsx
+++ b/icons/refly-icons.tsx
@@ -36,7 +36,6 @@ import {
XCircle,
MessageSquareWarning,
Clock,
- ClockAlert,
BadgeCheck,
PlaneOff,
CircleDot,
@@ -139,6 +138,66 @@ export const ReflyRegulator = (props: ReflyIconProps) => (
);
+/** Aircraft (ВС) */
+export const ReflyAircraft = (props: ReflyIconProps) => (
+
+);
+
+/** Audits: shield + magnifier */
+export const ReflyAudits = (props: ReflyIconProps) => (
+
+);
+
+/** Documents */
+export const ReflyDocuments = (props: ReflyIconProps) => (
+
+);
+
+/** Defects */
+export const ReflyDefects = (props: ReflyIconProps) => (
+
+);
+
+/** Risks */
+export const ReflyRisks = (props: ReflyIconProps) => (
+
+);
+
+/** Maintenance */
+export const ReflyMaintenance = (props: ReflyIconProps) => (
+
+);
+
/* -----------------------
ICON MAPS
----------------------- */
@@ -175,19 +234,19 @@ export type SidebarKey =
export const sidebarIcons: Record> = {
dashboard: LayoutDashboard,
organizations: Building2,
- aircraft: Plane,
+ aircraft: ReflyAircraft,
applications: ClipboardList,
checklists: CheckSquare,
- audits: SearchCheck,
- risks: AlertTriangle,
+ audits: ReflyAudits,
+ risks: ReflyRisks,
users: Users,
airworthiness: ReflyAirworthiness,
calendar: Calendar,
"airworthiness-core": ReflyContinuedAirworthiness,
- maintenance: Wrench,
- defects: Bug,
+ maintenance: ReflyMaintenance,
+ defects: ReflyDefects,
modifications: GitBranch,
- documents: FileText,
+ documents: ReflyDocuments,
inbox: Inbox,
regulations: BookOpen,
monitoring: Activity,
@@ -232,7 +291,7 @@ export const statusIcons: Record |