| | | 1 | | @model List<OzonDomains.Models.AssemblyListRun> |
| | | 2 | | @{ |
| | 0 | 3 | | ViewData["Title"] = "История листов подбора"; |
| | | 4 | | } |
| | | 5 | | @functions { |
| | 0 | 6 | | private static readonly TimeZoneInfo Msk = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time"); |
| | | 7 | | |
| | | 8 | | private static string FormatMsk(DateTime? utc, string fmt = "dd.MM.yyyy HH:mm:ss") |
| | | 9 | | { |
| | 0 | 10 | | if (!utc.HasValue) return "—"; |
| | 0 | 11 | | var dt = utc.Value.Kind == DateTimeKind.Unspecified |
| | 0 | 12 | | ? DateTime.SpecifyKind(utc.Value, DateTimeKind.Utc) |
| | 0 | 13 | | : utc.Value.ToUniversalTime(); |
| | 0 | 14 | | return TimeZoneInfo.ConvertTimeFromUtc(dt, Msk).ToString(fmt); |
| | | 15 | | } |
| | | 16 | | |
| | 0 | 17 | | private static string StatusBadge(string s) => s switch |
| | 0 | 18 | | { |
| | 0 | 19 | | "completed" => "bg-success", |
| | 0 | 20 | | "error" => "bg-danger", |
| | 0 | 21 | | _ => "bg-warning text-dark", |
| | 0 | 22 | | }; |
| | | 23 | | |
| | 0 | 24 | | private static string StatusLabel(string s) => s switch |
| | 0 | 25 | | { |
| | 0 | 26 | | "completed" => "Завершён", |
| | 0 | 27 | | "error" => "Ошибка", |
| | 0 | 28 | | _ => "Выполняется", |
| | 0 | 29 | | }; |
| | | 30 | | |
| | 0 | 31 | | private static string FilterStatusLabel(string s) => s switch |
| | 0 | 32 | | { |
| | 0 | 33 | | "awaiting_deliver" => "Ожидает отгрузки", |
| | 0 | 34 | | "awaiting_packaging" => "Ожидает упаковки", |
| | 0 | 35 | | "awaiting_registration" => "Ожидает регистрации", |
| | 0 | 36 | | "acceptance_in_progress" => "Идёт приёмка", |
| | 0 | 37 | | "awaiting_approve" => "Ожидает подтверждения", |
| | 0 | 38 | | "arbitration" => "Арбитраж", |
| | 0 | 39 | | "client_arbitration" => "Клиентский арбитраж", |
| | 0 | 40 | | "delivering" => "Доставляется", |
| | 0 | 41 | | "driver_pickup" => "У водителя", |
| | 0 | 42 | | "cancelled" => "Отменено", |
| | 0 | 43 | | "not_accepted" => "Не принято на СЦ", |
| | 0 | 44 | | _ => s, |
| | 0 | 45 | | }; |
| | | 46 | | } |
| | | 47 | | |
| | | 48 | | <link rel="stylesheet" href="~/css/Studio2/OzonClients/ozon-clients-form.css" asp-append-version="true" /> |
| | | 49 | | |
| | | 50 | | @section HeaderActions { |
| | | 51 | | <div class="header-page-actions"> |
| | | 52 | | <span class="header-page-actions-sep"></span> |
| | | 53 | | <div class="btn-group"> |
| | | 54 | | <a href="/Studio2/AssemblyList" class="hpa-btn"> |
| | | 55 | | <i class="fas fa-plus"></i><span class="hpa-label">Новая генерация</span> |
| | | 56 | | </a> |
| | | 57 | | </div> |
| | | 58 | | </div> |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | <div class="container-fluid py-3"> |
| | | 62 | | |
| | | 63 | | <div class="oc-form-hero"> |
| | | 64 | | <h1><i class="fas fa-history me-2 text-primary"></i>История листов подбора FBS</h1> |
| | | 65 | | <div class="subtitle-help"> |
| | | 66 | | Все запуски генерации — настройки, длительность и результаты. |
| | | 67 | | Завершённые запуски можно открыть для просмотра и скачать в Excel/PDF. |
| | | 68 | | </div> |
| | | 69 | | </div> |
| | | 70 | | |
| | 0 | 71 | | @if (!Model.Any()) |
| | | 72 | | { |
| | | 73 | | <div class="oc-form-card"> |
| | | 74 | | <div class="oc-intro"> |
| | | 75 | | <i class="fas fa-circle-info"></i> |
| | | 76 | | <div class="oc-intro-text"> |
| | | 77 | | Генераций ещё не было. |
| | | 78 | | <a href="/Studio2/AssemblyList">Запустить первую генерацию</a>. |
| | | 79 | | </div> |
| | | 80 | | </div> |
| | | 81 | | </div> |
| | | 82 | | } |
| | | 83 | | else |
| | | 84 | | { |
| | | 85 | | <div class="oc-form-card p-0"> |
| | | 86 | | <div class="oc-form-card-header px-3 pt-3 pb-2"> |
| | | 87 | | <h2><i class="fas fa-table me-2 text-muted"></i>Запуски</h2> |
| | 0 | 88 | | <span class="badge bg-secondary">Всего: @Model.Count</span> |
| | | 89 | | </div> |
| | | 90 | | <div class="table-responsive"> |
| | | 91 | | <table class="table table-sm table-hover align-middle mb-0"> |
| | | 92 | | <thead class="table-light"> |
| | | 93 | | <tr> |
| | | 94 | | <th style="width:60px; padding-left:1rem;">ID</th> |
| | | 95 | | <th>Запущен (МСК)</th> |
| | | 96 | | <th>Завершён</th> |
| | | 97 | | <th>Длительность</th> |
| | | 98 | | <th>Клиент</th> |
| | | 99 | | <th>Статус фильтра</th> |
| | | 100 | | <th>Период</th> |
| | | 101 | | <th>Статус</th> |
| | | 102 | | <th class="text-center">Отправлений</th> |
| | | 103 | | <th class="text-center">Позиций</th> |
| | | 104 | | <th data-column="actions" style="width:120px;"></th> |
| | | 105 | | </tr> |
| | | 106 | | </thead> |
| | | 107 | | <tbody> |
| | 0 | 108 | | @foreach (var run in Model) |
| | | 109 | | { |
| | 0 | 110 | | var duration = run.CompletedAtUtc.HasValue |
| | 0 | 111 | | ? (run.CompletedAtUtc.Value - run.StartedAtUtc).TotalSeconds |
| | 0 | 112 | | : (double?)null; |
| | | 113 | | |
| | | 114 | | <tr> |
| | 0 | 115 | | <td class="text-muted small" style="padding-left:1rem;">@run.Id</td> |
| | 0 | 116 | | <td class="small">@FormatMsk(run.StartedAtUtc)</td> |
| | | 117 | | <td class="small"> |
| | 0 | 118 | | @if (run.CompletedAtUtc.HasValue) |
| | | 119 | | { |
| | 0 | 120 | | @FormatMsk(run.CompletedAtUtc, "HH:mm:ss") |
| | | 121 | | } |
| | | 122 | | else |
| | | 123 | | { |
| | | 124 | | <span class="text-muted">—</span> |
| | | 125 | | } |
| | | 126 | | </td> |
| | | 127 | | <td class="small text-muted"> |
| | 0 | 128 | | @if (duration.HasValue) { @($"{duration:0}с") } else { <text>—</text> } |
| | | 129 | | </td> |
| | 0 | 130 | | <td class="small">@run.OzonClientName</td> |
| | 0 | 131 | | <td><span class="badge bg-light text-dark border" style="font-size:0.72rem;">@FilterStat |
| | 0 | 132 | | <td class="small text-nowrap">@run.CutoffFrom.ToString("dd.MM") – @run.CutoffTo.ToString |
| | | 133 | | <td> |
| | 0 | 134 | | <span class="badge @StatusBadge(run.RunStatus)" style="font-size:0.72rem;">@StatusLa |
| | 0 | 135 | | @if (!string.IsNullOrEmpty(run.ErrorMessage)) |
| | | 136 | | { |
| | | 137 | | <i class="fas fa-info-circle text-danger ms-1" |
| | | 138 | | title="@run.ErrorMessage" |
| | | 139 | | data-bs-toggle="tooltip" data-bs-placement="top"></i> |
| | | 140 | | } |
| | | 141 | | </td> |
| | 0 | 142 | | <td class="text-center small">@run.TotalPostings</td> |
| | 0 | 143 | | <td class="text-center small">@run.TotalItems</td> |
| | | 144 | | <td> |
| | 0 | 145 | | @if (run.RunStatus == "completed" && run.TotalItems > 0) |
| | | 146 | | { |
| | | 147 | | <a href="/Studio2/AssemblyList/HistoryDetail/@run.Id" |
| | | 148 | | class="btn btn-sm btn-outline-primary"> |
| | | 149 | | <i class="fas fa-eye me-1"></i>Просмотр |
| | | 150 | | </a> |
| | | 151 | | } |
| | | 152 | | </td> |
| | | 153 | | </tr> |
| | | 154 | | } |
| | | 155 | | </tbody> |
| | | 156 | | </table> |
| | | 157 | | </div> |
| | | 158 | | </div> |
| | | 159 | | } |
| | | 160 | | </div> |
| | | 161 | | |
| | | 162 | | @section Scripts { |
| | | 163 | | <script> |
| | | 164 | | document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) { |
| | | 165 | | new bootstrap.Tooltip(el); |
| | | 166 | | }); |
| | | 167 | | </script> |
| | | 168 | | } |