< Summary

Information
Class: OzonOrdersWeb.WebServices.Sidebar.SidebarNavigationCatalog
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Sidebar/SidebarNavigationCatalog.cs
Line coverage
100%
Covered lines: 161
Uncovered lines: 0
Coverable lines: 161
Total lines: 203
Line coverage: 100%
Branch coverage
100%
Covered branches: 8
Total branches: 8
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
For(...)100%22100%
get_AllIds()100%11100%
Flatten()100%66100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Sidebar/SidebarNavigationCatalog.cs

#LineLine coverage
 1namespace OzonOrdersWeb.WebServices.Sidebar;
 2
 3/// <summary>Узел бокового меню: группа (уровень 0), подгруппа (уровень 1) или ссылка.</summary>
 4public sealed record SidebarNode(
 5    string Id,
 6    string Title,
 7    string Icon,
 8    string? Href = null,
 9    string? Keywords = null,
 10    string? BadgeId = null,
 11    bool External = false,
 12    IReadOnlyList<SidebarNode>? Children = null)
 13{
 14    public bool IsLeaf => Href is not null;
 15}
 16
 17/// <summary>
 18/// Дерево бокового меню. Идентификаторы узлов стабильны — по ним пользовательские настройки
 19/// (избранное, порядок, свёрнутые группы) хранятся в <c>UserSidebarSettings</c>; переименование
 20/// или перенос пункта не теряет их, а удаление узла просто делает его настройки невидимыми.
 21/// </summary>
 22public static class SidebarNavigationCatalog
 23{
 124    private static readonly IReadOnlyList<SidebarNode> Common = new[]
 125    {
 126        new SidebarNode("orders", "Заказы", "fa-table", Children: new[]
 127        {
 128            new SidebarNode("orders.grid", "Таблица заказов", "fa-table-cells", "/Studio2/OrdersGrid", "grid ag-grid таб
 129            new SidebarNode("orders.list", "Список заказов", "fa-list", "/Studio2/Orders", BadgeId: "sidebar-order-contr
 130            new SidebarNode("orders.upload", "Загрузка заказов", "fa-cloud-upload-alt", Children: new[]
 131            {
 132                new SidebarNode("orders.upload.api", "Из API / файла", "fa-cloud-arrow-down", "/Studio2/Orders/Upload"),
 133                new SidebarNode("orders.upload.file", "Только файл", "fa-file-arrow-up", "/Studio2/Orders/UploadFile"),
 134                new SidebarNode("orders.upload.percentage", "Проценка из отчёта Ozon", "fa-percent", "/Studio2/Percentag
 135            }),
 136            new SidebarNode("orders.work", "Работа с заказом", "fa-pen-to-square", Children: new[]
 137            {
 138                new SidebarNode("orders.history", "История заказов", "fa-clock-rotate-left", "/Studio2/OrderHistory"),
 139                new SidebarNode("orders.control", "Контроль заказов", "fa-list-check", "/Studio2/OrderControl"),
 140                new SidebarNode("orders.labels", "Этикетки", "fa-tags", "/Studio2/Labels"),
 141                new SidebarNode("orders.assembly", "Лист подбора", "fa-clipboard-list", "/Studio2/AssemblyList"),
 142                new SidebarNode("orders.assembly.keys", "Ключи доступа к листам", "fa-key", "/Studio2/AssemblyListAccess
 143            }),
 144            new SidebarNode("orders.docs", "Документы и операции", "fa-file-invoice", Children: new[]
 145            {
 146                new SidebarNode("orders.transactions", "Журнал документов", "fa-book", "/Studio2/Transaction"),
 147                new SidebarNode("orders.transaction.jobs", "Задания транзакций", "fa-diagram-project", "/Studio2/Transac
 148                new SidebarNode("orders.time.tracking", "Трекинг времени", "fa-stopwatch", "/Studio2/TransactionTimeTrac
 149            }),
 150            new SidebarNode("orders.returns", "Возвраты и архив", "fa-box-archive", Children: new[]
 151            {
 152                new SidebarNode("orders.archive", "Архив заказов", "fa-box-archive", "/Studio2/OrderArchive"),
 153                new SidebarNode("orders.returns.ozon", "Возвраты Ozon", "fa-rotate-left", "/Studio2/OrderReturns"),
 154                new SidebarNode("orders.returns.journal", "Журнал возвратов", "fa-receipt", "/Studio2/Orders/Returns"),
 155                new SidebarNode("orders.returns.ozon.external", "Возвраты озон", "fa-arrow-up-right-from-square", "https
 156            }),
 157        }),
 158        new SidebarNode("purchases", "Закупки и склады", "fa-cart-shopping", Children: new[]
 159        {
 160            new SidebarNode("purchases.cart", "Заказы и корзины поставщиков", "fa-cart-shopping", "/Studio2/SupplierCart
 161            new SidebarNode("purchases.bitrix.stock", "Остатки 1С → Ozon", "fa-boxes-stacked", "/Studio2/BitrixStock?tab
 162            new SidebarNode("purchases.warehouse.stock", "Остатки склада", "fa-warehouse", "/Studio2/WarehouseStock"),
 163            new SidebarNode("purchases.onec.stock", "Остатки 1С по складам", "fa-pallet", "/Studio2/OneCStock", "1с odat
 164            new SidebarNode("purchases.match.files", "Сравнение файлов", "fa-code-compare", "/Studio2/MatchFileData"),
 165            new SidebarNode("purchases.interparts.currency", "Курсы Interparts", "fa-coins", "/Studio2/InterpartsSutCurr
 166            new SidebarNode("purchases.interparts.access", "Доступ к складам Interparts", "fa-warehouse", "/Studio2/Inte
 167        }),
 168        new SidebarNode("settlements", "Взаиморасчёты", "fa-scale-balanced", "/Settlements", "взаиморасчеты поставщики с
 169        new SidebarNode("analytics", "Аналитика и коммуникации", "fa-chart-line", Children: new[]
 170        {
 171            new SidebarNode("analytics.products", "Анализ продуктов", "fa-chart-pie", "/Studio2/ProductAnalysis"),
 172            new SidebarNode("analytics.prices", "История цен", "fa-chart-line", "/Studio2/PriceHistory"),
 173            new SidebarNode("analytics.ozon.chat", "Чаты Ozon", "fa-comments", "/Studio2/OzonChat", BadgeId: "sidebar-oz
 174            new SidebarNode("analytics.ai", "AI Ассистент", "fa-robot", "/Studio2/AiAssistant"),
 175        }),
 176        new SidebarNode("integrations", "Интеграции", "fa-plug", Children: new[]
 177        {
 178            new SidebarNode("integrations.onec", "1С", "fa-database", Children: new[]
 179            {
 180                new SidebarNode("integrations.onec.fresh", "1С Fresh", "fa-database", "/OneCFresh/Home"),
 181                new SidebarNode("integrations.onec.m2", "М2 Проект", "fa-arrow-up-right-from-square", "https://1c.mpsupe
 182            }),
 183            new SidebarNode("integrations.mail", "Почта и файлы", "fa-envelope-open-text", Children: new[]
 184            {
 185                new SidebarNode("integrations.mail.results", "Импорт заказов из почты", "fa-inbox", "/Studio2/EmailRouti
 186                new SidebarNode("integrations.mail.rules", "Правила импорта", "fa-filter", "/Studio2/EmailRoutingRules")
 187                new SidebarNode("integrations.mail.scheduler", "Планировщик импорта", "fa-calendar-days", "/Studio2/Emai
 188                new SidebarNode("integrations.files.records", "Записи загрузки файлов", "fa-file-lines", "/Studio2/FileU
 189                new SidebarNode("integrations.files.mappings", "Сопоставления Excel / CSV", "fa-table-columns", "/Studio
 190            }),
 191            new SidebarNode("integrations.feeds", "Фиды", "fa-chart-line", Children: new[]
 192            {
 193                new SidebarNode("integrations.feeds.stat", "Фиды", "fa-chart-line", "/Studio2/FidStat"),
 194                new SidebarNode("integrations.feeds.links", "Связи с фидом", "fa-link", "/Studio2/FidSupplierLink"),
 195            }),
 196            new SidebarNode("integrations.ozon", "Ozon", "fa-store", Children: new[]
 197            {
 198                new SidebarNode("integrations.ozon.webhooks", "Push-уведомления Ozon", "fa-bell", "/Studio2/OzonWebhookL
 199                new SidebarNode("integrations.ozon.clients", "Клиенты Ozon", "fa-users", "/Studio2/OzonClients"),
 1100            }),
 1101            new SidebarNode("integrations.suppliers", "Поставщики API", "fa-truck-fast", Children: new[]
 1102            {
 1103                new SidebarNode("integrations.suppliers.settings", "Настройки API-поставщиков", "fa-sliders", "/Studio2/
 1104                new SidebarNode("integrations.suppliers.audit", "Логи запросов API", "fa-file-waveform", "/Studio2/ApiSu
 1105                new SidebarNode("integrations.suppliers.consoles", "Консоли поставщиков", "fa-terminal", "/Armtek", "кон
 1106            }),
 1107        }),
 1108    };
 109
 1110    private static readonly IReadOnlyList<SidebarNode> AdminOnly = new[]
 1111    {
 1112        new SidebarNode("catalogs", "Справочники", "fa-book-open", Children: new[]
 1113        {
 1114            new SidebarNode("catalogs.goods", "Товары и контрагенты", "fa-box", Children: new[]
 1115            {
 1116                new SidebarNode("catalogs.products", "Товары", "fa-box", "/Studio2/Products"),
 1117                new SidebarNode("catalogs.suppliers", "Поставщики", "fa-truck", "/Studio2/Supplier"),
 1118                new SidebarNode("catalogs.manufacturers", "Производители Ozon", "fa-industry", "/Studio2/Manufacturers")
 1119                new SidebarNode("catalogs.producers", "Производители ET", "fa-industry", "/Studio2/Producers"),
 1120                new SidebarNode("catalogs.excluded.articles", "Исключённые артикулы", "fa-ban", "/Studio2/ExcludedArticl
 1121            }),
 1122            new SidebarNode("catalogs.warehouses", "Склады", "fa-warehouse", Children: new[]
 1123            {
 1124                new SidebarNode("catalogs.warehouse", "Склады", "fa-warehouse", "/Studio2/Warehouse"),
 1125                new SidebarNode("catalogs.warehouse.mapping", "Связи складов", "fa-shuffle", "/Studio2/WarehouseMapping"
 1126                new SidebarNode("catalogs.warehouse.excluded", "Исключённые склады Bitrix", "fa-circle-minus", "/Studio2
 1127            }),
 1128            new SidebarNode("catalogs.statuses", "Статусы", "fa-tag", Children: new[]
 1129            {
 1130                new SidebarNode("catalogs.status.app", "Статусы заказов", "fa-tag", "/Studio2/AppStatus"),
 1131                new SidebarNode("catalogs.status.transitions", "Переходы статусов", "fa-arrow-right-arrow-left", "/Studi
 1132                new SidebarNode("catalogs.status.supplier", "Статусы поставщиков", "fa-tags", "/Studio2/SupplierStatus")
 1133                new SidebarNode("catalogs.status.client", "Статусы клиентов", "fa-user-tag", "/Studio2/ClientStatus"),
 1134            }),
 1135            new SidebarNode("catalogs.expense.types", "Типы расходов", "fa-wallet", "/Studio2/ExpenseType"),
 1136        }),
 1137        new SidebarNode("admin", "Администрирование", "fa-user-shield", Children: new[]
 1138        {
 1139            new SidebarNode("admin.center", "Центр администрирования", "fa-user-shield", "/Admin/Home/Index"),
 1140            new SidebarNode("admin.access.profiles", "Профили доступа", "fa-user-lock", "/Studio2/AccessProfiles"),
 1141            new SidebarNode("admin.data.scopes", "Наборы областей данных", "fa-filter", "/Admin/DataScopes/Index"),
 1142            new SidebarNode("admin.partners", "Кабинет партнёров", "fa-handshake", "/PartnerOffice/Partners", "партнёры 
 1143            new SidebarNode("admin.job.logs", "Журнал задач", "fa-clipboard-list", "/Studio2/JobLogs", BadgeId: "sidebar
 1144            new SidebarNode("admin.config", "Конфигурация и база данных", "fa-screwdriver-wrench", "/Studio2/Config/Logi
 1145        }),
 1146        new SidebarNode("system", "Система", "fa-server", Children: new[]
 1147        {
 1148            new SidebarNode("system.hangfire.stats", "Статистика Hangfire", "fa-chart-bar", "/Studio2/HangfireQueue"),
 1149            new SidebarNode("system.hangfire", "Задачи Hangfire", "fa-tasks", "/Hangfire", External: true),
 1150            new SidebarNode("system.autoload", "Автозагрузка", "fa-robot", "/Studio2/Job"),
 1151        }),
 1152    };
 153
 154    /// <summary>
 155    /// Меню раздела /Admin (_AdminSidebar): своё дерево, настройки пользователя — под префиксом
 156    /// <see cref="AdminKeyPrefix"/>, чтобы избранное и порядок не смешивались с меню Studio2.
 157    /// </summary>
 1158    public static readonly IReadOnlyList<SidebarNode> Admin = new[]
 1159    {
 1160        new SidebarNode("adm.main", "Основное", "fa-gauge", Children: new[]
 1161        {
 1162            new SidebarNode("adm.home", "Обзор", "fa-gauge", "/Admin/Home/Index", "админка панель обзор kpi"),
 1163            new SidebarNode("adm.studio2", "В основное приложение", "fa-arrow-up-right-from-square", "/Studio2/OrdersGri
 1164        }),
 1165        new SidebarNode("adm.access", "Права доступа", "fa-user-shield", Children: new[]
 1166        {
 1167            new SidebarNode("adm.roles", "Роли", "fa-id-badge", "/Admin/Roles/Index", "роли матрица права страницы поля"
 1168            new SidebarNode("adm.users", "Пользователи", "fa-users", "/Admin/Users/Index", "пользователи вход одобрение 
 1169        }),
 1170        new SidebarNode("adm.registry", "Реестры", "fa-layer-group", Children: new[]
 1171        {
 1172            new SidebarNode("adm.pages", "Страницы", "fa-window-restore", "/Admin/Pages/Index", "страницы реестр синхрон
 1173            new SidebarNode("adm.fields", "Колонки заказов", "fa-table-columns", "/Admin/Fields/Index", "поля колонки or
 1174            new SidebarNode("adm.scopes", "Наборы данных", "fa-filter", "/Admin/DataScopes/Index", "наборы данных data s
 1175        }),
 1176        new SidebarNode("adm.control", "Контроль", "fa-clipboard-check", Children: new[]
 1177        {
 1178            new SidebarNode("adm.audit", "Журнал изменений", "fa-book", "/Admin/AuditLog/Index", "аудит журнал изменений
 1179            new SidebarNode("adm.order.control", "Требует действий", "fa-bell", "/Studio2/OrderControl"),
 1180            new SidebarNode("adm.order.rules", "Правила SLA", "fa-sliders", "/Studio2/OrderControl/Rules"),
 1181        }),
 1182    };
 183
 184    /// <summary>Префикс ключей настроек меню /Admin в <c>UserSidebarSettings</c>.</summary>
 185    public const string AdminKeyPrefix = "admin.";
 186
 187    /// <summary>Дерево для пользователя: администраторы видят дополнительные группы.</summary>
 188    public static IReadOnlyList<SidebarNode> For(bool isAdmin) =>
 3189        isAdmin ? Common.Concat(AdminOnly).ToArray() : Common;
 190
 191    /// <summary>Все идентификаторы узлов (для валидации сохраняемых настроек).</summary>
 85192    public static IReadOnlySet<string> AllIds { get; } = Flatten(Common.Concat(AdminOnly)).Select(n => n.Id).ToHashSet(S
 193
 194    public static IEnumerable<SidebarNode> Flatten(IEnumerable<SidebarNode> nodes)
 195    {
 624196        foreach (var node in nodes)
 197        {
 252198            yield return node;
 252199            if (node.Children is null) continue;
 1167200            foreach (var child in Flatten(node.Children)) yield return child;
 57201        }
 60202    }
 203}