- deps: авторизация через app.services.security (JWT/OIDC), без oidc fallback - main: AUTH_DEPENDENCY для роутеров, RequestLoggerMiddleware, убран on_event(startup) - attachments: защита path traversal, проверка владельца/authority - docker-compose: SECRET_KEY обязателен, отдельная БД keycloak-db - middleware: ужесточён CSP (без unsafe-eval в prod, без api.openai.com) - api-client: токен только в памяти, без sessionStorage - cert_applications: _next_number с SELECT FOR UPDATE - Удалён lib/api.ts, импорты на @/lib/api/api-client - docs ERROR_HANDLING: aircraftApi.list(), middleware __init__.py Co-authored-by: Cursor <cursoragent@cursor.com>
37 lines
1.8 KiB
TypeScript
37 lines
1.8 KiB
TypeScript
'use client';
|
||
import { useState, useMemo } from 'react';
|
||
import { Modal, DataTable, StatusBadge } from '@/components/ui';
|
||
import { Aircraft } from '@/lib/api/api-client';
|
||
|
||
interface Props { isOpen: boolean; onClose: () => void; aircraft: Aircraft[]; searchType?: string; }
|
||
|
||
export default function SearchModal({ isOpen, onClose, aircraft, searchType }: Props) {
|
||
const [query, setQuery] = useState('');
|
||
|
||
const filtered = useMemo(() => {
|
||
if (!query.trim()) return aircraft;
|
||
const q = query.toLowerCase();
|
||
return aircraft.filter(a =>
|
||
a.registrationNumber?.toLowerCase().includes(q) ||
|
||
a.aircraftType?.toLowerCase().includes(q) ||
|
||
a.operator?.toLowerCase().includes(q) ||
|
||
a.serialNumber?.toLowerCase().includes(q)
|
||
);
|
||
}, [aircraft, query]);
|
||
|
||
return (
|
||
<Modal isOpen={isOpen} onClose={onClose} title={searchType === 'organization' ? 'Поиск ВС организации' : 'Поиск воздушных судов'} size="lg">
|
||
<input value={query} onChange={e => setQuery(e.target.value)} placeholder="Поиск по регистрации, типу, оператору..."
|
||
className="input-field mb-4" autoFocus />
|
||
<div className="text-sm text-gray-500 mb-3">Найдено: {filtered.length}</div>
|
||
<DataTable data={filtered.slice(0, 50)} emptyMessage="Ничего не найдено"
|
||
columns={[
|
||
{ key: 'registrationNumber', header: 'Регистрация', render: (a) => <span className="font-medium text-primary-500">{a.registrationNumber}</span> },
|
||
{ key: 'aircraftType', header: 'Тип' },
|
||
{ key: 'operator', header: 'Оператор' },
|
||
{ key: 'status', header: 'Статус', render: (a) => <StatusBadge status={a.status} /> },
|
||
]} />
|
||
</Modal>
|
||
);
|
||
}
|