⟨/⟩ 65_Ui/Pages/ProcessingQueuePage.cs

190 строк · в начало

using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Icd;
using Ban.Sdaid.Icd.Api.Endpoints.InputPackets.GetInputQueue;
using Ban.Sdaid.Icd.Api.Endpoints.InputPackets.DeleteInputPacket;
using Ban.Sdaid.Icd.Api.Endpoints.InputPackets.GetFrameContent;
using Ban.Sdaid.Icd.Api.Endpoints.InputPackets.NormalizePacket;
using Ban.Sdaid.Icd.Domain.Capture;
using Ban.Sdaid.Icd.Domain.Cards.Template.Vocabularies;

namespace Ban.Sdaid.Icd.Ui.Pages
{
    /// <summary>Экран очереди и обработки пакета — мастер-деталь.</summary>
    public class ProcessingQueuePage : IPageDocument, IFolder<UiFolder>, IHasStructuralLinks
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<InputPacketSpec>(),
            Rel.Uses<DOC_GetInputQueue>(),
            Rel.Uses<DOC_DeleteInputPacket>(),
            Rel.Uses<DOC_GetFrameContent>(),
            Rel.Uses<DOC_NormalizePacket>(),
            Rel.Uses<VocabularyCatalog>(),
        };

        public string Name => "Очередь обработки";

        /// <summary>Display-текст для типизированных ссылок <c>SdaidAnchors.RefTo&lt;ProcessingQueuePage&gt;()</c>.</summary>
        public static string _refName = "экран «Очередь обработки»";

        public string Description =>
            @"Очередь пакетов слева, проверка выбранного пакета на двух вкладках справа: «Пакет ввода» и «Проверка карты» (ADR-008).";

        public string Version => "0.7";
        public string Status => "draft";
        public string[] Comments => new[]
        {
            "2026-08-18. Переписана под две вкладки: меню этапов с подтверждениями отменено ADR-008.",
            "2026-08-26. По CR «Обязательные поля ИК»: пометка обязательных полей, «Взять следующий», открытие вкладки на первом поле.",
            "2026-08-27. Справочные поля: выбор из набора вместо ввода текстом; место хранения берётся из учётной записи.",
            "2026-08-28. Повторяющиеся группы — эксперты и мониторинг — показаны списками вместо полей с номерами.",
            "2026-08-31. Убран подбор экземпляра фонда: выведен из состава Сервиса.",
        };

        public static string S0_Implemented = $"""
            ## Реализовано (упрощённый срез)

            Мастер-деталь: список пакетов очереди слева, карточка выбранного справа (номер, состояние,
            время; удаление **мягкое**/**жёсткое**). Поиск по номеру/идентификаторам, относительное время,
            обновление, автоскролл к выбранному.

            Карточка пакета — **две вкладки проверки** (ADR-008, горизонтальные): реализована только
            «**Пакет ввода**», «Проверка карты» — недоступна (заглушка). На «Пакете ввода» — **сетка
            карточек кадров** с крупным превью (портрет 3:4): клик по превью — **просмотр** (полноэкранный
            просмотрщик), кнопки **«Обработать заново»** (нормализация orthocover) и **удаления кадра** —
            пер-кадрово. Обработанный кадр помечается меткой «обработано» на превью.

            **Просмотрщик** (переиспользуемый) — полноэкранный оверлей, инфо-панель сверху, перелистывание кадров;
            при наличии обработанной версии — переключатель **«Оригинал ↔ Обработано»**.

            Прочие действия по кадру (переназначить страницу, переснять) и вкладка «Проверка карты» —
            целевое видение (ниже), ещё не реализованы.

            Контракты среза: очередь — {SdaidAnchors.RefTo<DOC_GetInputQueue>()}, содержимое кадра
            (оригинал/обработанное) — {SdaidAnchors.RefTo<DOC_GetFrameContent>()}, обработка —
            {SdaidAnchors.RefTo<DOC_NormalizePacket>()}, удаление (мягкое/жёсткое) —
            {SdaidAnchors.RefTo<DOC_DeleteInputPacket>()}. Сущность очереди — {SdaidAnchors.RefTo<InputPacketSpec>()}.
            """;

        public static string S1_Layout = """
            ## Раскладка

            Мастер-деталь: слева очередь пакетов, справа обработка выбранного. Полностраничный список здесь не
            подходит — пакеты разбирают подряд, и вход в карточку не должен уводить с экрана очередь.

            Панель очереди сворачивается, когда нужна вся ширина.
            """;

        public static string S2_Queue = $"""
            ## Очередь

            Поиск по номеру пакета, шифру, инвентарному номеру и заглавию — по тому, что пользователь
            помнит о карте, а не только по служебному номеру.

            Фильтр по состояниям пакета — множественный выбор с счётчиками, четыре значения
            ({nameof(Ban.Sdaid.Icd.Arch.Adr.ADR_008_PacketStatesAndTwoTabs)}): в очереди,
            проверка, завершён, ошибка. Переключатель «только мои» и отдельная ссылка сброса всех фильтров:
            принадлежность и сброс — разные понятия и не смешиваются в одном элементе.

            Кнопка «Взять в работу» выдаёт первый свободный пакет, подходящий под настройки профиля,
            и показывает, какой именно будет взят.

            Строка пакета: номер, время, опознанный вид ИК, состояние и уверенность распознавания числом.
            Пока обработка не прошла, уверенности нет — так и показывается, а не нулём.

            Индикатор состояния отвечает на вопрос «чья очередь ходить»: работает автоматика, ждёт
            оператора либо завершено. Дублировать им название состояния незачем — оно выведено словами.
            """;

        public static string S3_Tabs = $"""
            ## Две вкладки

            Обработка пакета делится не на этапы с подтверждениями, а на два вопроса, и каждому отведена
            своя вкладка: **«Пакет ввода»** — верны ли исходные данные, **«Проверка карты»** — верно ли
            Сервис их прочитал. Переход между вкладками свободный, подтверждать нечего: прохождение
            пакета отражает его состояние, а не цепочка подтверждений.

            ### Пакет ввода

            Вкладка строится **от опознанной формы**, а не от списка кадров: перечислены страницы формы,
            напротив каждой — отнесённый к ней кадр либо возможность отснять недостающую. Так видно,
            чего не хватает, — по списку кадров этого не понять.

            Вид ИК и форма показаны явно, с уверенностью определения. Если вид определён неверно,
            действие **«Вид ИК не распознан»** отклоняет его с возможностью указать верный; пакет уходит
            в очередь вместе с этим контекстом — указанный вид не теряется, — а пользователь берёт следующий.

            Кадр открывается в полном размере: переключение «оригинал ↔ обработанное», смена страницы,
            переснять, повторить обработку, удалить. Кадры, не отнесённые ни к одной странице, — лишние:
            их удаление повторной обработки не требует.

            Правка, меняющая исходные данные разбора, делает результаты распознавания недействительными —
            это показано, а на повторную обработку пакет уходит **по команде пользователя**, не сам.

            ### Проверка карты

            Поля сгруппированы по страницам и разделам формы, у каждого — уверенность. Рядом
            с исправленным значением видно, что прочла модель.

            **Обязательные поля помечены**, а незаполненное среди них выделено так, чтобы его было
            видно, не открывая каждый раздел. Состав обязательных зависит от вида документа
            и берётся из настройки, а не зашит в экран.

            **Поле со справочным значением текстом не вводится.** Отметке в клетке бланка отвечает
            выбор из набора допустимых значений; наборы переданы заказчиком
            ({SdaidAnchors.RefTo<VocabularyCatalog>()}). Пользователь видит значение, хранится
            и выгружается код. Несколько отметок на бланке дают несколько выбранных значений,
            а отметка «Ничего» исключает остальные и исключается ими.

            Если модель прочла то, чего в наборе нет, **поле остаётся незаполненным**, а прочитанное
            показано рядом: подставить нечего, а выбрать верное — работа пользователя. Так же
            выглядит расхождение бланка и справочника, когда клетка напечатана, а значения
            в наборе нет.

            **Повторяющаяся группа бланка показывается списком.** Строка — эксперт, закладка
            биотеста, замер: отдельный {SdaidAnchors.RefTo<Ban.Sdaid.Icd.Domain.Cards.CardDocumentSpec>()},
            а не набор полей с номерами. Строки добавляются и удаляются — бумага отводит под
            экспертов пять строк, но это свойство бланка, а не состава вида.

            В строке видно только значение, уверенность передана его цветом; редактор ячейки
            открывается под таблицей. Числа уверенности в каждой ячейке и редакторы у всех ячеек
            сразу таблицу не читают.

            Отдельным блоком **над полями формы** — реквизиты самой карты, которых на бланке нет:
            **место хранения** выбирается из справочника и по умолчанию берётся из учётной записи
            пользователя — у заказчика они связаны. В состав формы такое поле не входит и в разделах
            бланка не показывается: читать его неоткуда, уверенности у него нет.

            Выбор поля показывает справа страницу-источник с подсвеченной областью. Изображение крупное,
            с прямоугольной лупой во всю ширину — иначе рукописный текст не разобрать. Вкладка
            открывается на первом поле бланка: пустой просмотрщик не объясняет, зачем он нужен.

            Ниже — состав экспертов со сверкой по справочнику. Экземпляр фонда здесь не подбирается:
            привязка карты к экземпляру в состав Сервиса не входит.

            Завершает работу действие **«Создать ИК»**. Оно недоступно, пока не заполнены обязательные
            поля; состав кадров на это не влияет — не снятую страницу пользователь вправе внести руками.
            Рядом названы недостающие поля.

            Этой же командой создаётся файл для загрузки в АБИС: отдельной кнопки выгрузки на экране
            нет и не нужно.

            Рядом с ним — **«Взять следующий»**, выход для того, кто карту сейчас не создаёт. Данных
            это действие не меняет: правки сохраняются по мере ввода, пакет остаётся за оператором,
            и он вернётся к нему через очередь. Отдельного «Сохранить» на экране нет и не нужно.

            Сценарий целиком — {nameof(Ban.Sdaid.Icd.Epics.PacketProcessing.UseCases.VerifyPacketUseCase)}.
            """;

        public static string S9_Prototype = $"""
            ## Прототип

            {SdaidHtml.LinkTo<ProcessingQueuePage>("queue.html", "Открыть прототип в новой вкладке")}

            {SdaidHtml.IframeOf<ProcessingQueuePage>("queue.html", 720)}
            """;


    }
}