// MDM SORP — AI import wizard (6.8). const { useState, useEffect, useMemo, useRef, useCallback, createContext, useContext } = React; function ImportScreen({ navigate }) { const { t, lang } = useI18n(); const toast = useToast(); const data = window.DATA.importRows; const [step, setStep] = useState(1); // 0=upload 1=mapping 2=preview 3=dryrun 4=apply const steps = [ { id: 0, label: t("import_step_upload") }, { id: 1, label: t("import_step_mapping") }, { id: 2, label: t("import_step_preview") }, { id: 3, label: t("import_step_dryrun") }, { id: 4, label: t("import_step_apply") }, ]; return (

{t("import_title")}

{data.fileName}
{steps.map(s => (
setStep(s.id)}>
{s.id < step ? : s.id + 1}
{s.label}
))}
{step === 0 && setStep(1)}/>} {step === 1 && setStep(0)} onNext={() => setStep(2)}/>} {step === 2 && setStep(1)} onNext={() => setStep(3)}/>} {step === 3 && setStep(2)} onNext={() => { setStep(4); toast("Импорт запущен · 47 строк в очередь обогащения", { onUndo: () => {} }); }}/>} {step === 4 && navigate("enrichment")}/>}

{lang === "ru" ? "Распознано ИИ" : "AI detected"}

Файл{data.fileName}
Лист{data.sheet}
Тип{data.detected}
ПривязкаSORP-3955
Строк47
Колонок{data.columns.length}
Неуверенных{data.columns.filter(c => c.uncertain).length}
Не сопоставлено{data.columns.filter(c => c.unmapped).length}

{lang === "ru" ? "Что предложил ИИ" : "AI proposals"}

«Артикул производителя» → name_requested
высокий confidence по образцам — артикулы SKF/NSK
Колонка «Валюта» неполная
в 3 строках валюта попала в цену («$ 480»). Распарсить?
«Коммент» не сопоставлено
сохранить как deal_item.notes?
); } function StepUpload({ onNext }) { const { lang, t } = useI18n(); return (

{t("import_drop")}

{t("import_drop_sub")}
Целевая сущность

Недавние импорты

{[ { name: "Equipment_RFQ_Severstal_2026-05.xlsx", when: "2 мин назад", status: "in-progress" }, { name: "Suppliers_China_export_2026-04.csv", when: "вчера", status: "done", rows: 142 }, { name: "Drive_dump_SORP-3929.zip", when: "3 дня назад", status: "done", rows: 18 }, { name: "AmoCRM_full_export_2026-Q1.csv", when: "10 апр", status: "partial", rows: 1842, err: 14 }, ].map((r, i) => (
{r.name}
{r.when}{r.rows ? ` · ${r.rows} строк` : ""}{r.err ? ` · ${r.err} с ошибкой` : ""}
{r.status === "done" && Готово} {r.status === "in-progress" && В работе} {r.status === "partial" && Частично}
))}
); } function StepMapping({ data, onPrev, onNext }) { const { t, lang } = useI18n(); return ( <>

{lang === "ru" ? "Маппинг колонок" : "Column mapping"}

Перетащите неверные сопоставления стрелкой
Из файла
В нашу схему
Conf.
{data.columns.map((c, i) => (
{c.src}
{c.unmapped ? <>не сопоставлено : <>{c.target} }
{c.conf > 0 ? : }
))}
2 неуверенно · 1 не сопоставлено
); } function StepPreview({ data, onPrev, onNext }) { const { t, lang } = useI18n(); return ( <>

{lang === "ru" ? "Предпросмотр после нормализации" : "Preview after normalization"}

Показаны первые 6 из 47
{data.preview.map((r, i) => ( ))}
idxname_requestedqtyprice_buycurrencylead_daysСтатус
{r.idx} {r.art} {r.qty} {r.price} {r.ccy} {r.lead == null ? : r.lead + "д"} {r.status === "ok" && OK} {r.status === "warn" && {r.note}} {r.status === "bad" && {r.note}}
); } function StepDryRun({ data, onPrev, onNext }) { const { t, lang } = useI18n(); const dry = data.dry_run; return ( <>
Всего строк
47
распарсено
Готово к загрузке
41
валидные
В очередь обогащения
5
требуют подтверждения
Отклонено
1
без поставщика

Найдены проблемы

{[ { sev: "warn", text: "Строка 3 (32320/Q): валюта пуста → ИИ предложил USD по контексту", action: "Применить" }, { sev: "warn", text: "Строка 6 (NN3014K/P5): «$ 480» — символ валюты внутри цены → распарсить?", action: "Распарсить" }, { sev: "bad", text: "Строка 4 (Подшипник 7224): нет поставщика → в очередь сорсинга", action: "Создать запрос" }, { sev: "warn", text: "12 артикулов уже есть в номенклатуре — связать с существующими?", action: "Связать" }, { sev: "warn", text: "1 контрагент по TIN совпадает с p_2003 (потенциальный дубль)", action: "Открыть ревью" }, ].map((p, i) => (
{p.sev === "bad" ? : } {p.text}
))}
); } function StepDone({ onClose }) { return (

Импорт применён

41 строка загружена · 5 в очереди обогащения · 1 в сорсинге.
); } window.ImportScreen = ImportScreen;