| | | 1 | | @{ |
| | 0 | 2 | | ViewData["Breadcrumb"] = new List<(string, string?)> { ("Админка", null) }; |
| | | 3 | | } |
| | | 4 | | |
| | | 5 | | <section class="admin-hero"> |
| | | 6 | | <h1>Панель администрирования</h1> |
| | | 7 | | <p class="subtitle">Управление ролями, пользователями и политиками доступа Studio2.</p> |
| | | 8 | | <p class="subtitle-help">Подтверждение email и ручное одобрение входа выполняются в карточке пользователя.</p> |
| | | 9 | | </section> |
| | | 10 | | |
| | | 11 | | <nav class="admin-quick-links"> |
| | | 12 | | <a asp-area="Admin" asp-controller="Users" asp-action="Index"><i class="bi bi-people"></i>Пользователи</a> |
| | | 13 | | <a asp-area="Admin" asp-controller="Roles" asp-action="Index"><i class="bi bi-person-badge"></i>Роли</a> |
| | | 14 | | <a asp-area="Admin" asp-controller="AuditLog" asp-action="Index"><i class="bi bi-journal-text"></i>Аудит</a> |
| | | 15 | | <a asp-area="Studio2" asp-controller="OrderControl" asp-action="Index"><i class="bi bi-bell"></i>Требует действий</a |
| | | 16 | | <a asp-area="Studio2" asp-controller="OrderControl" asp-action="Rules"><i class="bi bi-sliders"></i>Правила SLA</a> |
| | | 17 | | </nav> |
| | | 18 | | |
| | | 19 | | <section class="admin-intro"> |
| | | 20 | | <i class="bi bi-info-circle"></i> |
| | | 21 | | <div>Все разделы используют единые паттерны Studio2: hero, подсказки и безопасные подтверждения действий.</div> |
| | | 22 | | </section> |
| | | 23 | | |
| | | 24 | | <div class="admin-toolbar admin-surface mb-3"> |
| | | 25 | | <div class="text-muted small">Быстрый переход к проверке одобрения входа пользователей.</div> |
| | | 26 | | <a asp-controller="Users" asp-action="Index" class="btn btn-primary">Перейти к пользователям</a> |
| | | 27 | | </div> |
| | | 28 | | |
| | | 29 | | <div class="admin-kpi-grid"> |
| | | 30 | | <div class="admin-kpi-card"> |
| | | 31 | | <div class="label">Ролей</div> |
| | 0 | 32 | | <div class="value">@ViewBag.RolesCount</div> |
| | | 33 | | </div> |
| | | 34 | | <div class="admin-kpi-card"> |
| | | 35 | | <div class="label">Пользователей с ролями</div> |
| | 0 | 36 | | <div class="value">@ViewBag.UsersCount</div> |
| | | 37 | | </div> |
| | | 38 | | <div class="admin-kpi-card"> |
| | | 39 | | <div class="label">Страниц</div> |
| | 0 | 40 | | <div class="value">@ViewBag.PagesCount</div> |
| | | 41 | | </div> |
| | | 42 | | <div class="admin-kpi-card"> |
| | | 43 | | <div class="label">Колонок списка</div> |
| | 0 | 44 | | <div class="value">@ViewBag.FieldsCount</div> |
| | | 45 | | </div> |
| | | 46 | | <div class="admin-kpi-card"> |
| | | 47 | | <div class="label">Наборов данных</div> |
| | 0 | 48 | | <div class="value">@ViewBag.DataScopesCount</div> |
| | | 49 | | </div> |
| | | 50 | | <div class="admin-kpi-card"> |
| | | 51 | | <div class="label">Записей аудита</div> |
| | 0 | 52 | | <div class="value">@ViewBag.AuditCount</div> |
| | | 53 | | </div> |
| | | 54 | | </div> |
| | | 55 | | |
| | | 56 | | <div class="row g-3 mb-4"> |
| | | 57 | | <div class="col-12"> |
| | | 58 | | <div class="card admin-form-card"> |
| | | 59 | | <div class="card-header"> |
| | | 60 | | <h5 class="mb-0">Как устроена система доступа и ролей</h5> |
| | | 61 | | </div> |
| | | 62 | | <div class="card-body"> |
| | | 63 | | <p class="card-text"> |
| | | 64 | | Система контроля доступа основана на ролях (RBAC) и связывает следующие сущности: |
| | | 65 | | </p> |
| | | 66 | | <ul class="list-group list-group-flush"> |
| | | 67 | | <li class="list-group-item"><strong>Роли</strong> — наборы прав доступа к страницам и полям.</li> |
| | | 68 | | <li class="list-group-item"><strong>Пользователи</strong> — получают одну или несколько ролей.</li> |
| | | 69 | | <li class="list-group-item"><strong>Страницы</strong> — пункты меню, к которым можно предоставить до |
| | | 70 | | <li class="list-group-item"><strong>Поля</strong> — колонки в списках (например, заказов), доступ к |
| | | 71 | | <li class="list-group-item"><strong>Наборы данных (Data Scopes)</strong> — ограничивают доступ к зап |
| | | 72 | | </ul> |
| | | 73 | | <p class="card-text mt-3"> |
| | | 74 | | <em>Пример:</em> Пользователь с ролью "Менеджер" может видеть страницу "Заказы", поля "Номер" и "Ста |
| | | 75 | | </p> |
| | | 76 | | <div class="mt-4"> |
| | | 77 | | <h6>Схема взаимодействия:</h6> |
| | | 78 | | <div class="admin-scheme">Пользователи ↔ Роли ↔ { Страницы, Поля } |
| | | 79 | | ↓ |
| | | 80 | | Наборы данных (Data Scopes) → ограничивают записи</div> |
| | | 81 | | </div> |
| | | 82 | | </div> |
| | | 83 | | </div> |
| | | 84 | | </div> |
| | | 85 | | </div> |
| | | 86 | | |
| | | 87 | | @{ |
| | 0 | 88 | | var recentAudit = ((IEnumerable<OzonDomains.Models.Authorization.AuditLogEntry>?)ViewBag.RecentAudit ?? Array.Empty< |
| | 0 | 89 | | .Select(a => new { a.Id, a.Timestamp, a.ActorUserName, a.Action, a.EntityType, a.EntityId, a.IpAddress }); |
| | | 90 | | } |
| | 0 | 91 | | @await Html.PartialAsync("~/Areas/Studio2/Views/Shared/_BsxDirectoryGrid.cshtml", new BsxDirectoryGridViewModel("admRece |
| | | 92 | | <div class="mb-3"><a asp-controller="AuditLog" asp-action="Index" class="btn btn-sm btn-outline-secondary"><i class="bi |
| | 0 | 93 | | <script type="application/json" id="admRecentAuditData">@Html.Raw(System.Text.Json.JsonSerializer.Serialize(recentAudit, |
| | | 94 | | |
| | | 95 | | @section Scripts { |
| | | 96 | | <script> |
| | | 97 | | (function () { |
| | | 98 | | const A = window.AdminGrid, D = window.BsxDirectory, BG = window.BsxGrid; |
| | 0 | 99 | | const details = id => '@Url.Action("Details", "AuditLog")/' + id; |
| | | 100 | | A.grid({ |
| | | 101 | | key: 'admRecentAudit', prefix: 'admRecentAudit', rows: A.data('admRecentAuditData'), autoHeight: true, |
| | | 102 | | rowUrl: r => details(r.id), |
| | | 103 | | columnDefs: [ |
| | | 104 | | { colId: 'timestamp', field: 'timestamp', headerName: 'Время', width: 150, valueFormatter: p => BG.f |
| | | 105 | | D.setCol({ colId: 'actorUserName', field: 'actorUserName', headerName: 'Пользователь', minWidth: 160 |
| | | 106 | | D.setCol({ colId: 'action', field: 'action', headerName: 'Действие', minWidth: 150, flex: 1, cellRen |
| | | 107 | | D.setCol({ colId: 'entityType', field: 'entityType', headerName: 'Сущность', minWidth: 150, flex: 1 |
| | | 108 | | { colId: 'entityId', field: 'entityId', headerName: 'ID', width: 120 }, |
| | | 109 | | { colId: 'ipAddress', field: 'ipAddress', headerName: 'IP', width: 140 } |
| | | 110 | | ], |
| | | 111 | | gridOptions: { overlayNoRowsTemplate: '<span class="bsx-grid-empty">Пока нет событий</span>' } |
| | | 112 | | }); |
| | | 113 | | })(); |
| | | 114 | | </script> |
| | | 115 | | } |