import type { LucideIcon } from 'lucide-react'; import { Baby, Building2, Car, CreditCard, FileText, HeartPulse, Plane, Stethoscope, Wallet } from 'lucide-react'; export type DocumentTypeCode = | 'PASSPORT_RF' | 'FOREIGN_PASSPORT' | 'BIRTH_CERTIFICATE' | 'DRIVER_LICENSE' | 'VEHICLE_REGISTRATION' | 'OMS' | 'DMS' | 'INN' | 'SNILS'; export type FieldKind = 'text' | 'date' | 'gender' | 'checkbox' | 'textarea' | 'categories'; export interface DocumentFieldDef { key: string; label: string; kind: FieldKind; placeholder?: string; latinKey?: string; latinLabel?: string; } export interface DocumentTypeDef { type: DocumentTypeCode; label: string; shortLabel: string; category: 'personal' | 'transport' | 'health' | 'finance'; icon: LucideIcon; accent: string; numberKey: string; fields: DocumentFieldDef[]; } export const DOCUMENT_CATEGORIES = [ { id: 'personal' as const, title: 'Личные документы' }, { id: 'transport' as const, title: 'Транспорт' }, { id: 'health' as const, title: 'Здоровье' }, { id: 'finance' as const, title: 'Финансы' } ]; const baseNameFields: DocumentFieldDef[] = [ { key: 'lastName', label: 'Фамилия', kind: 'text', placeholder: 'Фамилия' }, { key: 'firstName', label: 'Имя', kind: 'text', placeholder: 'Имя' }, { key: 'middleName', label: 'Отчество', kind: 'text', placeholder: 'Отчество' }, { key: 'noMiddleName', label: 'Нет отчества', kind: 'checkbox' } ]; const latinNameFields: DocumentFieldDef[] = [ { key: 'lastName', label: 'Фамилия', kind: 'text', placeholder: 'Фамилия', latinKey: 'lastNameLatin', latinLabel: 'Фамилия латинскими буквами' }, { key: 'firstName', label: 'Имя', kind: 'text', placeholder: 'Имя', latinKey: 'firstNameLatin', latinLabel: 'Имя латинскими буквами' }, { key: 'middleName', label: 'Отчество', kind: 'text', placeholder: 'Отчество', latinKey: 'middleNameLatin', latinLabel: 'Отчество латинскими буквами' }, { key: 'noMiddleName', label: 'Нет отчества', kind: 'checkbox' } ]; export const DOCUMENT_TYPES: DocumentTypeDef[] = [ { type: 'PASSPORT_RF', label: 'Паспорт РФ', shortLabel: 'Паспорт РФ', category: 'personal', icon: FileText, accent: 'text-red-500 bg-red-50', numberKey: 'seriesNumber', fields: [ { key: 'seriesNumber', label: 'Серия и номер', kind: 'text', placeholder: '0000 000000' }, ...baseNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'birthPlace', label: 'Место рождения', kind: 'text', placeholder: 'Место рождения' }, { key: 'gender', label: 'Пол', kind: 'gender' }, { key: 'issuedBy', label: 'Кем выдан', kind: 'text', placeholder: 'Кем выдан' }, { key: 'departmentCode', label: 'Код подразделения', kind: 'text', placeholder: '000-000' }, { key: 'issuedAt', label: 'Дата выдачи', kind: 'date' }, { key: 'registrationAddress', label: 'Адрес регистрации', kind: 'text', placeholder: 'Адрес регистрации' } ] }, { type: 'FOREIGN_PASSPORT', label: 'Загран', shortLabel: 'Загран', category: 'personal', icon: Plane, accent: 'text-red-500 bg-red-50', numberKey: 'number', fields: [ { key: 'number', label: 'Номер', kind: 'text', placeholder: 'Номер' }, ...latinNameFields, { key: 'citizenship', label: 'Гражданство', kind: 'text', placeholder: 'Гражданство', latinKey: 'citizenshipLatin', latinLabel: 'Гражданство латинскими буквами' }, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'birthPlace', label: 'Место рождения', kind: 'text', placeholder: 'Место рождения', latinKey: 'birthPlaceLatin', latinLabel: 'Место рождения латинскими буквами' }, { key: 'gender', label: 'Пол', kind: 'gender' }, { key: 'issuedAt', label: 'Дата выдачи', kind: 'date' }, { key: 'expiresAt', label: 'Дата окончания срока действия', kind: 'date' } ] }, { type: 'BIRTH_CERTIFICATE', label: 'Свидетельство о рождении', shortLabel: 'Св-во о рождении', category: 'personal', icon: Baby, accent: 'text-red-500 bg-red-50', numberKey: 'seriesNumber', fields: [ { key: 'series', label: 'Серия', kind: 'text', placeholder: 'Серия' }, { key: 'number', label: 'Номер', kind: 'text', placeholder: 'Номер' }, ...baseNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'birthPlace', label: 'Место рождения', kind: 'text' }, { key: 'recordNumber', label: 'Номер записи акта о рождении', kind: 'text' }, { key: 'fatherLastName', label: 'Фамилия отца', kind: 'text', placeholder: 'Фамилия' }, { key: 'fatherFirstName', label: 'Имя отца', kind: 'text', placeholder: 'Имя' }, { key: 'fatherMiddleName', label: 'Отчество отца', kind: 'text', placeholder: 'Отчество' }, { key: 'fatherNoMiddleName', label: 'Нет отчества (отец)', kind: 'checkbox' }, { key: 'fatherCitizenship', label: 'Гражданство отца', kind: 'text' }, { key: 'fatherBirthDate', label: 'Дата рождения отца', kind: 'date' } ] }, { type: 'DRIVER_LICENSE', label: 'ВУ', shortLabel: 'ВУ', category: 'transport', icon: CreditCard, accent: 'text-violet-500 bg-violet-50', numberKey: 'number', fields: [ { key: 'number', label: 'Номер', kind: 'text' }, ...latinNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'birthPlace', label: 'Место рождения', kind: 'text', latinKey: 'birthPlaceLatin', latinLabel: 'Место рождения латинскими буквами' }, { key: 'issuedAt', label: 'Дата выдачи', kind: 'date' }, { key: 'expiresAt', label: 'Действует до', kind: 'date' }, { key: 'issuedBy', label: 'Кем выдано', kind: 'text', latinKey: 'issuedByLatin', latinLabel: 'Кем выдано латинскими буквами' }, { key: 'issuePlace', label: 'Место выдачи', kind: 'text', latinKey: 'issuePlaceLatin', latinLabel: 'Место выдачи латинскими буквами' }, { key: 'categories', label: 'Категории', kind: 'categories' }, { key: 'specialMarks', label: 'Особые отметки', kind: 'textarea' } ] }, { type: 'VEHICLE_REGISTRATION', label: 'СТС / СРТС', shortLabel: 'СТС / СРТС', category: 'transport', icon: Car, accent: 'text-violet-500 bg-violet-50', numberKey: 'seriesNumber', fields: [ { key: 'seriesNumber', label: 'Серия и номер', kind: 'text' }, { key: 'plateNumber', label: 'Регистрационный знак', kind: 'text', placeholder: 'X000XX00' }, { key: 'vin', label: 'Идентификационный номер (VIN)', kind: 'text' }, { key: 'makeModel', label: 'Марка, модель', kind: 'text', latinKey: 'makeModelLatin', latinLabel: 'Марка, модель латинскими буквами' }, { key: 'vehicleType', label: 'Тип ТС', kind: 'text' }, { key: 'vehicleCategory', label: 'Категория ТС', kind: 'text' }, { key: 'year', label: 'Год выпуска', kind: 'text' }, { key: 'chassisNumber', label: 'Номер шасси (рамы)', kind: 'text' }, { key: 'bodyNumber', label: 'Номер кузова (кабины, прицепа)', kind: 'text' }, { key: 'color', label: 'Цвет', kind: 'text' }, { key: 'enginePower', label: 'Мощность двигателя, кВт/л.с.', kind: 'text' }, { key: 'ecoClass', label: 'Экологический класс', kind: 'text' }, { key: 'maxMass', label: 'Разрешенная максимальная масса, кг', kind: 'text' }, { key: 'curbWeight', label: 'Масса в снаряженном состоянии, кг', kind: 'text' }, { key: 'tempRegistration', label: 'Срок временной регистрации', kind: 'text' }, { key: 'pts', label: 'ПТС', kind: 'text', placeholder: '77TX000006 или 123456789012345' }, ...latinNameFields.slice(0, 3), { key: 'registrationAddress', label: 'Адрес регистрации', kind: 'text' }, { key: 'departmentCode', label: 'Код подразделения (выдано ГИБДД)', kind: 'text' }, { key: 'issuedAt', label: 'Дата выдачи', kind: 'date' }, { key: 'specialMarks', label: 'Особые отметки', kind: 'textarea' } ] }, { type: 'OMS', label: 'ОМС', shortLabel: 'ОМС', category: 'health', icon: Stethoscope, accent: 'text-sky-500 bg-sky-50', numberKey: 'number', fields: [ { key: 'number', label: 'Номер', kind: 'text' }, ...baseNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'gender', label: 'Пол', kind: 'gender' }, { key: 'blankSeriesNumber', label: 'Серия и номер бланка', kind: 'text' } ] }, { type: 'DMS', label: 'ДМС', shortLabel: 'ДМС', category: 'health', icon: HeartPulse, accent: 'text-sky-500 bg-sky-50', numberKey: 'number', fields: [ { key: 'number', label: 'Номер', kind: 'text' }, ...baseNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'gender', label: 'Пол', kind: 'gender' }, { key: 'validFrom', label: 'Действует с', kind: 'date' }, { key: 'validTo', label: 'Действует до', kind: 'date' }, { key: 'insurer', label: 'Страхователь', kind: 'text' } ] }, { type: 'INN', label: 'ИНН', shortLabel: 'ИНН', category: 'finance', icon: Building2, accent: 'text-emerald-500 bg-emerald-50', numberKey: 'number', fields: [ { key: 'number', label: 'ИНН', kind: 'text' }, ...baseNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'birthPlace', label: 'Место рождения', kind: 'text' }, { key: 'gender', label: 'Пол', kind: 'gender' }, { key: 'issuedBy', label: 'Орган, выдавший документ', kind: 'text' }, { key: 'issuedAt', label: 'Дата выдачи', kind: 'date' } ] }, { type: 'SNILS', label: 'СНИЛС', shortLabel: 'СНИЛС', category: 'finance', icon: Wallet, accent: 'text-emerald-500 bg-emerald-50', numberKey: 'number', fields: [ { key: 'number', label: 'СНИЛС', kind: 'text' }, ...baseNameFields, { key: 'birthDate', label: 'Дата рождения', kind: 'date' }, { key: 'birthPlace', label: 'Место рождения', kind: 'text' }, { key: 'gender', label: 'Пол', kind: 'gender' }, { key: 'registeredAt', label: 'Дата регистрации', kind: 'date' } ] } ]; export const QUICK_DOCUMENT_TYPES = ['PASSPORT_RF', 'DRIVER_LICENSE', 'FOREIGN_PASSPORT'] as const; export const LICENSE_CATEGORIES = ['A', 'B', 'C', 'D', 'A1', 'B1', 'C1', 'D1', 'M', 'BE', 'CE', 'DE', 'Tm', 'Tb', 'C1E', 'D1E']; export function getDocumentType(type: string) { return DOCUMENT_TYPES.find((item) => item.type === type); } export function buildDocumentNumber(type: DocumentTypeDef, values: Record) { if (type.type === 'BIRTH_CERTIFICATE') { return [values.series, values.number].filter(Boolean).join(' '); } if (type.type === 'PASSPORT_RF' || type.type === 'VEHICLE_REGISTRATION') { return values.seriesNumber || values.number || '—'; } return values[type.numberKey] || '—'; } export function parseMetadata(metadataJson?: string) { if (!metadataJson) return {} as Record; try { return JSON.parse(metadataJson) as Record; } catch { return {} as Record; } } export function parseDocumentPhotos(metadata: Record): string[] { const keys = metadata.photoStorageKeys; if (Array.isArray(keys)) { return keys.filter((item): item is string => typeof item === 'string' && item.length > 0); } if (typeof metadata.photoStorageKey === 'string' && metadata.photoStorageKey) { return [metadata.photoStorageKey]; } return []; } export type DocumentAttachmentMeta = { fileName?: string; contentType?: string; }; export function parseAttachmentMeta(metadata: Record): Record { const raw = metadata.attachmentMeta; if (!raw || typeof raw !== 'object' || Array.isArray(raw)) { return {}; } const result: Record = {}; for (const [key, value] of Object.entries(raw as Record)) { if (!value || typeof value !== 'object' || Array.isArray(value)) continue; const entry = value as Record; result[key] = { fileName: typeof entry.fileName === 'string' ? entry.fileName : undefined, contentType: typeof entry.contentType === 'string' ? entry.contentType : undefined }; } return result; } export function withDocumentPhotos(metadata: Record, photoStorageKeys: string[], attachmentMeta?: Record) { const next: Record = { ...metadata, photoStorageKeys }; delete next.photoStorageKey; if (attachmentMeta && Object.keys(attachmentMeta).length > 0) { next.attachmentMeta = attachmentMeta; } return next; } /** Берёт самый свежий документ каждого типа (список с бэкенда отсортирован по updatedAt desc). */ export function indexDocumentsByType(documents: T[]): Map { const map = new Map(); for (const document of documents) { if (!map.has(document.type)) { map.set(document.type, document); } } return map; }