mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
* UI: Move section navigation to sidebar * Scroll active nav link into view * Move CTAs to top right * Server Settings: Make Policies first page * Responsive table fixes * Spacing fixes * Add breadcrumb samples * store settings fixes * payment request fixes * updates pull payment title * adds invoice detail fix * updates server settings breadcrumbs + copy fix * Don't open Server Settings on Plugins page * Add breadcrumbs to pull payment views * adds breadcrumbs to account * server and store breadcrumb fixes * fixes access tokens * Fix payment processor breadcrumbs * fixes webhook 404 * Final touches * Fix test * Add breadcrumb for email rules page * Design system updates --------- Co-authored-by: dstrukt <gfxdsign@gmail.com>
127 lines
5.5 KiB
Text
127 lines
5.5 KiB
Text
@using BTCPayServer.Client
|
|
@using BTCPayServer.Views.Server
|
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
|
@using BTCPayServer.Abstractions.TagHelpers
|
|
@using BTCPayServer.Controllers
|
|
@using BTCPayServer.Views.Stores
|
|
@model UpdateRoleViewModel
|
|
@{
|
|
var role = Context.GetRouteValue("role") as string;
|
|
|
|
if (role == "create")
|
|
role = null;
|
|
|
|
var storeId = Context.GetRouteValue("storeId") as string;
|
|
if (storeId is null)
|
|
ViewData.SetActivePage(ServerNavPages.Roles, role is null ? "Create role" : "Update Role");
|
|
else
|
|
{
|
|
ViewData.SetActivePage(StoreNavPages.Roles, role is null ? "Create role" : "Update Role");
|
|
}
|
|
var storePolicies = Policies.AllPolicies.Where(Policies.IsStorePolicy).ToArray();
|
|
}
|
|
|
|
<form method="post">
|
|
<div class="sticky-header">
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item">
|
|
<a asp-action="ListRoles" asp-route-storeId="@storeId">Roles</a>
|
|
</li>
|
|
<li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
|
|
</ol>
|
|
<h2>@ViewData["Title"]</h2>
|
|
</nav>
|
|
<button id="Save" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-xxl-constrain">
|
|
@if (!ViewContext.ModelState.IsValid)
|
|
{
|
|
<div asp-validation-summary="ModelOnly"></div>
|
|
}
|
|
<div class="form-group" style="max-width:320px">
|
|
<label asp-for="Role" class="form-label"></label>
|
|
@if (role == null)
|
|
{
|
|
<input asp-for="Role" required="required" class="form-control" />
|
|
}
|
|
else
|
|
{
|
|
<input type="hidden" asp-for="Role" />
|
|
<input required="required" class="form-control" disabled value="@Model.Role" />
|
|
}
|
|
<span asp-validation-for="Role" class="text-danger"></span>
|
|
</div>
|
|
|
|
<h4 class="mt-4 mb-3">Permissions</h4>
|
|
<select multiple="multiple" asp-for="Policies" class="form-select hide-when-js">
|
|
@foreach (var policy in storePolicies)
|
|
{
|
|
<option value="@policy" class="text-truncate" asp-selected="@(Model.Policies?.Contains(policy) ?? false)">@policy</option>
|
|
}
|
|
</select>
|
|
<div class="list-group mb-2">
|
|
@{
|
|
var storePolicyMap = Permission.PolicyMap.Where(pair => Policies.IsStorePolicy(pair.Key)).ToArray();
|
|
var topMostPolicies = storePolicyMap.Where(pair => !storePolicyMap.Any(valuePair => valuePair.Value.Contains(pair.Key)));
|
|
@foreach (var policy in topMostPolicies)
|
|
{
|
|
RenderTree(policy, storePolicyMap, Model.Policies.Contains(policy.Key));
|
|
}
|
|
}
|
|
</div>
|
|
<span asp-validation-for="Policies" class="text-danger"></span>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
@{
|
|
void RenderTree(KeyValuePair<string, HashSet<string>> policy, KeyValuePair<string, HashSet<string>>[] storePolicyMap, bool isChecked)
|
|
{
|
|
<div class="form-check mb-0">
|
|
<input type="checkbox" class="form-check-input policy-cb" checked="@isChecked" value="@policy.Key" id="Policy-@policy.Key.Replace(".", "_")" />
|
|
<label class="h5 fw-semibold form-check-label mb-1" for="Policy-@policy.Key.Replace(".", "_")" data-bs-toggle="tooltip" title="@policy.Key">
|
|
@UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions[policy.Key].Title
|
|
</label>
|
|
<p class="text-muted">@UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions[policy.Key].Description</p>
|
|
@if (policy.Value?.Any() is true)
|
|
{
|
|
<div class="list-group">
|
|
@foreach (var subPolicy in policy.Value)
|
|
{
|
|
var match = storePolicyMap.SingleOrDefault(pair => pair.Key == subPolicy);
|
|
RenderTree(match.Key is not null ? match : new KeyValuePair<string, HashSet<string>>(subPolicy, null), storePolicyMap, !isChecked && Model.Policies.Contains(subPolicy));
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
}
|
|
<script>
|
|
function handleCheckboxChange(element) {
|
|
const { checked, value: policy } = element;
|
|
const policySelect = document.getElementById('Policies');
|
|
const subPolicies = element.parentElement.querySelectorAll(`.list-group .policy-cb:not([value="${policy}"])`);
|
|
|
|
policySelect.querySelector(`option[value="${policy}"]`).selected = checked;
|
|
subPolicies.forEach(subPolicy => {
|
|
subPolicy.checked = checked? false : subPolicy.checked;
|
|
if (checked){
|
|
subPolicy.setAttribute("disabled", "disabled");
|
|
} else {
|
|
subPolicy.removeAttribute("disabled");
|
|
}
|
|
policySelect.querySelector(`option[value="${subPolicy.value}"]`).selected = subPolicy.checked;
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.querySelectorAll(".policy-cb:checked").forEach(handleCheckboxChange);
|
|
|
|
delegate('change', '.policy-cb', event => {
|
|
handleCheckboxChange(event.target);
|
|
});
|
|
});
|
|
</script>
|