< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_StatusTransition_Index
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/StatusTransition/Index.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 55
Coverable lines: 55
Total lines: 282
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 88
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ExecuteAsync()0%210140%
<ExecuteAsync()0%420200%
<ExecuteAsync()0%506220%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/StatusTransition/Index.cshtml

#LineLine coverage
 1@using OzonDomains
 2@using OzonDomains.Models
 3@using OzonOrdersWeb.Areas.Studio2.ViewModels.Grid
 4@model List<StatusTransition>
 5@{
 06    ViewData["Title"] = "Переходы между статусами";
 07    Layout = "~/Views/Shared/_Layout.cshtml";
 08    var rows = Model.Select(t => new
 09    {
 010        id = t.Id,
 011        displayOrder = t.DisplayOrder,
 012        fromName = t.FromStatus?.Name ?? "—",
 013        fromColor = t.FromStatus?.GetStatusColor(),
 014        fromFinal = t.FromStatus?.IsFinal == true,
 015        toName = t.ToStatus?.Name ?? "—",
 016        toColor = t.ToStatus?.GetStatusColor(),
 017        toFinal = t.ToStatus?.IsFinal == true,
 018        transactionType = t.TransactionType.GetDisplayName(),
 019        transactionIcon = t.TransactionType.GetTransactionTypeIcon(),
 020        transactionCss = t.TransactionType.GetTransactionTypeCssClass(),
 021        displayName = t.DisplayName,
 022        isActive = t.IsActive
 023    });
 24}
 25
 026@await Html.PartialAsync("_BsxGridShell")
 27<link rel="stylesheet" href="~/css/Studio2/StatusTransition/status-transition-index.css" asp-append-version="true"/>
 28
 29<div class="container-fluid py-3 st-page bsx-surface">
 30<div class="app-section-hero app-section-hero--import d-flex flex-wrap justify-content-between align-items-start gap-3">
 31    <div>
 32        <h1><i class="fas fa-project-diagram me-2 text-primary"></i>Переходы между статусами</h1>
 33        <div class="subtitle">Правила смены статуса заказа при выполнении транзакций — граф и справочник записей</div>
 34        <div class="subtitle-help">
 35            Каждый переход связывает <strong>тип транзакции</strong> с парой статусов «из → в».
 36            Система использует эти правила в меню транзакций на странице заказов и при валидации проводок.
 37            На графе можно визуально связать статусы; в таблице — отредактировать или удалить запись.
 38        </div>
 39        <div class="app-section-hero__quick-links">
 40            <a asp-area="Studio2" asp-controller="AppStatus" asp-action="Index"><i class="fas fa-tags"></i>Статусы заказ
 41            <a asp-area="Studio2" asp-controller="Orders" asp-action="Index"><i class="fas fa-list"></i>Активные заказы<
 42            <a asp-area="Studio2" asp-controller="TransactionTypeSettings" asp-action="Index"><i class="fas fa-sliders-h
 43        </div>
 44    </div>
 45    <div class="d-flex flex-wrap gap-2 align-items-center">
 46        <div class="bsx-dir-stats">
 047            <span class="bsx-dir-stat">Переходов <b>@Model.Count</b></span>
 048            <span class="bsx-dir-stat">Активных <b>@Model.Count(t => t.IsActive)</b></span>
 49        </div>
 50        <a asp-action="Create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>Добавить</a>
 51    </div>
 52</div>
 053@if (TempData["StatusResult"] != null)
 54{
 55    <div class="alert alert-success alert-dismissible mb-3">
 56        <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
 057        @TempData["StatusResult"]
 58    </div>
 59}
 60<!-- ── Graph card ──────────────────────────────────────────── -->
 61<div class="graph-card">
 62    <div class="graph-card-header">
 63        <div class="graph-card-title">
 64            <i class="fas fa-sitemap"></i>
 65            Граф переходов
 66        </div>
 67        <div class="graph-toolbar">
 68            <button class="g-btn active" id="btnLayoutHier" onclick="setLayout('hierarchical')" title="Слева направо по 
 69                <i class="fas fa-stream"></i> Слева направо
 70            </button>
 71            <button class="g-btn" id="btnLayoutForce" onclick="setLayout('force')" title="Силовая раскладка — самооргани
 72                <i class="fas fa-atom"></i> Свободный
 73            </button>
 74            <div class="g-divider"></div>
 75            <button class="g-btn" onclick="zoomGraphOut()" title="Уменьшить">
 76                <i class="fas fa-search-minus"></i>
 77            </button>
 78            <button class="g-btn" onclick="zoomGraphIn()" title="Увеличить">
 79                <i class="fas fa-search-plus"></i>
 80            </button>
 81            <button class="g-btn" onclick="fitGraph()" title="Вписать всё в экран">
 82                <i class="fas fa-compress-arrows-alt"></i> Вписать
 83            </button>
 84            <button class="g-btn" onclick="rebuildGraph()" title="Перестроить расположение">
 85                <i class="fas fa-redo"></i>
 86            </button>
 87            <div class="g-divider"></div>
 88            <button class="g-btn" id="editModeBtn" onclick="toggleEditMode()" title="Клик на два узла создаёт переход">
 89                <i class="fas fa-magic"></i> Связать
 90            </button>
 91            <div class="g-divider"></div>
 92            <button class="g-btn" onclick="toggleGraph()" id="graphToggleBtn">
 93                <i class="fas fa-chevron-up"></i>
 94            </button>
 95        </div>
 96    </div>
 97
 98    <div id="graphContainer">
 99        <div id="transitionGraph"></div>
 100        <div class="graph-legend">
 101            <div class="lg-item"><div class="lg-node"></div><span>Статус</span></div>
 102            <div class="lg-item"><div class="lg-node lg-node--final"></div><span>Конечный статус</span></div>
 103            <div class="lg-item"><div class="lg-arrow"></div><span>Активный переход</span></div>
 104            <div class="lg-item"><div class="lg-arrow lg-arrow--dash"></div><span>Неактивный</span></div>
 105            <div class="lg-item">
 106                <i class="fas fa-mouse-pointer bsx-muted" style="font-size:10px;"></i>
 107                <span>ПКМ — добавить статус · колесо — масштаб</span>
 108            </div>
 109            <div class="edit-hint" id="editModeHint"></div>
 110        </div>
 111    </div>
 112</div>
 113
 0114@await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("statusTransition", "Список переходов", "fa-
 0115    "Фильтры в колонках: исходный и целевой статус, тип транзакции, активность. Двойной клик — страница изменения."))
 116<!-- ── Модальное окно создания перехода из графа ──────────── -->
 117<div class="modal fade" id="createTransitionModal" tabindex="-1" aria-hidden="true">
 118    <div class="modal-dialog">
 119        <div class="modal-content">
 120            <div class="modal-header">
 121                <h5 class="modal-title"><i class="fas fa-plus-circle me-2 text-primary"></i>Новый переход</h5>
 122                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
 123            </div>
 124            <form asp-action="Create" method="post" id="quickCreateForm">
 125                <div class="modal-body">
 126                    <input type="hidden" id="modalFromStatusId" name="FromStatusId"/>
 127                    <input type="hidden" id="modalToStatusId" name="ToStatusId"/>
 128
 129                    <div class="d-flex align-items-center gap-3 mb-4 p-3 rounded st-modal-route">
 130                        <div id="modalFromStatusDisplay" class="flex-fill"></div>
 131                        <i class="fas fa-long-arrow-alt-right fa-lg" style="flex-shrink:0;"></i>
 132                        <div id="modalToStatusDisplay" class="flex-fill"></div>
 133                    </div>
 134
 135                    <div class="mb-3">
 136                        <label class="form-label fw-bold" style="font-size:12px;">Тип транзакции <span class="text-dange
 137                        <select class="form-select" name="TransactionType" id="modalTransactionType" required>
 138                            <option value="">— выберите тип —</option>
 0139                            @if (ViewBag.TransactionTypes != null)
 140                            {
 0141                                foreach (var tt in (List<TransactionType>)ViewBag.TransactionTypes)
 142                                {
 0143                                    <option value="@((int)tt)">@tt.GetDisplayName()</option>
 144                                }
 145                            }
 146                        </select>
 147                    </div>
 148
 149                    <div class="mb-3">
 150                        <label class="form-label fw-bold" style="font-size:12px;">Название (для UI)</label>
 151                        <input type="text" class="form-control" name="DisplayName" id="modalDisplayName" placeholder="По
 152                    </div>
 153
 154                    <div class="row g-3 mb-3">
 155                        <div class="col">
 156                            <label class="form-label fw-bold" style="font-size:12px;">Порядок</label>
 157                            <input type="number" class="form-control" name="DisplayOrder" value="0"/>
 158                        </div>
 159                        <div class="col d-flex align-items-end gap-3 pb-1">
 160                            <div class="form-check mb-0">
 161                                <input type="hidden" name="IsActive" value="false"/>
 162                                <input type="checkbox" class="form-check-input" name="IsActive" id="modalIsActive" value
 163                                <label class="form-check-label" for="modalIsActive" style="font-size:12px;">Активен</lab
 164                            </div>
 165                            <div class="form-check mb-0">
 166                                <input type="hidden" name="IsRepeatable" value="false"/>
 167                                <input type="checkbox" class="form-check-input" name="IsRepeatable" id="modalIsRepeatabl
 168                                <label class="form-check-label" for="modalIsRepeatable" style="font-size:12px;">В обе ст
 169                            </div>
 170                        </div>
 171                    </div>
 172                </div>
 173                <div class="modal-footer">
 174                    <button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Отмена</butto
 175                    <button type="submit" class="btn btn-primary btn-sm"><i class="fas fa-save me-1"></i>Создать</button
 176                </div>
 177            </form>
 178        </div>
 179    </div>
 180</div>
 181
 182<!-- ── Контекстное меню ────────────────────────────────────── -->
 183<div class="graph-ctx-menu" id="graphContextMenu">
 184    <div class="ctx-header"><i class="fas fa-plus-circle me-1"></i>Добавить статус на граф</div>
 185    <div id="contextMenuItems"></div>
 186</div>
 187</div>
 188
 189@section Scripts {
 190    <script>
 191    window.statusTransitionGraph = {
 0192        hasData: @Html.Raw(Model != null && Model.Any() ? "true" : "false"),
 0193        allStatuses: @if (ViewBag.Statuses != null)
 194        {
 0195            @Html.Raw(System.Text.Json.JsonSerializer.Serialize(
 0196                ((List<AppStatus>)ViewBag.Statuses).Select(s => new {
 0197                    id = s.Id,
 0198                    name = s.Name,
 0199                    color = s.GetStatusColor(),
 0200                    isFinal = s.IsFinal
 0201                })
 0202            ))
 203        }
 204        else
 205        {
 206            @:[]
 207        },
 0208        transitions: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(
 0209            (Model ?? new List<StatusTransition>()).Select(t => new {
 0210                fromStatusId = t.FromStatusId,
 0211                fromStatusName = t.FromStatus?.Name ?? "—",
 0212                fromStatusColor = t.FromStatus?.GetStatusColor() ?? "#94a3b8",
 0213                toStatusId = t.ToStatusId,
 0214                toStatusName = t.ToStatus?.Name ?? "—",
 0215                toStatusColor = t.ToStatus?.GetStatusColor() ?? "#94a3b8",
 0216                transactionType = t.TransactionType.GetDisplayName(),
 0217                transactionCssClass = t.TransactionType.GetTransactionTypeCssClass(),
 0218                isActive = t.IsActive,
 0219                displayName = t.DisplayName ?? t.TransactionType.GetDisplayName()
 0220            })
 0221        ))
 222    };
 223    </script>
 224    <script src="~/js/Studio2/Grid/bsx-directory.js" asp-append-version="true"></script>
 225    <script>
 226        (function () {
 227            const D = window.BsxDirectory;
 228            const esc = BsxGrid.esc;
 0229            const editUrl = id => '@Url.Action("Edit", "StatusTransition", new { area = "Studio2" })/' + id;
 230            // Класс типа транзакции (link-*) → тон темы.
 231            const TONES = { 'link-success': 'success', 'link-danger': 'danger', 'link-warning': 'warning', 'link-info': 
 232                            'link-primary': 'accent', 'link-secondary': 'muted', 'link-purple': 'purple' };
 233            const finalMark = on => on ? ' <span class="bsx-tone bsx-tone--warning" title="Конечный статус">F</span>' : 
 234
 235            D.grid({
 236                key: 'dirStatusTransition',
 237                prefix: 'statusTransition',
 0238                rows: @Json.Serialize(rows),
 239                rowUrl: r => editUrl(r.id),
 240                columnDefs: [
 241                    D.numCol({ field: 'displayOrder', headerName: '#', headerTooltip: 'Порядок в меню', width: 80, sort:
 242                    D.setCol({ field: 'fromName', headerName: 'Исходный статус', flex: 1, minWidth: 200,
 243                      cellRenderer: p => D.colorDot(p.value, p.data.fromColor, finalMark(p.data.fromFinal)) }),
 244                    { colId: 'arrow', headerName: '', width: 44, sortable: false, filter: false, resizable: false, suppr
 245                      cellRenderer: () => '<i class="fas fa-long-arrow-alt-right bsx-muted"></i>' },
 246                    D.setCol({ field: 'toName', headerName: 'Целевой статус', flex: 1, minWidth: 200,
 247                      cellRenderer: p => D.colorDot(p.value, p.data.toColor, finalMark(p.data.toFinal)) }),
 248                    D.setCol({ field: 'transactionType', headerName: 'Тип транзакции', width: 240,
 249                      cellRenderer: p => D.tone(p.value, TONES[p.data.transactionCss] || 'muted', p.data.transactionIcon
 250                    { field: 'displayName', headerName: 'Название', flex: 1, minWidth: 180, valueFormatter: p => p.value
 251                    D.boolCol({ field: 'isActive', headerName: 'Активен', width: 110 }),
 252                    D.actionsCol(r => [
 253                        { icon: 'fa-pencil-alt', title: 'Изменить', href: editUrl(r.id) },
 254                        { icon: 'fa-up-right-from-square', title: 'Изменить в новой вкладке', href: editUrl(r.id), targe
 0255                        { icon: 'fa-trash', title: 'Удалить', kind: 'danger', href: '@Url.Action("Delete", "StatusTransi
 256                    ], { width: 130 })
 257                ]
 258            });
 259        })();
 260    </script>
 261    <link href="https://cdn.jsdelivr.net/npm/vis-network@9.1.9/dist/dist/vis-network.min.css" rel="stylesheet"/>
 262    <script src="~/js/Studio2/StatusTransition/status-transition-graph.js" asp-append-version="true"></script>
 263    <script>
 264        (function () {
 265            const graphScript = document.createElement('script');
 266            graphScript.src = 'https://cdn.jsdelivr.net/npm/vis-network@9.1.9/standalone/umd/vis-network.min.js';
 267            graphScript.async = false;
 268            graphScript.onload = function () {
 269                if (typeof initStatusTransitionGraph === 'function') {
 270                    initStatusTransitionGraph();
 271                }
 272            };
 273            graphScript.onerror = function () {
 274                const el = document.getElementById('transitionGraph');
 275                if (el) {
 276                    el.innerHTML = '<div class="st-graph-error"><i class="fas fa-exclamation-triangle fa-2x d-block mb-2
 277                }
 278            };
 279            document.head.appendChild(graphScript);
 280        })();
 281    </script>
 282}