< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_PartnerOffice_Views_Partners_Index
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/PartnerOffice/Views/Partners/Index.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 15
Coverable lines: 15
Total lines: 106
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
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%110100%
<ExecuteAsync()0%620%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/PartnerOffice/Views/Partners/Index.cshtml

#LineLine coverage
 1@using OzonOrdersWeb.Areas.Studio2.ViewModels.Grid
 2@using OzonOrdersWeb.WebServices.Partners
 3@model IReadOnlyList<PartnerGridRow>
 4@{
 05    ViewData["Title"] = "Кабинет партнёров";
 6}
 7
 08@await Html.PartialAsync("~/Areas/Studio2/Views/Shared/_BsxGridShell.cshtml")
 9
 10<div class="container-fluid py-3 bsx-surface">
 11    <div class="app-section-hero app-section-hero--import d-flex flex-wrap justify-content-between align-items-start gap
 12        <div>
 13            <h1><i class="fas fa-handshake me-2 text-primary"></i>Кабинет партнёров</h1>
 14            <div class="subtitle">Партнёры, правила видимости заказов и возможности</div>
 15            <div class="subtitle-help">
 16                Здесь вы заводите партнёров, задаёте, какие заказы они видят, и включаете возможности.
 17                Кабинет партнёра работает в отдельном сервисе PartnerOffice и ходит в Studio2 через API.
 18                <strong>Партнёр без правил не видит ни одного заказа.</strong>
 19            </div>
 20            <div class="app-section-hero__quick-links">
 21                <a asp-area="PartnerOffice" asp-controller="Partners" asp-action="Audit"><i class="fas fa-clipboard-list
 22                <a href="/swagger/index.html" target="_blank" rel="noopener"><i class="fas fa-code"></i>Swagger API парт
 23                <a asp-area="Studio2" asp-controller="AssemblyListAccessKeys" asp-action="Index"><i class="fas fa-key"><
 24            </div>
 25        </div>
 26        <div class="d-flex flex-wrap gap-2 align-items-center">
 27            <div class="bsx-dir-stats">
 028                <span class="bsx-dir-stat">Всего <b>@Model.Count</b></span>
 029                <span class="bsx-dir-stat">Активных <b>@Model.Count(x => x.IsActive)</b></span>
 030                <span class="bsx-dir-stat">Без правил <b>@Model.Count(x => x.RulesCount == 0)</b></span>
 31            </div>
 32            <a asp-action="Create" class="btn btn-primary btn-sm"><i class="fas fa-plus me-1"></i>Создать партнёра</a>
 33        </div>
 34    </div>
 35
 036    @if (TempData["Success"] is string ok)
 37    {
 38        <div class="alert alert-success alert-dismissible fade show py-2 small" role="alert">
 039            <i class="fas fa-check-circle me-2"></i>@ok
 40            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Закрыть"></button>
 41        </div>
 42    }
 043    @if (TempData["Error"] is string err)
 44    {
 45        <div class="alert alert-danger alert-dismissible fade show py-2 small" role="alert">
 046            <i class="fas fa-exclamation-circle me-2"></i>@err
 47            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Закрыть"></button>
 48        </div>
 49    }
 50
 051    @await Html.PartialAsync("~/Areas/Studio2/Views/Shared/_BsxDirectoryGrid.cshtml", new BsxDirectoryGridViewModel("par
 052        "Двойной клик по строке открывает настройки партнёра."))
 53</div>
 54
 55@section Scripts {
 56<script src="~/js/Studio2/Grid/bsx-directory.js" asp-append-version="true"></script>
 57<script>
 58    (function () {
 59        const D = window.BsxDirectory;
 60        const esc = v => window.BsxGrid.esc(v);
 061        const editUrl = id => '@Url.Action("Edit", "Partners", new { area = "PartnerOffice" })/' + id;
 062        const auditUrl = id => '@Url.Action("Audit", "Partners", new { area = "PartnerOffice" })?partnerId=' + id;
 63        const fmtDateTime = v => {
 64            if (!v) return '';
 65            const d = new Date(v + (/[zZ]|[+-]\d\d:?\d\d$/.test(v) ? '' : 'Z'));
 66            return isNaN(d) ? String(v) : d.toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric',
 67        };
 68
 69        D.grid({
 70            key: 'dirPartner',
 71            prefix: 'partners',
 072            rows: @Json.Serialize(Model),
 73            rowUrl: r => editUrl(r.id),
 74            columnDefs: [
 75                D.numCol({ field: 'id', headerName: 'ID', width: 80, hide: true }),
 76                { field: 'name', headerName: 'Партнёр', flex: 1, minWidth: 220, cellClass: 'fw-semibold', sort: 'asc',
 77                  cellRenderer: p => p.data ? '<a href="' + esc(editUrl(p.data.id)) + '">' + esc(p.value) + '</a>' : '' 
 78                { field: 'email', headerName: 'Почта', width: 250, tooltipField: 'email',
 79                  cellRenderer: p => !p.data ? '' : p.value ? esc(p.value) : D.tone('нет почты — вход закрыт', 'danger',
 80                { field: 'googleEmail', headerName: 'Google-почта', width: 230, tooltipField: 'googleEmail',
 81                  cellRenderer: p => p.value ? esc(p.value) : '<span class="bsx-muted">не указана</span>' },
 82                D.numCol({ field: 'rulesCount', headerName: 'Правил', width: 110,
 83                  headerTooltip: 'Без правил партнёр не видит ни одного заказа',
 84                  cellRenderer: p => !p.data ? '' : p.value > 0 ? String(p.value) : D.tone('нет правил', 'danger', 'fa-t
 85                { field: 'permissionsText', headerName: 'Возможности', width: 260, tooltipField: 'permissionsText',
 086                  cellRenderer: p => !p.data ? '' : p.value === '@PartnerGridRows.ViewOnlyText'
 87                      ? '<span class="bsx-muted">' + esc(p.value) + '</span>'
 88                      : p.value.split(', ').map(t => D.tone(t, 'accent')).join(' ') },
 89                D.boolCol({ field: 'labelPrint', headerName: 'Этикетки', width: 110, hide: true }),
 90                D.boolCol({ field: 'assemble', headerName: 'Сборка', width: 110, hide: true }),
 91                D.boolCol({ field: 'trackNumber', headerName: 'Трек', width: 110, hide: true }),
 92                { field: 'lastLoginAtUtc', headerName: 'Последний вход', width: 170, filter: 'agDateColumnFilter',
 93                  filterParams: { maxNumConditions: 1, inRangeInclusive: true },
 94                  valueFormatter: p => p.data ? (fmtDateTime(p.value) || 'не входил') : '' },
 95                D.boolCol({ field: 'isActive', headerName: 'Статус', width: 130,
 96                  filterParams: { labels: { 'true': 'Активен', 'false': 'Отключён' } },
 97                  cellRenderer: p => D.yesNo(p.value, 'Активен', 'Отключён') }),
 98                D.actionsCol(r => [
 99                    { icon: 'fa-pen', title: 'Настройки партнёра', kind: 'view', href: editUrl(r.id) },
 100                    { icon: 'fa-clipboard-list', title: 'Журнал партнёра', kind: 'view', href: auditUrl(r.id) }
 101                ], { width: 100 })
 102            ]
 103        });
 104    })();
 105</script>
 106}

Methods/Properties

ExecuteAsync()
<ExecuteAsync()