< Summary

Information
Class: AspNetCoreGeneratedDocument.Views_Shared__Sidebar
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Views/Shared/_Sidebar.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 36
Coverable lines: 36
Total lines: 124
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 64
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%3422580%
<ExecuteAsync()0%620%
<ExecuteAsync()0%2040%
<ExecuteAsync()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Views/Shared/_Sidebar.cshtml

#LineLine coverage
 1@using Microsoft.AspNetCore.Identity
 2@using OzonRepositories.Context.Identity
 3@using System.Security.Claims
 4@using System.Text.Json
 5@using OzonOrdersWeb.WebServices.Sidebar
 6@using Services.CacheServices.Cache.UserCacheService
 7@inject IUserCacheService UserCache
 8@inject IUserSidebarSettingsService SidebarSettings
 9@{
 010    var currentController = ViewContext.RouteData.Values["controller"]?.ToString() ?? "";
 011    var currentAction = ViewContext.RouteData.Values["action"]?.ToString() ?? "";
 012    var currentArea = ViewContext.RouteData.Values["area"]?.ToString() ?? "";
 13    // Sidebar рендерится в _Layout на КАЖДОЙ странице — берём пользователя из 10-мин memcache,
 14    // а не свежим запросом UserManager.GetUserAsync к БД на каждый рендер.
 015    var currentUser = await UserCache.GetCachedUserAsync(User);
 016    var displayName = currentUser?.GoogleDisplayName
 017        ?? currentUser?.UserName
 018        ?? User.Identity?.Name
 019        ?? "User";
 020    var avatarUrl = currentUser?.GoogleAvatarUrl;
 021    var safeName = (displayName ?? "U").Trim();
 022    var userInitials = safeName.Length >= 2 ? safeName[..2].ToUpperInvariant() : safeName.ToUpperInvariant();
 023    var isAdmin = User.IsInRole("Admin");
 24
 25    // Настройки меню (избранное, порядок, свёрнутые группы, pin) — из БД через 10-мин memory-кеш.
 026    var sidebarUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
 027    var sidebarSettings = string.IsNullOrEmpty(sidebarUserId)
 028        ? new Dictionary<string, string>()
 029        : await SidebarSettings.GetAllAsync(sidebarUserId, Context.RequestAborted);
 030    var sidebarPinned = false;
 031    if (sidebarSettings.TryGetValue("prefs", out var prefsJson))
 32    {
 33        try
 34        {
 035            using var prefs = JsonDocument.Parse(prefsJson);
 036            sidebarPinned = prefs.RootElement.TryGetProperty("pinned", out var pinnedEl) && pinnedEl.ValueKind == JsonVa
 037        }
 038        catch (JsonException) { /* битые настройки — как будто их нет */ }
 39    }
 040    var sidebarNav = new SidebarNavViewModel
 041    {
 042        Tree = SidebarNavigationCatalog.For(isAdmin),
 043        Settings = sidebarSettings,
 044        SaveUrl = Url.Action("SaveSetting", "SidebarSettings") ?? "/Sidebar/SaveSetting",
 045        Pinned = sidebarPinned
 046    };
 47}
 48
 049<aside class="app-sidebar app-sidebar--quartz @(sidebarPinned ? "is-pinned" : "")" id="appSidebar" data-sidebar-pinned="
 50    <div class="sidebar-header">
 51        <a class="sidebar-logo" asp-area="Studio2" asp-controller="Home" asp-action="Index" title="Открыть навигацию Stu
 52            <div class="sidebar-logo-icon">
 53                <img class="sidebar-logo-image" src="~/images/branding/studio2-app-icon-v2.png" asp-append-version="true
 54            </div>
 55            <div class="sidebar-logo-meta">
 56                <span class="sidebar-logo-text">Studio2</span>
 057                <span class="sidebar-build-info" title="@AppBuildInfoDisplay.Tooltip">@AppBuildInfoDisplay.CompactLabel<
 58            </div>
 59        </a>
 60        <button class="sidebar-pin-toggle @(sidebarPinned ? "is-active" : "")" id="sidebarPinToggle" type="button"
 61                aria-pressed="@(sidebarPinned ? "true" : "false")" title="@(sidebarPinned ? "Открепить меню" : "Закрепит
 62            <span class="ag-icon @(sidebarPinned ? "ag-icon-un-pin" : "ag-icon-pin")" role="presentation"></span>
 63            <span class="visually-hidden">Закрепить раскрытое меню</span>
 64        </button>
 65    </div>
 66
 67    <partial name="_SidebarStructuredNav" model="sidebarNav" />
 68
 69    <div class="sidebar-footer">
 70        <a class="sidebar-user" asp-area="Studio2" asp-controller="Profile" asp-action="Index" title="Открыть профиль">
 071            @if (!string.IsNullOrWhiteSpace(avatarUrl))
 72            {
 73                <img class="sidebar-user-avatar" src="@avatarUrl" alt="@displayName" referrerpolicy="no-referrer" />
 74            }
 75            else
 76            {
 077                <div class="sidebar-user-avatar">@userInitials</div>
 78            }
 79            <div class="sidebar-user-info">
 080                <div class="sidebar-user-name">@displayName</div>
 081                <div class="sidebar-user-role">@(isAdmin ? "Администратор" : "Пользователь")</div>
 82            </div>
 83        </a>
 84    </div>
 85</aside>
 86
 87<script>
 88    const orderControlBadge = document.getElementById('sidebar-order-control-badge');
 89    const badgeUpdatedAt = () => new Date().toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
 90    const updateOrderControlBadge = total => {
 91        if (!orderControlBadge || !Number.isInteger(total)) return;
 92        orderControlBadge.textContent = total > 99 ? '99+' : String(total);
 93        const description = `Активных задач контроля заказов: ${total}. Обновлено ${badgeUpdatedAt()}`;
 94        orderControlBadge.setAttribute('aria-label', description);
 95        orderControlBadge.title = description;
 96        orderControlBadge.style.display = total > 0 ? 'inline-flex' : 'none';
 97    };
 98    window.addEventListener('orderControlTasksCountChanged', event => updateOrderControlBadge(Number(event.detail?.total
 99
 100    // Счётчик загружается фоном: недоступность API АВД не должна задерживать навигацию.
 101    document.addEventListener('DOMContentLoaded', async () => {
 102        const badge = document.getElementById('sidebar-supplier-cart-badge');
 103        if (!badge) return;
 104
 105        try {
 0106            const response = await fetch('@Url.Action("Count", "SupplierCart", new { area = "Studio2" })', {
 107                headers: { 'Accept': 'application/json' }
 108            });
 109            if (!response.ok) return;
 110
 111            const data = await response.json();
 112            const count = Number(data?.count);
 113            if (!Number.isInteger(count) || count <= 0) return;
 114
 115            badge.textContent = count > 99 ? '99+' : String(count);
 116            const description = `Позиций в корзине поставщиков: ${count}. Обновлено ${badgeUpdatedAt()}`;
 117            badge.setAttribute('aria-label', description);
 118            badge.title = description;
 119            badge.style.display = 'inline-flex';
 120        } catch {
 121            // Индикатор необязателен: при сетевой ошибке оставляем его скрытым.
 122        }
 123    });
 124</script>