Compare commits

...

5 Commits

Author SHA1 Message Date
5b31d4de20 v1.13: Update changelog (#5879) 2024-03-28 22:40:18 +09:00
14f8c73b08 POS: Increase size of quantity buttons (#5877)
Closes #5873.
2024-03-28 09:01:56 +09:00
529075f64c Make "Employee" default role on store settings (#5874)
* Refactoring: Use property rather than injecting StoreRepository

* Update info text

* Make "Employee" default role on store settings

Closes #5867.
2024-03-28 09:01:34 +09:00
dba102e74f Template Editor: Fix mobile view (#5871)
Fixes #5869.
2024-03-27 19:20:49 +09:00
0f3f8b6bf9 Contact Us improvements (#5872)
* Add contact link to sidebar

Closes #5866.

* Obfuscate contact email on public pages

Closes #5870.

* Fix
2024-03-27 19:19:39 +09:00
15 changed files with 135 additions and 88 deletions

View File

@ -296,6 +296,15 @@
</li>
</ul>
</li>
@if (!string.IsNullOrWhiteSpace(Model.ContactUrl))
{
<li class="nav-item">
<a href="@Model.ContactUrl" class="nav-link" id="Nav-ContactUs">
<vc:icon symbol="contact"/>
<span>Contact Us</span>
</a>
</li>
}
</ul>
}
</nav>

View File

@ -29,6 +29,8 @@ namespace BTCPayServer.Components.MainNav
private readonly UserManager<ApplicationUser> _userManager;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly CustodianAccountRepository _custodianAccountRepository;
private readonly SettingsRepository _settingsRepository;
public PoliciesSettings PoliciesSettings { get; }
public MainNav(
AppService appService,
@ -38,6 +40,7 @@ namespace BTCPayServer.Components.MainNav
UserManager<ApplicationUser> userManager,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
CustodianAccountRepository custodianAccountRepository,
SettingsRepository settingsRepository,
PoliciesSettings policiesSettings)
{
_storeRepo = storeRepo;
@ -47,13 +50,19 @@ namespace BTCPayServer.Components.MainNav
_storesController = storesController;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_custodianAccountRepository = custodianAccountRepository;
_settingsRepository = settingsRepository;
PoliciesSettings = policiesSettings;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var store = ViewContext.HttpContext.GetStoreData();
var vm = new MainNavViewModel { Store = store };
var serverSettings = await _settingsRepository.GetSettingAsync<ServerSettings>() ?? new ServerSettings();
var vm = new MainNavViewModel
{
Store = store,
ContactUrl = serverSettings.ContactUrl
};
#if ALTCOINS
vm.AltcoinsBuild = true;
#endif
@ -92,7 +101,5 @@ namespace BTCPayServer.Components.MainNav
}
private string UserId => _userManager.GetUserId(HttpContext.User);
public PoliciesSettings PoliciesSettings { get; }
}
}

View File

@ -13,6 +13,7 @@ namespace BTCPayServer.Components.MainNav
public CustodianAccountData[] CustodianAccounts { get; set; }
public bool AltcoinsBuild { get; set; }
public int ArchivedAppsCount { get; set; }
public string ContactUrl { get; set; }
}
public class StoreApp

View File

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@ -16,13 +15,12 @@ namespace BTCPayServer.Controllers
{
[Route("server/roles")]
public async Task<IActionResult> ListRoles(
[FromServices] StoreRepository storeRepository,
RolesViewModel model,
string sortOrder = null
)
{
var roles = await storeRepository.GetStoreRoles(null, true);
var defaultRole = (await storeRepository.GetDefaultRole()).Role;
var roles = await _StoreRepository.GetStoreRoles(null, true);
var defaultRole = (await _StoreRepository.GetDefaultRole()).Role;
model ??= new RolesViewModel();
model.DefaultRole = defaultRole;
@ -44,32 +42,26 @@ namespace BTCPayServer.Controllers
}
[HttpGet("server/roles/{role}")]
public async Task<IActionResult> CreateOrEditRole(
[FromServices] StoreRepository storeRepository,
string role)
public async Task<IActionResult> CreateOrEditRole(string role)
{
if (role == "create")
{
ModelState.Remove(nameof(role));
return View(new UpdateRoleViewModel());
}
else
var roleData = await _StoreRepository.GetStoreRole(new StoreRoleId(role));
if (roleData == null)
return NotFound();
return View(new UpdateRoleViewModel
{
var roleData = await storeRepository.GetStoreRole(new StoreRoleId(role));
if (roleData == null)
return NotFound();
return View(new UpdateRoleViewModel()
{
Policies = roleData.Permissions,
Role = roleData.Role
});
}
Policies = roleData.Permissions,
Role = roleData.Role
});
}
[HttpPost("server/roles/{role}")]
public async Task<IActionResult> CreateOrEditRole(
[FromServices] StoreRepository storeRepository,
[FromRoute] string role, UpdateRoleViewModel viewModel)
public async Task<IActionResult> CreateOrEditRole([FromRoute] string role, UpdateRoleViewModel viewModel)
{
string successMessage = null;
if (role == "create")
@ -80,7 +72,7 @@ namespace BTCPayServer.Controllers
else
{
successMessage = "Role updated";
var storeRole = await storeRepository.GetStoreRole(new StoreRoleId(role));
var storeRole = await _StoreRepository.GetStoreRole(new StoreRoleId(role));
if (storeRole == null)
return NotFound();
}
@ -90,7 +82,7 @@ namespace BTCPayServer.Controllers
return View(viewModel);
}
var r = await storeRepository.AddOrUpdateStoreRole(new StoreRoleId(role), viewModel.Policies);
var r = await _StoreRepository.AddOrUpdateStoreRole(new StoreRoleId(role), viewModel.Policies);
if (r is null)
{
TempData.SetStatusMessageModel(new StatusMessageModel()
@ -113,11 +105,9 @@ namespace BTCPayServer.Controllers
[HttpGet("server/roles/{role}/delete")]
public async Task<IActionResult> DeleteRole(
[FromServices] StoreRepository storeRepository,
string role)
public async Task<IActionResult> DeleteRole(string role)
{
var roleData = await storeRepository.GetStoreRole(new StoreRoleId(role), true);
var roleData = await _StoreRepository.GetStoreRole(new StoreRoleId(role), true);
if (roleData == null)
return NotFound();
@ -131,12 +121,10 @@ namespace BTCPayServer.Controllers
}
[HttpPost("server/roles/{role}/delete")]
public async Task<IActionResult> DeleteRolePost(
[FromServices] StoreRepository storeRepository,
string role)
public async Task<IActionResult> DeleteRolePost(string role)
{
var roleId = new StoreRoleId(role);
var roleData = await storeRepository.GetStoreRole(roleId, true);
var roleData = await _StoreRepository.GetStoreRole(roleId, true);
if (roleData == null)
return NotFound();
if (roleData.IsUsed is true)
@ -144,7 +132,7 @@ namespace BTCPayServer.Controllers
return BadRequest();
}
var errorMessage = await storeRepository.RemoveStoreRole(roleId);
var errorMessage = await _StoreRepository.RemoveStoreRole(roleId);
if (errorMessage is null)
{
@ -159,19 +147,16 @@ namespace BTCPayServer.Controllers
}
[HttpGet("server/roles/{role}/default")]
public async Task<IActionResult> SetDefaultRole(
[FromServices] StoreRepository storeRepository,
string role)
public async Task<IActionResult> SetDefaultRole(string role)
{
var resolved = await storeRepository.ResolveStoreRoleId(null, role);
var resolved = await _StoreRepository.ResolveStoreRoleId(null, role);
if (resolved is null)
{
TempData[WellKnownTempData.ErrorMessage] = "Role could not be set as default";
}
else
{
await storeRepository.SetDefaultRole(role);
await _StoreRepository.SetDefaultRole(role);
TempData[WellKnownTempData.SuccessMessage] = "Role set default";
}

View File

@ -45,7 +45,7 @@ namespace BTCPayServer.Controllers
[HttpGet("{storeId}/users")]
public async Task<IActionResult> StoreUsers()
{
var vm = new StoreUsersViewModel { Role = StoreRoleId.Guest.Role };
var vm = new StoreUsersViewModel { Role = StoreRoleId.Employee.Role };
await FillUsers(vm);
return View(vm);
}

View File

@ -163,13 +163,13 @@
<span class="badge text-bg-warning inventory" v-if="item.inventory">
{{ item.inventory > 0 ? `${item.inventory} left` : "Sold out" }}
</span>
<div class="d-flex align-items-center gap-2">
<button type="button" v-on:click="updateQuantity(item.id, -1)" class="btn btn-light p-1 rounded-pill d-flex align-items-center justify-content-center">
<vc:icon symbol="minus" />
<div class="d-flex align-items-center gap-2 quantities">
<button type="button" v-on:click="updateQuantity(item.id, -1)" class="btn btn-minus">
<span><vc:icon symbol="minus" /></span>
</button>
<input class="form-control hide-number-spin w-50px" type="number" placeholder="Qty" min="1" step="1" :max="item.inventory" v-model.number="item.count">
<button type="button" v-on:click="updateQuantity(item.id, +1)" class="btn btn-light p-1 rounded-pill d-flex align-items-center justify-content-center">
<vc:icon symbol="plus" />
<input class="form-control hide-number-spin w-50px text-center" type="number" placeholder="Qty" min="1" step="1" :max="item.inventory" v-model.number="item.count">
<button type="button" v-on:click="updateQuantity(item.id, +1)" class="btn btn-plus">
<span><vc:icon symbol="plus" /></span>
</button>
</div>
</div>

View File

@ -115,7 +115,7 @@
<div class="d-flex align-items-start w-100 gap-3">
@if (!string.IsNullOrWhiteSpace(item.Image))
{
<div class="img">
<div class="img d-none d-sm-block">
<img src="@item.Image" alt="@item.Title" asp-append-version="true" />
</div>
}
@ -138,13 +138,13 @@
}
</div>
</div>
<div class="d-flex align-items-center gap-2 ms-auto" v-if="inStock(@index)">
<button type="button" v-on:click="updateQuantity(`@Safe.Raw(item.Id)`, -1, true)" class="btn btn-light p-1 rounded-pill d-flex align-items-center justify-content-center btn-minus" :disabled="getQuantity(`@Safe.Raw(item.Id)`) <= 0">
<vc:icon symbol="minus" />
<div class="d-flex align-items-center gap-2 ms-auto quantities">
<button type="button" v-on:click="updateQuantity(`@Safe.Raw(item.Id)`, -1, true)" class="btn btn-minus" :disabled="getQuantity(`@Safe.Raw(item.Id)`) <= 0">
<span><vc:icon symbol="minus" /></span>
</button>
<div class="quantity text-center fs-6" style="width:2rem">{{ getQuantity(`@Safe.Raw(item.Id)`) }}</div>
<button type="button" v-on:click="updateQuantity(`@Safe.Raw(item.Id)`, +1, true)" class="btn btn-light p-1 rounded-pill d-flex align-items-center justify-content-center btn-plus">
<vc:icon symbol="plus" />
<div class="quantity text-center fs-5" style="width:2rem">{{ getQuantity(`@Safe.Raw(item.Id)`) }}</div>
<button type="button" v-on:click="updateQuantity(`@Safe.Raw(item.Id)`, +1, true)" class="btn btn-plus" :disabled="!inStock(@index)">
<span><vc:icon symbol="plus" /></span>
</button>
</div>
</div>

View File

@ -153,17 +153,11 @@
<div class="col-xl-5 offcanvas-xl offcanvas-end" tabindex="-1" ref="editorOffcanvas">
<div class="offcanvas-header p-3">
<h5 class="offcanvas-title">Edit Item</h5>
<button type="button" class="btn-close" aria-label="Close" v-on:click="hideOffcanvas">
<vc:icon symbol="close" />
</button>
<button type="button" class="btn btn-sm rounded-pill" :class="{ 'btn-primary': itemChanged, 'btn-outline-secondary': !itemChanged }" v-on:click="hideOffcanvas" v-text="itemChanged ? 'Apply' : 'Close'"></button>
</div>
<div class="offcanvas-body p-3 p-xl-0">
<item-editor ref="itemEditor" :item="selectedItem" class="bg-tile w-100 p-xl-4 rounded" />
</div>
<div class="offcanvas-header p-3">
<button class="btn btn-primary" type="button" v-on:click="() => { $refs.itemEditor.apply(); hideOffcanvas() }">Apply and close</button>
<button class="btn btn-secondary" type="button" v-on:click="hideOffcanvas">Cancel</button>
</div>
</div>
</div>
</div>

View File

@ -1,13 +1,19 @@
@using BTCPayServer.Services
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Components.MainLogo
@using System.Text
@inject SettingsRepository SettingsRepository
@{
Layout = "_LayoutSimple";
ViewBag.ShowTitle ??= true;
ViewBag.ShowLeadText ??= false;
// obfuscate email on public page, decode via JS (see below)
var settings = await SettingsRepository.GetSettingAsync<ServerSettings>() ?? new ServerSettings();
var contactUrl = settings.ContactUrl;
if (contactUrl?.StartsWith("mailto:") is true)
{
contactUrl = Convert.ToBase64String(Encoding.UTF8.GetBytes(contactUrl));
}
}
@section PageHeadContent {
@ -64,11 +70,20 @@
@RenderBody()
</div>
@if (!string.IsNullOrWhiteSpace(settings.ContactUrl))
@if (!string.IsNullOrWhiteSpace(contactUrl))
{
<p class="text-center mt-n5 mb-5 pt-2">
<a class="text-secondary" href="@settings.ContactUrl" id="ContactLink">Contact Us</a>
<a class="text-secondary" href="@contactUrl" id="ContactLink">Contact Us</a>
</p>
@if (contactUrl != settings.ContactUrl)
{
<script>
(function() {
const link = document.getElementById('ContactLink')
link.setAttribute('href', atob(link.getAttribute('href')))
})()
</script>
}
}
<div class="row justify-content-center mt-5">

View File

@ -4,13 +4,13 @@
@using BTCPayServer.Client
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model StoreUsersViewModel
@inject IScopeProvider ScopeProvider
@inject StoreRepository StoreRepository
@{
var storeId = Context.GetStoreData().Id;
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePage(StoreNavPages.Users, "Store Users", Context.GetStoreData().Id);
ViewData.SetActivePage(StoreNavPages.Users, "Store Users", storeId);
var roles = new SelectList(
await StoreRepository.GetStoreRoles(ScopeProvider.GetCurrentStoreId()),
await StoreRepository.GetStoreRoles(storeId),
nameof(StoreRepository.StoreRole.Id), nameof(StoreRepository.StoreRole.Role),
Model.Role);
}
@ -26,8 +26,9 @@
<div class="col-xxl-constrain">
<h3 class="mb-3">@ViewData["Title"]</h3>
<p>
Give other registered BTCPay Server users access to your store.<br />
Guests will not be able to see or modify the store settings.
Give other registered BTCPay Server users access to your store. See the
<a asp-controller="UIStores" asp-action="ListRoles" asp-route-storeId="@storeId">roles</a>
for granted permissions.
</p>
@if (!ViewContext.ModelState.IsValid)

View File

@ -26,6 +26,7 @@
<symbol id="home" viewBox="0 0 24 24" fill="none"><path d="M15.6923 19.3845H8.30766C6.27689 19.3845 4.61536 17.7229 4.61536 15.6922V8.30754C4.61536 6.27677 6.27689 4.61523 8.30766 4.61523H15.6923C17.723 4.61523 19.3846 6.27677 19.3846 8.30754V15.6922C19.3846 17.7229 17.723 19.3845 15.6923 19.3845Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10"/><path d="M7.56921 11.938H9.04614L10.5846 14.1534L13.3538 9.72266L14.8923 11.938H16.2461" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></symbol>
<symbol id="hot-wallet" viewBox="0 0 32 32" fill="none"><path d="M26.5362 7.08746H25.9614V3.25512C25.9614 2.10542 25.3865 1.14734 24.6201 0.572488C23.8536 -0.00236247 22.7039 -0.193979 21.7458 -0.00236246L4.11707 5.36291C2.00929 5.93776 0.667969 7.85392 0.667969 9.96171V12.0695V12.836V27.3988C0.667969 30.0815 2.77575 32.1893 5.45839 32.1893H26.5362C29.2189 32.1893 31.3267 30.0815 31.3267 27.3988V12.0695C31.3267 9.38686 29.2189 7.08746 26.5362 7.08746ZM4.69192 7.08746L22.129 1.91381C22.5123 1.72219 23.0871 1.91381 23.4704 2.10542C23.8536 2.29704 24.0452 2.87189 24.0452 3.25512V7.08746H5.45839C4.88354 7.08746 4.5003 7.27908 3.92545 7.47069C4.11707 7.27908 4.5003 7.08746 4.69192 7.08746ZM29.4105 27.2072C29.4105 28.7402 28.0692 30.0815 26.5362 30.0815H5.45839C3.92545 30.0815 2.58414 28.7402 2.58414 27.2072V12.836V11.8779C2.58414 10.3449 3.92545 9.00362 5.45839 9.00362H26.5362C28.0692 9.00362 29.4105 10.3449 29.4105 11.8779V27.2072Z" fill="currentColor"/><path d="M25.9591 21.6487C27.0174 21.6487 27.8753 20.7908 27.8753 19.7326C27.8753 18.6743 27.0174 17.8164 25.9591 17.8164C24.9009 17.8164 24.043 18.6743 24.043 19.7326C24.043 20.7908 24.9009 21.6487 25.9591 21.6487Z" fill="currentColor"/></symbol>
<symbol id="info" viewBox="0 0 24 24" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 22.39c5.739 0 10.39-4.651 10.39-10.39C22.39 6.261 17.74 1.61 12 1.61 6.261 1.61 1.61 6.26 1.61 12c0 5.739 4.651 10.39 10.39 10.39zm0-2.597a7.793 7.793 0 1 0 0-15.586 7.793 7.793 0 0 0 0 15.586z" fill="currentColor"/><path d="M12 6.805a1.299 1.299 0 1 0 0 2.597 1.299 1.299 0 0 0 0-2.597zM10.701 12s0-1.299 1.299-1.299S13.299 12 13.299 12v3.897s0 1.298-1.299 1.298-1.299-1.298-1.299-1.298z" fill="currentColor"/></symbol>
<symbol id="contact" viewBox="0 0 24 24" fill="none"><g transform="scale(.65) translate(7, 7)" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 22.39c5.739 0 10.39-4.651 10.39-10.39C22.39 6.261 17.74 1.61 12 1.61 6.261 1.61 1.61 6.26 1.61 12c0 5.739 4.651 10.39 10.39 10.39zm0-2.597a7.793 7.793 0 1 0 0-15.586 7.793 7.793 0 0 0 0 15.586z"/><path d="M12 6.805a1.299 1.299 0 1 0 0 2.597 1.299 1.299 0 0 0 0-2.597zM10.701 12s0-1.299 1.299-1.299S13.299 12 13.299 12v3.897s0 1.298-1.299 1.298-1.299-1.298-1.299-1.298z"/></g></symbol>
<symbol id="invoice-2" viewBox="0 0 16 18" fill="none"><path d="M1.16699 3V16.5429L3.50033 15.6286L5.83366 17L8.16699 15.6286L10.5003 17L12.8337 15.6286L15.167 16.5429V3C15.167 1.89543 14.2716 1 13.167 1H3.16699C2.06242 1 1.16699 1.89543 1.16699 3Z" stroke="currentColor" stroke-width="1.6"/><path d="M4.66699 5H11.667" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M4.66699 8.5H11.667" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M4.66699 12H8.66699" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></symbol>
<symbol id="invoice-expired" viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="22.5" stroke="currentColor" stroke-width="3"/><path d="m17 31 14-14m-14 0 14 14" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></symbol>
<symbol id="invoice" viewBox="0 0 24 24" fill="none"><path d="M8.30766 4.61523H15.6923C17.723 4.61523 19.3846 6.27677 19.3846 8.30754V15.6922C19.3846 17.7229 17.723 19.3845 15.6923 19.3845H8.30766C6.27689 19.3845 4.61536 17.7229 4.61536 15.6922V8.30754C4.61536 6.27677 6.27689 4.61523 8.30766 4.61523Z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10"/><path d="M8.30774 8.92383H15.6924" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.30774 12H15.6924" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.30774 15.0156H12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/></symbol>

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -235,7 +235,8 @@ document.addEventListener('DOMContentLoaded', () => {
return {
items,
selectedItem: null,
editorOffcanvas: null
selectedItemInitial: null,
editorOffcanvas: null,
}
},
computed: {
@ -247,6 +248,19 @@ document.addEventListener('DOMContentLoaded', () => {
(item.categories || []).forEach(category => { res.add(category); });
return res;
}, new Set()));
},
itemChanged() {
return this.selectedItem && this.selectedItemInitial && (
this.selectedItem.id !== this.selectedItemInitial.id ||
this.selectedItem.title !== this.selectedItemInitial.title ||
this.selectedItem.price !== this.selectedItemInitial.price ||
this.selectedItem.image !== this.selectedItemInitial.image ||
this.selectedItem.disabled !== this.selectedItemInitial.disabled ||
this.selectedItem.inventory !== this.selectedItemInitial.inventory ||
this.selectedItem.priceType !== this.selectedItemInitial.priceType ||
this.selectedItem.categories !== this.selectedItemInitial.categories ||
this.selectedItem.description !== this.selectedItemInitial.description
)
}
},
methods: {
@ -254,7 +268,7 @@ document.addEventListener('DOMContentLoaded', () => {
const items = parseConfig(event.target.value)
if (!items) return
this.items = items
this.selectedItem = null
this.selectedItem = this.selectedItemInitial = null
},
addItem(event) {
const length = this.items.push({
@ -268,16 +282,16 @@ document.addEventListener('DOMContentLoaded', () => {
inventory: null,
disabled: false
})
this.selectedItem = this.items[length - 1]
this.showOffcanvas()
this.selectItem(null, length - 1)
},
selectItem(event, index) {
this.selectedItem = this.items[index]
this.selectedItemInitial = { ...this.selectedItem } // pristine copy
this.showOffcanvas()
},
removeItem(event, index) {
this.items.splice(index, 1)
this.selectedItem = null
this.selectedItem = this.selectedItemInitial = null
},
sortItems(event) {
const { newIndex, oldIndex } = event

View File

@ -100,14 +100,6 @@ header .cart-toggle-btn {
background-color: var(--btcpay-bg-tile);
}
#cart .quantity .btn {
width: 2rem;
height: 2rem;
}
#cart .quantity .btn .icon{
--btn-icon-size: .75rem;
}
#CartBadge {
position: absolute;
top: 0;

View File

@ -96,3 +96,30 @@
max-height: 210px;
object-fit: scale-down;
}
.quantities .btn {
--btcpay-btn-disabled-opacity: .3;
display: flex;
align-items: center;
justify-content: center;
width: 2.5rem;
height: 2.5rem;
background: none;
border: transparent;
padding: 0;
}
.quantities .btn span {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 50%;
background: var(--btcpay-light);
}
.quantities .btn:hover span {
background: var(--btcpay-light-bg-hover);
}
.quantities .icon {
width: 1rem;
height: 1rem;
}

View File

@ -4,10 +4,10 @@
### New feature
* Server Settings: Customize instance name and add contact URL (#5718) @dennisreimann
* Server Settings: Customize instance name and add contact URL (#5718 #5872) @dennisreimann
* Admin overview of the stores on the instance (#5745 #5782) @dennisreimann @Kukks
* Onboarding: Invite new users (#5714 #5719) @dennisreimann @dstrukt
* POS: Add item list to keypad (#5814) @dennisreimann @dstrukt
* Onboarding: Invite new users (#5714 #5719 #5874) @dennisreimann @dstrukt
* POS: Add item list to keypad (#5814 #5857 #5877) @dennisreimann @dstrukt
* Wallet: Support BBQr PSBTSs (#5852) @Kukks
### Improvements
@ -17,7 +17,7 @@
* Wallet: Support 16mb PSBTs (#5768) @Kukks
* Invoice: Improve events display (#5775) @dennisreimann
* Crowdfund: Add forms (like with the POS) (#5659) @Nisaba
* API docs: Add link to API usage examples in docs (#5772) @ndeet
* API docs: Adding introduction, Authentication and Usage examples sections (#5772 #5858) @ndeet
* Policies: Cleanup and improvements (#5731) @dennisreimann @dstrukt
* Add legacy report (#5740) @Kukks
* Store: Move support URL to Checkout Appearance and improve wording (#5717) @dennisreimann
@ -33,7 +33,7 @@
* Pull Payments: When opened in mobile, use deeplink to setup card (#5613) @NicolasDorier
* UI consistency: Use toggles in various setting views (#5769) @TChukwuleta
* Wallet: Improve info message (#5756) @rockstardev
* Item Editor: Apply item changes directly (#5849) @dennisreimann
* Item Editor: Apply item changes directly (#5849 #5871) @dennisreimann
* Specify mailto: prefix for emails in Server Settings (#5844) @TChukwuleta @dennisreimann
* UI: Improve Create First Store view (#5854) @dennisreimann
* Receipts: Smaller printed receipts (#5856) @Kukks
@ -51,6 +51,7 @@
* Reports: Fix old payments not showing up in reports (#5812) @NicolasDorier
* POS: Fix exception when asking for data with a top up item (#5816) @dennisreimann
* Plugins: Do not have report name conflict with old plugin (#5826) @Kukks
* Lightning: Do not throw when local node is not synced and using external ln node (#5859) @Kukks
## 1.12.5