fix: Исправление логики загрузки документов и расчёта прогресса
- Исправлена ошибка порядка объявления allDocsProcessed (Cannot access before initialization) - Исправлена логика поиска незагруженного документа: поиск с начала, если сохранённый индекс уже обработан - Исправлен расчёт прогресса: теперь используется количество обработанных документов (uploadedDocs + skippedDocs), а не currentDocIndex - Убрана синхронизация currentDocIndex из formData, которая перезаписывала правильный индекс - Добавлена логика автоматического пропуска уже загруженных документов при открытии формы - Добавлено подробное логирование для отладки состояния документов - Исправлена логика определения завершённости: проверяется каждый документ из documentsRequired Результат: - Форма корректно показывает следующий незагруженный документ - Прогресс правильно отображает процент обработанных документов (75% при 3 из 4) - Система не требует повторной загрузки уже загруженных документов
This commit is contained in:
@@ -249,15 +249,15 @@ export default function StepWizardPlan({
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...blocks,
|
...blocks,
|
||||||
{
|
{
|
||||||
id: generateBlockId(docId),
|
id: generateBlockId(docId),
|
||||||
fieldName: docId,
|
fieldName: docId,
|
||||||
description: autoDescription,
|
description: autoDescription,
|
||||||
category: category,
|
category: category,
|
||||||
docLabel: docLabel,
|
docLabel: docLabel,
|
||||||
files: [],
|
files: [],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -341,7 +341,7 @@ export default function StepWizardPlan({
|
|||||||
// Автоматически создаём блоки для ВСЕХ документов из плана при загрузке
|
// Автоматически создаём блоки для ВСЕХ документов из плана при загрузке
|
||||||
// Используем ref чтобы отслеживать какие блоки уже созданы
|
// Используем ref чтобы отслеживать какие блоки уже созданы
|
||||||
const createdDocBlocksRef = useRef<Set<string>>(new Set());
|
const createdDocBlocksRef = useRef<Set<string>>(new Set());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!plan || !documents || documents.length === 0) return;
|
if (!plan || !documents || documents.length === 0) return;
|
||||||
|
|
||||||
@@ -495,7 +495,7 @@ export default function StepWizardPlan({
|
|||||||
// TODO: onNext() для перехода к StepDocumentsNew
|
// TODO: onNext() для перехода к StepDocumentsNew
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wizardPayload = extractWizardPayload(payload);
|
const wizardPayload = extractWizardPayload(payload);
|
||||||
const hasWizardPlan = Boolean(wizardPayload);
|
const hasWizardPlan = Boolean(wizardPayload);
|
||||||
|
|
||||||
@@ -602,14 +602,14 @@ export default function StepWizardPlan({
|
|||||||
// Для обязательных документов описание не требуется
|
// Для обязательных документов описание не требуется
|
||||||
// Для предопределённых документов описание не требуется
|
// Для предопределённых документов описание не требуется
|
||||||
if (!doc.required && !isPredefinedDoc) {
|
if (!doc.required && !isPredefinedDoc) {
|
||||||
const missingDescription = blocks.some(
|
const missingDescription = blocks.some(
|
||||||
(block) => block.files.length > 0 && !block.description?.trim()
|
(block) => block.files.length > 0 && !block.description?.trim()
|
||||||
);
|
);
|
||||||
if (missingDescription) {
|
if (missingDescription) {
|
||||||
return `Заполните описание для документа "${doc.name}"`;
|
return `Заполните описание для документа "${doc.name}"`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const customMissingDescription = customFileBlocks.some(
|
const customMissingDescription = customFileBlocks.some(
|
||||||
(block) => block.files.length > 0 && !block.description?.trim()
|
(block) => block.files.length > 0 && !block.description?.trim()
|
||||||
@@ -737,8 +737,8 @@ export default function StepWizardPlan({
|
|||||||
}
|
}
|
||||||
if (cat.includes('payment') || cat.includes('cheque') || cat.includes('receipt') ||
|
if (cat.includes('payment') || cat.includes('cheque') || cat.includes('receipt') ||
|
||||||
cat.includes('подтверждение') || cat === 'payment_proof') {
|
cat.includes('подтверждение') || cat === 'payment_proof') {
|
||||||
return 'upload_payment';
|
return 'upload_payment';
|
||||||
}
|
}
|
||||||
if (cat.includes('correspondence') || cat.includes('chat') || cat.includes('переписка')) {
|
if (cat.includes('correspondence') || cat.includes('chat') || cat.includes('переписка')) {
|
||||||
return 'upload_correspondence';
|
return 'upload_correspondence';
|
||||||
}
|
}
|
||||||
@@ -906,7 +906,7 @@ export default function StepWizardPlan({
|
|||||||
eventSourceRef.current = null;
|
eventSourceRef.current = null;
|
||||||
|
|
||||||
// Переходим к следующему шагу (форма подтверждения)
|
// Переходим к следующему шагу (форма подтверждения)
|
||||||
onNext();
|
onNext();
|
||||||
} else if (data.event_type === 'claim_plan_error' || data.status === 'error') {
|
} else if (data.event_type === 'claim_plan_error' || data.status === 'error') {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.error(data.message || 'Ошибка получения данных заявления');
|
message.error(data.message || 'Ошибка получения данных заявления');
|
||||||
@@ -1049,14 +1049,14 @@ export default function StepWizardPlan({
|
|||||||
// Кнопка "Удалить" только если это дополнительный блок (idx > 0)
|
// Кнопка "Удалить" только если это дополнительный блок (idx > 0)
|
||||||
// Первый блок предустановленного документа удалять нельзя
|
// Первый блок предустановленного документа удалять нельзя
|
||||||
(currentBlocks.length > 1 && idx > 0) && (
|
(currentBlocks.length > 1 && idx > 0) && (
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
danger
|
danger
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => removeDocumentBlock(docId, block.id)}
|
onClick={() => removeDocumentBlock(docId, block.id)}
|
||||||
>
|
>
|
||||||
Удалить
|
Удалить
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -1064,28 +1064,28 @@ export default function StepWizardPlan({
|
|||||||
{/* Поле описания показываем только для дополнительных блоков (idx > 0)
|
{/* Поле описания показываем только для дополнительных блоков (idx > 0)
|
||||||
или для общих документов (docs_exist) */}
|
или для общих документов (docs_exist) */}
|
||||||
{(idx > 0 || !isPredefinedDoc) && (
|
{(idx > 0 || !isPredefinedDoc) && (
|
||||||
<Input
|
<Input
|
||||||
placeholder="Уточните тип документа (например: Претензия от 12.05)"
|
placeholder="Уточните тип документа (например: Претензия от 12.05)"
|
||||||
value={block.description}
|
value={block.description}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateDocumentBlock(docId, block.id, { description: e.target.value })
|
updateDocumentBlock(docId, block.id, { description: e.target.value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Выпадашка категорий только для общих вопросов (docs_exist, correspondence_exist) */}
|
{/* Выпадашка категорий только для общих вопросов (docs_exist, correspondence_exist) */}
|
||||||
{!isPredefinedDoc && (
|
{!isPredefinedDoc && (
|
||||||
<Select
|
<Select
|
||||||
value={block.category || docId}
|
value={block.category || docId}
|
||||||
onChange={(value) => updateDocumentBlock(docId, block.id, { category: value })}
|
onChange={(value) => updateDocumentBlock(docId, block.id, { category: value })}
|
||||||
placeholder="Категория блока"
|
placeholder="Категория блока"
|
||||||
>
|
>
|
||||||
{documentCategoryOptions.map((option) => (
|
{documentCategoryOptions.map((option) => (
|
||||||
<Option key={`${docId}-${option.value}`} value={option.value}>
|
<Option key={`${docId}-${option.value}`} value={option.value}>
|
||||||
{option.label}
|
{option.label}
|
||||||
</Option>
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Dragger
|
<Dragger
|
||||||
@@ -1129,15 +1129,15 @@ export default function StepWizardPlan({
|
|||||||
))}
|
))}
|
||||||
{/* Кнопка "Добавить" только если документ не пропущен */}
|
{/* Кнопка "Добавить" только если документ не пропущен */}
|
||||||
{!isSkipped && (!isPredefinedDoc || currentBlocks.length === 0) && (
|
{!isSkipped && (!isPredefinedDoc || currentBlocks.length === 0) && (
|
||||||
<Button
|
<Button
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={() => addDocumentBlock(docId, docLabel, docList)}
|
onClick={() => addDocumentBlock(docId, docLabel, docList)}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
>
|
>
|
||||||
{isPredefinedDoc && currentBlocks.length === 0
|
{isPredefinedDoc && currentBlocks.length === 0
|
||||||
? `Загрузить ${singleDocName || docLabel}`
|
? `Загрузить ${singleDocName || docLabel}`
|
||||||
: `Добавить документы (${docLabel})`}
|
: `Добавить документы (${docLabel})`}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
@@ -1258,14 +1258,14 @@ export default function StepWizardPlan({
|
|||||||
return prev[question.ask_if!.field] !== curr[question.ask_if!.field];
|
return prev[question.ask_if!.field] !== curr[question.ask_if!.field];
|
||||||
} : true} // ✅ Для безусловных полей shouldUpdate=true, чтобы render function работала
|
} : true} // ✅ Для безусловных полей shouldUpdate=true, чтобы render function работала
|
||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
const values = form.getFieldsValue(true);
|
const values = form.getFieldsValue(true);
|
||||||
if (!evaluateCondition(question.ask_if, values)) {
|
if (!evaluateCondition(question.ask_if, values)) {
|
||||||
console.log(`⏭️ Question ${question.name} skipped: condition not met`, question.ask_if, values);
|
console.log(`⏭️ Question ${question.name} skipped: condition not met`, question.ask_if, values);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const questionDocs = documentGroups[question.name] || [];
|
const questionDocs = documentGroups[question.name] || [];
|
||||||
const questionValue = values?.[question.name];
|
const questionValue = values?.[question.name];
|
||||||
|
|
||||||
// Скрываем вопросы, которые связаны с загрузкой документов
|
// Скрываем вопросы, которые связаны с загрузкой документов
|
||||||
// Если в плане визарда есть документы, не показываем поля про загрузку (text/textarea/file)
|
// Если в плане визарда есть документы, не показываем поля про загрузку (text/textarea/file)
|
||||||
@@ -1308,23 +1308,23 @@ export default function StepWizardPlan({
|
|||||||
|
|
||||||
console.log(`✅ Question ${question.name} will render:`, { input_type: question.input_type, label: question.label, required: question.required });
|
console.log(`✅ Question ${question.name} will render:`, { input_type: question.input_type, label: question.label, required: question.required });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={question.label}
|
label={question.label}
|
||||||
name={question.name}
|
name={question.name}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: question.required,
|
required: question.required,
|
||||||
message: 'Поле обязательно для заполнения',
|
message: 'Поле обязательно для заполнения',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{renderQuestionField(question)}
|
{renderQuestionField(question)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{questionDocs.length > 0 && isAffirmative(questionValue) && (
|
{questionDocs.length > 0 && isAffirmative(questionValue) && (
|
||||||
<div style={{ marginBottom: 24 }}>
|
<div style={{ marginBottom: 24 }}>
|
||||||
<Text strong>Загрузите документы:</Text>
|
<Text strong>Загрузите документы:</Text>
|
||||||
<Space direction="vertical" style={{ width: '100%', marginTop: 16 }}>
|
<Space direction="vertical" style={{ width: '100%', marginTop: 16 }}>
|
||||||
{questionDocs.map((doc) => {
|
{questionDocs.map((doc) => {
|
||||||
// Используем doc.id как ключ для отдельного хранения блоков каждого документа
|
// Используем doc.id как ключ для отдельного хранения блоков каждого документа
|
||||||
@@ -1336,12 +1336,12 @@ export default function StepWizardPlan({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
@@ -1354,7 +1354,7 @@ export default function StepWizardPlan({
|
|||||||
</Form>
|
</Form>
|
||||||
{renderCustomUploads()}
|
{renderCustomUploads()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!formData.session_id) {
|
if (!formData.session_id) {
|
||||||
@@ -1383,18 +1383,191 @@ export default function StepWizardPlan({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Состояние для поэкранной загрузки документов (новый флоу)
|
// Состояние для поэкранной загрузки документов (новый флоу)
|
||||||
const [currentDocIndex, setCurrentDocIndex] = useState(formData.current_doc_index || 0);
|
|
||||||
// Убираем дубликаты при инициализации
|
// Убираем дубликаты при инициализации
|
||||||
const initialUploadedDocs = formData.documents_uploaded?.map((d: any) => d.type || d.id) || [];
|
const initialUploadedDocs = formData.documents_uploaded?.map((d: any) => d.type || d.id) || [];
|
||||||
const [uploadedDocs, setUploadedDocs] = useState<string[]>(Array.from(new Set(initialUploadedDocs)));
|
const [uploadedDocs, setUploadedDocs] = useState<string[]>(Array.from(new Set(initialUploadedDocs)));
|
||||||
const [skippedDocs, setSkippedDocs] = useState<string[]>(formData.documents_skipped || []);
|
const [skippedDocs, setSkippedDocs] = useState<string[]>(formData.documents_skipped || []);
|
||||||
|
|
||||||
|
// Отладка: логируем инициализацию
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('🔍 Инициализация документов:', {
|
||||||
|
documentsRequiredCount: documentsRequired.length,
|
||||||
|
initialUploadedDocs,
|
||||||
|
uploadedDocs,
|
||||||
|
skippedDocs,
|
||||||
|
formDataCurrentDocIndex: formData.current_doc_index,
|
||||||
|
});
|
||||||
|
}, []); // Только при первой загрузке
|
||||||
|
|
||||||
|
// Находим первый незагруженный документ при инициализации
|
||||||
|
const findFirstUnprocessedDoc = useCallback((startIndex: number = 0) => {
|
||||||
|
for (let i = startIndex; i < documentsRequired.length; i++) {
|
||||||
|
const doc = documentsRequired[i];
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
if (!uploadedDocs.includes(docId) && !skippedDocs.includes(docId)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return documentsRequired.length; // Все документы обработаны
|
||||||
|
}, [documentsRequired, uploadedDocs, skippedDocs]);
|
||||||
|
|
||||||
|
const [currentDocIndex, setCurrentDocIndex] = useState(() => {
|
||||||
|
const savedIndex = formData.current_doc_index || 0;
|
||||||
|
// Используем initialUploadedDocs и formData.documents_skipped для инициализации
|
||||||
|
const initUploaded = Array.from(new Set(initialUploadedDocs));
|
||||||
|
const initSkipped = formData.documents_skipped || [];
|
||||||
|
|
||||||
|
// Находим первый незагруженный документ
|
||||||
|
// Сначала проверяем с сохранённого индекса, потом с начала
|
||||||
|
let firstUnprocessed = documentsRequired.length; // По умолчанию - все обработаны
|
||||||
|
|
||||||
|
// Проверяем с сохранённого индекса до конца
|
||||||
|
for (let i = savedIndex; i < documentsRequired.length; i++) {
|
||||||
|
const doc = documentsRequired[i];
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
if (!initUploaded.includes(docId) && !initSkipped.includes(docId)) {
|
||||||
|
firstUnprocessed = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если не нашли с сохранённого индекса, проверяем с начала до сохранённого
|
||||||
|
if (firstUnprocessed === documentsRequired.length) {
|
||||||
|
for (let i = 0; i < savedIndex; i++) {
|
||||||
|
const doc = documentsRequired[i];
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
if (!initUploaded.includes(docId) && !initSkipped.includes(docId)) {
|
||||||
|
firstUnprocessed = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🔍 Инициализация currentDocIndex:', {
|
||||||
|
savedIndex,
|
||||||
|
firstUnprocessed,
|
||||||
|
documentsRequiredLength: documentsRequired.length,
|
||||||
|
initUploaded,
|
||||||
|
initSkipped,
|
||||||
|
documentsRequiredList: documentsRequired.map((d: any) => ({
|
||||||
|
id: d.id || d.name,
|
||||||
|
name: d.name,
|
||||||
|
uploaded: initUploaded.includes(d.id || d.name),
|
||||||
|
skipped: initSkipped.includes(d.id || d.name),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
// Убеждаемся, что индекс не выходит за границы
|
||||||
|
return Math.min(firstUnprocessed, documentsRequired.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Исправляем currentDocIndex только если он выходит за границы или указывает на уже обработанный документ
|
||||||
|
useEffect(() => {
|
||||||
|
if (documentsRequired.length === 0) return;
|
||||||
|
|
||||||
|
// Если текущий индекс выходит за границы, исправляем
|
||||||
|
if (currentDocIndex >= documentsRequired.length) {
|
||||||
|
const firstUnprocessed = findFirstUnprocessedDoc(0);
|
||||||
|
console.log('🔄 Исправление currentDocIndex (выход за границы):', {
|
||||||
|
currentIndex: currentDocIndex,
|
||||||
|
documentsRequiredLength: documentsRequired.length,
|
||||||
|
firstUnprocessed,
|
||||||
|
});
|
||||||
|
setCurrentDocIndex(firstUnprocessed);
|
||||||
|
updateFormData({ current_doc_index: firstUnprocessed });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если текущий документ уже обработан, переходим к следующему
|
||||||
|
const currentDoc = documentsRequired[currentDocIndex];
|
||||||
|
if (currentDoc) {
|
||||||
|
const docId = currentDoc.id || currentDoc.name;
|
||||||
|
if (uploadedDocs.includes(docId) || skippedDocs.includes(docId)) {
|
||||||
|
const firstUnprocessed = findFirstUnprocessedDoc(currentDocIndex + 1);
|
||||||
|
console.log('🔄 Исправление currentDocIndex (документ уже обработан):', {
|
||||||
|
currentIndex: currentDocIndex,
|
||||||
|
currentDocId: docId,
|
||||||
|
firstUnprocessed,
|
||||||
|
});
|
||||||
|
setCurrentDocIndex(firstUnprocessed);
|
||||||
|
updateFormData({ current_doc_index: firstUnprocessed });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [currentDocIndex, documentsRequired.length, uploadedDocs, skippedDocs, findFirstUnprocessedDoc, updateFormData]);
|
||||||
|
|
||||||
const [docChoice, setDocChoice] = useState<'upload' | 'none'>('upload'); // Выбор: загрузить или нет документа (по умолчанию - загрузить)
|
const [docChoice, setDocChoice] = useState<'upload' | 'none'>('upload'); // Выбор: загрузить или нет документа (по умолчанию - загрузить)
|
||||||
const [currentUploadedFiles, setCurrentUploadedFiles] = useState<any[]>([]); // Массив загруженных файлов
|
const [currentUploadedFiles, setCurrentUploadedFiles] = useState<any[]>([]); // Массив загруженных файлов
|
||||||
|
|
||||||
// Текущий документ для загрузки
|
// Текущий документ для загрузки
|
||||||
const currentDoc = documentsRequired[currentDocIndex];
|
const currentDoc = documentsRequired[currentDocIndex];
|
||||||
const isLastDoc = currentDocIndex >= documentsRequired.length - 1;
|
const isLastDoc = currentDocIndex >= documentsRequired.length - 1;
|
||||||
const allDocsProcessed = currentDocIndex >= documentsRequired.length;
|
|
||||||
|
// Проверяем, что ВСЕ документы либо загружены, либо пропущены
|
||||||
|
const allDocsProcessed = useMemo(() => {
|
||||||
|
// Проверяем каждый документ из documentsRequired
|
||||||
|
const allRequiredDocsProcessed = documentsRequired.every((doc: any) => {
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
return uploadedDocs.includes(docId) || skippedDocs.includes(docId);
|
||||||
|
});
|
||||||
|
|
||||||
|
const processedCount = uploadedDocs.length + skippedDocs.length;
|
||||||
|
const allProcessed = allRequiredDocsProcessed && processedCount >= documentsRequired.length;
|
||||||
|
|
||||||
|
console.log('🔍 Проверка завершённости:', {
|
||||||
|
uploadedDocs: uploadedDocs.length,
|
||||||
|
skippedDocs: skippedDocs.length,
|
||||||
|
totalRequired: documentsRequired.length,
|
||||||
|
processedCount,
|
||||||
|
allRequiredDocsProcessed,
|
||||||
|
allProcessed,
|
||||||
|
uploadedDocsList: uploadedDocs,
|
||||||
|
skippedDocsList: skippedDocs,
|
||||||
|
requiredDocsList: documentsRequired.map((d: any) => {
|
||||||
|
const docId = d.id || d.name;
|
||||||
|
return {
|
||||||
|
id: docId,
|
||||||
|
name: d.name,
|
||||||
|
uploaded: uploadedDocs.includes(docId),
|
||||||
|
skipped: skippedDocs.includes(docId),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return allProcessed;
|
||||||
|
}, [uploadedDocs, skippedDocs, documentsRequired]);
|
||||||
|
|
||||||
|
// Отладка: логируем состояние текущего документа
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('🔍 Текущий документ для загрузки:', {
|
||||||
|
currentDocIndex,
|
||||||
|
documentsRequiredLength: documentsRequired.length,
|
||||||
|
currentDoc: currentDoc ? { id: currentDoc.id, name: currentDoc.name } : null,
|
||||||
|
uploadedDocs,
|
||||||
|
skippedDocs,
|
||||||
|
allDocsProcessed,
|
||||||
|
});
|
||||||
|
}, [currentDocIndex, documentsRequired.length, currentDoc, uploadedDocs, skippedDocs, allDocsProcessed]);
|
||||||
|
|
||||||
|
// Автоматически пропускаем уже загруженные документы
|
||||||
|
useEffect(() => {
|
||||||
|
if (!currentDoc || allDocsProcessed) return;
|
||||||
|
|
||||||
|
const docId = currentDoc.id || currentDoc.name;
|
||||||
|
const isAlreadyUploaded = uploadedDocs.includes(docId);
|
||||||
|
const isAlreadySkipped = skippedDocs.includes(docId);
|
||||||
|
|
||||||
|
if (isAlreadyUploaded || isAlreadySkipped) {
|
||||||
|
console.log(`⏭️ Документ "${currentDoc.name}" уже обработан, переходим к следующему`);
|
||||||
|
const nextIndex = findFirstUnprocessedDoc(currentDocIndex + 1);
|
||||||
|
|
||||||
|
// Обновляем только если следующий индекс отличается от текущего
|
||||||
|
if (nextIndex !== currentDocIndex) {
|
||||||
|
setCurrentDocIndex(nextIndex);
|
||||||
|
updateFormData({
|
||||||
|
current_doc_index: nextIndex,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [currentDoc, uploadedDocs, skippedDocs, currentDocIndex, allDocsProcessed, findFirstUnprocessedDoc, updateFormData]);
|
||||||
|
|
||||||
// Обработчик выбора файлов (НЕ отправляем сразу, только сохраняем)
|
// Обработчик выбора файлов (НЕ отправляем сразу, только сохраняем)
|
||||||
const handleFilesChange = (fileList: any[]) => {
|
const handleFilesChange = (fileList: any[]) => {
|
||||||
@@ -1418,13 +1591,26 @@ export default function StepWizardPlan({
|
|||||||
const newSkipped = [...skippedDocs, currentDoc.id];
|
const newSkipped = [...skippedDocs, currentDoc.id];
|
||||||
setSkippedDocs(newSkipped);
|
setSkippedDocs(newSkipped);
|
||||||
|
|
||||||
|
// Находим следующий незагруженный документ (используем обновлённый список)
|
||||||
|
const findNextUnprocessed = (startIndex: number) => {
|
||||||
|
for (let i = startIndex; i < documentsRequired.length; i++) {
|
||||||
|
const doc = documentsRequired[i];
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
if (!uploadedDocs.includes(docId) && !newSkipped.includes(docId)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return documentsRequired.length;
|
||||||
|
};
|
||||||
|
const nextIndex = findNextUnprocessed(currentDocIndex + 1);
|
||||||
|
|
||||||
updateFormData({
|
updateFormData({
|
||||||
documents_skipped: newSkipped,
|
documents_skipped: newSkipped,
|
||||||
current_doc_index: currentDocIndex + 1,
|
current_doc_index: nextIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Переход к следующему (сброс состояния в useEffect)
|
// Переход к следующему незагруженному документу
|
||||||
setCurrentDocIndex(prev => prev + 1);
|
setCurrentDocIndex(nextIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1488,13 +1674,26 @@ export default function StepWizardPlan({
|
|||||||
: [...uploadedDocs, currentDoc.id];
|
: [...uploadedDocs, currentDoc.id];
|
||||||
setUploadedDocs(newUploaded);
|
setUploadedDocs(newUploaded);
|
||||||
|
|
||||||
|
// Находим следующий незагруженный документ (используем обновлённый список)
|
||||||
|
const findNextUnprocessed = (startIndex: number) => {
|
||||||
|
for (let i = startIndex; i < documentsRequired.length; i++) {
|
||||||
|
const doc = documentsRequired[i];
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
if (!newUploaded.includes(docId) && !skippedDocs.includes(docId)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return documentsRequired.length;
|
||||||
|
};
|
||||||
|
const nextIndex = findNextUnprocessed(currentDocIndex + 1);
|
||||||
|
|
||||||
updateFormData({
|
updateFormData({
|
||||||
documents_uploaded: uploadedDocsData,
|
documents_uploaded: uploadedDocsData,
|
||||||
current_doc_index: currentDocIndex + 1,
|
current_doc_index: nextIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Переход к следующему (сброс состояния в useEffect)
|
// Переход к следующему незагруженному документу
|
||||||
setCurrentDocIndex(prev => prev + 1);
|
setCurrentDocIndex(nextIndex);
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`Ошибка загрузки: ${error.message}`);
|
message.error(`Ошибка загрузки: ${error.message}`);
|
||||||
@@ -1540,16 +1739,16 @@ export default function StepWizardPlan({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* ✅ НОВЫЙ ФЛОУ: Поэкранная загрузка документов */}
|
{/* ✅ НОВЫЙ ФЛОУ: Поэкранная загрузка документов */}
|
||||||
{hasNewFlowDocs && !allDocsProcessed && currentDoc && (
|
{hasNewFlowDocs && !allDocsProcessed && currentDocIndex < documentsRequired.length && currentDoc ? (
|
||||||
<div style={{ padding: '24px 0' }}>
|
<div style={{ padding: '24px 0' }}>
|
||||||
{/* Прогресс */}
|
{/* Прогресс */}
|
||||||
<div style={{ marginBottom: 24 }}>
|
<div style={{ marginBottom: 24 }}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
|
||||||
<Text type="secondary">Документ {currentDocIndex + 1} из {documentsRequired.length}</Text>
|
<Text type="secondary">Документ {currentDocIndex + 1} из {documentsRequired.length}</Text>
|
||||||
<Text type="secondary">{Math.round((currentDocIndex / documentsRequired.length) * 100)}% завершено</Text>
|
<Text type="secondary">{Math.round(((uploadedDocs.length + skippedDocs.length) / documentsRequired.length) * 100)}% завершено</Text>
|
||||||
</div>
|
</div>
|
||||||
<Progress
|
<Progress
|
||||||
percent={Math.round((currentDocIndex / documentsRequired.length) * 100)}
|
percent={Math.round(((uploadedDocs.length + skippedDocs.length) / documentsRequired.length) * 100)}
|
||||||
showInfo={false}
|
showInfo={false}
|
||||||
strokeColor="#595959"
|
strokeColor="#595959"
|
||||||
/>
|
/>
|
||||||
@@ -1659,20 +1858,52 @@ export default function StepWizardPlan({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : hasNewFlowDocs && !allDocsProcessed && currentDocIndex >= documentsRequired.length ? (
|
||||||
|
<div style={{ padding: '24px 0', textAlign: 'center' }}>
|
||||||
{/* ✅ НОВЫЙ ФЛОУ: Все документы загружены */}
|
<Text type="warning">
|
||||||
{hasNewFlowDocs && allDocsProcessed && (
|
⚠️ Ошибка: индекс документа ({currentDocIndex}) выходит за границы массива ({documentsRequired.length}).
|
||||||
<div style={{ textAlign: 'center', padding: '40px 0' }}>
|
<br />
|
||||||
<Title level={4}>✅ Все документы загружены!</Title>
|
|
||||||
<Paragraph type="secondary">
|
|
||||||
Загружено: {uploadedDocs.length}, пропущено: {skippedDocs.length}
|
Загружено: {uploadedDocs.length}, пропущено: {skippedDocs.length}
|
||||||
</Paragraph>
|
</Text>
|
||||||
<Button type="primary" size="large" onClick={handleAllDocsComplete}>
|
<Button
|
||||||
Продолжить →
|
type="primary"
|
||||||
|
style={{ marginTop: 16 }}
|
||||||
|
onClick={() => {
|
||||||
|
const nextIndex = findFirstUnprocessedDoc(0);
|
||||||
|
setCurrentDocIndex(nextIndex);
|
||||||
|
updateFormData({ current_doc_index: nextIndex });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Исправить и продолжить
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
|
{/* ✅ НОВЫЙ ФЛОУ: Все документы загружены */}
|
||||||
|
{hasNewFlowDocs && allDocsProcessed && (() => {
|
||||||
|
// Правильно считаем загруженные и пропущенные документы из documentsRequired
|
||||||
|
const uploadedCount = documentsRequired.filter((doc: any) => {
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
return uploadedDocs.includes(docId);
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
const skippedCount = documentsRequired.filter((doc: any) => {
|
||||||
|
const docId = doc.id || doc.name;
|
||||||
|
return skippedDocs.includes(docId);
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: 'center', padding: '40px 0' }}>
|
||||||
|
<Title level={4}>✅ Все документы обработаны!</Title>
|
||||||
|
<Paragraph type="secondary">
|
||||||
|
Загружено: {uploadedCount} из {documentsRequired.length}, пропущено: {skippedCount}
|
||||||
|
</Paragraph>
|
||||||
|
<Button type="primary" size="large" onClick={handleAllDocsComplete}>
|
||||||
|
Продолжить →
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
|
||||||
{/* СТАРЫЙ ФЛОУ: Ожидание визарда */}
|
{/* СТАРЫЙ ФЛОУ: Ожидание визарда */}
|
||||||
{!hasNewFlowDocs && isWaiting && (
|
{!hasNewFlowDocs && isWaiting && (
|
||||||
@@ -1719,41 +1950,41 @@ export default function StepWizardPlan({
|
|||||||
|
|
||||||
{documents.length > 0 && (
|
{documents.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
border: '1px solid #d9d9d9',
|
border: '1px solid #d9d9d9',
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
}}
|
}}
|
||||||
title="Документы, которые понадобятся"
|
title="Документы, которые понадобятся"
|
||||||
>
|
>
|
||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
{documents.map((doc: any) => (
|
{documents.map((doc: any) => (
|
||||||
<div
|
<div
|
||||||
key={doc.id}
|
key={doc.id}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 8,
|
gap: 8,
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Text strong>{doc.name}</Text>
|
<Text strong>{doc.name}</Text>
|
||||||
<Paragraph type="secondary" style={{ marginBottom: 0 }}>
|
<Paragraph type="secondary" style={{ marginBottom: 0 }}>
|
||||||
{doc.hints}
|
{doc.hints}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</div>
|
|
||||||
<Tag color={doc.required ? 'volcano' : 'geekblue'}>
|
|
||||||
{doc.required ? 'Обязательно' : 'Опционально'}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<Tag color={doc.required ? 'volcano' : 'geekblue'}>
|
||||||
</Space>
|
{doc.required ? 'Обязательно' : 'Опционально'}
|
||||||
</Card>
|
</Tag>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* Блоки загрузки для каждого документа из плана */}
|
{/* Блоки загрузки для каждого документа из плана */}
|
||||||
<div style={{ marginTop: 16, marginBottom: 24 }}>
|
<div style={{ marginTop: 16, marginBottom: 24 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user