From bdf56c0a6f63112e33e8262c2c453b714b935532 Mon Sep 17 00:00:00 2001 From: d11n Date: Thu, 30 Nov 2023 10:19:03 +0100 Subject: [PATCH] Keypad: List recent transactions (#5478) * Keypad: List recent transactions Closes #5379. * UI updates * Optional: No border * Fix class * Decrease keypad max-width --- .../Controllers/UIPointOfSaleController.cs | 34 ++++++++++++++++ .../Shared/PointOfSale/Public/VueLight.cshtml | 37 +++++++++++++++++- BTCPayServer/wwwroot/img/icon-sprite.svg | 1 + BTCPayServer/wwwroot/pos/cart.js | 2 +- BTCPayServer/wwwroot/pos/common.js | 3 -- BTCPayServer/wwwroot/pos/keypad.css | 39 ++++++++++++++++++- BTCPayServer/wwwroot/pos/keypad.js | 31 ++++++++++++--- 7 files changed, 134 insertions(+), 13 deletions(-) diff --git a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs index eee430a7a..9fee422c5 100644 --- a/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs +++ b/BTCPayServer/Plugins/PointOfSale/Controllers/UIPointOfSaleController.cs @@ -47,6 +47,7 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers AppService appService, CurrencyNameTable currencies, StoreRepository storeRepository, + InvoiceRepository invoiceRepository, UIInvoiceController invoiceController, FormDataService formDataService, DisplayFormatter displayFormatter) @@ -54,12 +55,14 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers _currencies = currencies; _appService = appService; _storeRepository = storeRepository; + _invoiceRepository = invoiceRepository; _invoiceController = invoiceController; _displayFormatter = displayFormatter; FormDataService = formDataService; } private readonly CurrencyNameTable _currencies; + private readonly InvoiceRepository _invoiceRepository; private readonly StoreRepository _storeRepository; private readonly AppService _appService; private readonly UIInvoiceController _invoiceController; @@ -520,6 +523,37 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers viewModel.FormParameters = formParameters; return View("Views/UIForms/View", viewModel); } + + [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] + [HttpGet("/apps/{appId}/pos/recent-transactions")] + public async Task RecentTransactions(string appId) + { + var app = await _appService.GetApp(appId, PointOfSaleAppType.AppType); + if (app == null) + return NotFound(); + + var from = DateTimeOffset.UtcNow - TimeSpan.FromDays(3); + var invoices = await AppService.GetInvoicesForApp(_invoiceRepository, app, from, new[] + { + InvoiceState.ToString(InvoiceStatusLegacy.New), + InvoiceState.ToString(InvoiceStatusLegacy.Paid), + InvoiceState.ToString(InvoiceStatusLegacy.Confirmed), + InvoiceState.ToString(InvoiceStatusLegacy.Complete), + InvoiceState.ToString(InvoiceStatusLegacy.Expired), + InvoiceState.ToString(InvoiceStatusLegacy.Invalid) + }); + var recent = invoices + .Take(10) + .Select(i => new JObject + { + ["id"] = i.Id, + ["date"] = i.InvoiceTime, + ["price"] = _displayFormatter.Currency(i.Price, i.Currency, DisplayFormatter.CurrencyFormat.Symbol), + ["status"] = i.GetInvoiceState().Status.ToModernStatus().ToString(), + ["url"] = Url.Action(nameof(UIInvoiceController.Invoice), "UIInvoice", new { invoiceId = i.Id }) + }); + return Json(recent); + } [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [HttpGet("{appId}/settings/pos")] diff --git a/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml b/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml index 0c1131a50..c87eb7f08 100644 --- a/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml +++ b/BTCPayServer/Views/Shared/PointOfSale/Public/VueLight.cshtml @@ -51,10 +51,45 @@
- + + diff --git a/BTCPayServer/wwwroot/img/icon-sprite.svg b/BTCPayServer/wwwroot/img/icon-sprite.svg index d6f08d33e..cd9696355 100644 --- a/BTCPayServer/wwwroot/img/icon-sprite.svg +++ b/BTCPayServer/wwwroot/img/icon-sprite.svg @@ -63,6 +63,7 @@ + diff --git a/BTCPayServer/wwwroot/pos/cart.js b/BTCPayServer/wwwroot/pos/cart.js index 9dcf012dd..c6d7ce3ac 100644 --- a/BTCPayServer/wwwroot/pos/cart.js +++ b/BTCPayServer/wwwroot/pos/cart.js @@ -205,7 +205,7 @@ document.addEventListener("DOMContentLoaded",function () { window.addEventListener('pagehide', () => { if (this.payButtonLoading) { - this.unsetPayButtonLoading(); + this.payButtonLoading = false; localStorage.removeItem(storageKey('cart')); } }) diff --git a/BTCPayServer/wwwroot/pos/common.js b/BTCPayServer/wwwroot/pos/common.js index 92669fcb4..64e85d968 100644 --- a/BTCPayServer/wwwroot/pos/common.js +++ b/BTCPayServer/wwwroot/pos/common.js @@ -85,9 +85,6 @@ const posCommon = { ? percentage : null; }, - unsetPayButtonLoading () { - this.payButtonLoading = false - }, formatCrypto (value, withSymbol) { const symbol = withSymbol ? ` ${this.currencySymbol || this.currencyCode}` : '' const { divisibility } = this.currencyInfo diff --git a/BTCPayServer/wwwroot/pos/keypad.css b/BTCPayServer/wwwroot/pos/keypad.css index 3df499f3f..3cbb42983 100644 --- a/BTCPayServer/wwwroot/pos/keypad.css +++ b/BTCPayServer/wwwroot/pos/keypad.css @@ -1,5 +1,40 @@ #PosKeypad { - --wrap-max-width: 575px; + --wrap-max-width: 500px; + overflow: hidden; + position: relative; +} + +#RecentTransactionsToggle { + position: absolute; + top: var(--btcpay-space-l); + left: var(--btcpay-space-m); + z-index: 1; +} + +#RecentTransactionsToggle .icon { + --btn-icon-size: 2.25em; +} +#RecentTransactionsRefresh[disabled] .icon { + animation: 1.25s linear infinite spinner-border; +} +#RecentTransactions .list-group { + margin: calc(var(--btcpay-modal-padding) * -1); + width: calc(100% + var(--btcpay-modal-padding) * 2); +} + +#RecentTransactions .list-group-item { + background-color: transparent; +} + +#RecentTransactions .list-group .badge-container { + flex: 0 0 5.125rem; + text-align: right; +} + +@media (max-width: 359px) { + #RecentTransactions .list-group .badge-container { + flex-grow: 1; + } } /* modes */ @@ -19,6 +54,7 @@ padding: 0; position: relative; border-radius: 0; + border-color: transparent !important; font-weight: var(--btcpay-font-weight-semibold); font-size: 24px; min-height: 3.5rem; @@ -79,7 +115,6 @@ } /* fix sticky hover effect on mobile browsers */ @media (hover: none) { - .keypad .btn-secondary:hover, .actions .btn-secondary:hover { border-color: var(--btcpay-secondary-border-active) !important; } diff --git a/BTCPayServer/wwwroot/pos/keypad.js b/BTCPayServer/wwwroot/pos/keypad.js index 63a63ea58..43e98f987 100644 --- a/BTCPayServer/wwwroot/pos/keypad.js +++ b/BTCPayServer/wwwroot/pos/keypad.js @@ -9,7 +9,10 @@ document.addEventListener("DOMContentLoaded",function () { fontSize: displayFontSize, defaultFontSize: displayFontSize, keys: ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', '0', '+'], - amounts: [null] + amounts: [null], + recentTransactions: [], + recentTransactionsLoading: false, + dateFormatter: new Intl.DateTimeFormat('default', { dateStyle: 'short', timeStyle: 'short' }) } }, computed: { @@ -117,15 +120,31 @@ document.addEventListener("DOMContentLoaded",function () { // clear completely this.clear(); } + }, + closeModal() { + bootstrap.Modal.getInstance(this.$refs.RecentTransactions).hide(); + }, + displayDate(val) { + const date = new Date(val); + return this.dateFormatter.format(date); + }, + async loadRecentTransactions() { + this.recentTransactionsLoading = true; + const { url } = this.$refs.RecentTransactions.dataset; + const response = await fetch(url); + if (response.ok) { + this.recentTransactions = await response.json(); + } + this.recentTransactionsLoading = false; } }, created() { - /** We need to unset state in case user clicks the browser back button */ - window.addEventListener('pagehide', this.unsetPayButtonLoading) - }, - destroyed() { - window.removeEventListener('pagehide', this.unsetPayButtonLoading) + // We need to unset state in case user clicks the browser back button + window.addEventListener('pagehide', () => { this.payButtonLoading = false }) }, + mounted() { + this.$refs.RecentTransactions.addEventListener('show.bs.modal', this.loadRecentTransactions); + } }); });