feat: Обновлена форма проверки полиса + автозамена кириллицы

Изменения в форме (Шаг 1):
- Полис в одну строку: E1000-302538524 (было: отдельно серия и номер)
- Email теперь обязателен (было: опционально)
- Убран ИНН (было: опционально)
- Автозамена кириллицы на латиницу (Е→E, О→O и т.д.)
- Валидация формата: буква + 4 цифры + тире + 9 цифр

Изменения в Backend API:
- PolicyCheckRequest: voucher + email (убран inn)
- policy_service: упрощен запрос к MySQL
- Добавлено подключение MySQL в lifespan

Изменения в ClaimForm:
- FormData обновлен: voucher вместо policyNumber/policySeries
- Убрано поле inn из всей логики

Статус: Frontend работает, MySQL требует настройки доступа
This commit is contained in:
AI Assistant
2025-10-24 20:54:57 +03:00
parent 30bbf2cf2c
commit 4c844d00a5
5 changed files with 73 additions and 57 deletions

View File

@@ -1,6 +1,6 @@
import { useState } from 'react';
import { Form, Input, Button, message } from 'antd';
import { FileProtectOutlined } from '@ant-design/icons';
import { FileProtectOutlined, MailOutlined } from '@ant-design/icons';
interface Props {
formData: any;
@@ -8,10 +8,29 @@ interface Props {
onNext: () => void;
}
// Функция автозамены кириллицы на латиницу
const cyrillicToLatin = (text: string): string => {
const map: Record<string, string> = {
'А': 'A', 'В': 'B', 'С': 'C', 'Е': 'E', 'Н': 'H', 'К': 'K',
'М': 'M', 'О': 'O', 'Р': 'P', 'Т': 'T', 'Х': 'X',
'а': 'a', 'в': 'b', 'с': 'c', 'е': 'e', 'н': 'h', 'к': 'k',
'м': 'm', 'о': 'o', 'р': 'p', 'т': 't', 'х': 'x'
};
return text.split('').map(char => map[char] || char).join('');
};
export default function Step1Policy({ formData, updateFormData, onNext }: Props) {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
// Обработчик изменения поля полиса с автозаменой
const handleVoucherChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
const converted = cyrillicToLatin(value.toUpperCase());
form.setFieldValue('voucher', converted);
};
const checkPolicy = async () => {
try {
const values = await form.validateFields();
@@ -22,9 +41,8 @@ export default function Step1Policy({ formData, updateFormData, onNext }: Props)
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
policy_number: values.policyNumber,
policy_series: values.policySeries,
inn: values.inn,
voucher: values.voucher,
email: values.email,
}),
});
@@ -32,11 +50,11 @@ export default function Step1Policy({ formData, updateFormData, onNext }: Props)
if (response.ok) {
if (result.found) {
message.success(`Полис найден! ${result.holder_name}`);
message.success(`Полис найден! Владелец: ${result.policy_data?.holder_name || 'не указан'}`);
updateFormData(values);
onNext();
} else {
message.warning('Полис не найден. Загрузите скан полиса на следующем шаге.');
message.warning('Полис не найден в базе. Продолжайте — на следующем шаге загрузите скан.');
updateFormData(values);
onNext();
}
@@ -63,36 +81,39 @@ export default function Step1Policy({ formData, updateFormData, onNext }: Props)
>
<Form.Item
label="Номер полиса"
name="policyNumber"
rules={[{ required: true, message: 'Введите номер полиса' }]}
name="voucher"
rules={[
{ required: true, message: 'Введите номер полиса' },
{
pattern: /^[A-Z]\d{4}-\d{9}$/,
message: 'Формат: E1000-302538524 (буква, 4 цифры, тире, 9 цифр)'
}
]}
tooltip="Формат: E1000-302538524. Кириллица автоматически заменяется на латиницу"
>
<Input
prefix={<FileProtectOutlined />}
placeholder="123456789"
placeholder="E1000-302538524"
size="large"
onChange={handleVoucherChange}
maxLength={15}
/>
</Form.Item>
<Form.Item
label="Серия полиса (необязательно)"
name="policySeries"
>
<Input placeholder="AB" size="large" />
</Form.Item>
<Form.Item
label="ИНН (необязательно)"
name="inn"
>
<Input placeholder="1234567890" maxLength={12} size="large" />
</Form.Item>
<Form.Item
label="Email (необязательно)"
label="Электронная почта"
name="email"
rules={[{ type: 'email', message: 'Неверный формат email' }]}
rules={[
{ required: true, message: 'Введите email' },
{ type: 'email', message: 'Неверный формат email' }
]}
>
<Input placeholder="example@mail.ru" size="large" />
<Input
prefix={<MailOutlined />}
placeholder="example@mail.ru"
size="large"
type="email"
/>
</Form.Item>
<Form.Item>
@@ -115,4 +136,3 @@ export default function Step1Policy({ formData, updateFormData, onNext }: Props)
</Form>
);
}

View File

@@ -9,11 +9,8 @@ const { Step } = Steps;
interface FormData {
// Шаг 1
phone: string;
email?: string;
inn?: string;
policyNumber: string;
policySeries?: string;
voucher: string; // Полис вида E1000-302538524
email: string; // Email обязателен
// Шаг 2
incidentDate?: string;
@@ -22,6 +19,7 @@ interface FormData {
uploadedFiles?: string[];
// Шаг 3
phone: string;
paymentMethod: string;
bankName?: string;
cardNumber?: string;
@@ -31,8 +29,9 @@ interface FormData {
export default function ClaimForm() {
const [currentStep, setCurrentStep] = useState(0);
const [formData, setFormData] = useState<FormData>({
voucher: '',
email: '',
phone: '',
policyNumber: '',
paymentMethod: 'sbp',
});
const [isPhoneVerified, setIsPhoneVerified] = useState(false);
@@ -57,11 +56,9 @@ export default function ClaimForm() {
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone: formData.phone,
voucher: formData.voucher,
email: formData.email,
inn: formData.inn,
policy_number: formData.policyNumber,
policy_series: formData.policySeries,
phone: formData.phone,
incident_date: formData.incidentDate,
incident_description: formData.incidentDescription,
transport_type: formData.transportType,
@@ -79,8 +76,9 @@ export default function ClaimForm() {
message.success(`Заявка ${result.claim_number} успешно создана!`);
// Сброс формы
setFormData({
voucher: '',
email: '',
phone: '',
policyNumber: '',
paymentMethod: 'sbp',
});
setCurrentStep(0);