mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Store-centric UI (#3091)
* Update layout structure and header * Implement store selector * Simplify homepage * Update layout * Use dropdown for store selector * Hide global nav in store context * Horizontal section nav * Remove outer section and container from content views * Update nav * Set store context for invoice and payment request lists * Test fixes * Persist menu collapse state on client-side * MainNav as view component * Update app routes to incorporate store context * Test fixes * Display ticker for altcoins build only * Plugins nav * Incorporate category for active page as well * Update invoice icon * Add apps list to nav * Add store context to app type controllers * Incorporate id for active page as well * Test fixes * AppsController cleanup * Nav: Display only apps for the current store * Remove leftover from merge * Nav styles optimization * Left-align content container * Increase sidebar padding on desktop * Use min-width for store selector menu * Store settings nav update * Update app and payment request routes * Test fixes * Refactor MainNav component to use StoresController * Set store context for invoice actions * Cleanups * Remove CurrentStore checks The response will be "Access denied" in case the CookieAuthorizationHandler cannot resolve the store. * Remove unnecessary store context setters * Test fix
This commit is contained in:
parent
2b1436e303
commit
f8e6b51e9d
79 changed files with 3782 additions and 3446 deletions
|
@ -9,6 +9,7 @@ namespace BTCPayServer.Abstractions.Extensions
|
|||
{
|
||||
private const string ACTIVE_CATEGORY_KEY = "ActiveCategory";
|
||||
private const string ACTIVE_PAGE_KEY = "ActivePage";
|
||||
private const string ACTIVE_ID_KEY = "ActiveId";
|
||||
|
||||
public static void SetActivePageAndTitle<T>(this ViewDataDictionary viewData, T activePage, string title = null, string mainTitle = null)
|
||||
where T : IConvertible
|
||||
|
@ -28,25 +29,38 @@ namespace BTCPayServer.Abstractions.Extensions
|
|||
viewData[ACTIVE_CATEGORY_KEY] = activeCategory;
|
||||
}
|
||||
|
||||
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T category)
|
||||
// TODO: Refactor this and merge it with SetActivePage
|
||||
public static void SetActiveId<T>(this ViewDataDictionary viewData, T activeId)
|
||||
{
|
||||
viewData[ACTIVE_ID_KEY] = activeId;
|
||||
}
|
||||
|
||||
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
|
||||
{
|
||||
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var activeId = viewData[ACTIVE_ID_KEY];
|
||||
var activeCategory = (T)viewData[ACTIVE_CATEGORY_KEY];
|
||||
return category.Equals(activeCategory) ? "active" : null;
|
||||
var categoryMatch = category.Equals(activeCategory);
|
||||
var idMatch = id == null || activeId == null || id.Equals(activeId);
|
||||
return categoryMatch && idMatch ? "active" : null;
|
||||
}
|
||||
|
||||
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page)
|
||||
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page, object id = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var activeId = viewData[ACTIVE_ID_KEY];
|
||||
var activePage = (T)viewData[ACTIVE_PAGE_KEY];
|
||||
return page.Equals(activePage) ? "active" : null;
|
||||
var activeCategory = viewData[ACTIVE_CATEGORY_KEY];
|
||||
var categoryAndPageMatch = activeCategory.Equals(activePage.GetType()) && page.Equals(activePage);
|
||||
var idMatch = id == null || activeId == null || id.Equals(activeId);
|
||||
return categoryAndPageMatch && idMatch ? "active" : null;
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTimeOffset date)
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
@model BTCPayServer.Plugins.Test.TestPluginPageViewModel
|
||||
<section>
|
||||
<div class="container">
|
||||
<h1>Challenge Completed!!</h1>
|
||||
Here is also an image loaded from the plugin<br/>
|
||||
<a href="https://twitter.com/NicolasDorier/status/1307221679014256640">
|
||||
<img src="/Resources/img/screengrab.png"/>
|
||||
</a>
|
||||
|
||||
<div class="row">
|
||||
<h2>Persisted Data</h2>
|
||||
<p>The following is data persisted to the configured database but in an isolated DbContext. Every time you start BTCPay Server with this plugin enabled, a timestamp is logged.</p>
|
||||
<ul class="list-group">
|
||||
@foreach (var item in Model.Data)
|
||||
{
|
||||
<li class="list-group-item">@item.Id at @item.Timestamp.ToString("F")</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1>Challenge Completed!!</h1>
|
||||
Here is also an image loaded from the plugin<br/>
|
||||
<a href="https://twitter.com/NicolasDorier/status/1307221679014256640">
|
||||
<img src="/Resources/img/screengrab.png"/>
|
||||
</a>
|
||||
|
||||
<div class="row">
|
||||
<h2>Persisted Data</h2>
|
||||
<p>The following is data persisted to the configured database but in an isolated DbContext. Every time you start BTCPay Server with this plugin enabled, a timestamp is logged.</p>
|
||||
<ul class="list-group">
|
||||
@foreach (var item in Model.Data)
|
||||
{
|
||||
<li class="list-group-item">@item.Id at @item.Timestamp.ToString("F")</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -607,15 +607,15 @@ namespace BTCPayServer.Tests
|
|||
tester.ActivateLTC();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
user.RegisterDerivationScheme("LTC");
|
||||
var apps = user.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.PointOfSale.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model)
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model)
|
||||
.Apps[0].Id;
|
||||
var vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
|
||||
|
@ -658,13 +658,11 @@ donation:
|
|||
.ViewPointOfSale(appId, PosViewType.Cart, 0, null, null, null, null, "orange").Result);
|
||||
|
||||
//
|
||||
var invoices = user.BitPay.GetInvoices();
|
||||
var invoices = await user.BitPay.GetInvoicesAsync();
|
||||
var orangeInvoice = invoices.First();
|
||||
Assert.Equal(10.00m, orangeInvoice.Price);
|
||||
Assert.Equal("CAD", orangeInvoice.Currency);
|
||||
Assert.Equal("orange", orangeInvoice.ItemDesc);
|
||||
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(appId, PosViewType.Cart, 0, null, null, null, null, "apple").Result);
|
||||
|
||||
|
@ -672,8 +670,7 @@ donation:
|
|||
var appleInvoice = invoices.SingleOrDefault(invoice => invoice.ItemCode.Equals("apple"));
|
||||
Assert.NotNull(appleInvoice);
|
||||
Assert.Equal("good apple", appleInvoice.ItemDesc);
|
||||
|
||||
|
||||
|
||||
// testing custom amount
|
||||
var action = Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(appId, PosViewType.Cart, 6.6m, null, null, null, null, "donation").Result);
|
||||
|
@ -772,7 +769,7 @@ noninventoryitem:
|
|||
Assert.Single(invoices.Where(invoice => invoice.ItemCode.Equals("inventoryitem")));
|
||||
Assert.NotNull(inventoryItemInvoice);
|
||||
|
||||
//let's mark the inventoryitem invoice as invalid, thsi should return the item to back in stock
|
||||
//let's mark the inventoryitem invoice as invalid, this should return the item to back in stock
|
||||
var controller = tester.PayTester.GetController<InvoiceController>(user.UserId, user.StoreId);
|
||||
var appService = tester.PayTester.GetService<AppService>();
|
||||
var eventAggregator = tester.PayTester.GetService<EventAggregator>();
|
||||
|
@ -786,9 +783,7 @@ noninventoryitem:
|
|||
appService.Parse(vmpos.Template, "BTC").Single(item => item.Id == "inventoryitem").Inventory);
|
||||
}, 10000);
|
||||
|
||||
|
||||
//test payment methods option
|
||||
|
||||
vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
|
||||
vmpos.Title = "hello";
|
||||
|
@ -858,7 +853,6 @@ g:
|
|||
Assert.Contains(items, item => item.Id == "f" && item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup);
|
||||
Assert.Contains(items, item => item.Id == "g" && item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup);
|
||||
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(appId, PosViewType.Static, null, null, null, null, null, "g").Result);
|
||||
invoices = user.BitPay.GetInvoices();
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace BTCPayServer.Tests
|
|||
var tester = s.Server;
|
||||
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
await user.MakeAdmin(false);
|
||||
s.GoToLogin();
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
|
|
|
@ -29,21 +29,21 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
var user2 = tester.NewAccount();
|
||||
user2.GrantAccess();
|
||||
await user2.GrantAccessAsync();
|
||||
var apps = user.GetController<AppsController>();
|
||||
var apps2 = user2.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
Assert.NotNull(vm.SelectedAppType);
|
||||
Assert.Null(vm.AppName);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
Assert.Equal(nameof(apps.UpdateCrowdfund), redirectToAction.ActionName);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var appList2 =
|
||||
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps().Result).Model);
|
||||
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps(user2.StoreId).Result).Model);
|
||||
Assert.Single(appList.Apps);
|
||||
Assert.Empty(appList2.Apps);
|
||||
Assert.Equal("test", appList.Apps[0].AppName);
|
||||
|
@ -54,13 +54,11 @@ namespace BTCPayServer.Tests
|
|||
Assert.IsType<ViewResult>(apps.DeleteApp(appList.Apps[0].Id).Result);
|
||||
redirectToAction = Assert.IsType<RedirectToActionResult>(apps.DeleteAppPost(appList.Apps[0].Id).Result);
|
||||
Assert.Equal(nameof(apps.ListApps), redirectToAction.ActionName);
|
||||
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model);
|
||||
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
Assert.Empty(appList.Apps);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanContributeOnlyWhenAllowed()
|
||||
|
@ -69,14 +67,14 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var apps = user.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model)
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model)
|
||||
.Apps[0].Id;
|
||||
|
||||
//Scenario 1: Not Enabled - Not Allowed
|
||||
|
@ -90,8 +88,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var anonAppPubsController = tester.PayTester.GetController<AppsPublicController>();
|
||||
var publicApps = user.GetController<AppsPublicController>();
|
||||
|
||||
|
||||
|
||||
Assert.IsType<NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(appId, new ContributeToCrowdfund()
|
||||
{
|
||||
Amount = new decimal(0.01)
|
||||
|
@ -119,7 +116,6 @@ namespace BTCPayServer.Tests
|
|||
}, default));
|
||||
|
||||
//Scenario 4: Enabled But End Date < Now - Not Allowed
|
||||
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
|
||||
crowdfundViewModel.EndDate = DateTime.Today.AddDays(-1);
|
||||
crowdfundViewModel.Enabled = true;
|
||||
|
@ -130,7 +126,6 @@ namespace BTCPayServer.Tests
|
|||
Amount = new decimal(0.01)
|
||||
}, default));
|
||||
|
||||
|
||||
//Scenario 5: Enabled and within correct timeframe, however target is enforced and Amount is Over - Not Allowed
|
||||
crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2);
|
||||
crowdfundViewModel.EndDate = DateTime.Today.AddDays(2);
|
||||
|
@ -149,7 +144,6 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
Amount = new decimal(0.05)
|
||||
}, default));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,11 +159,11 @@ namespace BTCPayServer.Tests
|
|||
user.RegisterDerivationScheme("BTC");
|
||||
await user.SetNetworkFeeMode(NetworkFeeMode.Never);
|
||||
var apps = user.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.Crowdfund.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model)
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model)
|
||||
.Apps[0].Id;
|
||||
|
||||
TestLogs.LogInformation("We create an invoice with a hardcap");
|
||||
|
@ -188,8 +182,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
||||
|
||||
|
||||
|
||||
Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount);
|
||||
Assert.Equal(crowdfundViewModel.EndDate, model.EndDate);
|
||||
Assert.Equal(crowdfundViewModel.StartDate, model.StartDate);
|
||||
|
@ -197,11 +190,10 @@ namespace BTCPayServer.Tests
|
|||
Assert.Equal(0m, model.Info.CurrentAmount);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
Assert.Equal(0m, model.Info.ProgressPercentage);
|
||||
|
||||
|
||||
|
||||
TestLogs.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap");
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution");
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
|
@ -211,10 +203,9 @@ namespace BTCPayServer.Tests
|
|||
TransactionSpeed = "high",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
|
||||
|
||||
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, string.Empty).Result).Model);
|
||||
|
||||
Assert.Equal(0m, model.Info.CurrentAmount);
|
||||
Assert.Equal(1m, model.Info.CurrentPendingAmount);
|
||||
|
@ -242,9 +233,9 @@ namespace BTCPayServer.Tests
|
|||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel, "save").Result);
|
||||
|
||||
TestLogs.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged");
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Buyer = new Buyer { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
|
@ -259,9 +250,9 @@ namespace BTCPayServer.Tests
|
|||
crowdfundViewModel.EnforceTargetAmount = false;
|
||||
crowdfundViewModel.UseAllStoreInvoices = true;
|
||||
Assert.IsType<RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel, "save").Result);
|
||||
invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
invoice = await user.BitPay.CreateInvoiceAsync(new Invoice
|
||||
{
|
||||
Buyer = new Buyer() { email = "test@fwf.com" },
|
||||
Buyer = new Buyer { email = "test@fwf.com" },
|
||||
Price = 1m,
|
||||
Currency = "BTC",
|
||||
PosData = "posData",
|
||||
|
@ -271,20 +262,15 @@ namespace BTCPayServer.Tests
|
|||
}, Facade.Merchant);
|
||||
Assert.Equal(0m, model.Info.CurrentPendingAmount);
|
||||
invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network);
|
||||
tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.5m));
|
||||
tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.2m));
|
||||
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.5m));
|
||||
await tester.ExplorerNode.SendToAddressAsync(invoiceAddress, Money.Coins(0.2m));
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
model = Assert.IsType<ViewCrowdfundViewModel>(Assert
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model);
|
||||
.IsType<ViewResult>(publicApps.ViewCrowdfund(appId, string.Empty).Result).Model);
|
||||
Assert.Equal(0.7m, model.Info.CurrentPendingAmount);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public static void AssertNoError(this IWebDriver driver)
|
||||
{
|
||||
Assert.NotEmpty(driver.FindElements(By.ClassName("navbar-brand")));
|
||||
if (!driver.PageSource.Contains("alert-danger")) return;
|
||||
foreach (var dangerAlert in driver.FindElements(By.ClassName("alert-danger")))
|
||||
Assert.False(dangerAlert.Displayed, $"No alert should be displayed, but found this on {driver.Url}: {dangerAlert.Text}");
|
||||
|
|
|
@ -25,14 +25,14 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
var apps = user.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.PointOfSale.ToString();
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model)
|
||||
Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
var appId = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model)
|
||||
.Apps[0].Id;
|
||||
var vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert
|
||||
.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Models.PaymentRequestViewModels;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.PaymentRequests;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
@ -32,16 +27,17 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var user2 = tester.NewAccount();
|
||||
user2.GrantAccess();
|
||||
|
||||
await user2.GrantAccessAsync();
|
||||
|
||||
var paymentRequestController = user.GetController<PaymentRequestController>();
|
||||
var guestpaymentRequestController = user2.GetController<PaymentRequestController>();
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
var request = new UpdatePaymentRequestViewModel
|
||||
{
|
||||
Title = "original juice",
|
||||
Currency = "BTC",
|
||||
|
@ -49,14 +45,13 @@ namespace BTCPayServer.Tests
|
|||
StoreId = user.StoreId,
|
||||
Description = "description"
|
||||
};
|
||||
var id = (Assert
|
||||
var id = Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request))
|
||||
.RouteValues.Values.First().ToString());
|
||||
.RouteValues.Values.Last().ToString();
|
||||
|
||||
|
||||
//permission guard for guests editing
|
||||
// Permission guard for guests editing
|
||||
Assert
|
||||
.IsType<NotFoundResult>(await guestpaymentRequestController.EditPaymentRequest(id));
|
||||
.IsType<NotFoundResult>(await guestpaymentRequestController.EditPaymentRequest(user.StoreId, id));
|
||||
|
||||
request.Title = "update";
|
||||
Assert.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request));
|
||||
|
@ -70,8 +65,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model);
|
||||
|
||||
//Archive
|
||||
|
||||
// Archive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
Assert.True(Assert
|
||||
|
@ -80,8 +74,9 @@ namespace BTCPayServer.Tests
|
|||
|
||||
Assert.Empty(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items);
|
||||
//unarchive
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests(user.StoreId)).Model).Items);
|
||||
|
||||
// Unarchive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
|
||||
|
@ -91,7 +86,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
Assert.Single(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items);
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests(user.StoreId)).Model).Items);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +98,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
var paymentRequestController = user.GetController<PaymentRequestController>();
|
||||
|
@ -122,7 +117,7 @@ namespace BTCPayServer.Tests
|
|||
};
|
||||
var response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.First();
|
||||
.RouteValues.Last();
|
||||
|
||||
var invoiceId = Assert
|
||||
.IsType<OkObjectResult>(
|
||||
|
@ -153,7 +148,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.First();
|
||||
.RouteValues.Last();
|
||||
|
||||
Assert
|
||||
.IsType<BadRequestObjectResult>(
|
||||
|
@ -187,7 +182,7 @@ namespace BTCPayServer.Tests
|
|||
};
|
||||
var response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.First();
|
||||
.RouteValues.Last();
|
||||
var invoiceId = response.Value.ToString();
|
||||
await paymentRequestController.PayPaymentRequest(invoiceId, false);
|
||||
Assert.IsType<BadRequestObjectResult>(await
|
||||
|
@ -197,7 +192,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
response = Assert
|
||||
.IsType<RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result)
|
||||
.RouteValues.First();
|
||||
.RouteValues.Last();
|
||||
|
||||
var paymentRequestId = response.Value.ToString();
|
||||
|
||||
|
@ -231,7 +226,7 @@ namespace BTCPayServer.Tests
|
|||
.Value
|
||||
.ToString();
|
||||
|
||||
invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
await user.BitPay.GetInvoiceAsync(invoiceId, Facade.Merchant);
|
||||
|
||||
//a hack to generate invoices for the payment request is to manually create an invoice with an order id that matches:
|
||||
user.BitPay.CreateInvoice(new Invoice(1, "USD")
|
||||
|
|
|
@ -2,14 +2,10 @@ using System;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Lightning.CLightning;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Manage;
|
||||
using BTCPayServer.Views.Server;
|
||||
using BTCPayServer.Views.Stores;
|
||||
|
@ -146,14 +142,14 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public (string storeName, string storeId) CreateNewStore(bool keepId = true)
|
||||
{
|
||||
Driver.WaitForElement(By.Id("Stores")).Click();
|
||||
Driver.WaitForElement(By.Id("CreateStore")).Click();
|
||||
Driver.WaitForElement(By.Id("StoreSelectorToggle")).Click();
|
||||
Driver.WaitForElement(By.Id("StoreSelectorMenuItem-Create")).Click();
|
||||
var name = "Store" + RandomUtils.GetUInt64();
|
||||
Driver.WaitForElement(By.Id("Name")).SendKeys(name);
|
||||
Driver.WaitForElement(By.Id("Create")).Click();
|
||||
Driver.FindElement(By.Id($"Nav-{StoreNavPages.GeneralSettings.ToString()}")).Click();
|
||||
Driver.FindElement(By.Id($"SectionNav-{StoreNavPages.GeneralSettings.ToString()}")).Click();
|
||||
var storeId = Driver.WaitForElement(By.Id("Id")).GetAttribute("value");
|
||||
Driver.FindElement(By.Id($"Nav-{StoreNavPages.PaymentMethods.ToString()}")).Click();
|
||||
Driver.FindElement(By.Id($"SectionNav-{StoreNavPages.PaymentMethods.ToString()}")).Click();
|
||||
if (keepId)
|
||||
StoreId = storeId;
|
||||
return (name, storeId);
|
||||
|
@ -279,9 +275,9 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
|
||||
public Logging.ILog TestLogs => Server.TestLogs;
|
||||
public void ClickOnAllSideMenus()
|
||||
public void ClickOnAllSectionLinks()
|
||||
{
|
||||
var links = Driver.FindElements(By.CssSelector(".nav .nav-link")).Select(c => c.GetAttribute("href")).ToList();
|
||||
var links = Driver.FindElements(By.CssSelector("#SectionNav .nav-link")).Select(c => c.GetAttribute("href")).ToList();
|
||||
Driver.AssertNoError();
|
||||
foreach (var l in links)
|
||||
{
|
||||
|
@ -315,6 +311,11 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("404 - Page not found</h1>", Driver.PageSource);
|
||||
}
|
||||
|
||||
internal void AssertAccessDenied()
|
||||
{
|
||||
Assert.Contains("Access denied</h", Driver.PageSource);
|
||||
}
|
||||
|
||||
public void GoToHome()
|
||||
{
|
||||
Driver.Navigate().GoToUrl(ServerUri);
|
||||
|
@ -331,24 +332,29 @@ namespace BTCPayServer.Tests
|
|||
Driver.FindElement(By.Id("Password")).SendKeys(password);
|
||||
Driver.FindElement(By.Id("LoginButton")).Click();
|
||||
}
|
||||
|
||||
public void GoToApps()
|
||||
{
|
||||
Driver.FindElement(By.Id("Apps")).Click();
|
||||
}
|
||||
public void GoToStores()
|
||||
{
|
||||
Driver.FindElement(By.Id("Stores")).Click();
|
||||
Driver.FindElement(By.Id("StoreNav-Apps")).Click();
|
||||
}
|
||||
|
||||
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.PaymentMethods)
|
||||
{
|
||||
GoToHome();
|
||||
Driver.WaitForAndClick(By.Id("Stores"));
|
||||
Driver.FindElement(By.Id($"update-store-{storeId}")).Click();
|
||||
Driver.WaitForAndClick(By.Id("StoreSelectorToggle"));
|
||||
Driver.WaitForAndClick(By.Id($"StoreSelectorMenuItem-{storeId}"));
|
||||
|
||||
if (storeNavPage != StoreNavPages.PaymentMethods)
|
||||
{
|
||||
Driver.FindElement(By.Id($"Nav-{storeNavPage.ToString()}")).Click();
|
||||
// FIXME: Review and optimize this once we decided on where which items belong
|
||||
try
|
||||
{
|
||||
Driver.FindElement(By.Id($"StoreNav-{storeNavPage.ToString()}")).Click();
|
||||
}
|
||||
catch (NoSuchElementException)
|
||||
{
|
||||
Driver.FindElement(By.Id($"SectionNav-{storeNavPage.ToString()}")).Click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,22 +372,23 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public void GoToInvoiceCheckout(string invoiceId)
|
||||
{
|
||||
Driver.FindElement(By.Id("Invoices")).Click();
|
||||
Driver.FindElement(By.Id("StoreNav-Invoices")).Click();
|
||||
Driver.FindElement(By.Id($"invoice-checkout-{invoiceId}")).Click();
|
||||
CheckForJSErrors();
|
||||
}
|
||||
|
||||
public void GoToInvoices()
|
||||
{
|
||||
Driver.FindElement(By.Id("Invoices")).Click();
|
||||
GoToHome();
|
||||
Driver.FindElement(By.Id("Nav-Invoices")).Click();
|
||||
}
|
||||
|
||||
public void GoToProfile(ManageNavPages navPages = ManageNavPages.Index)
|
||||
{
|
||||
Driver.FindElement(By.Id("MySettings")).Click();
|
||||
Driver.FindElement(By.Id("Nav-Account")).Click();
|
||||
if (navPages != ManageNavPages.Index)
|
||||
{
|
||||
Driver.FindElement(By.Id(navPages.ToString())).Click();
|
||||
Driver.FindElement(By.Id($"SectionNav-{navPages.ToString()}")).Click();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,7 +484,7 @@ namespace BTCPayServer.Tests
|
|||
Driver.Navigate().GoToUrl(new Uri(ServerUri, $"wallets/{walletId}"));
|
||||
if (navPages != WalletsNavPages.Transactions)
|
||||
{
|
||||
Driver.FindElement(By.Id($"Wallet{navPages}")).Click();
|
||||
Driver.FindElement(By.Id($"SectionNav-{navPages}")).Click();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,10 +495,10 @@ namespace BTCPayServer.Tests
|
|||
|
||||
public void GoToServer(ServerNavPages navPages = ServerNavPages.Index)
|
||||
{
|
||||
Driver.FindElement(By.Id("ServerSettings")).Click();
|
||||
Driver.FindElement(By.Id("Nav-ServerSettings")).Click();
|
||||
if (navPages != ServerNavPages.Index)
|
||||
{
|
||||
Driver.FindElement(By.Id($"Server-{navPages}")).Click();
|
||||
Driver.FindElement(By.Id($"SectionNav-{navPages}")).Click();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ using System.Collections.ObjectModel;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
@ -13,15 +11,11 @@ using BTCPayServer.Abstractions.Models;
|
|||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Lightning.Charge;
|
||||
using BTCPayServer.Lightning.CLightning;
|
||||
using BTCPayServer.Lightning.LND;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Manage;
|
||||
using BTCPayServer.Views.Server;
|
||||
using BTCPayServer.Views.Stores;
|
||||
using BTCPayServer.Views.Wallets;
|
||||
|
@ -38,8 +32,6 @@ using OpenQA.Selenium.Support.UI;
|
|||
using Renci.SshNet.Security.Cryptography;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
using CreateInvoiceRequest = BTCPayServer.Lightning.Charge.CreateInvoiceRequest;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -60,9 +52,9 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
s.Driver.FindElement(By.Id("ServerSettings")).Click();
|
||||
s.Driver.FindElement(By.Id("Nav-ServerSettings")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.ClickOnAllSideMenus();
|
||||
s.ClickOnAllSectionLinks();
|
||||
s.Driver.FindElement(By.LinkText("Services")).Click();
|
||||
|
||||
TestLogs.LogInformation("Let's check if we can access the logs");
|
||||
|
@ -82,7 +74,7 @@ namespace BTCPayServer.Tests
|
|||
s.Server.ActivateLightning();
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
s.Driver.FindElement(By.Id("ServerSettings")).Click();
|
||||
s.Driver.FindElement(By.Id("Nav-ServerSettings")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.FindElement(By.LinkText("Services")).Click();
|
||||
|
||||
|
@ -174,8 +166,8 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("ReturnUrl=%2Fserver%2Fusers", s.Driver.Url);
|
||||
|
||||
//Change Password & Log Out
|
||||
s.Driver.FindElement(By.Id("MySettings")).Click();
|
||||
s.Driver.FindElement(By.Id("ChangePassword")).Click();
|
||||
s.Driver.FindElement(By.Id("Nav-Account")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-ChangePassword")).Click();
|
||||
s.Driver.FindElement(By.Id("OldPassword")).SendKeys("123456");
|
||||
s.Driver.FindElement(By.Id("NewPassword")).SendKeys("abc???");
|
||||
s.Driver.FindElement(By.Id("ConfirmPassword")).SendKeys("abc???");
|
||||
|
@ -189,8 +181,8 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("LoginButton")).Click();
|
||||
Assert.True(s.Driver.PageSource.Contains("Stores"), "Can't Access Stores");
|
||||
|
||||
s.Driver.FindElement(By.Id("MySettings")).Click();
|
||||
s.ClickOnAllSideMenus();
|
||||
s.Driver.FindElement(By.Id("Nav-Account")).Click();
|
||||
s.ClickOnAllSectionLinks();
|
||||
|
||||
//let's test invite link
|
||||
s.Logout();
|
||||
|
@ -300,7 +292,6 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.CssSelector("button[value=\"ResetPassword\"]")).Submit();
|
||||
s.FindAlertMessage();
|
||||
}
|
||||
|
||||
CanSetupEmailCore(s);
|
||||
s.CreateNewStore();
|
||||
s.GoToUrl($"stores/{s.StoreId}/emails");
|
||||
|
@ -376,9 +367,6 @@ namespace BTCPayServer.Tests
|
|||
Assert.True(s.Driver.PageSource.Contains(onchainHint), "Wallet hint not present");
|
||||
Assert.True(s.Driver.PageSource.Contains(offchainHint), "Lightning hint not present");
|
||||
|
||||
s.GoToStores();
|
||||
Assert.True(s.Driver.PageSource.Contains($"warninghint_{storeId}"), "Warning hint on list not present");
|
||||
|
||||
s.GoToStore(storeId);
|
||||
Assert.Contains(storeName, s.Driver.PageSource);
|
||||
Assert.True(s.Driver.PageSource.Contains(onchainHint), "Wallet hint should be present at this point");
|
||||
|
@ -402,7 +390,7 @@ namespace BTCPayServer.Tests
|
|||
"Lightning hint should be dismissed at this point");
|
||||
|
||||
var storeUrl = s.Driver.Url;
|
||||
s.ClickOnAllSideMenus();
|
||||
s.ClickOnAllSectionLinks();
|
||||
s.GoToInvoices();
|
||||
var invoiceId = s.CreateInvoice(storeName);
|
||||
s.FindAlertMessage();
|
||||
|
@ -425,19 +413,20 @@ namespace BTCPayServer.Tests
|
|||
s.GoToInvoices();
|
||||
Assert.Contains(invoiceId, s.Driver.PageSource);
|
||||
|
||||
// When logout we should not be able to access store and invoice details
|
||||
// When logout out we should not be able to access store and invoice details
|
||||
s.Driver.FindElement(By.Id("Logout")).Click();
|
||||
s.Driver.Navigate().GoToUrl(storeUrl);
|
||||
Assert.Contains("ReturnUrl", s.Driver.Url);
|
||||
s.Driver.Navigate().GoToUrl(invoiceUrl);
|
||||
Assert.Contains("ReturnUrl", s.Driver.Url);
|
||||
s.GoToRegister();
|
||||
// When logged we should not be able to access store and invoice details
|
||||
|
||||
// When logged in as different user we should not be able to access store and invoice details
|
||||
var bob = s.RegisterNewUser();
|
||||
s.Driver.Navigate().GoToUrl(storeUrl);
|
||||
Assert.Contains("ReturnUrl", s.Driver.Url);
|
||||
s.Driver.Navigate().GoToUrl(invoiceUrl);
|
||||
s.AssertNotFound();
|
||||
s.AssertAccessDenied();
|
||||
s.GoToHome();
|
||||
s.Logout();
|
||||
|
||||
|
@ -458,11 +447,10 @@ namespace BTCPayServer.Tests
|
|||
// Alice should be able to delete the store
|
||||
s.Logout();
|
||||
s.LogIn(alice);
|
||||
s.Driver.FindElement(By.Id("Stores")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Delete")).Click();
|
||||
s.GoToStore(storeId, StoreNavPages.GeneralSettings);
|
||||
s.Driver.FindElement(By.Id("DeleteStore")).Click();
|
||||
s.Driver.WaitForElement(By.Id("ConfirmInput")).SendKeys("DELETE");
|
||||
s.Driver.FindElement(By.Id("ConfirmContinue")).Click();
|
||||
s.Driver.FindElement(By.Id("Stores")).Click();
|
||||
s.Driver.Navigate().GoToUrl(storeUrl);
|
||||
Assert.Contains("ReturnUrl", s.Driver.Url);
|
||||
}
|
||||
|
@ -481,7 +469,7 @@ namespace BTCPayServer.Tests
|
|||
s.CreateNewStore();
|
||||
s.AddDerivationScheme();
|
||||
|
||||
s.Driver.FindElement(By.Id("Nav-Tokens")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Tokens")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewToken")).Click();
|
||||
s.Driver.FindElement(By.Id("RequestPairing")).Click();
|
||||
var pairingCode = AssertUrlHasPairingCode(s);
|
||||
|
@ -520,13 +508,11 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser();
|
||||
var (storeName, _) = s.CreateNewStore();
|
||||
(string storeName, _) = s.CreateNewStore();
|
||||
|
||||
s.Driver.FindElement(By.Id("Apps")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
|
||||
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
|
||||
s.Driver.FindElement(By.Name("AppName")).SendKeys("PoS" + Guid.NewGuid());
|
||||
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Point of Sale");
|
||||
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(storeName);
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.CssSelector(".template-item:nth-of-type(1) .btn-primary")).Click();
|
||||
s.Driver.FindElement(By.Id("BuyButtonText")).SendKeys("Take my money");
|
||||
|
@ -560,14 +546,12 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser();
|
||||
var (storeName, _) = s.CreateNewStore();
|
||||
(string storeName, _) = s.CreateNewStore();
|
||||
s.AddDerivationScheme();
|
||||
|
||||
s.Driver.FindElement(By.Id("Apps")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
|
||||
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
|
||||
s.Driver.FindElement(By.Name("AppName")).SendKeys("CF" + Guid.NewGuid());
|
||||
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund");
|
||||
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(storeName);
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter");
|
||||
s.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC");
|
||||
|
@ -590,13 +574,13 @@ namespace BTCPayServer.Tests
|
|||
s.CreateNewStore();
|
||||
s.AddDerivationScheme();
|
||||
|
||||
s.Driver.FindElement(By.Id("PaymentRequests")).Click();
|
||||
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
|
||||
s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click();
|
||||
s.Driver.FindElement(By.Id("Title")).SendKeys("Pay123");
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("700");
|
||||
s.Driver.FindElement(By.Id("Currency")).SendKeys("BTC");
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
s.Driver.FindElement(By.Name("ViewAppButton")).Click();
|
||||
s.Driver.FindElement(By.Id("ViewAppButton")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Equal("Amount due", s.Driver.FindElement(By.CssSelector("[data-test='amount-due-title']")).Text);
|
||||
Assert.Equal("Pay Invoice",
|
||||
|
@ -796,8 +780,9 @@ namespace BTCPayServer.Tests
|
|||
|
||||
TestLogs.LogInformation("Let's see if we can delete store with some webhooks inside");
|
||||
s.GoToStore(storeId, StoreNavPages.GeneralSettings);
|
||||
s.Driver.FindElement(By.Id("delete-store")).Click();
|
||||
s.Driver.WaitForElement(By.Id("ConfirmContinue")).Click();
|
||||
s.Driver.FindElement(By.Id("DeleteStore")).Click();
|
||||
s.Driver.WaitForElement(By.Id("ConfirmInput")).SendKeys("DELETE");
|
||||
s.Driver.FindElement(By.Id("ConfirmContinue")).Click();
|
||||
s.FindAlertMessage();
|
||||
}
|
||||
}
|
||||
|
@ -830,23 +815,22 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await s.StartAsync();
|
||||
s.RegisterNewUser(true);
|
||||
var (storeName, storeId) = s.CreateNewStore();
|
||||
var cryptoCode = "BTC";
|
||||
(string storeName, string storeId) = s.CreateNewStore();
|
||||
const string cryptoCode = "BTC";
|
||||
|
||||
// In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0',
|
||||
// then try to use the seed to sign the transaction
|
||||
s.GenerateWallet(cryptoCode, "", true);
|
||||
|
||||
//let's test quickly the receive wallet page
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Send")).Click();
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
|
||||
//you cannot use the Sign with NBX option without saving private keys when generating the wallet.
|
||||
Assert.DoesNotContain("nbx-seed", s.Driver.PageSource);
|
||||
|
||||
s.Driver.FindElement(By.Id("WalletReceive")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Receive")).Click();
|
||||
//generate a receiving address
|
||||
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
|
||||
Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed);
|
||||
|
@ -874,9 +858,8 @@ namespace BTCPayServer.Tests
|
|||
//change the wallet and ensure old address is not there and generating a new one does not result in the prev one
|
||||
s.GoToStore(storeId);
|
||||
s.GenerateWallet(cryptoCode, "", true);
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.Driver.FindElement(By.Id("WalletReceive")).Click();
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Receive")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
|
||||
|
||||
Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("address")).GetAttribute("value"));
|
||||
|
@ -905,9 +888,9 @@ namespace BTCPayServer.Tests
|
|||
Money.Coins(3.0m));
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.ClickOnAllSideMenus();
|
||||
s.GoToStore(storeId);
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
s.ClickOnAllSectionLinks();
|
||||
|
||||
// Make sure wallet info is correct
|
||||
s.GoToWalletSettings(storeId, cryptoCode);
|
||||
|
@ -916,21 +899,20 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("m/84'/1'/0'",
|
||||
s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).GetAttribute("value"));
|
||||
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
|
||||
// Make sure we can rescan, because we are admin!
|
||||
s.Driver.FindElement(By.Id("WalletRescan")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Rescan")).Click();
|
||||
Assert.Contains("The batch size make sure", s.Driver.PageSource);
|
||||
|
||||
// Check the tx sent earlier arrived
|
||||
s.Driver.FindElement(By.Id("WalletTransactions")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Transactions")).Click();
|
||||
|
||||
var walletTransactionLink = s.Driver.Url;
|
||||
Assert.Contains(tx.ToString(), s.Driver.PageSource);
|
||||
|
||||
// Send to bob
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Send")).Click();
|
||||
var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
SetTransactionOutput(s, 0, bob, 1);
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
|
@ -941,9 +923,8 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
Assert.Equal(walletTransactionLink, s.Driver.Url);
|
||||
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Send")).Click();
|
||||
|
||||
var jack = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
SetTransactionOutput(s, 0, jack, 0.01m);
|
||||
|
@ -959,9 +940,8 @@ namespace BTCPayServer.Tests
|
|||
//let's make bip21 more interesting
|
||||
bip21 += "&label=Solid Snake&message=Snake? Snake? SNAAAAKE!";
|
||||
var parsedBip21 = new BitcoinUrlBuilder(bip21, Network.RegTest);
|
||||
s.Driver.FindElement(By.Id("Wallets")).Click();
|
||||
s.Driver.FindElement(By.LinkText("Manage")).Click();
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
s.Driver.FindElement(By.Id($"StoreNav-Wallet{cryptoCode}")).Click();
|
||||
s.Driver.FindElement(By.Id("SectionNav-Send")).Click();
|
||||
s.Driver.FindElement(By.Id("bip21parse")).Click();
|
||||
s.Driver.SwitchTo().Alert().SendKeys(bip21);
|
||||
s.Driver.SwitchTo().Alert().Accept();
|
||||
|
@ -1126,7 +1106,7 @@ namespace BTCPayServer.Tests
|
|||
});
|
||||
s.GoToHome();
|
||||
//offline/external payout test
|
||||
s.Driver.FindElement(By.Id("NotificationsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("NotificationsHandle")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#notificationsForm button")).Click();
|
||||
|
||||
var newStore = s.CreateNewStore();
|
||||
|
@ -1241,7 +1221,6 @@ namespace BTCPayServer.Tests
|
|||
s.GoToStore(newStore.storeId, StoreNavPages.Payouts);
|
||||
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike)}-view")).Click();
|
||||
|
||||
|
||||
s.Driver.FindElement(By.Id($"{PayoutState.Completed}-view")).Click();
|
||||
if (!s.Driver.PageSource.Contains(bolt))
|
||||
{
|
||||
|
@ -1272,22 +1251,21 @@ namespace BTCPayServer.Tests
|
|||
s.RegisterNewUser(true);
|
||||
var cryptoCode = "BTC";
|
||||
(_, string storeId) = s.CreateNewStore();
|
||||
var network = s.Server.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode).NBitcoinNetwork;
|
||||
s.GoToStore(storeId);
|
||||
s.AddLightningNode(cryptoCode, LightningConnectionType.CLightning, false);
|
||||
s.GoToLightningSettings(storeId, cryptoCode);
|
||||
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);
|
||||
s.GoToApps();
|
||||
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
|
||||
s.Driver.FindElement(By.Id("StoreNav-CreateApp")).Click();
|
||||
s.Driver.FindElement(By.Id("SelectedAppType")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("option[value='PointOfSale']")).Click();
|
||||
s.Driver.FindElement(By.Id("AppName")).SendKeys(Guid.NewGuid().ToString());
|
||||
s.Driver.FindElement(By.Id("Create")).Click();
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
Thread.Sleep(5000);
|
||||
Assert.Contains("App successfully created", s.FindAlertMessage().Text);
|
||||
s.Driver.FindElement(By.Id("DefaultView")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("option[value='3']")).Click();
|
||||
s.Driver.FindElement(By.Id("SaveSettings")).Click();
|
||||
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
|
||||
Assert.Contains("App updated", s.FindAlertMessage().Text);
|
||||
|
||||
s.Driver.FindElement(By.Id("ViewApp")).Click();
|
||||
var btns = s.Driver.FindElements(By.ClassName("lnurl"));
|
||||
|
@ -1299,7 +1277,6 @@ namespace BTCPayServer.Tests
|
|||
Assert.EndsWith(choice, parsed.ToString());
|
||||
Assert.IsType<LNURLPayRequest>(await LNURL.LNURL.FetchInformation(parsed, new HttpClient()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1611,7 +1588,7 @@ retry:
|
|||
private static void CanSetupEmailCore(SeleniumTester s)
|
||||
{
|
||||
s.Driver.FindElement(By.Id("QuickFillDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.ClassName("dropdown-item")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("#quick-fill .dropdown-menu .dropdown-item:first-child")).Click();
|
||||
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
|
||||
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
|
||||
|
|
|
@ -959,7 +959,7 @@ namespace BTCPayServer.Tests
|
|||
private void AssertSearchInvoice(TestAccount acc, bool expected, string invoiceId, string filter)
|
||||
{
|
||||
var result =
|
||||
(Models.InvoicingModels.InvoicesModel)((ViewResult)acc.GetController<InvoiceController>()
|
||||
(InvoicesModel)((ViewResult)acc.GetController<InvoiceController>()
|
||||
.ListInvoices(new InvoicesModel { SearchTerm = filter }).Result).Model;
|
||||
Assert.Equal(expected, result.Invoices.Any(i => i.InvoiceId == invoiceId));
|
||||
}
|
||||
|
@ -2016,8 +2016,7 @@ namespace BTCPayServer.Tests
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanCreateAndDeleteApps()
|
||||
|
@ -2026,21 +2025,21 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.GrantAccessAsync();
|
||||
var user2 = tester.NewAccount();
|
||||
user2.GrantAccess();
|
||||
await user2.GrantAccessAsync();
|
||||
var apps = user.GetController<AppsController>();
|
||||
var apps2 = user2.GetController<AppsController>();
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp().Result).Model);
|
||||
var vm = Assert.IsType<CreateAppViewModel>(Assert.IsType<ViewResult>(apps.CreateApp(user.StoreId)).Model);
|
||||
Assert.NotNull(vm.SelectedAppType);
|
||||
Assert.Null(vm.AppName);
|
||||
vm.AppName = "test";
|
||||
vm.SelectedAppType = AppType.PointOfSale.ToString();
|
||||
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(vm).Result);
|
||||
var redirectToAction = Assert.IsType<RedirectToActionResult>(apps.CreateApp(user.StoreId, vm).Result);
|
||||
Assert.Equal(nameof(apps.UpdatePointOfSale), redirectToAction.ActionName);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model);
|
||||
var appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
var appList2 =
|
||||
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps().Result).Model);
|
||||
Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps2.ListApps(user2.StoreId).Result).Model);
|
||||
Assert.Single(appList.Apps);
|
||||
Assert.Empty(appList2.Apps);
|
||||
Assert.Equal("test", appList.Apps[0].AppName);
|
||||
|
@ -2051,7 +2050,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.IsType<ViewResult>(apps.DeleteApp(appList.Apps[0].Id).Result);
|
||||
redirectToAction = Assert.IsType<RedirectToActionResult>(apps.DeleteAppPost(appList.Apps[0].Id).Result);
|
||||
Assert.Equal(nameof(apps.ListApps), redirectToAction.ActionName);
|
||||
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps().Result).Model);
|
||||
appList = Assert.IsType<ListAppsViewModel>(Assert.IsType<ViewResult>(apps.ListApps(user.StoreId).Result).Model);
|
||||
Assert.Empty(appList.Apps);
|
||||
}
|
||||
}
|
||||
|
|
285
BTCPayServer/Components/MainNav/Default.cshtml
Normal file
285
BTCPayServer/Components/MainNav/Default.cshtml
Normal file
|
@ -0,0 +1,285 @@
|
|||
@using BTCPayServer.Views.Server
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using BTCPayServer.Views.Apps
|
||||
@using BTCPayServer.Views.Invoice
|
||||
@using BTCPayServer.Views.Manage
|
||||
@using BTCPayServer.Views.PaymentRequest
|
||||
@using BTCPayServer.Views.Wallets
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject ISettingsRepository SettingsRepository
|
||||
|
||||
@model BTCPayServer.Components.MainNav.MainNavViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
|
||||
@{
|
||||
var theme = await SettingsRepository.GetTheme();
|
||||
}
|
||||
|
||||
<nav id="mainNav" class="d-flex flex-column justify-content-between">
|
||||
<div class="accordion px-3 px-lg-4">
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
@if (Model.Store == null)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Payments-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Payments" aria-expanded="true" aria-controls="Nav-Payments">
|
||||
Payments
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Payments" class="accordion-collapse collapse show" aria-labelledby="Nav-Payments-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(InvoiceNavPages))" id="Nav-Invoices">
|
||||
<vc:icon symbol="invoice"/>
|
||||
<span>Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
@* FIXME: The wallets item is in here only for the tests *@
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Wallets" asp-action="ListWallets" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(WalletsNavPages))" id="Nav-Wallets">
|
||||
<vc:icon symbol="wallet-onchain"/>
|
||||
<span>Wallets</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Plugins-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Plugins" aria-expanded="true" aria-controls="Nav-Plugins">
|
||||
Plugins
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Plugins" class="accordion-collapse collapse show" aria-labelledby="Nav-Plugins-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<vc:ui-extension-point location="header-nav" model="@Model"/>
|
||||
@* TODO: Limit this to admins *@
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Server" asp-action="ListPlugins" class="nav-link js-scroll-trigger @ViewData.IsActivePage(ServerNavPages.Plugins)" id="Nav-AddPlugin">
|
||||
<vc:icon symbol="new"/>
|
||||
<span>Add Plugin</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
@foreach (var scheme in Model.DerivationSchemes.OrderBy(scheme => scheme.Collapsed))
|
||||
{
|
||||
var isSetUp = !string.IsNullOrWhiteSpace(scheme.Value);
|
||||
<li class="nav-item">
|
||||
@if (isSetUp)
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="WalletSettings" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Modify{scheme.Crypto}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "disabled")"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.Crypto} " : "")Wallet</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="SetupWallet" asp-route-cryptoCode="@scheme.Crypto" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Modify{scheme.Crypto}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "disabled")"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.Crypto} " : "")Wallet</span>
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
@foreach (var scheme in Model.LightningNodes)
|
||||
{
|
||||
var isSetUp = !string.IsNullOrWhiteSpace(scheme.Address);
|
||||
<li class="nav-item">
|
||||
@if (isSetUp)
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="LightningSettings" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--@(scheme.Enabled ? "enabled" : "disabled")"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.CryptoCode} " : "")Lightning</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-area="" asp-controller="Stores" asp-action="SetupLightningNode" asp-route-cryptoCode="@scheme.CryptoCode" asp-route-storeId="@Model.Store.Id" class="nav-link" id="@($"StoreNav-Lightning{scheme.CryptoCode}")">
|
||||
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.CryptoCode} " : "")Lightning</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Payments-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Payments" aria-expanded="true" aria-controls="Nav-Payments">
|
||||
Payments
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Payments" class="accordion-collapse collapse show" aria-labelledby="Nav-Payments-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
@foreach (var scheme in Model.DerivationSchemes.OrderBy(scheme => scheme.Collapsed))
|
||||
{
|
||||
var isSetUp = !string.IsNullOrWhiteSpace(scheme.Value);
|
||||
if (isSetUp && scheme.WalletSupported)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Wallets" asp-action="WalletTransactions" asp-route-walletId="@scheme.WalletId" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(WalletsNavPages), scheme.Crypto)" id="@($"StoreNav-Wallet{scheme.Crypto}")">
|
||||
<vc:icon symbol="wallet-onchain"/>
|
||||
<span>@(Model.AltcoinsBuild ? $"{scheme.Crypto} " : "")Wallet</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(InvoiceNavPages))" id="StoreNav-Invoices">
|
||||
<vc:icon symbol="invoice"/>
|
||||
<span>Invoices</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="PaymentRequest" asp-action="GetPaymentRequests" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(PaymentRequestsNavPages))" id="StoreNav-PaymentRequests">
|
||||
<vc:icon symbol="payment-1"/>
|
||||
<span>Requests</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="StorePullPayments" asp-action="PullPayments" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" id="StoreNav-@(nameof(StoreNavPages.PullPayments))">
|
||||
<vc:icon symbol="payment-2"/>
|
||||
<span>Pull Payments</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="StorePullPayments" asp-action="Payouts" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" id="StoreNav-@(nameof(StoreNavPages.Payouts))">
|
||||
<vc:icon symbol="payment-2"/>
|
||||
<span>Payouts</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Apps-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Apps" aria-expanded="true" aria-controls="Nav-Apps">
|
||||
Apps
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Apps" class="accordion-collapse collapse show" aria-labelledby="Nav-Apps-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
@foreach (var app in Model.Apps)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Apps" asp-action="@app.Action" asp-route-appId="@app.Id" class="nav-link js-scroll-trigger @ViewData.IsActivePage(AppsNavPages.Update, app.Id)" id="@($"StoreNav-App-{app.Id}")">
|
||||
<vc:icon symbol="@app.AppType.ToLower()"/>
|
||||
<span>@app.AppName</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Apps" asp-action="CreateApp" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActivePage(AppsNavPages.Create)" id="StoreNav-CreateApp">
|
||||
<vc:icon symbol="new"/>
|
||||
<span>New App</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<header class="accordion-header" id="Nav-Manage-Header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#Nav-Manage" aria-expanded="true" aria-controls="Nav-Manage">
|
||||
Manage
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</header>
|
||||
<div id="Nav-Manage" class="accordion-collapse collapse show" aria-labelledby="Nav-Manage-Header">
|
||||
<div class="accordion-body">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Model.Store.Id" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(StoreNavPages))" id="StoreNav-Invoices">
|
||||
<vc:icon symbol="settings"/>
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<script>
|
||||
const navCollapsed = window.localStorage.getItem('btcpay-nav-collapsed')
|
||||
const collapsed = navCollapsed ? JSON.parse(navCollapsed) : []
|
||||
collapsed.forEach(id => {
|
||||
const el = document.getElementById(id)
|
||||
const btn = el && el.previousElementSibling.querySelector(`[aria-controls="${id}"]`)
|
||||
if (el && btn) {
|
||||
el.classList.remove('show')
|
||||
btn.classList.add('collapsed')
|
||||
btn.setAttribute('aria-expanded', 'false')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
}
|
||||
else if (Env.IsSecure)
|
||||
{
|
||||
<ul class="navbar-nav">
|
||||
@if (!(await SettingsRepository.GetPolicies()).LockSubscription)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger" id="Nav-Register">Register</a>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp-action="Login" class="nav-link js-scroll-trigger" id="Nav-Login">Log in</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<ul id="mainNavSettings" class="navbar-nav border-top p-3 px-lg-4">
|
||||
@if (User.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(ServerNavPages))" id="Nav-ServerSettings">
|
||||
<vc:icon symbol="server-settings"/>
|
||||
<span>Server Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Manage" asp-action="Index" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(ManageNavPages))" id="Nav-Account">
|
||||
<vc:icon symbol="account"/>
|
||||
<span>Account</span>
|
||||
</a>
|
||||
</li>
|
||||
@if (!theme.CustomTheme)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<vc:theme-switch css-class="nav-link"/>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp- asp-action="Logout" class="nav-link js-scroll-trigger" id="Logout">
|
||||
<i class="fa fa-sign-out"></i>
|
||||
<span>Logout</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
79
BTCPayServer/Components/MainNav/MainNav.cs
Normal file
79
BTCPayServer/Components/MainNav/MainNav.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Secp256k1;
|
||||
|
||||
namespace BTCPayServer.Components.MainNav
|
||||
{
|
||||
public class MainNav : ViewComponent
|
||||
{
|
||||
private const string RootName = "Global";
|
||||
private readonly AppService _appService;
|
||||
private readonly StoreRepository _storeRepo;
|
||||
private readonly StoresController _storesController;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
|
||||
public MainNav(
|
||||
AppService appService,
|
||||
StoreRepository storeRepo,
|
||||
StoresController storesController,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_storeRepo = storeRepo;
|
||||
_appService = appService;
|
||||
_userManager = userManager;
|
||||
_networkProvider = networkProvider;
|
||||
_storesController = storesController;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync()
|
||||
{
|
||||
var store = ViewContext.HttpContext.GetStoreData();
|
||||
var vm = new MainNavViewModel { Store = store };
|
||||
#if ALTCOINS
|
||||
vm.AltcoinsBuild = true;
|
||||
#endif
|
||||
if (store != null)
|
||||
{
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
|
||||
// Wallets
|
||||
_storesController.AddPaymentMethods(store, storeBlob,
|
||||
out var derivationSchemes, out var lightningNodes);
|
||||
vm.DerivationSchemes = derivationSchemes;
|
||||
vm.LightningNodes = lightningNodes;
|
||||
|
||||
// Apps
|
||||
var apps = await _appService.GetAllApps(UserId, false, store.Id);
|
||||
vm.Apps = apps.Select(a => new StoreApp
|
||||
{
|
||||
Id = a.Id,
|
||||
AppName = a.AppName,
|
||||
AppType = a.AppType,
|
||||
IsOwner = a.IsOwner
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
private string UserId => _userManager.GetUserId(HttpContext.User);
|
||||
}
|
||||
}
|
24
BTCPayServer/Components/MainNav/MainNavViewModel.cs
Normal file
24
BTCPayServer/Components/MainNav/MainNavViewModel.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
|
||||
namespace BTCPayServer.Components.MainNav
|
||||
{
|
||||
public class MainNavViewModel
|
||||
{
|
||||
public StoreData Store { get; set; }
|
||||
public List<StoreDerivationScheme> DerivationSchemes { get; set; }
|
||||
public List<StoreLightningNode> LightningNodes { get; set; }
|
||||
public List<StoreApp> Apps { get; set; }
|
||||
public bool AltcoinsBuild { get; set; }
|
||||
}
|
||||
|
||||
public class StoreApp
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string AppName { get; set; }
|
||||
public string AppType { get; set; }
|
||||
public string Action { get => $"Update{AppType}"; }
|
||||
public bool IsOwner { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,23 +1,19 @@
|
|||
@inject UserManager<ApplicationUser> UserManager
|
||||
@inject ISettingsRepository SettingsRepository
|
||||
@using BTCPayServer.HostedServices
|
||||
@using BTCPayServer.Views.Notifications
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Services
|
||||
@model BTCPayServer.Components.NotificationsDropdown.NotificationSummaryViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
|
||||
@if (Model.UnseenCount > 0)
|
||||
{
|
||||
<li class="nav-item dropdown" id="notifications-nav-item">
|
||||
<a class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(NotificationsNavPages))" href="#" id="NotificationsDropdownToggle" role="button" data-bs-toggle="dropdown">
|
||||
<span class="d-inline-block d-lg-none">Notifications</span>
|
||||
<i class="fa fa-bell d-lg-inline-block d-none"></i>
|
||||
<span class="notification-badge badge rounded-pill bg-danger">@Model.UnseenCount</span>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end text-center notification-dropdown" aria-labelledby="NotificationsDropdownToggle">
|
||||
<div id="Notifications">
|
||||
@if (Model.UnseenCount > 0)
|
||||
{
|
||||
<button id="NotificationsHandle" class="mainMenuButton @ViewData.IsActiveCategory(typeof(NotificationsNavPages))" title="Notifications" type="button" data-bs-toggle="dropdown">
|
||||
<vc:icon symbol="notifications" />
|
||||
<span class="badge rounded-pill bg-danger p-1 ms-1" id="NotificationsBadge">@Model.UnseenCount</span>
|
||||
</button>
|
||||
<div class="dropdown-menu text-center" id="NotificationsDropdown" aria-labelledby="NotificationsHandle">
|
||||
<div class="d-flex align-items-center justify-content-between py-3 px-4 border-bottom border-light">
|
||||
<h5 class="m-0">Notifications</h5>
|
||||
<form id="notificationsForm" asp-controller="Notifications" asp-action="MarkAllAsSeen" asp-route-returnUrl="@Context.Request.GetCurrentPathWithQueryString()" method="post">
|
||||
|
@ -30,7 +26,6 @@
|
|||
<div class="me-3">
|
||||
<vc:icon symbol="note" />
|
||||
</div>
|
||||
|
||||
<div class="notification-item__content">
|
||||
<div class="text-start text-wrap">
|
||||
@notif.Body
|
||||
|
@ -45,13 +40,11 @@
|
|||
<a asp-controller="Notifications" asp-action="Index">View all</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item" id="notifications-nav-item">
|
||||
<a asp-controller="Notifications" asp-action="Index" title="Notifications" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(NotificationsNavPages))" id="Notifications">
|
||||
<span class="d-lg-none d-sm-block">Notifications</span><i class="fa fa-bell d-lg-inline-block d-none"></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-controller="Notifications" asp-action="Index" id="NotificationsHandle" class="mainMenuButton @ViewData.IsActiveCategory(typeof(NotificationsNavPages))" title="Notifications">
|
||||
<vc:icon symbol="notifications" />
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Models.NotificationViewModels;
|
||||
|
||||
namespace BTCPayServer.Components.NotificationsDropdown
|
||||
{
|
||||
|
|
20
BTCPayServer/Components/StoreSelector/Default.cshtml
Normal file
20
BTCPayServer/Components/StoreSelector/Default.cshtml
Normal file
|
@ -0,0 +1,20 @@
|
|||
@model BTCPayServer.Components.StoreSelector.StoreSelectorViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
|
||||
<div id="StoreSelector">
|
||||
<div id="StoreSelectorDropdown" class="dropdown only-for-js">
|
||||
<button id="StoreSelectorToggle" class="btn btn-secondary dropdown-toggle rounded-pill @(Model.CurrentStoreId == null ? "text-secondary" :"")" type="button" data-bs-toggle="dropdown" aria-expanded="false">@(Model.CurrentStoreId == null ? "Select Store" : Model.CurrentDisplayName)</button>
|
||||
<ul id="StoreSelectorMenu" class="dropdown-menu" aria-labelledby="StoreSelectorToggle">
|
||||
@foreach (var option in Model.Options)
|
||||
{
|
||||
<li><a asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@option.Value" class="dropdown-item@(option.Selected ? " active" : "")" id="StoreSelectorMenuItem-@option.Value">@option.Text</a></li>
|
||||
}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a asp-controller="UserStores" asp-action="CreateStore" class="dropdown-item" id="StoreSelectorMenuItem-Create">Create Store</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<noscript>
|
||||
<span class="h5 mb-0 me-2">@Model.CurrentDisplayName</span>
|
||||
<a asp-controller="UserStores" asp-action="ListStores">Stores</a>
|
||||
</noscript>
|
||||
</div>
|
48
BTCPayServer/Components/StoreSelector/StoreSelector.cs
Normal file
48
BTCPayServer/Components/StoreSelector/StoreSelector.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using NBitcoin.Secp256k1;
|
||||
|
||||
namespace BTCPayServer.Components.StoreSelector
|
||||
{
|
||||
public class StoreSelector : ViewComponent
|
||||
{
|
||||
private const string RootName = "Global";
|
||||
private readonly StoreRepository _storeRepo;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public StoreSelector(StoreRepository storeRepo, UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_storeRepo = storeRepo;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync()
|
||||
{
|
||||
var userId = _userManager.GetUserId(UserClaimsPrincipal);
|
||||
var stores = await _storeRepo.GetStoresByUserId(userId);
|
||||
var currentStore = ViewContext.HttpContext.GetStoreData();
|
||||
var options = stores
|
||||
.Select(store => new SelectListItem
|
||||
{
|
||||
Text = store.StoreName,
|
||||
Value = store.Id,
|
||||
Selected = store.Id == currentStore?.Id
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var vm = new StoreSelectorViewModel
|
||||
{
|
||||
Options = options,
|
||||
CurrentStoreId = currentStore?.Id,
|
||||
CurrentDisplayName = currentStore?.StoreName ?? RootName
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace BTCPayServer.Components.StoreSelector
|
||||
{
|
||||
public class StoreSelectorViewModel
|
||||
{
|
||||
public List<SelectListItem> Options { get; set; }
|
||||
public string CurrentStoreId { get; set; }
|
||||
public string CurrentDisplayName { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
@model BTCPayServer.Components.ThemeSwitch.ThemeSwitchViewModel
|
||||
|
||||
<button class="btcpay-theme-switch @Model.CssClass">
|
||||
<svg class="@(string.IsNullOrEmpty(Model.Responsive) || Model.Responsive == "none" ? "d-inline-block" : $"d-{Model.Responsive}-inline-block d-none")" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10">
|
||||
<path class="btcpay-theme-switch-dark" transform="translate(1 1)" d="M2.72 0A3.988 3.988 0 000 3.78c0 2.21 1.79 4 4 4 1.76 0 3.25-1.14 3.78-2.72-.4.13-.83.22-1.28.22-2.21 0-4-1.79-4-4 0-.45.08-.88.22-1.28z"/>
|
||||
<path class="btcpay-theme-switch-light" transform="translate(1 1)" d="M4 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5S4.28 0 4 0zM1.5 1c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm5 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM4 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM.5 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM1.5 6c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm5 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM4 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5S4.28 7 4 7z"/>
|
||||
<svg class="d-inline-block icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15">
|
||||
<path class="btcpay-theme-switch-dark" transform="translate(3 3)" d="M2.72 0A3.988 3.988 0 000 3.78c0 2.21 1.79 4 4 4 1.76 0 3.25-1.14 3.78-2.72-.4.13-.83.22-1.28.22-2.21 0-4-1.79-4-4 0-.45.08-.88.22-1.28z"/>
|
||||
<path class="btcpay-theme-switch-light" transform="translate(3 3)" d="M4 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5S4.28 0 4 0zM1.5 1c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm5 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM4 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM.5 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM1.5 6c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm5 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM4 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5S4.28 7 4 7z"/>
|
||||
</svg>
|
||||
@if (!string.IsNullOrEmpty(Model.Responsive))
|
||||
{
|
||||
<span class="d-@Model.Responsive-none d-inline-block"><span class="btcpay-theme-switch-dark">Dark theme</span><span class="btcpay-theme-switch-light">Light theme</span></span>
|
||||
}
|
||||
<span class="d-inline-block"><span class="btcpay-theme-switch-dark">Dark theme</span><span class="btcpay-theme-switch-light">Light theme</span></span>
|
||||
</button>
|
||||
|
|
|
@ -4,12 +4,11 @@ namespace BTCPayServer.Components.ThemeSwitch
|
|||
{
|
||||
public class ThemeSwitch : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke(string cssClass = null, string responsive = null)
|
||||
public IViewComponentResult Invoke(string cssClass = null)
|
||||
{
|
||||
var vm = new ThemeSwitchViewModel
|
||||
{
|
||||
CssClass = cssClass,
|
||||
Responsive = responsive
|
||||
};
|
||||
return View(vm);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,5 @@ namespace BTCPayServer.Components.ThemeSwitch
|
|||
public class ThemeSwitchViewModel
|
||||
{
|
||||
public string CssClass { get; set; }
|
||||
public string Responsive { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using BTCPayServer.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
|
@ -21,6 +23,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[HttpGet("{appId}/settings/crowdfund")]
|
||||
public async Task<IActionResult> UpdateCrowdfund(string appId)
|
||||
{
|
||||
|
@ -64,8 +67,8 @@ namespace BTCPayServer.Controllers
|
|||
};
|
||||
return View(vm);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("{appId}/settings/crowdfund")]
|
||||
|
||||
[HttpPost("{appId}/settings/crowdfund")]
|
||||
public async Task<IActionResult> UpdateCrowdfund(string appId, UpdateCrowdfundViewModel vm, string command)
|
||||
{
|
||||
var app = await GetOwnedApp(appId, AppType.Crowdfund);
|
||||
|
@ -77,7 +80,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
try
|
||||
{
|
||||
vm.PerksTemplate = _AppService.SerializeTemplate(_AppService.Parse(vm.PerksTemplate, vm.TargetCurrency));
|
||||
vm.PerksTemplate = _appService.SerializeTemplate(_appService.Parse(vm.PerksTemplate, vm.TargetCurrency));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -155,9 +158,9 @@ namespace BTCPayServer.Controllers
|
|||
app.TagAllInvoices = vm.UseAllStoreInvoices;
|
||||
app.SetSettings(newSettings);
|
||||
|
||||
await _AppService.UpdateOrCreateApp(app);
|
||||
await _appService.UpdateOrCreateApp(app);
|
||||
|
||||
_EventAggregator.Publish(new AppUpdated()
|
||||
_eventAggregator.Publish(new AppUpdated()
|
||||
{
|
||||
AppId = appId,
|
||||
StoreId = app.StoreDataId,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Data;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -88,13 +87,13 @@ namespace BTCPayServer.Controllers
|
|||
public bool? RedirectAutomatically { get; set; }
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{appId}/settings/pos")]
|
||||
[HttpGet("{appId}/settings/pos")]
|
||||
public async Task<IActionResult> UpdatePointOfSale(string appId)
|
||||
{
|
||||
var app = await GetOwnedApp(appId, AppType.PointOfSale);
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
settings.DefaultView = settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
|
||||
settings.EnableShoppingCart = false;
|
||||
|
@ -143,8 +142,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
try
|
||||
{
|
||||
|
||||
var items = _AppService.Parse(settings.Template, settings.Currency);
|
||||
var items = _appService.Parse(settings.Template, settings.Currency);
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine($"<form method=\"POST\" action=\"{encoder.Encode(appUrl)}\">");
|
||||
builder.AppendLine($" <input type=\"hidden\" name=\"email\" value=\"customer@example.com\" />");
|
||||
|
@ -162,10 +160,10 @@ namespace BTCPayServer.Controllers
|
|||
vm.ExampleCallback = "{\n \"id\":\"SkdsDghkdP3D3qkj7bLq3\",\n \"url\":\"https://btcpay.example.com/invoice?id=SkdsDghkdP3D3qkj7bLq3\",\n \"status\":\"paid\",\n \"price\":10,\n \"currency\":\"EUR\",\n \"invoiceTime\":1520373130312,\n \"expirationTime\":1520374030312,\n \"currentTime\":1520373179327,\n \"exceptionStatus\":false,\n \"buyerFields\":{\n \"buyerEmail\":\"customer@example.com\",\n \"buyerNotify\":false\n },\n \"paymentSubtotals\": {\n \"BTC\":114700\n },\n \"paymentTotals\": {\n \"BTC\":118400\n },\n \"transactionCurrency\": \"BTC\",\n \"amountPaid\": \"1025900\",\n \"exchangeRates\": {\n \"BTC\": {\n \"EUR\": 8721.690715789999,\n \"USD\": 10817.99\n }\n }\n}";
|
||||
return View(vm);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("{appId}/settings/pos")]
|
||||
|
||||
[HttpPost("{appId}/settings/pos")]
|
||||
public async Task<IActionResult> UpdatePointOfSale(string appId, UpdatePointOfSaleViewModel vm)
|
||||
{
|
||||
{
|
||||
var app = await GetOwnedApp(appId, AppType.PointOfSale);
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
|
@ -178,7 +176,7 @@ namespace BTCPayServer.Controllers
|
|||
ModelState.AddModelError(nameof(vm.Currency), "Invalid currency");
|
||||
try
|
||||
{
|
||||
vm.Template = _AppService.SerializeTemplate(_AppService.Parse(vm.Template, vm.Currency));
|
||||
vm.Template = _appService.SerializeTemplate(_appService.Parse(vm.Template, vm.Currency));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -211,12 +209,11 @@ namespace BTCPayServer.Controllers
|
|||
RedirectAutomatically = string.IsNullOrEmpty(vm.RedirectAutomatically) ? (bool?)null : bool.Parse(vm.RedirectAutomatically),
|
||||
RequiresRefundEmail = vm.RequiresRefundEmail,
|
||||
});
|
||||
await _AppService.UpdateOrCreateApp(app);
|
||||
await _appService.UpdateOrCreateApp(app);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App updated";
|
||||
return RedirectToAction(nameof(UpdatePointOfSale), new { appId });
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int[] ListSplit(string list, string separator = ",")
|
||||
{
|
||||
if (string.IsNullOrEmpty(list))
|
||||
|
@ -229,7 +226,7 @@ namespace BTCPayServer.Controllers
|
|||
Regex charsToDestroy = new Regex(@"[^\d|\" + separator + "]");
|
||||
list = charsToDestroy.Replace(list, "");
|
||||
|
||||
return list.Split(separator, System.StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
|
||||
return list.Split(separator, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,10 @@ using BTCPayServer.Data;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -18,65 +15,58 @@ using Microsoft.AspNetCore.Mvc;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
[Route("apps")]
|
||||
public partial class AppsController : Controller
|
||||
{
|
||||
public AppsController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
ApplicationDbContextFactory contextFactory,
|
||||
EventAggregator eventAggregator,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
CurrencyNameTable currencies,
|
||||
EmailSenderFactory emailSenderFactory,
|
||||
Services.Stores.StoreRepository storeRepository,
|
||||
AppService AppService)
|
||||
StoreRepository storeRepository,
|
||||
AppService appService)
|
||||
{
|
||||
_UserManager = userManager;
|
||||
_ContextFactory = contextFactory;
|
||||
_EventAggregator = eventAggregator;
|
||||
_NetworkProvider = networkProvider;
|
||||
_userManager = userManager;
|
||||
_eventAggregator = eventAggregator;
|
||||
_currencies = currencies;
|
||||
_emailSenderFactory = emailSenderFactory;
|
||||
_storeRepository = storeRepository;
|
||||
_AppService = AppService;
|
||||
_appService = appService;
|
||||
}
|
||||
|
||||
private readonly UserManager<ApplicationUser> _UserManager;
|
||||
private readonly ApplicationDbContextFactory _ContextFactory;
|
||||
private readonly EventAggregator _EventAggregator;
|
||||
private readonly BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly CurrencyNameTable _currencies;
|
||||
private readonly EmailSenderFactory _emailSenderFactory;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly AppService _AppService;
|
||||
private readonly AppService _appService;
|
||||
|
||||
public string CreatedAppId { get; set; }
|
||||
|
||||
|
||||
[HttpGet("/stores/{storeId}/apps")]
|
||||
public async Task<IActionResult> ListApps(
|
||||
string storeId,
|
||||
string sortOrder = null,
|
||||
string sortOrderColumn = null
|
||||
)
|
||||
{
|
||||
var apps = await _AppService.GetAllApps(GetUserId());
|
||||
var apps = await _appService.GetAllApps(GetUserId(), false, CurrentStore.Id);
|
||||
|
||||
if (sortOrder != null && sortOrderColumn != null)
|
||||
{
|
||||
apps = apps.OrderByDescending(app =>
|
||||
{
|
||||
switch (sortOrderColumn)
|
||||
{
|
||||
switch (sortOrderColumn)
|
||||
{
|
||||
case nameof(app.AppName):
|
||||
return app.AppName;
|
||||
case nameof(app.StoreName):
|
||||
return app.StoreName;
|
||||
case nameof(app.AppType):
|
||||
return app.AppType;
|
||||
default:
|
||||
return app.Id;
|
||||
}
|
||||
}).ToArray();
|
||||
case nameof(app.AppName):
|
||||
return app.AppName;
|
||||
case nameof(app.StoreName):
|
||||
return app.StoreName;
|
||||
case nameof(app.AppType):
|
||||
return app.AppType;
|
||||
default:
|
||||
return app.Id;
|
||||
}
|
||||
}).ToArray();
|
||||
|
||||
switch (sortOrder)
|
||||
{
|
||||
|
@ -90,64 +80,27 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
return View(new ListAppsViewModel()
|
||||
return View(new ListAppsViewModel
|
||||
{
|
||||
Apps = apps
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{appId}/delete")]
|
||||
public async Task<IActionResult> DeleteAppPost(string appId)
|
||||
[HttpGet("/stores/{storeId}/apps/create")]
|
||||
public IActionResult CreateApp(string storeId)
|
||||
{
|
||||
var appData = await GetOwnedApp(appId);
|
||||
if (appData == null)
|
||||
return NotFound();
|
||||
if (await _AppService.DeleteApp(appData))
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App deleted successfully.";
|
||||
return RedirectToAction(nameof(ListApps));
|
||||
return View(new CreateAppViewModel
|
||||
{
|
||||
StoreId = CurrentStore.Id
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("create")]
|
||||
public async Task<IActionResult> CreateApp()
|
||||
[HttpPost("/stores/{storeId}/apps/create")]
|
||||
public async Task<IActionResult> CreateApp(string storeId, CreateAppViewModel vm)
|
||||
{
|
||||
var stores = await _AppService.GetOwnedStores(GetUserId());
|
||||
if (stores.Length == 0)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Html =
|
||||
$"Error: You need to create at least one store. <a href='{(Url.Action("CreateStore", "UserStores"))}' class='alert-link'>Create store</a>",
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(ListApps));
|
||||
}
|
||||
var vm = new CreateAppViewModel();
|
||||
vm.SetStores(stores);
|
||||
return View(vm);
|
||||
}
|
||||
vm.StoreId = CurrentStore.Id;
|
||||
|
||||
[HttpPost]
|
||||
[Route("create")]
|
||||
public async Task<IActionResult> CreateApp(CreateAppViewModel vm)
|
||||
{
|
||||
var stores = await _AppService.GetOwnedStores(GetUserId());
|
||||
if (stores.Length == 0)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Html =
|
||||
$"Error: You need to create at least one store. <a href='{(Url.Action("CreateStore", "UserStores"))}' class='alert-link'>Create store</a>",
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction(nameof(ListApps));
|
||||
}
|
||||
var selectedStore = vm.SelectedStore;
|
||||
vm.SetStores(stores);
|
||||
vm.SelectedStore = selectedStore;
|
||||
|
||||
if (!Enum.TryParse<AppType>(vm.SelectedAppType, out AppType appType))
|
||||
if (!Enum.TryParse(vm.SelectedAppType, out AppType appType))
|
||||
ModelState.AddModelError(nameof(vm.SelectedAppType), "Invalid App Type");
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
|
@ -155,14 +108,9 @@ namespace BTCPayServer.Controllers
|
|||
return View(vm);
|
||||
}
|
||||
|
||||
if (!stores.Any(s => s.Id == selectedStore))
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "You are not owner of this store";
|
||||
return RedirectToAction(nameof(ListApps));
|
||||
}
|
||||
var appData = new AppData
|
||||
{
|
||||
StoreDataId = selectedStore,
|
||||
StoreDataId = CurrentStore.Id,
|
||||
Name = vm.AppName,
|
||||
AppType = appType.ToString()
|
||||
};
|
||||
|
@ -171,18 +119,16 @@ namespace BTCPayServer.Controllers
|
|||
switch (appType)
|
||||
{
|
||||
case AppType.Crowdfund:
|
||||
var emptyCrowdfund = new CrowdfundSettings();
|
||||
emptyCrowdfund.TargetCurrency = defaultCurrency;
|
||||
var emptyCrowdfund = new CrowdfundSettings { TargetCurrency = defaultCurrency };
|
||||
appData.SetSettings(emptyCrowdfund);
|
||||
break;
|
||||
case AppType.PointOfSale:
|
||||
var empty = new PointOfSaleSettings();
|
||||
empty.Currency = defaultCurrency;
|
||||
var empty = new PointOfSaleSettings { Currency = defaultCurrency };
|
||||
appData.SetSettings(empty);
|
||||
break;
|
||||
}
|
||||
|
||||
await _AppService.UpdateOrCreateApp(appData);
|
||||
await _appService.UpdateOrCreateApp(appData);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App successfully created";
|
||||
CreatedAppId = appData.Id;
|
||||
|
||||
|
@ -193,19 +139,10 @@ namespace BTCPayServer.Controllers
|
|||
case AppType.Crowdfund:
|
||||
return RedirectToAction(nameof(UpdateCrowdfund), new { appId = appData.Id });
|
||||
default:
|
||||
return RedirectToAction(nameof(ListApps));
|
||||
return RedirectToAction(nameof(ListApps), new { storeId = appData.StoreDataId });
|
||||
}
|
||||
}
|
||||
|
||||
async Task<string> GetStoreDefaultCurrentIfEmpty(string storeId, string currency)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(currency))
|
||||
{
|
||||
currency = (await _storeRepository.FindStore(storeId)).GetStoreBlob().DefaultCurrency;
|
||||
}
|
||||
return currency.Trim().ToUpperInvariant();
|
||||
}
|
||||
|
||||
[HttpGet("{appId}/delete")]
|
||||
public async Task<IActionResult> DeleteApp(string appId)
|
||||
{
|
||||
|
@ -215,14 +152,39 @@ namespace BTCPayServer.Controllers
|
|||
return View("Confirm", new ConfirmModel("Delete app", $"The app <strong>{appData.Name}</strong> and its settings will be permanently deleted. Are you sure?", "Delete"));
|
||||
}
|
||||
|
||||
[HttpPost("{appId}/delete")]
|
||||
public async Task<IActionResult> DeleteAppPost(string appId)
|
||||
{
|
||||
var appData = await GetOwnedApp(appId);
|
||||
if (appData == null)
|
||||
return NotFound();
|
||||
if (await _appService.DeleteApp(appData))
|
||||
TempData[WellKnownTempData.SuccessMessage] = "App deleted successfully.";
|
||||
return RedirectToAction(nameof(ListApps), new { storeId = appData.StoreDataId });
|
||||
}
|
||||
|
||||
async Task<string> GetStoreDefaultCurrentIfEmpty(string storeId, string currency)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(currency))
|
||||
{
|
||||
currency = (await _storeRepository.FindStore(storeId)).GetStoreBlob().DefaultCurrency;
|
||||
}
|
||||
return currency.Trim().ToUpperInvariant();
|
||||
}
|
||||
|
||||
private Task<AppData> GetOwnedApp(string appId, AppType? type = null)
|
||||
{
|
||||
return _AppService.GetAppDataIfOwner(GetUserId(), appId, type);
|
||||
return _appService.GetAppDataIfOwner(GetUserId(), appId, type);
|
||||
}
|
||||
|
||||
private string GetUserId()
|
||||
{
|
||||
return _UserManager.GetUserId(User);
|
||||
return _userManager.GetUserId(User);
|
||||
}
|
||||
|
||||
private StoreData CurrentStore
|
||||
{
|
||||
get => HttpContext.GetStoreData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("invoices/{invoiceId}")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public async Task<IActionResult> Invoice(string invoiceId)
|
||||
{
|
||||
var invoice = (await _InvoiceRepository.GetInvoices(new InvoiceQuery()
|
||||
|
@ -97,12 +97,12 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
var store = await _StoreRepository.FindStore(invoice.StoreId);
|
||||
var invoiceState = invoice.GetInvoiceState();
|
||||
var model = new InvoiceDetailsModel()
|
||||
var model = new InvoiceDetailsModel
|
||||
{
|
||||
StoreId = store.Id,
|
||||
StoreName = store.StoreName,
|
||||
StoreLink = Url.Action(nameof(StoresController.PaymentMethods), "Stores", new { storeId = store.Id }),
|
||||
PaymentRequestLink = Url.Action(nameof(PaymentRequestController.ViewPaymentRequest), "PaymentRequest", new { id = invoice.Metadata.PaymentRequestId }),
|
||||
PaymentRequestLink = Url.Action(nameof(PaymentRequestController.ViewPaymentRequest), "PaymentRequest", new { payReqId = invoice.Metadata.PaymentRequestId }),
|
||||
Id = invoice.Id,
|
||||
State = invoiceState.Status.ToModernStatus().ToString(),
|
||||
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" :
|
||||
|
@ -141,7 +141,7 @@ namespace BTCPayServer.Controllers
|
|||
var details = InvoicePopulatePayments(invoice);
|
||||
model.CryptoPayments = details.CryptoPayments;
|
||||
model.Payments = details.Payments;
|
||||
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
|
@ -183,6 +183,8 @@ namespace BTCPayServer.Controllers
|
|||
new { pullPaymentId = ppId });
|
||||
}
|
||||
|
||||
HttpContext.SetStoreData(invoice.StoreData);
|
||||
|
||||
var paymentMethods = invoice.GetBlob(_NetworkProvider).GetPaymentMethods();
|
||||
var pmis = paymentMethods.Select(method => method.GetId()).ToList();
|
||||
var options = (await payoutHandlers.GetSupportedPaymentMethods(invoice.StoreData)).Where(id => pmis.Contains(id)).ToList();
|
||||
|
@ -216,18 +218,21 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpPost("invoices/{invoiceId}/refund")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public async Task<IActionResult> Refund(string invoiceId, RefundModel model, CancellationToken cancellationToken)
|
||||
{
|
||||
using var ctx = _dbContextFactory.CreateContext();
|
||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
||||
if (invoice is null)
|
||||
return NotFound();
|
||||
|
||||
var store = await _StoreRepository.FindStore(invoice.StoreId, GetUserId());
|
||||
if (store is null)
|
||||
return NotFound();
|
||||
|
||||
if (!CanRefund(invoice.GetInvoiceState()))
|
||||
return NotFound();
|
||||
|
||||
var paymentMethodId = PaymentMethodId.Parse(model.SelectedPaymentMethod);
|
||||
var cdCurrency = _CurrencyNameTable.GetCurrencyData(invoice.Currency, true);
|
||||
var paymentMethodDivisibility = _CurrencyNameTable.GetCurrencyData(paymentMethodId.CryptoCode, false)?.Divisibility ?? 8;
|
||||
|
@ -421,7 +426,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> MassAction(string command, string[] selectedItems)
|
||||
public async Task<IActionResult> MassAction(string command, string[] selectedItems, string? storeId = null)
|
||||
{
|
||||
if (selectedItems != null)
|
||||
{
|
||||
|
@ -435,7 +440,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(ListInvoices));
|
||||
return RedirectToAction(nameof(ListInvoices), new { storeId });
|
||||
}
|
||||
|
||||
[HttpGet("i/{invoiceId}")]
|
||||
|
@ -732,17 +737,29 @@ namespace BTCPayServer.Controllers
|
|||
return Ok("{}");
|
||||
}
|
||||
|
||||
[HttpGet("/stores/{storeId}/invoices")]
|
||||
[HttpGet("invoices")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> ListInvoices(InvoicesModel? model = null)
|
||||
public async Task<IActionResult> ListInvoices(InvoicesModel? model = null, string? storeId = null)
|
||||
{
|
||||
model = this.ParseListQuery(model ?? new InvoicesModel());
|
||||
|
||||
var fs = new SearchString(model.SearchTerm);
|
||||
var storeIds = fs.GetFilterArray("storeid") != null ? fs.GetFilterArray("storeid") : new List<string>().ToArray();
|
||||
var storeIds = storeId == null
|
||||
? fs.GetFilterArray("storeid") != null ? fs.GetFilterArray("storeid") : new List<string>().ToArray()
|
||||
: new []{ storeId };
|
||||
|
||||
model.StoreIds = storeIds;
|
||||
|
||||
if (storeId != null)
|
||||
{
|
||||
var store = await _StoreRepository.FindStore(storeId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
HttpContext.SetStoreData(store);
|
||||
model.StoreId = store.Id;
|
||||
}
|
||||
|
||||
InvoiceQuery invoiceQuery = GetInvoiceQuery(model.SearchTerm, model.TimezoneOffset ?? 0);
|
||||
var counting = _InvoiceRepository.GetInvoicesTotal(invoiceQuery);
|
||||
|
@ -823,6 +840,7 @@ namespace BTCPayServer.Controllers
|
|||
nameof(SelectListItem.Text));
|
||||
}
|
||||
|
||||
[HttpGet("/stores/{storeId}/invoices/create")]
|
||||
[HttpGet("invoices/create")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
|
@ -839,16 +857,26 @@ namespace BTCPayServer.Controllers
|
|||
TempData[WellKnownTempData.ErrorMessage] = "You need to create at least one store before creating a transaction";
|
||||
return RedirectToAction(nameof(UserStoresController.ListStores), "UserStores");
|
||||
}
|
||||
|
||||
if (model?.StoreId != null)
|
||||
{
|
||||
var store = await _StoreRepository.FindStore(model.StoreId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
HttpContext.SetStoreData(store);
|
||||
}
|
||||
|
||||
var vm = new CreateInvoiceModel
|
||||
{
|
||||
Stores = stores,
|
||||
StoreId = model?.StoreId,
|
||||
AvailablePaymentMethods = GetPaymentMethodsSelectList()
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost("/stores/{storeId}/invoices/create")]
|
||||
[HttpPost("invoices/create")]
|
||||
[Authorize(Policy = Policies.CanCreateInvoice, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[BitpayAPIConstraint(false)]
|
||||
|
@ -901,7 +929,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Invoice {result.Data.Id} just created!";
|
||||
CreatedInvoiceId = result.Data.Id;
|
||||
return RedirectToAction(nameof(ListInvoices));
|
||||
return RedirectToAction(nameof(ListInvoices), new { result.Data.StoreId });
|
||||
}
|
||||
catch (BitpayHttpException ex)
|
||||
{
|
||||
|
|
|
@ -4,8 +4,7 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
|
@ -18,15 +17,14 @@ using BTCPayServer.Services.Stores;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using PaymentRequestData = BTCPayServer.Data.PaymentRequestData;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Route("payment-requests")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public class PaymentRequestController : Controller
|
||||
{
|
||||
private readonly InvoiceController _InvoiceController;
|
||||
|
@ -61,16 +59,17 @@ namespace BTCPayServer.Controllers
|
|||
_linkGenerator = linkGenerator;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> GetPaymentRequests(ListPaymentRequestsViewModel model = null)
|
||||
[HttpGet("/stores/{storeId}/payment-requests")]
|
||||
public async Task<IActionResult> GetPaymentRequests(string storeId, ListPaymentRequestsViewModel model = null)
|
||||
{
|
||||
model = this.ParseListQuery(model ?? new ListPaymentRequestsViewModel());
|
||||
|
||||
var includeArchived = new SearchString(model.SearchTerm).GetFilterBool("includearchived") == true;
|
||||
var result = await _PaymentRequestRepository.FindPaymentRequests(new PaymentRequestQuery()
|
||||
var result = await _PaymentRequestRepository.FindPaymentRequests(new PaymentRequestQuery
|
||||
{
|
||||
UserId = GetUserId(),
|
||||
StoreId = CurrentStore.Id,
|
||||
Skip = model.Skip,
|
||||
Count = model.Count,
|
||||
IncludeArchived = includeArchived
|
||||
|
@ -81,40 +80,30 @@ namespace BTCPayServer.Controllers
|
|||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet("edit/{id?}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(string id)
|
||||
[HttpGet("/stores/{storeId}/payment-requests/edit/{payReqId?}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(string storeId, string payReqId)
|
||||
{
|
||||
var data = await _PaymentRequestRepository.FindPaymentRequest(id, GetUserId());
|
||||
if (data == null && !string.IsNullOrEmpty(id))
|
||||
var data = await _PaymentRequestRepository.FindPaymentRequest(payReqId, GetUserId());
|
||||
if (data == null && !string.IsNullOrEmpty(payReqId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
SelectList stores = new SelectList(await _StoreRepository.GetStoresByUserId(GetUserId()), nameof(StoreData.Id),
|
||||
nameof(StoreData.StoreName), data?.StoreDataId);
|
||||
if (!stores.Any())
|
||||
return View(nameof(EditPaymentRequest), new UpdatePaymentRequestViewModel(data)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Html =
|
||||
$"Error: You need to create at least one store. <a href='{Url.Action("CreateStore", "UserStores")}' class='alert-link'>Create store</a>",
|
||||
Severity = StatusMessageModel.StatusSeverity.Error
|
||||
});
|
||||
return RedirectToAction("GetPaymentRequests");
|
||||
}
|
||||
|
||||
return View(nameof(EditPaymentRequest), new UpdatePaymentRequestViewModel(data) { Stores = stores });
|
||||
StoreId = CurrentStore.Id
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("edit/{id?}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(string id, UpdatePaymentRequestViewModel viewModel)
|
||||
[HttpPost("/stores/{storeId}/payment-requests/edit/{payReqId?}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(string payReqId, UpdatePaymentRequestViewModel viewModel)
|
||||
{
|
||||
if (string.IsNullOrEmpty(viewModel.Currency) ||
|
||||
_Currencies.GetCurrencyData(viewModel.Currency, false) == null)
|
||||
ModelState.AddModelError(nameof(viewModel.Currency), "Invalid currency");
|
||||
|
||||
var data = await _PaymentRequestRepository.FindPaymentRequest(id, GetUserId());
|
||||
if (data == null && !string.IsNullOrEmpty(id))
|
||||
var data = await _PaymentRequestRepository.FindPaymentRequest(payReqId, GetUserId());
|
||||
if (data == null && !string.IsNullOrEmpty(payReqId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
@ -126,10 +115,6 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
viewModel.Stores = new SelectList(await _StoreRepository.GetStoresByUserId(GetUserId()),
|
||||
nameof(StoreData.Id),
|
||||
nameof(StoreData.StoreName), data?.StoreDataId);
|
||||
|
||||
return View(nameof(EditPaymentRequest), viewModel);
|
||||
}
|
||||
|
||||
|
@ -153,7 +138,7 @@ namespace BTCPayServer.Controllers
|
|||
blob.AllowCustomPaymentAmounts = viewModel.AllowCustomPaymentAmounts;
|
||||
|
||||
data.SetBlob(blob);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
if (string.IsNullOrEmpty(payReqId))
|
||||
{
|
||||
data.Created = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
@ -162,14 +147,14 @@ namespace BTCPayServer.Controllers
|
|||
_EventAggregator.Publish(new PaymentRequestUpdated { Data = data, PaymentRequestId = data.Id, });
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Saved";
|
||||
return RedirectToAction(nameof(EditPaymentRequest), new { id = data.Id });
|
||||
return RedirectToAction(nameof(EditPaymentRequest), new { storeId = CurrentStore.Id, payReqId = data.Id });
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[HttpGet("{payReqId}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> ViewPaymentRequest(string id)
|
||||
public async Task<IActionResult> ViewPaymentRequest(string payReqId)
|
||||
{
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(id, GetUserId());
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId());
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound();
|
||||
|
@ -179,9 +164,9 @@ namespace BTCPayServer.Controllers
|
|||
return View(result);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/pay")]
|
||||
[HttpGet("{payReqId}/pay")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> PayPaymentRequest(string id, bool redirectToInvoice = true,
|
||||
public async Task<IActionResult> PayPaymentRequest(string payReqId, bool redirectToInvoice = true,
|
||||
decimal? amount = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (amount.HasValue && amount.Value <= 0)
|
||||
|
@ -189,7 +174,7 @@ namespace BTCPayServer.Controllers
|
|||
return BadRequest("Please provide an amount greater than 0");
|
||||
}
|
||||
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(id, GetUserId());
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId());
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound();
|
||||
|
@ -199,7 +184,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
if (redirectToInvoice)
|
||||
{
|
||||
return RedirectToAction("ViewPaymentRequest", new { Id = id });
|
||||
return RedirectToAction("ViewPaymentRequest", new { Id = payReqId });
|
||||
}
|
||||
|
||||
return BadRequest("Payment Request cannot be paid as it has been archived");
|
||||
|
@ -210,7 +195,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
if (redirectToInvoice)
|
||||
{
|
||||
return RedirectToAction("ViewPaymentRequest", new { Id = id });
|
||||
return RedirectToAction("ViewPaymentRequest", new { Id = payReqId });
|
||||
}
|
||||
|
||||
return BadRequest("Payment Request has already been settled.");
|
||||
|
@ -220,7 +205,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
if (redirectToInvoice)
|
||||
{
|
||||
return RedirectToAction("ViewPaymentRequest", new { Id = id });
|
||||
return RedirectToAction("ViewPaymentRequest", new { Id = payReqId });
|
||||
}
|
||||
|
||||
return BadRequest("Payment Request has expired");
|
||||
|
@ -249,18 +234,18 @@ namespace BTCPayServer.Controllers
|
|||
else
|
||||
amount = result.AmountDue;
|
||||
|
||||
var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null, cancellationToken);
|
||||
var pr = await _PaymentRequestRepository.FindPaymentRequest(payReqId, null, cancellationToken);
|
||||
var blob = pr.GetBlob();
|
||||
var store = pr.StoreData;
|
||||
try
|
||||
{
|
||||
var redirectUrl = _linkGenerator.PaymentRequestLink(id, Request.Scheme, Request.Host, Request.PathBase);
|
||||
var redirectUrl = _linkGenerator.PaymentRequestLink(payReqId, Request.Scheme, Request.Host, Request.PathBase);
|
||||
|
||||
var invoiceMetadata =
|
||||
new InvoiceMetadata
|
||||
{
|
||||
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(id),
|
||||
PaymentRequestId = id,
|
||||
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(payReqId),
|
||||
PaymentRequestId = payReqId,
|
||||
BuyerEmail = result.Email
|
||||
};
|
||||
|
||||
|
@ -273,7 +258,7 @@ namespace BTCPayServer.Controllers
|
|||
Checkout = {RedirectURL = redirectUrl}
|
||||
};
|
||||
|
||||
var additionalTags = new List<string> {PaymentRequestRepository.GetInternalTag(id)};
|
||||
var additionalTags = new List<string> {PaymentRequestRepository.GetInternalTag(payReqId)};
|
||||
var newInvoice = await _InvoiceController.CreateInvoiceCoreRaw(invoiceRequest,store, Request.GetAbsoluteRoot(), additionalTags, cancellationToken);
|
||||
|
||||
if (redirectToInvoice)
|
||||
|
@ -289,10 +274,10 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
[HttpGet("{id}/cancel")]
|
||||
public async Task<IActionResult> CancelUnpaidPendingInvoice(string id, bool redirect = true)
|
||||
[HttpGet("{payReqId}/cancel")]
|
||||
public async Task<IActionResult> CancelUnpaidPendingInvoice(string payReqId, bool redirect = true)
|
||||
{
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(id, GetUserId());
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId());
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound();
|
||||
|
@ -318,21 +303,16 @@ namespace BTCPayServer.Controllers
|
|||
if (redirect)
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Payment cancelled";
|
||||
return RedirectToAction(nameof(ViewPaymentRequest), new { Id = id });
|
||||
return RedirectToAction(nameof(ViewPaymentRequest), new { Id = payReqId });
|
||||
}
|
||||
|
||||
return Ok("Payment cancelled");
|
||||
}
|
||||
|
||||
private string GetUserId()
|
||||
[HttpGet("{payReqId}/clone")]
|
||||
public async Task<IActionResult> ClonePaymentRequest(string payReqId)
|
||||
{
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/clone")]
|
||||
public async Task<IActionResult> ClonePaymentRequest(string id)
|
||||
{
|
||||
var result = await EditPaymentRequest(id);
|
||||
var result = await EditPaymentRequest(CurrentStore.Id, payReqId);
|
||||
if (result is ViewResult viewResult)
|
||||
{
|
||||
var model = (UpdatePaymentRequestViewModel)viewResult.Model;
|
||||
|
@ -346,15 +326,15 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/archive")]
|
||||
public async Task<IActionResult> TogglePaymentRequestArchival(string id)
|
||||
[HttpGet("{payReqId}/archive")]
|
||||
public async Task<IActionResult> TogglePaymentRequestArchival(string payReqId)
|
||||
{
|
||||
var result = await EditPaymentRequest(id);
|
||||
var result = await EditPaymentRequest(CurrentStore.Id, payReqId);
|
||||
if (result is ViewResult viewResult)
|
||||
{
|
||||
var model = (UpdatePaymentRequestViewModel)viewResult.Model;
|
||||
model.Archived = !model.Archived;
|
||||
await EditPaymentRequest(id, model);
|
||||
await EditPaymentRequest(payReqId, model);
|
||||
TempData[WellKnownTempData.SuccessMessage] = model.Archived
|
||||
? "The payment request has been archived and will no longer appear in the payment request list by default again."
|
||||
: "The payment request has been unarchived and will appear in the payment request list by default.";
|
||||
|
@ -363,5 +343,15 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
private string GetUserId()
|
||||
{
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
|
||||
private StoreData CurrentStore
|
||||
{
|
||||
get => HttpContext.GetStoreData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,9 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
|
@ -17,7 +14,6 @@ using BTCPayServer.Models.StoreViewModels;
|
|||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Security.Bitpay;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
|
@ -25,18 +21,13 @@ using BTCPayServer.Services.Invoices;
|
|||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BundlerMinifier.TagHelpers;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBXplorer;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
|
@ -513,7 +504,7 @@ namespace BTCPayServer.Controllers
|
|||
});
|
||||
}
|
||||
|
||||
private void AddPaymentMethods(StoreData store, StoreBlob storeBlob,
|
||||
internal void AddPaymentMethods(StoreData store, StoreBlob storeBlob,
|
||||
out List<StoreDerivationScheme> derivationSchemes, out List<StoreLightningNode> lightningNodes)
|
||||
{
|
||||
var excludeFilters = storeBlob.GetExcludedPaymentMethods();
|
||||
|
@ -850,13 +841,6 @@ namespace BTCPayServer.Controllers
|
|||
var userId = GetUserId();
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
return Challenge(AuthenticationSchemes.Cookie);
|
||||
var storeId = CurrentStore?.Id;
|
||||
if (storeId != null)
|
||||
{
|
||||
var store = await _Repo.FindStore(storeId, userId);
|
||||
if (store != null)
|
||||
HttpContext.SetStoreData(store);
|
||||
}
|
||||
var model = new CreateTokenViewModel();
|
||||
ViewBag.HidePublicKey = true;
|
||||
ViewBag.ShowStores = true;
|
||||
|
|
|
@ -26,18 +26,7 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
public string AppName { get; set; }
|
||||
|
||||
[Display(Name = "Store")]
|
||||
public string SelectedStore { get; set; }
|
||||
|
||||
public void SetStores(StoreData[] stores)
|
||||
{
|
||||
var defaultStore = stores[0].Id;
|
||||
var choices = stores.Select(o => new Format() { Name = o.StoreName, Value = o.Id }).ToArray();
|
||||
var chosen = choices.FirstOrDefault(f => f.Value == defaultStore) ?? choices.FirstOrDefault();
|
||||
Stores = new SelectList(choices, nameof(chosen.Value), nameof(chosen.Name), chosen);
|
||||
SelectedStore = chosen.Value;
|
||||
}
|
||||
|
||||
public SelectList Stores { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
|
||||
[Display(Name = "App Type")]
|
||||
public string SelectedAppType { get; set; }
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
{
|
||||
public List<InvoiceModel> Invoices { get; set; } = new List<InvoiceModel>();
|
||||
public string[] StoreIds { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
}
|
||||
|
||||
public class InvoiceModel
|
||||
|
|
|
@ -13,7 +13,6 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||
public class ListPaymentRequestsViewModel : BasePagingViewModel
|
||||
{
|
||||
public List<ViewPaymentRequestViewModel> Items { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class UpdatePaymentRequestViewModel
|
||||
|
@ -82,6 +81,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||
public ViewPaymentRequestViewModel(PaymentRequestData data)
|
||||
{
|
||||
Id = data.Id;
|
||||
StoreId = data.StoreDataId;
|
||||
var blob = data.GetBlob();
|
||||
Archived = data.Archived;
|
||||
Title = blob.Title;
|
||||
|
@ -121,6 +121,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||
public string AmountDueFormatted { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
public string Title { get; set; }
|
||||
|
|
|
@ -2,10 +2,14 @@ using System.Threading.Tasks;
|
|||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
|
@ -14,14 +18,23 @@ namespace BTCPayServer.Security
|
|||
private readonly HttpContext _HttpContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly AppService _appService;
|
||||
private readonly PaymentRequestService _paymentRequestService;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
|
||||
public CookieAuthorizationHandler(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
StoreRepository storeRepository,
|
||||
AppService appService,
|
||||
InvoiceRepository invoiceRepository,
|
||||
PaymentRequestService paymentRequestService)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_appService = appService;
|
||||
_storeRepository = storeRepository;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_paymentRequestService = paymentRequestService;
|
||||
}
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
|
||||
{
|
||||
|
@ -37,15 +50,46 @@ namespace BTCPayServer.Security
|
|||
return;
|
||||
}
|
||||
|
||||
string storeId = context.Resource is string s? s :_HttpContext.GetImplicitStoreId();
|
||||
string storeId = context.Resource is string s ? s : _HttpContext.GetImplicitStoreId();
|
||||
if (storeId == null)
|
||||
return;
|
||||
|
||||
{
|
||||
var routeData = _HttpContext.GetRouteData();
|
||||
if (routeData != null)
|
||||
{
|
||||
// resolve from app
|
||||
if (routeData.Values.TryGetValue("appId", out var vAppId))
|
||||
{
|
||||
string appId = vAppId as string;
|
||||
var app = await _appService.GetApp(appId, null);
|
||||
storeId = app?.StoreDataId;
|
||||
}
|
||||
// resolve from payment request
|
||||
else if (routeData.Values.TryGetValue("payReqId", out var vPayReqId))
|
||||
{
|
||||
string payReqId = vPayReqId as string;
|
||||
var paymentRequest = await _paymentRequestService.GetPaymentRequest(payReqId);
|
||||
storeId = paymentRequest?.StoreId;
|
||||
}
|
||||
// resolve from app
|
||||
if (routeData.Values.TryGetValue("invoiceId", out var vInvoiceId))
|
||||
{
|
||||
string invoiceId = vInvoiceId as string;
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId);
|
||||
storeId = invoice?.StoreId;
|
||||
}
|
||||
}
|
||||
|
||||
// store could not be found
|
||||
if (storeId == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var userid = _userManager.GetUserId(context.User);
|
||||
if (string.IsNullOrEmpty(userid))
|
||||
return;
|
||||
|
||||
|
||||
var store = await _storeRepository.FindStore(storeId, userid);
|
||||
|
||||
bool success = false;
|
||||
|
|
|
@ -2,14 +2,11 @@
|
|||
ViewData["Title"] = "Access denied";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<hr />
|
||||
<p class="text-danger">You do not have access to this resource.</p>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<p class="text-danger">You do not have access to this resource.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -4,36 +4,32 @@
|
|||
Layout = "_LayoutSimple";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<form asp-action="ForgotPassword" method="post">
|
||||
<h4>Start password reset</h4>
|
||||
<p>
|
||||
We all forget passwords every now and then. Just provide email address tied to
|
||||
your account and we'll start the process of helping you recover your account.
|
||||
</p>
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<form asp-action="ForgotPassword" method="post">
|
||||
<h4>Start password reset</h4>
|
||||
<p>
|
||||
We all forget passwords every now and then. Just provide email address tied to
|
||||
your account and we'll start the process of helping you recover your account.
|
||||
</p>
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
|
|
|
@ -2,21 +2,11 @@
|
|||
ViewData["Title"] = "Email sent!";
|
||||
}
|
||||
|
||||
<h2></h2>
|
||||
<p>
|
||||
</p>
|
||||
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<hr />
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
@{
|
||||
ViewData["Title"] = "Locked out";
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12 lead">
|
||||
<p>This account has been locked out because of multiple invalid login attempts. Please try again later.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
</section>
|
||||
<div class="col-lg-12 lead">
|
||||
<p>This account has been locked out because of multiple invalid login attempts. Please try again later.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,44 +1,39 @@
|
|||
@model LoginWith2faViewModel
|
||||
|
||||
<section class="pt-5">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>Two-factor authentication</h2>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form class="col-12" method="post" asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-action="LoginWith2fa">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<input asp-for="RememberMe" type="hidden"/>
|
||||
<div class="form-group">
|
||||
<label asp-for="TwoFactorCode" class="form-label"></label>
|
||||
<input asp-for="TwoFactorCode" class="form-control" autocomplete="off" autofocus style="width:13em" />
|
||||
<span asp-validation-for="TwoFactorCode" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label asp-for="RememberMachine" class="form-label">
|
||||
<input asp-for="RememberMachine"/>
|
||||
@Html.DisplayNameFor(m => m.RememberMachine)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="text-secondary">
|
||||
Don't have access to your authenticator device?
|
||||
<br>
|
||||
You can <a asp-action="LoginWithRecoveryCode" asp-route-returnUrl="@ViewData["ReturnUrl"]">log in with a recovery code</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-5">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>Two-factor authentication</h2>
|
||||
<hr class="primary">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<form class="col-12" method="post" asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-action="LoginWith2fa">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<input asp-for="RememberMe" type="hidden"/>
|
||||
<div class="form-group">
|
||||
<label asp-for="TwoFactorCode" class="form-label"></label>
|
||||
<input asp-for="TwoFactorCode" class="form-control" autocomplete="off" autofocus style="width:13em" />
|
||||
<span asp-validation-for="TwoFactorCode" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label asp-for="RememberMachine" class="form-label">
|
||||
<input asp-for="RememberMachine"/>
|
||||
@Html.DisplayNameFor(m => m.RememberMachine)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="text-secondary">
|
||||
Don't have access to your authenticator device?
|
||||
<br>
|
||||
You can <a asp-action="LoginWithRecoveryCode" asp-route-returnUrl="@ViewData["ReturnUrl"]">log in with a recovery code</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,22 +7,18 @@
|
|||
<input type="hidden" asp-for="RememberMe"/>
|
||||
</form>
|
||||
|
||||
<section class="pt-5">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>FIDO2 Authentication</h2>
|
||||
<hr class="primary">
|
||||
<div>
|
||||
<span id="spinner" class="fa fa-spinner fa-spin float-end ms-3 me-5 mt-1 fido-running" style="font-size:2.5em"></span>
|
||||
<p>Insert your security key into your computer's USB port. If it has a button, tap on it.</p>
|
||||
</div>
|
||||
<p id="error-message" class="d-none alert alert-danger"></p>
|
||||
<button id="btn-retry" class="btn btn-secondary d-none" type="button">Retry</button>
|
||||
</div>
|
||||
<div class="row pt-5">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>FIDO2 Authentication</h2>
|
||||
<hr class="primary">
|
||||
<div>
|
||||
<span id="spinner" class="fa fa-spinner fa-spin float-end ms-3 me-5 mt-1 fido-running" style="font-size:2.5em"></span>
|
||||
<p>Insert your security key into your computer's USB port. If it has a button, tap on it.</p>
|
||||
</div>
|
||||
<p id="error-message" class="d-none alert alert-danger"></p>
|
||||
<button id="btn-retry" class="btn btn-secondary d-none" type="button">Retry</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('btn-retry').addEventListener('click', () => window.location.reload())
|
||||
|
|
|
@ -3,33 +3,27 @@
|
|||
ViewData["Title"] = "Recovery code verification";
|
||||
}
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
<p>
|
||||
You have requested to login with a recovery code. This login will not be remembered until you provide
|
||||
an authenticator app code at login or disable 2FA and login again.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form method="post" class="col-12">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RecoveryCode" class="form-label"></label>
|
||||
<input asp-for="RecoveryCode" class="form-control" autocomplete="off" />
|
||||
<span asp-validation-for="RecoveryCode" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<hr class="primary">
|
||||
<p>
|
||||
You have requested to login with a recovery code. This login will not be remembered until you provide
|
||||
an authenticator app code at login or disable 2FA and login again.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row">
|
||||
<form method="post" class="col-12">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RecoveryCode" class="form-label"></label>
|
||||
<input asp-for="RecoveryCode" class="form-control" autocomplete="off" />
|
||||
<span asp-validation-for="RecoveryCode" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
|
|
|
@ -3,38 +3,34 @@
|
|||
ViewData["Title"] = "Two-factor/U2F authentication";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
@if (Model.LoginWith2FaViewModel != null && Model.LoginWithFido2ViewModel != null)
|
||||
{
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
}
|
||||
else if (Model.LoginWith2FaViewModel == null && Model.LoginWithFido2ViewModel == null)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2 class="bg-danger">Both 2FA and U2F/FIDO2 Authentication Methods are not available. Please go to the https endpoint.</h2>
|
||||
<hr class="danger">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row justify-content-center">
|
||||
@if (Model.LoginWith2FaViewModel != null)
|
||||
{
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<partial name="LoginWith2fa" model="@Model.LoginWith2FaViewModel"/>
|
||||
</div>
|
||||
}
|
||||
@if (Model.LoginWithFido2ViewModel != null)
|
||||
{
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<partial name="LoginWithFido2" model="@Model.LoginWithFido2ViewModel"/>
|
||||
</div>
|
||||
}
|
||||
@if (Model.LoginWith2FaViewModel != null && Model.LoginWithFido2ViewModel != null)
|
||||
{
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
}
|
||||
else if (Model.LoginWith2FaViewModel == null && Model.LoginWithFido2ViewModel == null)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2 class="bg-danger">Both 2FA and U2F/FIDO2 Authentication Methods are not available. Please go to the https endpoint.</h2>
|
||||
<hr class="danger">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
<div class="row justify-content-center">
|
||||
@if (Model.LoginWith2FaViewModel != null)
|
||||
{
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<partial name="LoginWith2fa" model="@Model.LoginWith2FaViewModel"/>
|
||||
</div>
|
||||
}
|
||||
@if (Model.LoginWithFido2ViewModel != null)
|
||||
{
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<partial name="LoginWithFido2" model="@Model.LoginWithFido2ViewModel"/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
|
|
|
@ -7,35 +7,27 @@
|
|||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form asp-action="CreateApp">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedStore" class="form-label" data-required></label>
|
||||
<select asp-for="SelectedStore" asp-items="Model.Stores" class="form-select"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedAppType" class="form-label" data-required></label>
|
||||
<select asp-for="SelectedAppType" asp-items="Model.AppTypes" class="form-select"></select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="AppName" class="form-label" data-required></label>
|
||||
<input asp-for="AppName" class="form-control" required />
|
||||
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
|
||||
<a asp-action="ListApps" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form asp-action="CreateApp">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedAppType" class="form-label" data-required></label>
|
||||
<select asp-for="SelectedAppType" asp-items="Model.AppTypes" class="form-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="AppName" class="form-label" data-required></label>
|
||||
<input asp-for="AppName" class="form-control" required />
|
||||
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
|
||||
<a asp-action="ListApps" asp-route-storeId="@Context.GetStoreData().Id" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -9,118 +9,117 @@
|
|||
var sortByAsc = "Sort by ascending...";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-2">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["PageTitle"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/Apps/" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
<a asp-action="CreateApp" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateNewApp"><span class="fa fa-plus"></span> Create a new app</a>
|
||||
</div>
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-2">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["PageTitle"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/Apps/" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
<a asp-action="CreateApp" asp-route-storeId="@Context.GetStoreData().Id" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateNewApp"><span class="fa fa-plus"></span> Create a new app</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Apps.Any())
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Apps.Any())
|
||||
{
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a
|
||||
asp-action="ListApps"
|
||||
asp-route-storeId="@Context.GetStoreData().Id"
|
||||
asp-route-sortOrder="@(storeNameSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="StoreName"
|
||||
class="text-nowrap"
|
||||
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
Store
|
||||
<span class="fa @(storeNameSortOrder == "asc" ? "fa-sort-alpha-desc" : storeNameSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a
|
||||
asp-action="ListApps"
|
||||
asp-route-storeId="@Context.GetStoreData().Id"
|
||||
asp-route-sortOrder="@(appNameSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="AppName"
|
||||
class="text-nowrap"
|
||||
title="@(appNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
Name
|
||||
<span class="fa @(appNameSortOrder == "asc" ? "fa-sort-alpha-desc" : appNameSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a
|
||||
asp-action="ListApps"
|
||||
asp-route-storeId="@Context.GetStoreData().Id"
|
||||
asp-route-sortOrder="@(appTypeSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="AppType"
|
||||
class="text-nowrap"
|
||||
title="@(appTypeSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
App Type
|
||||
<span class="fa @(appTypeSortOrder == "asc" ? "fa-sort-alpha-desc" : appTypeSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th style="text-align:right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var app in Model.Apps)
|
||||
{
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a
|
||||
asp-action="ListApps"
|
||||
asp-route-sortOrder="@(storeNameSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="StoreName"
|
||||
class="text-nowrap"
|
||||
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
Store
|
||||
<span class="fa @(storeNameSortOrder == "asc" ? "fa-sort-alpha-desc" : storeNameSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a
|
||||
asp-action="ListApps"
|
||||
asp-route-sortOrder="@(appNameSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="AppName"
|
||||
class="text-nowrap"
|
||||
title="@(appNameSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
Name
|
||||
<span class="fa @(appNameSortOrder == "asc" ? "fa-sort-alpha-desc" : appNameSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a
|
||||
asp-action="ListApps"
|
||||
asp-route-sortOrder="@(appTypeSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="AppType"
|
||||
class="text-nowrap"
|
||||
title="@(appTypeSortOrder == "desc" ? sortByDesc : sortByAsc)"
|
||||
>
|
||||
App Type
|
||||
<span class="fa @(appTypeSortOrder == "asc" ? "fa-sort-alpha-desc" : appTypeSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th style="text-align:right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var app in Model.Apps)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@if (app.IsOwner)
|
||||
{
|
||||
<span><a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@app.StoreId">@app.StoreName</a></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@app.StoreName</span>
|
||||
}
|
||||
</td>
|
||||
<td>@app.AppName</td>
|
||||
<td>
|
||||
@typeof(AppType).DisplayName(app.AppType)
|
||||
@if (app.AppType != AppType.Crowdfund.ToString())
|
||||
{
|
||||
<span>-</span>
|
||||
}
|
||||
|
||||
@app.ViewStyle
|
||||
</td>
|
||||
<td style="text-align:right">
|
||||
@if (app.IsOwner)
|
||||
{
|
||||
<a asp-action="@app.UpdateAction" asp-controller="Apps" asp-route-appId="@app.Id">Settings</a>
|
||||
<span> - </span>
|
||||
}
|
||||
<a asp-action="@app.ViewAction" asp-controller="AppsPublic" asp-route-appId="@app.Id">View</a>
|
||||
<a asp-action="@app.ViewAction" asp-controller="AppsPublic" asp-route-appId="@app.Id" target="_blank"
|
||||
title="View in New Window"><span class="fa fa-external-link"></span></a>
|
||||
<span> - </span>
|
||||
<a asp-action="DeleteApp" asp-route-appId="@app.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The app <strong>@app.AppName</strong> and its settings will be permanently deleted from your store <strong>@app.StoreName</strong>." data-confirm-input="DELETE">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<tr>
|
||||
<td>
|
||||
@if (app.IsOwner)
|
||||
{
|
||||
<span><a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@app.StoreId">@app.StoreName</a></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@app.StoreName</span>
|
||||
}
|
||||
</td>
|
||||
<td>@app.AppName</td>
|
||||
<td>
|
||||
@typeof(AppType).DisplayName(app.AppType)
|
||||
@if (app.AppType != AppType.Crowdfund.ToString())
|
||||
{
|
||||
<span>-</span>
|
||||
}
|
||||
|
||||
@app.ViewStyle
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@if (app.IsOwner)
|
||||
{
|
||||
<a asp-action="@app.UpdateAction" asp-controller="Apps" asp-route-appId="@app.Id" asp-route-storeId="@app.StoreId">Settings</a>
|
||||
<span> - </span>
|
||||
}
|
||||
<a asp-action="@app.ViewAction" asp-controller="AppsPublic" asp-route-appId="@app.Id">View</a>
|
||||
<a asp-action="@app.ViewAction" asp-controller="AppsPublic" asp-route-appId="@app.Id" target="_blank"
|
||||
title="View in New Window"><span class="fa fa-external-link"></span></a>
|
||||
<span> - </span>
|
||||
<a asp-action="DeleteApp" asp-route-appId="@app.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The app <strong>@app.AppName</strong> and its settings will be permanently deleted from your store <strong>@app.StoreName</strong>." data-confirm-input="DELETE">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no apps yet.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no apps yet.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<partial name="_Confirm" model="@(new ConfirmModel("Delete app", "This app will be removed from this store.", "Delete"))" />
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
@model UpdateCrowdfundViewModel
|
||||
@{
|
||||
ViewData.SetActivePageAndTitle(AppsNavPages.Update, "Update Crowdfund", Model.StoreName);
|
||||
ViewData.SetActiveId(Model.AppId);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
@ -15,242 +16,238 @@
|
|||
<bundle name="wwwroot/bundles/crowdfund-admin-bundle.min.js" asp-append-version="true"></bundle>
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<h2 class="mb-4">@ViewData["PageTitle"] - @Model.AppName</h2>
|
||||
<h2 class="mb-4">@ViewData["PageTitle"] - @Model.AppName</h2>
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="StoreId" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="StoreId" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="AppName" class="form-label" data-required></label>
|
||||
<input asp-for="AppName" class="form-control" required />
|
||||
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required></label>
|
||||
<input asp-for="Title" class="form-control" required />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Tagline" class="form-label"></label>
|
||||
<input asp-for="Tagline" class="form-control" />
|
||||
<span asp-validation-for="Tagline" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="AppName" class="form-label" data-required></label>
|
||||
<input asp-for="AppName" class="form-control" required />
|
||||
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required></label>
|
||||
<input asp-for="Title" class="form-control" required />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Tagline" class="form-label"></label>
|
||||
<input asp-for="Tagline" class="form-control" />
|
||||
<span asp-validation-for="Tagline" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label" data-required></label>
|
||||
<textarea asp-for="Description" rows="20" cols="40" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="TargetCurrency" class="form-label"></label>
|
||||
<input asp-for="TargetCurrency" class="form-control" placeholder="Use store's default settings" />
|
||||
<span asp-validation-for="TargetCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="TargetAmount" class="form-label"></label>
|
||||
<input asp-for="TargetAmount" class="form-control" />
|
||||
<span asp-validation-for="TargetAmount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="StartDate" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="datetime-local" asp-for="StartDate"
|
||||
value="@(Model.StartDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker"
|
||||
placeholder="No start date has been set" />
|
||||
<button class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label" data-required></label>
|
||||
<textarea asp-for="Description" rows="20" cols="40" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
<span asp-validation-for="StartDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EndDate" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="datetime-local" asp-for="EndDate"
|
||||
value="@(Model.EndDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker"
|
||||
placeholder="No end date has been set" />
|
||||
<button class="btn btn-secondary input-group-clear input-group-text" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="TargetCurrency" class="form-label"></label>
|
||||
<input asp-for="TargetCurrency" class="form-control" placeholder="Use store's default settings" />
|
||||
<span asp-validation-for="TargetCurrency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="TargetAmount" class="form-label"></label>
|
||||
<input asp-for="TargetAmount" class="form-control" />
|
||||
<span asp-validation-for="TargetAmount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="StartDate" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="datetime-local" asp-for="StartDate"
|
||||
value="@(Model.StartDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker"
|
||||
placeholder="No start date has been set" />
|
||||
<button class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
<span asp-validation-for="EndDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="ResetEvery" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="number" asp-for="ResetEveryAmount" placeholder="Amount" class="form-control">
|
||||
<select class="form-select" asp-for="ResetEvery">
|
||||
@foreach (var opt in Model.ResetEveryValues)
|
||||
{
|
||||
<option value="@opt">@opt</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="Enabled" type="checkbox" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="Enabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="Enabled" class="text-danger"></span>
|
||||
<div class="text-muted" hidden="@Model.Enabled">The crowdfund is only visible to you. To make it visible to anyone else, enable this.</div>
|
||||
<div class="text-muted" hidden="@(!Model.Enabled)">The crowdfund is visible to anyone. To make it only visible to you, disable this.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.PerksTemplate), "Perks")" />
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="PerksTemplate" class="form-label"></label>
|
||||
<textarea asp-for="PerksTemplate" rows="10" cols="40" class="js-product-template form-control"></textarea>
|
||||
<span asp-validation-for="PerksTemplate" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Contributions</h4>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="SortPerksByPopularity" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="SortPerksByPopularity" class="form-check-label"></label>
|
||||
<span asp-validation-for="SortPerksByPopularity" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="DisplayPerksRanking" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="DisplayPerksRanking" class="form-check-label"></label>
|
||||
<span asp-validation-for="DisplayPerksRanking" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="DisplayPerksValue" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="DisplayPerksValue" class="form-check-label"></label>
|
||||
<span asp-validation-for="DisplayPerksValue" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="EnforceTargetAmount" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="EnforceTargetAmount" class="form-check-label"></label>
|
||||
<span asp-validation-for="EnforceTargetAmount" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Crowdfund Behavior</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationUrl" class="form-label"></label>
|
||||
<input asp-for="NotificationUrl" class="form-control" />
|
||||
<span asp-validation-for="NotificationUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input asp-for="UseAllStoreInvoices" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="UseAllStoreInvoices" class="form-check-label"></label>
|
||||
<span asp-validation-for="UseAllStoreInvoices" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Sound</h4>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="SoundsEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#SoundsEnabledSettings" aria-expanded="@Model.SoundsEnabled" aria-controls="SoundsEnabledSettings"/>
|
||||
<label asp-for="SoundsEnabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="SoundsEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.SoundsEnabled ? "show" : "")" id="SoundsEnabledSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="Sounds" class="form-label"></label>
|
||||
<textarea asp-for="Sounds" class="form-control"></textarea>
|
||||
<span asp-validation-for="Sounds" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Animation</h4>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="AnimationsEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#AnimationsEnabledSettings" aria-expanded="@Model.AnimationsEnabled" aria-controls="AnimationsEnabledSettings"/>
|
||||
<label asp-for="AnimationsEnabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="AnimationsEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.AnimationsEnabled ? "show" : "")" id="AnimationsEnabledSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="AnimationColors" class="form-label"></label>
|
||||
<textarea asp-for="AnimationColors" class="form-control"></textarea>
|
||||
<span asp-validation-for="AnimationColors" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Discussion</h4>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="DisqusEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#DisqusEnabledSettings" aria-expanded="@Model.DisqusEnabled" aria-controls="DisqusEnabledSettings"/>
|
||||
<label asp-for="DisqusEnabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="DisqusEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.DisqusEnabled ? "show" : "")" id="DisqusEnabledSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="DisqusShortname" class="form-label"></label>
|
||||
<input asp-for="DisqusShortname" class="form-control" />
|
||||
<span asp-validation-for="DisqusShortname" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</div>
|
||||
<span asp-validation-for="StartDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EndDate" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="datetime-local" asp-for="EndDate"
|
||||
value="@(Model.EndDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker"
|
||||
placeholder="No end date has been set" />
|
||||
<button class="btn btn-secondary input-group-clear input-group-text" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<span asp-validation-for="EndDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="ResetEvery" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input type="number" asp-for="ResetEveryAmount" placeholder="Amount" class="form-control">
|
||||
<select class="form-select" asp-for="ResetEvery">
|
||||
@foreach (var opt in Model.ResetEveryValues)
|
||||
{
|
||||
<option value="@opt">@opt</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="Enabled" type="checkbox" class="btcpay-toggle me-2"/>
|
||||
<label asp-for="Enabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="Enabled" class="text-danger"></span>
|
||||
<div class="text-muted" hidden="@Model.Enabled">The crowdfund is only visible to you. To make it visible to anyone else, enable this.</div>
|
||||
<div class="text-muted" hidden="@(!Model.Enabled)">The crowdfund is visible to anyone. To make it only visible to you, disable this.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.PerksTemplate), "Perks")" />
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="PerksTemplate" class="form-label"></label>
|
||||
<textarea asp-for="PerksTemplate" rows="10" cols="40" class="js-product-template form-control"></textarea>
|
||||
<span asp-validation-for="PerksTemplate" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Contributions</h4>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="SortPerksByPopularity" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="SortPerksByPopularity" class="form-check-label"></label>
|
||||
<span asp-validation-for="SortPerksByPopularity" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="DisplayPerksRanking" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="DisplayPerksRanking" class="form-check-label"></label>
|
||||
<span asp-validation-for="DisplayPerksRanking" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="DisplayPerksValue" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="DisplayPerksValue" class="form-check-label"></label>
|
||||
<span asp-validation-for="DisplayPerksValue" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="EnforceTargetAmount" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="EnforceTargetAmount" class="form-check-label"></label>
|
||||
<span asp-validation-for="EnforceTargetAmount" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Crowdfund Behavior</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationUrl" class="form-label"></label>
|
||||
<input asp-for="NotificationUrl" class="form-control" />
|
||||
<span asp-validation-for="NotificationUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input asp-for="UseAllStoreInvoices" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="UseAllStoreInvoices" class="form-check-label"></label>
|
||||
<span asp-validation-for="UseAllStoreInvoices" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Sound</h4>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="SoundsEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#SoundsEnabledSettings" aria-expanded="@Model.SoundsEnabled" aria-controls="SoundsEnabledSettings"/>
|
||||
<label asp-for="SoundsEnabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="SoundsEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.SoundsEnabled ? "show" : "")" id="SoundsEnabledSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="Sounds" class="form-label"></label>
|
||||
<textarea asp-for="Sounds" class="form-control"></textarea>
|
||||
<span asp-validation-for="Sounds" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Animation</h4>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="AnimationsEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#AnimationsEnabledSettings" aria-expanded="@Model.AnimationsEnabled" aria-controls="AnimationsEnabledSettings"/>
|
||||
<label asp-for="AnimationsEnabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="AnimationsEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.AnimationsEnabled ? "show" : "")" id="AnimationsEnabledSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="AnimationColors" class="form-label"></label>
|
||||
<textarea asp-for="AnimationColors" class="form-control"></textarea>
|
||||
<span asp-validation-for="AnimationColors" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Discussion</h4>
|
||||
<div class="form-group mb-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<input asp-for="DisqusEnabled" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#DisqusEnabledSettings" aria-expanded="@Model.DisqusEnabled" aria-controls="DisqusEnabledSettings"/>
|
||||
<label asp-for="DisqusEnabled" class="form-label mb-0"></label>
|
||||
</div>
|
||||
<span asp-validation-for="DisqusEnabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.DisqusEnabled ? "show" : "")" id="DisqusEnabledSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="DisqusShortname" class="form-label"></label>
|
||||
<input asp-for="DisqusShortname" class="form-control" />
|
||||
<span asp-validation-for="DisqusShortname" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="MainImageUrl" class="form-label"></label>
|
||||
<input asp-for="MainImageUrl" class="form-control" />
|
||||
<span asp-validation-for="MainImageUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/#2-bootstrap-themes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="MainImageUrl" class="form-label"></label>
|
||||
<input asp-for="MainImageUrl" class="form-control" />
|
||||
<span asp-validation-for="MainImageUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/#2-bootstrap-themes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9 mt-2 mb-5">
|
||||
<div class="d-grid gap-3 d-md-block">
|
||||
<button type="submit" class="btn btn-primary me-md-2" id="SaveSettings">Save Settings</button>
|
||||
<div class="btn-group me-md-2">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@Model.SearchTerm">Invoices</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@Model.SearchTerm"
|
||||
target="viewinvoices_@Model.AppId"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
@if (Model.ModelWithMinimumData)
|
||||
{
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ViewCrowdfund" asp-controller="AppsPublic" asp-route-appId="@Model.AppId" id="ViewApp">View Crowdfund</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ViewCrowdfund" asp-controller="AppsPublic" asp-route-appId="@Model.AppId"
|
||||
target="viewapp_@Model.AppId"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-9 mt-2 mb-5">
|
||||
<div class="d-grid gap-3 d-md-block">
|
||||
<button type="submit" class="btn btn-primary me-md-2" id="SaveSettings">Save Settings</button>
|
||||
<div class="btn-group me-md-2">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ListInvoices" asp-controller="Invoice" asp-route-storeId="@Model.StoreId" asp-route-searchterm="@Model.SearchTerm">Invoices</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ListInvoices" asp-controller="Invoice" asp-route-storeId="@Model.StoreId" asp-route-searchterm="@Model.SearchTerm"
|
||||
target="viewinvoices_@Model.AppId"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
@if (Model.ModelWithMinimumData)
|
||||
{
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ViewCrowdfund" asp-controller="AppsPublic" asp-route-appId="@Model.AppId" id="ViewApp">View Crowdfund</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ViewCrowdfund" asp-controller="AppsPublic" asp-route-appId="@Model.AppId"
|
||||
target="viewapp_@Model.AppId"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -3,257 +3,255 @@
|
|||
@model UpdatePointOfSaleViewModel
|
||||
@{
|
||||
ViewData.SetActivePageAndTitle(AppsNavPages.Update, "Update Point of Sale", Model.StoreName);
|
||||
ViewData.SetActiveId(Model.Id);
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<h2 class="mb-4">@ViewData["PageTitle"] - @Model.AppName</h2>
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="StoreId" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<h2 class="mb-4">@ViewData["PageTitle"] - @Model.AppName</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="AppName" class="form-label" data-required></label>
|
||||
<input asp-for="AppName" class="form-control" required />
|
||||
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required></label>
|
||||
<input asp-for="Title" class="form-control" required />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control" placeholder="Use store's default settings" />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="StoreId" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label asp-for="AppName" class="form-label" data-required></label>
|
||||
<input asp-for="AppName" class="form-control" required />
|
||||
<span asp-validation-for="AppName" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required></label>
|
||||
<input asp-for="Title" class="form-control" required />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control" placeholder="Use store's default settings" />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label"></label>
|
||||
<textarea asp-for="Description" rows="10" cols="40" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.Template), "Products")" />
|
||||
<div class="form-group">
|
||||
<label asp-for="Template" class="form-label"></label>
|
||||
<textarea asp-for="Template" rows="10" cols="40" class="js-product-template form-control"></textarea>
|
||||
<span asp-validation-for="Template" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<h4 class="mt-5 mb-4">Appearance</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultView" class="form-label" data-required></label>
|
||||
<select asp-for="DefaultView" asp-items="@Html.GetEnumSelectList<PosViewType>()" class="form-select" required></select>
|
||||
<span asp-validation-for="DefaultView" class="text-danger"></span>
|
||||
<div class="mt-2">
|
||||
<span class="text-secondary">Choose the point of sale style for your customers.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="button-price-text">
|
||||
<label asp-for="ButtonText" class="form-label" data-required></label>
|
||||
<input asp-for="ButtonText" class="form-control" required />
|
||||
<span asp-validation-for="ButtonText" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RequiresRefundEmail" class="form-label"></label>
|
||||
<select asp-for="RequiresRefundEmail" asp-items="@Html.GetEnumSelectList<RequiresRefundEmail>()" class="form-select" required></select>
|
||||
<span asp-validation-for="RequiresRefundEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<section id="discounts" class="p-0">
|
||||
<h4 class="mt-5 mb-4">Discounts</h4>
|
||||
<div class="form-check mb-4">
|
||||
<input asp-for="ShowDiscount" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="ShowDiscount" class="form-check-label"></label>
|
||||
<span asp-validation-for="ShowDiscount" class="text-danger"></span>
|
||||
<div class="mt-2">
|
||||
<span class="text-secondary">Not recommended for customer self-checkout.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label"></label>
|
||||
<textarea asp-for="Description" rows="10" cols="40" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</section>
|
||||
<section id="custom-payments" class="p-0">
|
||||
<h4 class="mt-5 mb-4">Custom Payments</h4>
|
||||
<div class="form-group mb-4 d-flex align-items-center">
|
||||
<input asp-for="ShowCustomAmount" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#CustomAmountSettings" aria-expanded="@Model.ShowCustomAmount" aria-controls="CustomAmountSettings"/>
|
||||
<label asp-for="ShowCustomAmount" class="form-label mb-0"></label>
|
||||
<span asp-validation-for="ShowCustomAmount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.ShowCustomAmount ? "show" : "")" id="CustomAmountSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="CustomButtonText" class="form-label" data-required></label>
|
||||
<input asp-for="CustomButtonText" class="form-control" required />
|
||||
<span asp-validation-for="CustomButtonText" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<partial name="TemplateEditor" model="@(nameof(Model.Template), "Products")" />
|
||||
</section>
|
||||
<section id="tips" class="p-0">
|
||||
<h4 class="mt-5 mb-4">Tips</h4>
|
||||
<div class="form-group mb-4 d-flex align-items-center">
|
||||
<input asp-for="EnableTips" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#CustomTipsSettings" aria-expanded="@Model.EnableTips" aria-controls="CustomTipsSettings" />
|
||||
<label asp-for="EnableTips" class="form-label mb-0"></label>
|
||||
<span asp-validation-for="EnableTips" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.EnableTips ? "show" : "")" id="CustomTipsSettings">
|
||||
<div class="form-group">
|
||||
<label asp-for="Template" class="form-label"></label>
|
||||
<textarea asp-for="Template" rows="10" cols="40" class="js-product-template form-control"></textarea>
|
||||
<span asp-validation-for="Template" class="text-danger"></span>
|
||||
<label asp-for="CustomTipText" class="form-label" data-required></label>
|
||||
<input asp-for="CustomTipText" class="form-control" required />
|
||||
<span asp-validation-for="CustomTipText" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="CustomTipPercentages" class="form-label"></label>
|
||||
<input asp-for="CustomTipPercentages" class="form-control" />
|
||||
<span asp-validation-for="CustomTipPercentages" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<h4 class="mt-5 mb-4">Appearance</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultView" class="form-label" data-required></label>
|
||||
<select asp-for="DefaultView" asp-items="@Html.GetEnumSelectList<PosViewType>()" class="form-select" required></select>
|
||||
<span asp-validation-for="DefaultView" class="text-danger"></span>
|
||||
<div class="mt-2">
|
||||
<span class="text-secondary">Choose the point of sale style for your customers.</span>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-embed-payment-button-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-embed-payment-button" aria-expanded="false" aria-controls="additional-embed-payment-button">
|
||||
Embed a payment button linking to POS item
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-embed-payment-button" class="accordion-collapse collapse" aria-labelledby="additional-embed-payment-button-header">
|
||||
<div class="accordion-body">
|
||||
<p>You can host point of sale buttons in an external website with the following code.</p>
|
||||
@if (Model.Example1 != null)
|
||||
{
|
||||
<span>For anything with a custom amount</span>
|
||||
<pre><code class="html">@Model.Example1</code></pre>
|
||||
}
|
||||
@if (Model.Example2 != null)
|
||||
{
|
||||
<span>For a specific item of your template</span>
|
||||
<pre><code class="html">@Model.Example2</code></pre>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="button-price-text">
|
||||
<label asp-for="ButtonText" class="form-label" data-required></label>
|
||||
<input asp-for="ButtonText" class="form-control" required />
|
||||
<span asp-validation-for="ButtonText" class="text-danger"></span>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-embed-pos-iframe-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-embed-pos-iframe" aria-expanded="false" aria-controls="additional-embed-pos-iframe">
|
||||
Embed Point of Sale via Iframe
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-embed-pos-iframe" class="accordion-collapse collapse" aria-labelledby="additional-embed-pos-iframe-header">
|
||||
<div class="accordion-body">
|
||||
You can embed this POS via an iframe.
|
||||
@{
|
||||
var iframe = $"<iframe src='{(Url.Action("ViewPointOfSale", "AppsPublic", new { appId = Model.Id }, Context.Request.Scheme))}' style='max-width: 100%; border: 0;'></iframe>";
|
||||
}
|
||||
<pre><code class="html">@iframe</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RequiresRefundEmail" class="form-label"></label>
|
||||
<select asp-for="RequiresRefundEmail" asp-items="@Html.GetEnumSelectList<RequiresRefundEmail>()" class="form-select" required></select>
|
||||
<span asp-validation-for="RequiresRefundEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<section id="discounts" class="p-0">
|
||||
<h4 class="mt-5 mb-4">Discounts</h4>
|
||||
<div class="form-check mb-4">
|
||||
<input asp-for="ShowDiscount" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="ShowDiscount" class="form-check-label"></label>
|
||||
<span asp-validation-for="ShowDiscount" class="text-danger"></span>
|
||||
<div class="mt-2">
|
||||
<span class="text-secondary">Not recommended for customer self-checkout.</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="custom-payments" class="p-0">
|
||||
<h4 class="mt-5 mb-4">Custom Payments</h4>
|
||||
<div class="form-group mb-4 d-flex align-items-center">
|
||||
<input asp-for="ShowCustomAmount" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#CustomAmountSettings" aria-expanded="@Model.ShowCustomAmount" aria-controls="CustomAmountSettings"/>
|
||||
<label asp-for="ShowCustomAmount" class="form-label mb-0"></label>
|
||||
<span asp-validation-for="ShowCustomAmount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.ShowCustomAmount ? "show" : "")" id="CustomAmountSettings">
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="CustomButtonText" class="form-label" data-required></label>
|
||||
<input asp-for="CustomButtonText" class="form-control" required />
|
||||
<span asp-validation-for="CustomButtonText" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="tips" class="p-0">
|
||||
<h4 class="mt-5 mb-4">Tips</h4>
|
||||
<div class="form-group mb-4 d-flex align-items-center">
|
||||
<input asp-for="EnableTips" type="checkbox" class="btcpay-toggle me-2" data-bs-toggle="collapse" data-bs-target="#CustomTipsSettings" aria-expanded="@Model.EnableTips" aria-controls="CustomTipsSettings" />
|
||||
<label asp-for="EnableTips" class="form-label mb-0"></label>
|
||||
<span asp-validation-for="EnableTips" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="collapse @(Model.EnableTips ? "show" : "")" id="CustomTipsSettings">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomTipText" class="form-label" data-required></label>
|
||||
<input asp-for="CustomTipText" class="form-control" required />
|
||||
<span asp-validation-for="CustomTipText" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-0 pb-3">
|
||||
<label asp-for="CustomTipPercentages" class="form-label"></label>
|
||||
<input asp-for="CustomTipPercentages" class="form-control" />
|
||||
<span asp-validation-for="CustomTipPercentages" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-embed-payment-button-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-embed-payment-button" aria-expanded="false" aria-controls="additional-embed-payment-button">
|
||||
Embed a payment button linking to POS item
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-embed-payment-button" class="accordion-collapse collapse" aria-labelledby="additional-embed-payment-button-header">
|
||||
<div class="accordion-body">
|
||||
<p>You can host point of sale buttons in an external website with the following code.</p>
|
||||
@if (Model.Example1 != null)
|
||||
{
|
||||
<span>For anything with a custom amount</span>
|
||||
<pre><code class="html">@Model.Example1</code></pre>
|
||||
}
|
||||
@if (Model.Example2 != null)
|
||||
{
|
||||
<span>For a specific item of your template</span>
|
||||
<pre><code class="html">@Model.Example2</code></pre>
|
||||
}
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-redirect-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-redirect" aria-expanded="false" aria-controls="additional-redirect">
|
||||
Redirects
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-redirect" class="accordion-collapse collapse" aria-labelledby="additional-redirect-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="RedirectUrl" class="form-label"></label>
|
||||
<input asp-for="RedirectUrl" class="form-control" />
|
||||
<span asp-validation-for="RedirectUrl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-embed-pos-iframe-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-embed-pos-iframe" aria-expanded="false" aria-controls="additional-embed-pos-iframe">
|
||||
Embed Point of Sale via Iframe
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-embed-pos-iframe" class="accordion-collapse collapse" aria-labelledby="additional-embed-pos-iframe-header">
|
||||
<div class="accordion-body">
|
||||
You can embed this POS via an iframe.
|
||||
@{
|
||||
var iframe = $"<iframe src='{(Url.Action("ViewPointOfSale", "AppsPublic", new { appId = Model.Id }, Context.Request.Scheme))}' style='max-width: 100%; border: 0;'></iframe>";
|
||||
}
|
||||
<pre><code class="html">@iframe</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-redirect-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-redirect" aria-expanded="false" aria-controls="additional-redirect">
|
||||
Redirects
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-redirect" class="accordion-collapse collapse" aria-labelledby="additional-redirect-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="RedirectUrl" class="form-label"></label>
|
||||
<input asp-for="RedirectUrl" class="form-control" />
|
||||
<span asp-validation-for="RedirectUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RedirectAutomatically" class="form-label"></label>
|
||||
<select asp-for="RedirectAutomatically" asp-items="Model.RedirectAutomaticallySelectList" class="form-select"></select>
|
||||
<span asp-validation-for="RedirectAutomatically" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-notification-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-notification" aria-expanded="false" aria-controls="additional-notification">
|
||||
Notification URL Callbacks
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-notification" class="accordion-collapse collapse" aria-labelledby="additional-notification-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationUrl" class="form-label"></label>
|
||||
<input asp-for="NotificationUrl" class="form-control" />
|
||||
<span asp-validation-for="NotificationUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<p>A <code>POST</code> callback will be sent to notification with the following form will be sent to <code>notificationUrl</code> once the enough is paid and once again once there is enough confirmations to the payment:</p>
|
||||
<pre><code class="json">@Model.ExampleCallback</code></pre>
|
||||
<p><strong>Never</strong> trust anything but <code>id</code>, <strong>ignore</strong> the other fields completely, an attacker can spoof those, they are present only for backward compatibility reason:</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li>Send a <code>GET</code> request to <code>https://btcpay.example.com/invoices/{invoiceId}</code> with <code>Content-Type: application/json; Authorization: Basic YourLegacyAPIkey"</code>, Legacy API key can be created with Access Tokens in Store settings</li>
|
||||
<li>Verify that the <code>orderId</code> is from your backend, that the <code>price</code> is correct and that <code>status</code> is <code>settled</code></li>
|
||||
<li>You can then ship your order</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/#2-bootstrap-themes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RedirectAutomatically" class="form-label"></label>
|
||||
<select asp-for="RedirectAutomatically" asp-items="Model.RedirectAutomaticallySelectList" class="form-select"></select>
|
||||
<span asp-validation-for="RedirectAutomatically" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9 mt-2 mb-5">
|
||||
<div class="d-grid gap-3 d-md-block">
|
||||
<button type="submit" class="btn btn-primary me-md-2" id="SaveSettings">Save Settings</button>
|
||||
<div class="btn-group me-md-2">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@Model.SearchTerm">Invoices</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@Model.SearchTerm"
|
||||
target="viewinvoices_@Model.Id"><span class="fa fa-external-link"></span></a>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-notification-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-notification" aria-expanded="false" aria-controls="additional-notification">
|
||||
Notification URL Callbacks
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-notification" class="accordion-collapse collapse" aria-labelledby="additional-notification-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationUrl" class="form-label"></label>
|
||||
<input asp-for="NotificationUrl" class="form-control" />
|
||||
<span asp-validation-for="NotificationUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<p>A <code>POST</code> callback will be sent to notification with the following form will be sent to <code>notificationUrl</code> once the enough is paid and once again once there is enough confirmations to the payment:</p>
|
||||
<pre><code class="json">@Model.ExampleCallback</code></pre>
|
||||
<p><strong>Never</strong> trust anything but <code>id</code>, <strong>ignore</strong> the other fields completely, an attacker can spoof those, they are present only for backward compatibility reason:</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li>Send a <code>GET</code> request to <code>https://btcpay.example.com/invoices/{invoiceId}</code> with <code>Content-Type: application/json; Authorization: Basic YourLegacyAPIkey"</code>, Legacy API key can be created with Access Tokens in Store settings</li>
|
||||
<li>Verify that the <code>orderId</code> is from your backend, that the <code>price</code> is correct and that <code>status</code> is <code>settled</code></li>
|
||||
<li>You can then ship your order</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ViewPointOfSale" asp-controller="AppsPublic" asp-route-appId="@Model.Id" id="ViewApp">View Point of Sale</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ViewPointOfSale" asp-controller="AppsPublic" asp-route-appId="@Model.Id"
|
||||
target="viewapp_@Model.Id"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/#2-bootstrap-themes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-9 mt-2 mb-5">
|
||||
<div class="d-grid gap-3 d-md-block">
|
||||
<button type="submit" class="btn btn-primary me-md-2" id="SaveSettings">Save Settings</button>
|
||||
<div class="btn-group me-md-2">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@Model.SearchTerm">Invoices</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@Model.SearchTerm"
|
||||
target="viewinvoices_@Model.Id"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-secondary flex-grow-1" asp-action="ViewPointOfSale" asp-controller="AppsPublic" asp-route-appId="@Model.Id" id="ViewApp">View Point of Sale</a>
|
||||
<a class="btn btn-secondary px-3 flex-grow-0" asp-action="ViewPointOfSale" asp-controller="AppsPublic" asp-route-appId="@Model.Id"
|
||||
target="viewapp_@Model.Id"><span class="fa fa-external-link"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
@section PageHeadContent {
|
||||
<link rel="stylesheet" href="~/vendor/highlightjs/default.min.css" asp-append-version="true">
|
||||
|
|
|
@ -261,7 +261,7 @@
|
|||
<div class="me-3" v-text="`Updated ${lastUpdated}`">Updated @Model.Info.LastUpdated</div>
|
||||
@if (!theme.CustomTheme)
|
||||
{
|
||||
<vc:theme-switch css-class="text-muted me-3" responsive="none" />
|
||||
<vc:theme-switch css-class="text-muted me-3" />
|
||||
}
|
||||
<div class="form-check me-3 my-0 only-for-js" v-if="srvModel.animationsEnabled || animation">
|
||||
<input class="form-check-input" type="checkbox" id="cbAnime" v-model="animation">
|
||||
|
|
|
@ -1,103 +1,84 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
ViewBag.AlwaysShrinkNavBar = false;
|
||||
}
|
||||
|
||||
<header class="masthead">
|
||||
<div class="header-content">
|
||||
<div class="header-content-inner text-white">
|
||||
<h1>Welcome to BTCPay Server</h1>
|
||||
<p class="fw-semibold">BTCPay Server is a free and open source server for merchants wanting to accept Bitcoin for their business.</p>
|
||||
<a style="background-color: #fff;color: #222;display:inline-block;text-align: center;white-space: nowrap;vertical-align: middle;user-select: none;line-height: 1.25;font-size: 1rem;text-decoration:none;font-weight: 700; text-transform: uppercase;border: none;border-radius: 300px;padding: 15px 30px;" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">Official website</a>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<div>
|
||||
<h1>Welcome to BTCPay Server</h1>
|
||||
<p class="lead" style="max-width:30em;margin:1.5em auto">BTCPay Server is a free and open source server for merchants wanting to accept Bitcoin for their business.</p>
|
||||
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener" class="btn btn-primary rounded-pill fs-5">Official website</a>
|
||||
</div>
|
||||
<hr class="primary my-5">
|
||||
<h2 class="mb-4">A Payment Server for Bitcoin</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="services">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<h2>A Payment Server for Bitcoin</h2>
|
||||
<hr class="primary">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="py-4 service-box">
|
||||
<img src="~/img/lock-logo.png" class="mb-2" alt="" asp-append-version="true" />
|
||||
<h3>Secure</h3>
|
||||
<p class="text-muted mb-0">The payment server does not need to know your private keys, so your money can't be stolen.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="py-4 service-box">
|
||||
<img src="~/img/qr-logo.png" class="mb-2" alt="" asp-append-version="true" />
|
||||
<h3>Easy</h3>
|
||||
<p class="text-muted mb-0">A user-friendly Bitcoin checkout page for your customers.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="py-4 service-box">
|
||||
<img src="~/img/money-logo.png" class="mb-2" alt="" asp-append-version="true" />
|
||||
<h3>Visibility</h3>
|
||||
<p class="text-muted mb-0">Manage, generate reports, and search for your invoices easily.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="service-box">
|
||||
<img src="~/img/lock-logo.png" alt="" asp-append-version="true" />
|
||||
<h3>Secure</h3>
|
||||
<p class="text-muted">The payment server does not need to know your private keys, so your money can't be stolen.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="service-box">
|
||||
<img src="~/img/qr-logo.png" alt="" asp-append-version="true" />
|
||||
<h3>Easy</h3>
|
||||
<p class="text-muted">A user-friendly Bitcoin checkout page for your customers.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 col-md-6 text-center">
|
||||
<div class="service-box">
|
||||
<img src="~/img/money-logo.png" alt="" asp-append-version="true" />
|
||||
<h3>Visibility</h3>
|
||||
<p class="text-muted">Manage, generate reports, and search for your invoices easily.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-center my-5">
|
||||
<h2 class="mb-4">Video tutorials</h2>
|
||||
<div class="text-center">
|
||||
<a href="https://www.youtube.com/channel/UCpG9WL6TJuoNfFVkaDMp9ug" target="_blank" rel="noreferrer noopener">
|
||||
<img src="~/img/youtube.png" alt="Video tutorials" class="img-fluid" asp-append-version="true" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="call-to-action bg-tile">
|
||||
<div class="container text-center">
|
||||
<h2>Video tutorials</h2>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<a href="https://www.youtube.com/channel/UCpG9WL6TJuoNfFVkaDMp9ug" target="_blank" rel="noreferrer noopener">
|
||||
<img src="~/img/youtube.png" class="img-fluid" asp-append-version="true" />
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mx-auto text-center">
|
||||
<h2>Donate</h2>
|
||||
<hr class="primary">
|
||||
<p>
|
||||
BTCPay Server is proudly free and open-source, built and maintained<br /> by a world-wide community of passionate contributors.<br />
|
||||
Support us by donating through BTCPay Server Foundation<br /> or by directly sending donation to a specific contributor.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://btcpayserver.org/donate/" rel="noreferrer noopener">
|
||||
<svg viewBox="0 0 208 55" width="208" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m208 48c0 3.866-3.135 7-7 7h-194c-3.866 0-7-3.134-7-7v-41c0-3.866 3.134-7 7-7h194c3.865 0 7 3.134 7 7z" fill="var(--btcpay-bg-cta, #0f3723)"/>
|
||||
<g fill="#fff">
|
||||
<path d="m125.169 20.648c.519 0 1.017.051 1.495.153.479.103.898.276 1.261.522s.654.57.873.972c.22.402.329.903.329 1.503 0 .336-.052.664-.156.981-.104.318-.25.606-.439.864-.19.258-.415.477-.674.657-.26.18-.551.306-.873.378v.036c.793.108 1.428.447 1.899 1.017.474.57.708 1.275.708 2.115 0 .204-.017.436-.052.693s-.104.521-.207.792c-.104.271-.254.537-.45.801-.195.265-.458.495-.785.693-.328.198-.731.36-1.209.485-.479.127-1.052.189-1.72.189h-4.368v-12.851zm0 5.634c.472 0 .881-.057 1.228-.171.346-.114.633-.27.863-.468s.403-.429.518-.693.174-.546.174-.846c0-1.608-.928-2.412-2.782-2.412h-3.192v4.59zm0 6.174c.438 0 .853-.039 1.244-.117s.737-.219 1.037-.423c.299-.204.536-.477.708-.818.173-.343.259-.771.259-1.287 0-.828-.278-1.449-.838-1.863s-1.362-.621-2.41-.621h-3.192v5.13h3.192z"/>
|
||||
<path d="m137.534 20.648v1.044h-3.167v11.808h-1.176v-11.808h-3.15v-1.044z"/>
|
||||
<path d="m146.747 23.301c-.231-.381-.513-.7-.845-.958s-.697-.454-1.096-.589c-.4-.135-.82-.202-1.259-.202-.799 0-1.485.158-2.06.475-.574.316-1.045.736-1.41 1.257-.366.522-.637 1.111-.811 1.768-.176.657-.263 1.332-.263 2.023 0 .68.087 1.351.263 2.013.174.663.443 1.255.811 1.777.365.521.835.941 1.41 1.258.574.316 1.261.475 2.06.475.563 0 1.069-.104 1.521-.315.451-.212.838-.499 1.166-.862.326-.363.591-.789.793-1.275.203-.485.332-1.011.388-1.574h1.147c-.078.774-.252 1.473-.522 2.094-.271.622-.619 1.148-1.047 1.583-.427.435-.931.769-1.511 1.002s-1.225.353-1.934.353c-.945 0-1.775-.18-2.49-.537-.716-.358-1.31-.836-1.781-1.435-.473-.599-.828-1.29-1.064-2.075s-.354-1.613-.354-2.479c0-.868.117-1.694.354-2.48.236-.785.592-1.479 1.064-2.084.473-.604 1.065-1.084 1.781-1.442.715-.357 1.545-.537 2.49-.537.574 0 1.135.088 1.68.264s1.041.437 1.486.783c.444.346.816.771 1.114 1.275s.487 1.085.565 1.741h-1.147c-.101-.485-.267-.916-.499-1.297z"/>
|
||||
<path d="m154.704 20.648c.553 0 1.054.081 1.503.243s.835.402 1.158.72c.322.318.57.705.742 1.161.174.456.26.972.26 1.548s-.086 1.943-.26 2.399c-.172.456-.42.843-.742 1.161s-.708.558-1.158.72-.95.243-1.503.243h-2.884v4.657h-1.176v-12.852zm-.259 7.151c.818 0 1.479-.216 1.986-.648.508-.432.762-1.943.762-2.831s-.254-1.548-.762-1.98c-.507-.432-1.168-.648-1.986-.648h-2.625v6.107z"/>
|
||||
<path d="m164.361 20.648 4.84 12.852h-1.262l-1.504-3.996h-5.615l-1.486 3.996h-1.244l4.959-12.852zm1.677 7.812-2.384-6.588-2.473 6.588z"/>
|
||||
<path d="m167.398 20.648h1.381l4.06 6.516 4.043-6.516h1.399l-4.872 7.56v5.292h-1.177v-5.292z"/>
|
||||
<path d="m27.005 22.02c.782-.118 1.711-.207 2.729-.207 1.844 0 3.158.428 4.028 1.24.885.812 1.402 1.962 1.402 3.571 0 1.623-.502 2.951-1.432 3.866-.929.93-2.464 1.432-4.397 1.432-.915 0-1.682-.045-2.331-.118v-9.784zm1.284 8.793c.324.06.796.074 1.298.074 2.745 0 4.235-1.534 4.235-4.221.015-2.346-1.313-3.836-4.028-3.836-.664 0-1.166.059-1.505.133z"/>
|
||||
<path d="m43.312 28.202c0 2.642-1.83 3.792-3.556 3.792-1.933 0-3.423-1.416-3.423-3.674 0-2.391 1.564-3.792 3.542-3.792 2.05 0 3.437 1.49 3.437 3.674zm-5.666.073c0 1.564.9 2.745 2.169 2.745 1.239 0 2.169-1.166 2.169-2.774 0-1.21-.605-2.745-2.14-2.745-1.533 0-2.198 1.417-2.198 2.774z"/>
|
||||
<path d="m44.953 26.623c0-.738-.015-1.343-.059-1.933h1.151l.074 1.181h.03c.354-.679 1.18-1.343 2.361-1.343.988 0 2.523.59 2.523 3.04v4.265h-1.298v-4.117c0-1.151-.428-2.11-1.653-2.11-.855 0-1.52.605-1.741 1.328-.059.163-.089.384-.089.605v4.294h-1.298v-5.21z"/>
|
||||
<path d="m57.099 31.832-.104-.9h-.044c-.398.561-1.166 1.062-2.184 1.062-1.446 0-2.184-1.018-2.184-2.051 0-1.727 1.534-2.671 4.293-2.656v-.148c0-.59-.162-1.652-1.623-1.652-.664 0-1.357.207-1.859.531l-.294-.856c.59-.384 1.446-.634 2.346-.634 2.184 0 2.715 1.49 2.715 2.921v2.671c0 .62.03 1.225.118 1.712zm-.192-3.644c-1.417-.029-3.025.221-3.025 1.608 0 .841.561 1.239 1.225 1.239.93 0 1.52-.59 1.727-1.195.044-.133.074-.28.074-.413v-1.239z"/>
|
||||
<path d="m61.764 22.639v2.051h1.859v.989h-1.859v3.852c0 .886.251 1.387.974 1.387.339 0 .59-.044.752-.088l.059.974c-.251.104-.649.177-1.151.177-.605 0-1.092-.191-1.402-.546-.369-.384-.502-1.018-.502-1.859v-3.896h-1.106v-.99h1.106v-1.711z"/>
|
||||
<path d="m65.72 28.497c.029 1.756 1.151 2.479 2.449 2.479.93 0 1.49-.163 1.977-.369l.222.93c-.458.206-1.24.442-2.376.442-2.198 0-3.512-1.446-3.512-3.601s1.269-3.851 3.35-3.851c2.332 0 2.951 2.051 2.951 3.364 0 .266-.03.472-.044.605h-5.017zm3.806-.93c.015-.826-.339-2.11-1.8-2.11-1.313 0-1.889 1.209-1.992 2.11z"/>
|
||||
<path d="m77.084 22.639v2.051h1.859v.989h-1.859v3.852c0 .886.251 1.387.974 1.387.339 0 .59-.044.752-.088l.059.974c-.251.104-.649.177-1.151.177-.605 0-1.092-.191-1.402-.546-.369-.384-.502-1.018-.502-1.859v-3.896h-1.106v-.99h1.106v-1.711z"/>
|
||||
<path d="m86.78 28.202c0 2.642-1.83 3.792-3.556 3.792-1.933 0-3.423-1.416-3.423-3.674 0-2.391 1.564-3.792 3.542-3.792 2.05 0 3.437 1.49 3.437 3.674zm-5.666.073c0 1.564.9 2.745 2.169 2.745 1.239 0 2.169-1.166 2.169-2.774 0-1.21-.605-2.745-2.14-2.745-1.534 0-2.198 1.417-2.198 2.774z"/>
|
||||
</g>
|
||||
<path d="m98.329 22.548v-8.32l10.112 4.812-7.447 5.435 3.511 2.537 8.67-6.263c.888-.685 1.47-1.238 1.421-2.137-.07-1.287-.923-1.772-1.695-2.081l-15.397-7.293s-1.7-.78-2.801.482c-.384.44-.453 1.765-.453 1.765v31.58h.029c-.036-1.295 1.166-1.9 1.166-1.9l2.884-1.372v-8.321z" fill="#cdd932"/>
|
||||
<path d="m104.505 27.01-6.176-4.462v8.929z" fill="#1d7a44"/>
|
||||
<path d="m113.176 33.271-8.67-6.262-3.511 2.536 7.446 5.435-10.111 4.813-2.884 1.372s-1.202.604-1.166 1.901c.009.324.093.688.294 1.101.745 1.521 2.93.615 2.93.615l15.398-7.292c.771-.309 1.625-.793 1.693-2.081.05-.899-.533-1.452-1.419-2.138z" fill="#51b13e"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="mb-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mx-auto text-center">
|
||||
<h2>Donate</h2>
|
||||
<hr class="primary">
|
||||
<p>
|
||||
BTCPay Server is proudly free and open-source, built and maintained<br /> by a world-wide community of passionate contributors.<br />
|
||||
Support us by donating through BTCPay Server Foundation<br /> or by directly sending donation to a specific contributor.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://btcpayserver.org/donate/" rel="noreferrer noopener">
|
||||
<svg viewBox="0 0 208 55" width="208" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m208 48c0 3.866-3.135 7-7 7h-194c-3.866 0-7-3.134-7-7v-41c0-3.866 3.134-7 7-7h194c3.865 0 7 3.134 7 7z" fill="var(--btcpay-bg-cta, #0f3723)"/>
|
||||
<g fill="#fff">
|
||||
<path d="m125.169 20.648c.519 0 1.017.051 1.495.153.479.103.898.276 1.261.522s.654.57.873.972c.22.402.329.903.329 1.503 0 .336-.052.664-.156.981-.104.318-.25.606-.439.864-.19.258-.415.477-.674.657-.26.18-.551.306-.873.378v.036c.793.108 1.428.447 1.899 1.017.474.57.708 1.275.708 2.115 0 .204-.017.436-.052.693s-.104.521-.207.792c-.104.271-.254.537-.45.801-.195.265-.458.495-.785.693-.328.198-.731.36-1.209.485-.479.127-1.052.189-1.72.189h-4.368v-12.851zm0 5.634c.472 0 .881-.057 1.228-.171.346-.114.633-.27.863-.468s.403-.429.518-.693.174-.546.174-.846c0-1.608-.928-2.412-2.782-2.412h-3.192v4.59zm0 6.174c.438 0 .853-.039 1.244-.117s.737-.219 1.037-.423c.299-.204.536-.477.708-.818.173-.343.259-.771.259-1.287 0-.828-.278-1.449-.838-1.863s-1.362-.621-2.41-.621h-3.192v5.13h3.192z"/>
|
||||
<path d="m137.534 20.648v1.044h-3.167v11.808h-1.176v-11.808h-3.15v-1.044z"/>
|
||||
<path d="m146.747 23.301c-.231-.381-.513-.7-.845-.958s-.697-.454-1.096-.589c-.4-.135-.82-.202-1.259-.202-.799 0-1.485.158-2.06.475-.574.316-1.045.736-1.41 1.257-.366.522-.637 1.111-.811 1.768-.176.657-.263 1.332-.263 2.023 0 .68.087 1.351.263 2.013.174.663.443 1.255.811 1.777.365.521.835.941 1.41 1.258.574.316 1.261.475 2.06.475.563 0 1.069-.104 1.521-.315.451-.212.838-.499 1.166-.862.326-.363.591-.789.793-1.275.203-.485.332-1.011.388-1.574h1.147c-.078.774-.252 1.473-.522 2.094-.271.622-.619 1.148-1.047 1.583-.427.435-.931.769-1.511 1.002s-1.225.353-1.934.353c-.945 0-1.775-.18-2.49-.537-.716-.358-1.31-.836-1.781-1.435-.473-.599-.828-1.29-1.064-2.075s-.354-1.613-.354-2.479c0-.868.117-1.694.354-2.48.236-.785.592-1.479 1.064-2.084.473-.604 1.065-1.084 1.781-1.442.715-.357 1.545-.537 2.49-.537.574 0 1.135.088 1.68.264s1.041.437 1.486.783c.444.346.816.771 1.114 1.275s.487 1.085.565 1.741h-1.147c-.101-.485-.267-.916-.499-1.297z"/>
|
||||
<path d="m154.704 20.648c.553 0 1.054.081 1.503.243s.835.402 1.158.72c.322.318.57.705.742 1.161.174.456.26.972.26 1.548s-.086 1.943-.26 2.399c-.172.456-.42.843-.742 1.161s-.708.558-1.158.72-.95.243-1.503.243h-2.884v4.657h-1.176v-12.852zm-.259 7.151c.818 0 1.479-.216 1.986-.648.508-.432.762-1.943.762-2.831s-.254-1.548-.762-1.98c-.507-.432-1.168-.648-1.986-.648h-2.625v6.107z"/>
|
||||
<path d="m164.361 20.648 4.84 12.852h-1.262l-1.504-3.996h-5.615l-1.486 3.996h-1.244l4.959-12.852zm1.677 7.812-2.384-6.588-2.473 6.588z"/>
|
||||
<path d="m167.398 20.648h1.381l4.06 6.516 4.043-6.516h1.399l-4.872 7.56v5.292h-1.177v-5.292z"/>
|
||||
<path d="m27.005 22.02c.782-.118 1.711-.207 2.729-.207 1.844 0 3.158.428 4.028 1.24.885.812 1.402 1.962 1.402 3.571 0 1.623-.502 2.951-1.432 3.866-.929.93-2.464 1.432-4.397 1.432-.915 0-1.682-.045-2.331-.118v-9.784zm1.284 8.793c.324.06.796.074 1.298.074 2.745 0 4.235-1.534 4.235-4.221.015-2.346-1.313-3.836-4.028-3.836-.664 0-1.166.059-1.505.133z"/>
|
||||
<path d="m43.312 28.202c0 2.642-1.83 3.792-3.556 3.792-1.933 0-3.423-1.416-3.423-3.674 0-2.391 1.564-3.792 3.542-3.792 2.05 0 3.437 1.49 3.437 3.674zm-5.666.073c0 1.564.9 2.745 2.169 2.745 1.239 0 2.169-1.166 2.169-2.774 0-1.21-.605-2.745-2.14-2.745-1.533 0-2.198 1.417-2.198 2.774z"/>
|
||||
<path d="m44.953 26.623c0-.738-.015-1.343-.059-1.933h1.151l.074 1.181h.03c.354-.679 1.18-1.343 2.361-1.343.988 0 2.523.59 2.523 3.04v4.265h-1.298v-4.117c0-1.151-.428-2.11-1.653-2.11-.855 0-1.52.605-1.741 1.328-.059.163-.089.384-.089.605v4.294h-1.298v-5.21z"/>
|
||||
<path d="m57.099 31.832-.104-.9h-.044c-.398.561-1.166 1.062-2.184 1.062-1.446 0-2.184-1.018-2.184-2.051 0-1.727 1.534-2.671 4.293-2.656v-.148c0-.59-.162-1.652-1.623-1.652-.664 0-1.357.207-1.859.531l-.294-.856c.59-.384 1.446-.634 2.346-.634 2.184 0 2.715 1.49 2.715 2.921v2.671c0 .62.03 1.225.118 1.712zm-.192-3.644c-1.417-.029-3.025.221-3.025 1.608 0 .841.561 1.239 1.225 1.239.93 0 1.52-.59 1.727-1.195.044-.133.074-.28.074-.413v-1.239z"/>
|
||||
<path d="m61.764 22.639v2.051h1.859v.989h-1.859v3.852c0 .886.251 1.387.974 1.387.339 0 .59-.044.752-.088l.059.974c-.251.104-.649.177-1.151.177-.605 0-1.092-.191-1.402-.546-.369-.384-.502-1.018-.502-1.859v-3.896h-1.106v-.99h1.106v-1.711z"/>
|
||||
<path d="m65.72 28.497c.029 1.756 1.151 2.479 2.449 2.479.93 0 1.49-.163 1.977-.369l.222.93c-.458.206-1.24.442-2.376.442-2.198 0-3.512-1.446-3.512-3.601s1.269-3.851 3.35-3.851c2.332 0 2.951 2.051 2.951 3.364 0 .266-.03.472-.044.605h-5.017zm3.806-.93c.015-.826-.339-2.11-1.8-2.11-1.313 0-1.889 1.209-1.992 2.11z"/>
|
||||
<path d="m77.084 22.639v2.051h1.859v.989h-1.859v3.852c0 .886.251 1.387.974 1.387.339 0 .59-.044.752-.088l.059.974c-.251.104-.649.177-1.151.177-.605 0-1.092-.191-1.402-.546-.369-.384-.502-1.018-.502-1.859v-3.896h-1.106v-.99h1.106v-1.711z"/>
|
||||
<path d="m86.78 28.202c0 2.642-1.83 3.792-3.556 3.792-1.933 0-3.423-1.416-3.423-3.674 0-2.391 1.564-3.792 3.542-3.792 2.05 0 3.437 1.49 3.437 3.674zm-5.666.073c0 1.564.9 2.745 2.169 2.745 1.239 0 2.169-1.166 2.169-2.774 0-1.21-.605-2.745-2.14-2.745-1.534 0-2.198 1.417-2.198 2.774z"/>
|
||||
</g>
|
||||
<path d="m98.329 22.548v-8.32l10.112 4.812-7.447 5.435 3.511 2.537 8.67-6.263c.888-.685 1.47-1.238 1.421-2.137-.07-1.287-.923-1.772-1.695-2.081l-15.397-7.293s-1.7-.78-2.801.482c-.384.44-.453 1.765-.453 1.765v31.58h.029c-.036-1.295 1.166-1.9 1.166-1.9l2.884-1.372v-8.321z" fill="#cdd932"/>
|
||||
<path d="m104.505 27.01-6.176-4.462v8.929z" fill="#1d7a44"/>
|
||||
<path d="m113.176 33.271-8.67-6.262-3.511 2.536 7.446 5.435-10.111 4.813-2.884 1.372s-1.202.604-1.166 1.901c.009.324.093.688.294 1.101.745 1.521 2.93.615 2.93.615l15.398-7.292c.771-.309 1.625-.793 1.693-2.081.05-.899-.533-1.452-1.419-2.138z" fill="#51b13e"/>
|
||||
</svg>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -25,127 +25,131 @@
|
|||
</script>
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form asp-action="CreateInvoice" method="post" id="create-invoice-form">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Stores" class="form-label"></label>
|
||||
<select asp-for="StoreId" asp-items="Model.Stores" class="form-select"></select>
|
||||
<span asp-validation-for="StoreId" class="text-danger"></span>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form asp-action="CreateInvoice" method="post" id="create-invoice-form">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
@if (Model.StoreId != null)
|
||||
{
|
||||
<input type="hidden" asp-for="StoreId" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="form-group">
|
||||
<label asp-for="Stores" class="form-label"></label>
|
||||
<select asp-for="StoreId" asp-items="Model.Stores" class="form-select"></select>
|
||||
<span asp-validation-for="StoreId" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-4">Invoice Details</h4>
|
||||
}
|
||||
<div class="form-group">
|
||||
<label asp-for="Amount" class="form-label"></label>
|
||||
<input asp-for="Amount" class="form-control" />
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control" />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="OrderId" class="form-label"></label>
|
||||
<input asp-for="OrderId" class="form-control" />
|
||||
<span asp-validation-for="OrderId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ItemDesc" class="form-label"></label>
|
||||
<input asp-for="ItemDesc" class="form-control" />
|
||||
<span asp-validation-for="ItemDesc" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="SupportedTransactionCurrencies" class="form-label"></label>
|
||||
@foreach (var item in Model.AvailablePaymentMethods)
|
||||
{
|
||||
<div class="form-check mb-2">
|
||||
<label class="form-check-label">
|
||||
<input name="SupportedTransactionCurrencies" class="form-check-input" checked="checked" type="checkbox" value="@item.Value">
|
||||
@item.Text
|
||||
</label>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Invoice Details</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="Amount" class="form-label"></label>
|
||||
<input asp-for="Amount" class="form-control" />
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label"></label>
|
||||
<input asp-for="Currency" class="form-control" />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="OrderId" class="form-label"></label>
|
||||
<input asp-for="OrderId" class="form-control" />
|
||||
<span asp-validation-for="OrderId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ItemDesc" class="form-label"></label>
|
||||
<input asp-for="ItemDesc" class="form-control" />
|
||||
<span asp-validation-for="ItemDesc" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="SupportedTransactionCurrencies" class="form-label"></label>
|
||||
@foreach (var item in Model.AvailablePaymentMethods)
|
||||
{
|
||||
<div class="form-check mb-2">
|
||||
<label class="form-check-label">
|
||||
<input name="SupportedTransactionCurrencies" class="form-check-input" checked="checked" type="checkbox" value="@item.Value">
|
||||
@item.Text
|
||||
</label>
|
||||
}
|
||||
<span asp-validation-for="SupportedTransactionCurrencies" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultPaymentMethod" class="form-label"></label>
|
||||
<select asp-for="DefaultPaymentMethod" asp-items="Model.AvailablePaymentMethods" class="form-select">
|
||||
<option value="" selected>Use the store’s default</option>
|
||||
</select>
|
||||
<span asp-validation-for="DefaultPaymentMethod" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Customer Information</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="BuyerEmail" class="form-label"></label>
|
||||
<input asp-for="BuyerEmail" class="form-control" />
|
||||
<span asp-validation-for="BuyerEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RequiresRefundEmail" class="form-label"></label>
|
||||
<select asp-for="RequiresRefundEmail" asp-items="@Html.GetEnumSelectList<RequiresRefundEmail>()" class="form-select"></select>
|
||||
<span asp-validation-for="RequiresRefundEmail" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-pos-data-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-pos-data" aria-expanded="false" aria-controls="additional-pos-data">
|
||||
Point Of Sale Data
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-pos-data" class="accordion-collapse collapse" aria-labelledby="additional-pos-data-header">
|
||||
<p>Custom data to correlate the invoice with an order. This data can be a simple text, number or JSON object, e.g. <code>{ "orderId": 615, "product": "Pizza" }</code></p>
|
||||
<div class="form-group">
|
||||
<label asp-for="PosData" class="form-label"></label>
|
||||
<input asp-for="PosData" class="form-control"/>
|
||||
<span asp-validation-for="PosData" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
<span asp-validation-for="SupportedTransactionCurrencies" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="DefaultPaymentMethod" class="form-label"></label>
|
||||
<select asp-for="DefaultPaymentMethod" asp-items="Model.AvailablePaymentMethods" class="form-select">
|
||||
<option value="" selected>Use the store’s default</option>
|
||||
</select>
|
||||
<span asp-validation-for="DefaultPaymentMethod" class="text-danger"></span>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Customer Information</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="BuyerEmail" class="form-label"></label>
|
||||
<input asp-for="BuyerEmail" class="form-control" />
|
||||
<span asp-validation-for="BuyerEmail" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="RequiresRefundEmail" class="form-label"></label>
|
||||
<select asp-for="RequiresRefundEmail" asp-items="@Html.GetEnumSelectList<RequiresRefundEmail>()" class="form-select"></select>
|
||||
<span asp-validation-for="RequiresRefundEmail" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-pos-data-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-pos-data" aria-expanded="false" aria-controls="additional-pos-data">
|
||||
Point Of Sale Data
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-pos-data" class="accordion-collapse collapse" aria-labelledby="additional-pos-data-header">
|
||||
<p>Custom data to correlate the invoice with an order. This data can be a simple text, number or JSON object, e.g. <code>{ "orderId": 615, "product": "Pizza" }</code></p>
|
||||
<div class="form-group">
|
||||
<label asp-for="PosData" class="form-label"></label>
|
||||
<input asp-for="PosData" class="form-control"/>
|
||||
<span asp-validation-for="PosData" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-notifications-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-notifications" aria-expanded="false" aria-controls="additional-notifications">
|
||||
Invoice Notifications
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-notifications" class="accordion-collapse collapse" aria-labelledby="additional-notifications-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationUrl" class="form-label"></label>
|
||||
<input asp-for="NotificationUrl" class="form-control"/>
|
||||
<span asp-validation-for="NotificationUrl" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-notifications-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-notifications" aria-expanded="false" aria-controls="additional-notifications">
|
||||
Invoice Notifications
|
||||
<vc:icon symbol="caret-down"/>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-notifications" class="accordion-collapse collapse" aria-labelledby="additional-notifications-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationUrl" class="form-label"></label>
|
||||
<input asp-for="NotificationUrl" class="form-control"/>
|
||||
<span asp-validation-for="NotificationUrl" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationEmail" class="form-label"></label>
|
||||
<input asp-for="NotificationEmail" class="form-control"/>
|
||||
<span asp-validation-for="NotificationEmail" class="text-danger"></span>
|
||||
<p id="InvoiceEmailHelpBlock" class="form-text text-muted">
|
||||
Receive updates for this invoice.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="NotificationEmail" class="form-label"></label>
|
||||
<input asp-for="NotificationEmail" class="form-control"/>
|
||||
<span asp-validation-for="NotificationEmail" class="text-danger"></span>
|
||||
<p id="InvoiceEmailHelpBlock" class="form-text text-muted">
|
||||
Receive updates for this invoice.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
|
||||
<a asp-action="ListInvoices" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
|
||||
<a asp-action="ListInvoices" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -44,353 +44,351 @@
|
|||
</script>
|
||||
}
|
||||
|
||||
<section class="invoice-details">
|
||||
<div class="container">
|
||||
<div class="invoice-details">
|
||||
<partial name="_StatusMessage" />
|
||||
<div class="row mb-5">
|
||||
<h2 class="col-xs-12 col-lg-6 mb-4 mb-lg-0">@ViewData["Title"]</h2>
|
||||
<div class="col-xs-12 col-lg-6 mb-2 mb-lg-0 text-lg-end">
|
||||
<div class="d-inline-flex">
|
||||
@if (Model.ShowCheckout)
|
||||
{
|
||||
<a asp-action="Checkout" class="invoice-checkout-link btn btn-primary text-nowrap ms-2" asp-route-invoiceId="@Model.Id">
|
||||
<i class="fa fa-qrcode"></i>
|
||||
Checkout
|
||||
</a>
|
||||
}
|
||||
@if (Model.CanRefund)
|
||||
{
|
||||
<a id="refundlink" class="btn btn-success text-nowrap" asp-action="Refund" asp-route-invoiceId="@Context.GetRouteValue("invoiceId")"><span class="fa fa-undo"></span> Issue Refund</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button href="#" class="btn btn-secondary text-nowrap" data-bs-toggle="tooltip" title="You can only refund an invoice that has been settled. Please wait for the transaction to confirm on the blockchain before attempting to refund it." disabled><span class="fa fa-undo me-1"></span> Issue refund</button>
|
||||
}
|
||||
<form class="p-0 ms-3" asp-action="ToggleArchive" asp-route-invoiceId="@Model.Id" method="post">
|
||||
<button type="submit" class="btn @(Model.Archived ? "btn-warning" : "btn btn-danger")" id="btn-archive-toggle">
|
||||
@if (Model.Archived)
|
||||
{
|
||||
<span class="text-nowrap" data-bs-toggle="tooltip" title="Unarchive this invoice">Unarchive</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-nowrap" data-bs-toggle="tooltip" title="Archive this invoice so that it does not appear in the invoice list by default"><i class="fa fa-archive me-1"></i> Archive</span>
|
||||
}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-md-5">
|
||||
<h3 class="mb-3">Invoice Information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
<tr>
|
||||
<th class="fw-semibold">Store</th>
|
||||
<td><a href="@Model.StoreLink" rel="noreferrer noopener">@Model.StoreName</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Invoice Id</th>
|
||||
<td>@Model.Id</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Order Id</th>
|
||||
<td>
|
||||
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderUrl))
|
||||
{
|
||||
@Model.TypedMetadata.OrderId
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@Model.TypedMetadata.OrderUrl" rel="noreferrer noopener" target="_blank">
|
||||
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderId))
|
||||
{
|
||||
<span>View Order</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Model.TypedMetadata.OrderId
|
||||
}
|
||||
</a>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Payment Request Id</th>
|
||||
<td><a href="@Model.PaymentRequestLink" rel="noreferrer noopener">@Model.TypedMetadata.PaymentRequestId</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">State</th>
|
||||
<td>
|
||||
@if (Model.CanMarkStatus)
|
||||
{
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary btn-sm dropdown-toggle py-1 px-2" type="button" id="markStatusDropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@Model.State
|
||||
@if (Model.StatusException != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format("({0})", Model.StatusException.ToString());
|
||||
}
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="markStatusDropdownMenuButton">
|
||||
@if (Model.CanMarkInvalid)
|
||||
{
|
||||
<a class="dropdown-item" href="#" data-id="@Model.Id" data-status="invalid" data-change-invoice-status-button>
|
||||
Mark as invalid <span class="fa fa-times"></span>
|
||||
</a>
|
||||
}
|
||||
@if (Model.CanMarkSettled)
|
||||
{
|
||||
<a class="dropdown-item" href="#" data-id="@Model.Id" data-status="settled" data-change-invoice-status-button>
|
||||
Mark as settled <span class="fa fa-check-circle"></span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Model.State
|
||||
@if (Model.StatusException != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format(" ({0})", Model.StatusException.ToString());
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Created Date</th>
|
||||
<td>@Model.CreatedDate.ToBrowserDate()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Expiration Date</th>
|
||||
<td>@Model.ExpirationDate.ToBrowserDate()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Monitoring Date</th>
|
||||
<td>@Model.MonitoringDate.ToBrowserDate()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Transaction Speed</th>
|
||||
<td>@Model.TransactionSpeed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Total Fiat Due</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
@if (!string.IsNullOrEmpty(Model.RefundEmail))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Refund Email</th>
|
||||
<td><a href="mailto:@Model.RefundEmail">@Model.RefundEmail</a></td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.NotificationUrl))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Notification Url</th>
|
||||
<td>@Model.NotificationUrl</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.RedirectUrl))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Redirect Url</th>
|
||||
<td><a href="@Model.RedirectUrl" rel="noreferrer noopener">@Model.RedirectUrl</a></td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
@if (Model.PosData.Count == 0)
|
||||
<div class="row mb-5">
|
||||
<h2 class="col-xs-12 col-lg-6 mb-4 mb-lg-0">@ViewData["Title"]</h2>
|
||||
<div class="col-xs-12 col-lg-6 mb-2 mb-lg-0 text-lg-end">
|
||||
<div class="d-inline-flex">
|
||||
@if (Model.ShowCheckout)
|
||||
{
|
||||
<h3 class="mb-3">Product Information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Item code</th>
|
||||
<td>@Model.TypedMetadata.ItemCode</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Item Description</th>
|
||||
<td>@Model.TypedMetadata.ItemDesc</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<th class="fw-semibold">Price</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Tax Included</th>
|
||||
<td>@Model.TaxIncluded</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a asp-action="Checkout" class="invoice-checkout-link btn btn-primary text-nowrap ms-2" asp-route-invoiceId="@Model.Id">
|
||||
<i class="fa fa-qrcode"></i>
|
||||
Checkout
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<h3 class="mb-3">Buyer Information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
<tr>
|
||||
<th class="fw-semibold">Name</th>
|
||||
<td>@Model.TypedMetadata.BuyerName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Email</th>
|
||||
<td><a href="mailto:@Model.TypedMetadata.BuyerEmail">@Model.TypedMetadata.BuyerEmail</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Phone</th>
|
||||
<td>@Model.TypedMetadata.BuyerPhone</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Address 1</th>
|
||||
<td>@Model.TypedMetadata.BuyerAddress1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Address 2</th>
|
||||
<td>@Model.TypedMetadata.BuyerAddress2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">City</th>
|
||||
<td>@Model.TypedMetadata.BuyerCity</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">State</th>
|
||||
<td>@Model.TypedMetadata.BuyerState</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Country</th>
|
||||
<td>@Model.TypedMetadata.BuyerCountry</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Zip</th>
|
||||
<td>@Model.TypedMetadata.BuyerZip</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.PosData.Count != 0)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3 class="mb-3">Product information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
||||
{
|
||||
<tr>
|
||||
<th>Item code</th>
|
||||
<td>@Model.TypedMetadata.ItemCode</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
||||
{
|
||||
<tr>
|
||||
<th>Item Description</th>
|
||||
<td>@Model.TypedMetadata.ItemDesc</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Tax included</th>
|
||||
<td>@Model.TaxIncluded</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4" id="posData">
|
||||
<h3 class="mb-3">Point of Sale Data</h3>
|
||||
|
||||
<partial name="PosData" model="(Model.PosData, 1)" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<partial name="ListInvoicesPaymentsPartial" model="(Model, true)" />
|
||||
|
||||
@if (Model.Deliveries.Count != 0)
|
||||
{
|
||||
<h3 class="mb-3">Webhook deliveries</h3>
|
||||
<ul class="list-group mb-5">
|
||||
@foreach (var delivery in Model.Deliveries)
|
||||
@if (Model.CanRefund)
|
||||
{
|
||||
<li class="list-group-item ">
|
||||
<form
|
||||
asp-action="RedeliverWebhook"
|
||||
asp-route-storeId="@Model.StoreId"
|
||||
asp-route-invoiceId="@Model.Id"
|
||||
asp-route-deliveryId="@delivery.Id"
|
||||
method="post">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="d-flex align-items-center flex-fill me-3">
|
||||
@if (delivery.Success)
|
||||
{
|
||||
<span class="d-flex align-items-center fa fa-check text-success" title="Success"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="d-flex align-items-center fa fa-times text-danger" title="@delivery.ErrorMessage"></span>
|
||||
}
|
||||
<span class="ms-3">
|
||||
<a
|
||||
asp-action="WebhookDelivery"
|
||||
asp-route-invoiceId="@Model.Id"
|
||||
asp-route-deliveryId="@delivery.Id"
|
||||
class="btn btn-link delivery-content" target="_blank">
|
||||
@delivery.Id
|
||||
</a>
|
||||
<span class="text-light mx-2">|</span>
|
||||
<span class="small text-muted">@delivery.Type</span>
|
||||
<span class="text-light mx-2">|</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="d-flex align-items-center">
|
||||
<span class="text-nowrap" data-bs-toggle="tooltip" title="@delivery.PayloadUrl" style="cursor: pointer;">
|
||||
<span class="small text-truncate d-inline-block" style="max-width: 250px;">@delivery.PayloadUrl</span>
|
||||
</span>
|
||||
<span class="text-light mx-2">|</span>
|
||||
<strong class="d-flex align-items-center text-muted small">
|
||||
@delivery.Time.ToBrowserDate()
|
||||
</strong>
|
||||
|
||||
<button id="#redeliver-@delivery.Id"
|
||||
type="submit"
|
||||
class="btn btn-info py-1 ms-3 redeliver">
|
||||
Redeliver
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
<a id="refundlink" class="btn btn-success text-nowrap" asp-action="Refund" asp-route-invoiceId="@Context.GetRouteValue("invoiceId")"><span class="fa fa-undo"></span> Issue Refund</a>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 class="mb-0">Events</h3>
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var evt in Model.Events)
|
||||
else
|
||||
{
|
||||
<button href="#" class="btn btn-secondary text-nowrap" data-bs-toggle="tooltip" title="You can only refund an invoice that has been settled. Please wait for the transaction to confirm on the blockchain before attempting to refund it." disabled><span class="fa fa-undo me-1"></span> Issue refund</button>
|
||||
}
|
||||
<form class="p-0 ms-3" asp-action="ToggleArchive" asp-route-invoiceId="@Model.Id" method="post">
|
||||
<button type="submit" class="btn @(Model.Archived ? "btn-warning" : "btn btn-danger")" id="btn-archive-toggle">
|
||||
@if (Model.Archived)
|
||||
{
|
||||
<tr class="text-@evt.GetCssClass()">
|
||||
<td>@evt.Timestamp.ToBrowserDate()</td>
|
||||
<td>@evt.Message</td>
|
||||
</tr>
|
||||
<span class="text-nowrap" data-bs-toggle="tooltip" title="Unarchive this invoice">Unarchive</span>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
else
|
||||
{
|
||||
<span class="text-nowrap" data-bs-toggle="tooltip" title="Archive this invoice so that it does not appear in the invoice list by default"><i class="fa fa-archive me-1"></i> Archive</span>
|
||||
}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-md-5">
|
||||
<h3 class="mb-3">Invoice Information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
<tr>
|
||||
<th class="fw-semibold">Store</th>
|
||||
<td><a href="@Model.StoreLink" rel="noreferrer noopener">@Model.StoreName</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Invoice Id</th>
|
||||
<td>@Model.Id</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Order Id</th>
|
||||
<td>
|
||||
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderUrl))
|
||||
{
|
||||
@Model.TypedMetadata.OrderId
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@Model.TypedMetadata.OrderUrl" rel="noreferrer noopener" target="_blank">
|
||||
@if (string.IsNullOrEmpty(Model.TypedMetadata.OrderId))
|
||||
{
|
||||
<span>View Order</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Model.TypedMetadata.OrderId
|
||||
}
|
||||
</a>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Payment Request Id</th>
|
||||
<td><a href="@Model.PaymentRequestLink" rel="noreferrer noopener">@Model.TypedMetadata.PaymentRequestId</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">State</th>
|
||||
<td>
|
||||
@if (Model.CanMarkStatus)
|
||||
{
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary btn-sm dropdown-toggle py-1 px-2" type="button" id="markStatusDropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@Model.State
|
||||
@if (Model.StatusException != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format("({0})", Model.StatusException.ToString());
|
||||
}
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="markStatusDropdownMenuButton">
|
||||
@if (Model.CanMarkInvalid)
|
||||
{
|
||||
<a class="dropdown-item" href="#" data-id="@Model.Id" data-status="invalid" data-change-invoice-status-button>
|
||||
Mark as invalid <span class="fa fa-times"></span>
|
||||
</a>
|
||||
}
|
||||
@if (Model.CanMarkSettled)
|
||||
{
|
||||
<a class="dropdown-item" href="#" data-id="@Model.Id" data-status="settled" data-change-invoice-status-button>
|
||||
Mark as settled <span class="fa fa-check-circle"></span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@Model.State
|
||||
@if (Model.StatusException != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format(" ({0})", Model.StatusException.ToString());
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Created Date</th>
|
||||
<td>@Model.CreatedDate.ToBrowserDate()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Expiration Date</th>
|
||||
<td>@Model.ExpirationDate.ToBrowserDate()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Monitoring Date</th>
|
||||
<td>@Model.MonitoringDate.ToBrowserDate()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Transaction Speed</th>
|
||||
<td>@Model.TransactionSpeed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Total Fiat Due</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
@if (!string.IsNullOrEmpty(Model.RefundEmail))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Refund Email</th>
|
||||
<td><a href="mailto:@Model.RefundEmail">@Model.RefundEmail</a></td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.NotificationUrl))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Notification Url</th>
|
||||
<td>@Model.NotificationUrl</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.RedirectUrl))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Redirect Url</th>
|
||||
<td><a href="@Model.RedirectUrl" rel="noreferrer noopener">@Model.RedirectUrl</a></td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
@if (Model.PosData.Count == 0)
|
||||
{
|
||||
<h3 class="mb-3">Product Information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Item code</th>
|
||||
<td>@Model.TypedMetadata.ItemCode</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
||||
{
|
||||
<tr>
|
||||
<th class="fw-semibold">Item Description</th>
|
||||
<td>@Model.TypedMetadata.ItemDesc</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<th class="fw-semibold">Price</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Tax Included</th>
|
||||
<td>@Model.TaxIncluded</td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<h3 class="mb-3">Buyer Information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
<tr>
|
||||
<th class="fw-semibold">Name</th>
|
||||
<td>@Model.TypedMetadata.BuyerName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Email</th>
|
||||
<td><a href="mailto:@Model.TypedMetadata.BuyerEmail">@Model.TypedMetadata.BuyerEmail</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Phone</th>
|
||||
<td>@Model.TypedMetadata.BuyerPhone</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Address 1</th>
|
||||
<td>@Model.TypedMetadata.BuyerAddress1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Address 2</th>
|
||||
<td>@Model.TypedMetadata.BuyerAddress2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">City</th>
|
||||
<td>@Model.TypedMetadata.BuyerCity</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">State</th>
|
||||
<td>@Model.TypedMetadata.BuyerState</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Country</th>
|
||||
<td>@Model.TypedMetadata.BuyerCountry</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-semibold">Zip</th>
|
||||
<td>@Model.TypedMetadata.BuyerZip</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.PosData.Count != 0)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3 class="mb-3">Product information</h3>
|
||||
<table class="table table-responsive-md mb-5">
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemCode))
|
||||
{
|
||||
<tr>
|
||||
<th>Item code</th>
|
||||
<td>@Model.TypedMetadata.ItemCode</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.TypedMetadata.ItemDesc))
|
||||
{
|
||||
<tr>
|
||||
<th>Item Description</th>
|
||||
<td>@Model.TypedMetadata.ItemDesc</td>
|
||||
</tr>
|
||||
}
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<td>@Model.Fiat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Tax included</th>
|
||||
<td>@Model.TaxIncluded</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4" id="posData">
|
||||
<h3 class="mb-3">Point of Sale Data</h3>
|
||||
|
||||
<partial name="PosData" model="(Model.PosData, 1)" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<partial name="ListInvoicesPaymentsPartial" model="(Model, true)" />
|
||||
|
||||
@if (Model.Deliveries.Count != 0)
|
||||
{
|
||||
<h3 class="mb-3">Webhook deliveries</h3>
|
||||
<ul class="list-group mb-5">
|
||||
@foreach (var delivery in Model.Deliveries)
|
||||
{
|
||||
<li class="list-group-item ">
|
||||
<form
|
||||
asp-action="RedeliverWebhook"
|
||||
asp-route-storeId="@Model.StoreId"
|
||||
asp-route-invoiceId="@Model.Id"
|
||||
asp-route-deliveryId="@delivery.Id"
|
||||
method="post">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="d-flex align-items-center flex-fill me-3">
|
||||
@if (delivery.Success)
|
||||
{
|
||||
<span class="d-flex align-items-center fa fa-check text-success" title="Success"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="d-flex align-items-center fa fa-times text-danger" title="@delivery.ErrorMessage"></span>
|
||||
}
|
||||
<span class="ms-3">
|
||||
<a
|
||||
asp-action="WebhookDelivery"
|
||||
asp-route-invoiceId="@Model.Id"
|
||||
asp-route-deliveryId="@delivery.Id"
|
||||
class="btn btn-link delivery-content" target="_blank">
|
||||
@delivery.Id
|
||||
</a>
|
||||
<span class="text-light mx-2">|</span>
|
||||
<span class="small text-muted">@delivery.Type</span>
|
||||
<span class="text-light mx-2">|</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="d-flex align-items-center">
|
||||
<span class="text-nowrap" data-bs-toggle="tooltip" title="@delivery.PayloadUrl" style="cursor: pointer;">
|
||||
<span class="small text-truncate d-inline-block" style="max-width: 250px;">@delivery.PayloadUrl</span>
|
||||
</span>
|
||||
<span class="text-light mx-2">|</span>
|
||||
<strong class="d-flex align-items-center text-muted small">
|
||||
@delivery.Time.ToBrowserDate()
|
||||
</strong>
|
||||
|
||||
<button id="#redeliver-@delivery.Id"
|
||||
type="submit"
|
||||
class="btn btn-info py-1 ms-3 redeliver">
|
||||
Redeliver
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 class="mb-0">Events</h3>
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var evt in Model.Events)
|
||||
{
|
||||
<tr class="text-@evt.GetCssClass()">
|
||||
<td>@evt.Timestamp.ToBrowserDate()</td>
|
||||
<td>@evt.Message</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -178,286 +178,287 @@
|
|||
}
|
||||
|
||||
@Html.HiddenFor(a => a.Count)
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/PaymentRequests/" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/PaymentRequests/" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
<a id="CreateNewInvoice" asp-action="CreateInvoice" asp-route-storeId="@Model.StoreId" asp-route-searchTerm="@Model.SearchTerm" class="btn btn-primary mt-3 mt-sm-0">
|
||||
<span class="fa fa-plus"></span>
|
||||
Create an invoice
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<partial name="InvoiceStatusChangePartial"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-5 mb-lg-2 ms-auto">
|
||||
<form asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" method="get">
|
||||
<input type="hidden" asp-for="Count"/>
|
||||
<input asp-for="TimezoneOffset" type="hidden"/>
|
||||
<div class="input-group">
|
||||
<a href="#help" class="input-group-text text-secondary text-decoration-none" data-bs-toggle="collapse">
|
||||
<span class="fa fa-filter"></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
<a id="CreateNewInvoice" asp-action="CreateInvoice" asp-route-searchTerm="@Model.SearchTerm" class="btn btn-primary mt-3 mt-sm-0">
|
||||
<span class="fa fa-plus"></span>
|
||||
Create an invoice
|
||||
</a>
|
||||
</div>
|
||||
<partial name="InvoiceStatusChangePartial"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-5 mb-lg-2 ms-auto">
|
||||
<form asp-action="ListInvoices" method="get">
|
||||
<input type="hidden" asp-for="Count"/>
|
||||
<input asp-for="TimezoneOffset" type="hidden"/>
|
||||
<div class="input-group">
|
||||
<a href="#help" class="input-group-text text-secondary text-decoration-none" data-bs-toggle="collapse">
|
||||
<span class="fa fa-filter"></span>
|
||||
</a>
|
||||
<input asp-for="SearchTerm" class="form-control"/>
|
||||
<button type="submit" class="btn btn-secondary" title="Search invoice">
|
||||
<span class="fa fa-search"></span> Search
|
||||
</button>
|
||||
<button type="button" id="SearchOptionsToggle" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<input asp-for="SearchTerm" class="form-control"/>
|
||||
<button type="submit" class="btn btn-secondary" title="Search invoice">
|
||||
<span class="fa fa-search"></span> Search
|
||||
</button>
|
||||
<button type="button" id="SearchOptionsToggle" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="SearchOptionsToggle">
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="status:invalid@{@storeIds}">Invalid Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="status:processing,status:settled@{@storeIds}">Paid Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="exceptionstatus:paidLate@{@storeIds}">Paid Late Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="exceptionstatus:paidPartial@{@storeIds}">Paid Partial Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="exceptionstatus:paidOver@{@storeIds}">Paid Over Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="unusual:true@{@storeIds}">Unusual Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-searchTerm="includearchived:true@{@storeIds}">Archived Invoices</a>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-timezoneoffset="0" asp-route-searchTerm="startDate:-24h@{@storeIds}">Last 24 hours</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-timezoneoffset="0" asp-route-searchTerm="startDate:-3d@{@storeIds}">Last 3 days</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-count="@Model.Count" asp-route-timezoneoffset="0" asp-route-searchTerm="startDate:-7d@{@storeIds}">Last 7 days</a>
|
||||
<button type="button" class="dropdown-item" data-bs-toggle="modal" data-bs-target="#customRangeModal">Custom Range</button>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="?searchTerm=">Unfiltered</a>
|
||||
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="SearchOptionsToggle">
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="status:invalid@{@storeIds}">Invalid Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="status:processing,status:settled@{@storeIds}">Paid Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="exceptionstatus:paidLate@{@storeIds}">Paid Late Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="exceptionstatus:paidPartial@{@storeIds}">Paid Partial Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="exceptionstatus:paidOver@{@storeIds}">Paid Over Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="unusual:true@{@storeIds}">Unusual Invoices</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-searchTerm="includearchived:true@{@storeIds}">Archived Invoices</a>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-timezoneoffset="0" asp-route-searchTerm="startDate:-24h@{@storeIds}">Last 24 hours</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-timezoneoffset="0" asp-route-searchTerm="startDate:-3d@{@storeIds}">Last 3 days</a>
|
||||
<a class="dropdown-item" asp-action="ListInvoices" asp-route-storeId="@Model.StoreId" asp-route-count="@Model.Count" asp-route-timezoneoffset="0" asp-route-searchTerm="startDate:-7d@{@storeIds}">Last 7 days</a>
|
||||
<button type="button" class="dropdown-item" data-bs-toggle="modal" data-bs-target="#customRangeModal">Custom Range</button>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="?searchTerm=">Unfiltered</a>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="SearchTerm" class="text-danger"></span>
|
||||
</form>
|
||||
|
||||
@* Custom Range Modal *@
|
||||
<div class="modal fade" id="customRangeModal" tabindex="-1" role="dialog" aria-labelledby="customRangeModalTitle" aria-hidden="true" data-bs-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 550px;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customRangeModalTitle">Filter invoices by Custom Range</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label for="dtpStartDate" class="col-sm-3 col-form-label">Start Date</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="dtpStartDate" class="form-control flatdtpicker" type="datetime-local"
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="Start Date" />
|
||||
<button type="button" class="btn btn-primary input-group-clear" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">End Date</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="dtpEndDate" class="form-control flatdtpicker" type="datetime-local"
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="End Date" />
|
||||
<button type="button" class="btn btn-primary input-group-clear" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="SearchTerm" class="text-danger"></span>
|
||||
</form>
|
||||
|
||||
@* Custom Range Modal *@
|
||||
<div class="modal fade" id="customRangeModal" tabindex="-1" role="dialog" aria-labelledby="customRangeModalTitle" aria-hidden="true" data-bs-backdrop="static">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 550px;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customRangeModalTitle">Filter invoices by Custom Range</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row">
|
||||
<label for="dtpStartDate" class="col-sm-3 col-form-label">Start Date</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="dtpStartDate" class="form-control flatdtpicker" type="datetime-local"
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="Start Date" />
|
||||
<button type="button" class="btn btn-primary input-group-clear" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-3 col-form-label">End Date</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="dtpEndDate" class="form-control flatdtpicker" type="datetime-local"
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="End Date" />
|
||||
<button type="button" class="btn btn-primary input-group-clear" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="btnCustomRangeDate" type="button" class="btn btn-primary">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="btnCustomRangeDate" type="button" class="btn btn-primary">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row collapse" id="help">
|
||||
<div class="col @(Model.Total > 0 ? "pt-3 pb-lg-5" : "")">
|
||||
<p>
|
||||
You can search for invoice Id, deposit address, price, order id, store id, any buyer information and any product information.
|
||||
Be sure to split your search parameters with comma, for example:<br />
|
||||
<code>startdate:2019-04-25 13:00:00, status:paid</code>
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
You can also apply filters to your search by searching for <code>filtername:value</code>, supported filters are:
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>storeid:id</code> for filtering a specific store</li>
|
||||
<li><code>orderid:id</code> for filtering a specific order</li>
|
||||
<li><code>itemcode:code</code> for filtering a specific type of item purchased through the pos or crowdfund apps</li>
|
||||
<li><code>status:(expired|invalid|settled|processing|new)</code> for filtering a specific status</li>
|
||||
<li><code>exceptionstatus:(paidover|paidlate|paidpartial)</code> for filtering a specific exception state</li>
|
||||
<li><code>unusual:(true|false)</code> for filtering invoices which might requires merchant attention (those invalid or with an exceptionstatus)</li>
|
||||
<li><code>startdate:yyyy-MM-dd HH:mm:ss</code> getting invoices that were created after certain date</li>
|
||||
<li><code>enddate:yyyy-MM-dd HH:mm:ss</code> getting invoices that were created before certain date</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<form method="post" id="MassAction" asp-action="MassAction" class="mt-lg-n5">
|
||||
<span class="me-2">
|
||||
<button class="btn btn-secondary dropdown-toggle mb-1" type="button" id="ActionsDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="ActionsDropdownToggle">
|
||||
<button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="archive"><i class="fa fa-archive"></i> Archive</button>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<a class="btn btn-secondary dropdown-toggle mb-1" href="#" role="button" id="ExportDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Export
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="ExportDropdownToggle">
|
||||
<a asp-action="Export" asp-route-timezoneoffset="0" asp-route-format="csv" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item export-link" target="_blank">CSV</a>
|
||||
<a asp-action="Export" asp-route-timezoneoffset="0" asp-route-format="json" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item export-link" target="_blank">JSON</a>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<a href="https://docs.btcpayserver.org/Accounting/" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
|
||||
<table id="invoices" class="table table-hover table-responsive-md mt-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:2rem;" class="only-for-js">
|
||||
<input id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
||||
</th>
|
||||
<th style="min-width:90px;" class="col-md-auto">
|
||||
Date
|
||||
<a id="switchTimeFormat" href="#">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th style="max-width: 180px;">OrderId</th>
|
||||
<th>InvoiceId</th>
|
||||
<th style="min-width: 150px;">Status</th>
|
||||
<th style="text-align:right">Amount</th>
|
||||
<th style="text-align:right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var invoice in Model.Invoices)
|
||||
{
|
||||
<tr id="invoice_@invoice.InvoiceId" class="invoice-row">
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@invoice.InvoiceId"/>
|
||||
</td>
|
||||
<td>
|
||||
<span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()">
|
||||
@invoice.Date.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width: 180px;">
|
||||
@if (invoice.RedirectUrl != string.Empty)
|
||||
{
|
||||
<a href="@invoice.RedirectUrl" class="wraptext200" rel="noreferrer noopener">@invoice.OrderId</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@invoice.OrderId</span>
|
||||
}
|
||||
</td>
|
||||
<td>@invoice.InvoiceId</td>
|
||||
<td>
|
||||
@if (invoice.Details.Archived)
|
||||
{
|
||||
<span class="badge bg-warning">archived</span>
|
||||
}
|
||||
@if (invoice.CanMarkStatus)
|
||||
{
|
||||
<div id="pavpill_@invoice.InvoiceId" class="badge badge-@invoice.Status.Status.ToModernStatus().ToString().ToLower()">
|
||||
<span class="dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@invoice.Status.Status.ToModernStatus().ToString() @* @invoice.Status.ToString() *@
|
||||
@if (invoice.Status.ExceptionStatus != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format("({0})", @invoice.Status.ExceptionStatus.ToString());
|
||||
}
|
||||
</span>
|
||||
<div class="dropdown-menu pull-right">
|
||||
@if (invoice.CanMarkInvalid)
|
||||
{
|
||||
<button class="dropdown-item small cursorPointer changeInvoiceState" data-invoice-id="@invoice.InvoiceId" data-new-state="invalid">
|
||||
Mark as invalid <span class="fa fa-times"></span>
|
||||
</button>
|
||||
}
|
||||
@if (invoice.CanMarkSettled)
|
||||
{
|
||||
<button class="dropdown-item small cursorPointer changeInvoiceState" data-invoice-id="@invoice.InvoiceId" data-new-state="settled">
|
||||
Mark as settled <span class="fa fa-check-circle"></span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge badge-@invoice.Status.Status.ToModernStatus().ToString().ToLower()">
|
||||
@invoice.Status.Status.ToModernStatus().ToString() @* @invoice.Status.ToString().ToLower() *@
|
||||
@if (invoice.Status.ExceptionStatus != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format("({0})", @invoice.Status.ExceptionStatus.ToString());
|
||||
}
|
||||
</span>
|
||||
}
|
||||
@foreach (var paymentType in invoice.Details.Payments.Select(payment => payment.GetPaymentMethodId()?.PaymentType).Distinct().Where(type => type != null && !string.IsNullOrEmpty(type.GetBadge())))
|
||||
{
|
||||
<span class="badge">@paymentType.GetBadge()</span>
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">@invoice.AmountCurrency</td>
|
||||
<td style="text-align:right">
|
||||
@if (invoice.ShowCheckout)
|
||||
{
|
||||
<span>
|
||||
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId" class="invoice-checkout-link" id="invoice-checkout-@invoice.InvoiceId">Checkout</a>
|
||||
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId" class="showInvoice only-for-js" data-invoice-id="@invoice.InvoiceId">[^]</a>
|
||||
@if (!invoice.CanMarkStatus)
|
||||
{
|
||||
<span>-</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
|
||||
<a asp-action="Invoice" class="invoice-details-link" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
|
||||
<a class="only-for-js invoice-details-toggle" href="#">
|
||||
<span title="Invoice Details Toggle" class="fa fa-1x fa-angle-double-down"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="invoice_details_@invoice.InvoiceId" class="invoice-details-row" style="display:none;">
|
||||
<td colspan="99" class="border-top-0">
|
||||
<div style="margin-left: 15px; margin-bottom: 0;">
|
||||
@* Leaving this as partial because it abstracts complexity of Invoice Payments *@
|
||||
<partial name="ListInvoicesPaymentsPartial" model="(invoice.Details, true)"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<vc:pager view-model="Model" />
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no invoices matching your criteria.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="row collapse" id="help">
|
||||
<div class="col @(Model.Total > 0 ? "pt-3 pb-lg-5" : "")">
|
||||
<p>
|
||||
You can search for invoice Id, deposit address, price, order id, store id, any buyer information and any product information.
|
||||
Be sure to split your search parameters with comma, for example:<br />
|
||||
<code>startdate:2019-04-25 13:00:00, status:paid</code>
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
You can also apply filters to your search by searching for <code>filtername:value</code>, supported filters are:
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>storeid:id</code> for filtering a specific store</li>
|
||||
<li><code>orderid:id</code> for filtering a specific order</li>
|
||||
<li><code>itemcode:code</code> for filtering a specific type of item purchased through the pos or crowdfund apps</li>
|
||||
<li><code>status:(expired|invalid|complete|confirmed|paid|new)</code> for filtering a specific status</li>
|
||||
<li><code>exceptionstatus:(paidover|paidlate|paidpartial)</code> for filtering a specific exception state</li>
|
||||
<li><code>unusual:(true|false)</code> for filtering invoices which might requires merchant attention (those invalid or with an exceptionstatus)</li>
|
||||
<li><code>startdate:yyyy-MM-dd HH:mm:ss</code> getting invoices that were created after certain date</li>
|
||||
<li><code>enddate:yyyy-MM-dd HH:mm:ss</code> getting invoices that were created before certain date</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<form method="post" id="MassAction" asp-action="MassAction" class="mt-lg-n5">
|
||||
<input type="hidden" name="storeId" value="@Model.StoreId" />
|
||||
<span class="me-2">
|
||||
<button class="btn btn-secondary dropdown-toggle mb-1" type="button" id="ActionsDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="ActionsDropdownToggle">
|
||||
<button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="archive"><i class="fa fa-archive"></i> Archive</button>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<a class="btn btn-secondary dropdown-toggle mb-1" href="#" role="button" id="ExportDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Export
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="ExportDropdownToggle">
|
||||
<a asp-action="Export" asp-route-timezoneoffset="0" asp-route-format="csv" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item export-link" target="_blank">CSV</a>
|
||||
<a asp-action="Export" asp-route-timezoneoffset="0" asp-route-format="json" asp-route-searchTerm="@Model.SearchTerm" class="dropdown-item export-link" target="_blank">JSON</a>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<a href="https://docs.btcpayserver.org/Accounting/" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
|
||||
<table id="invoices" class="table table-hover table-responsive-md mt-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:2rem;" class="only-for-js">
|
||||
<input id="selectAllCheckbox" type="checkbox" class="form-check-input"/>
|
||||
</th>
|
||||
<th style="min-width:90px;" class="col-md-auto">
|
||||
Date
|
||||
<a id="switchTimeFormat" href="#">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th style="max-width: 180px;">OrderId</th>
|
||||
<th>InvoiceId</th>
|
||||
<th style="min-width: 150px;">Status</th>
|
||||
<th style="text-align:right">Amount</th>
|
||||
<th style="text-align:right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var invoice in Model.Invoices)
|
||||
{
|
||||
<tr id="invoice_@invoice.InvoiceId" class="invoice-row">
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@invoice.InvoiceId"/>
|
||||
</td>
|
||||
<td>
|
||||
<span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()">
|
||||
@invoice.Date.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width: 180px;">
|
||||
@if (invoice.RedirectUrl != string.Empty)
|
||||
{
|
||||
<a href="@invoice.RedirectUrl" class="wraptext200" rel="noreferrer noopener">@invoice.OrderId</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@invoice.OrderId</span>
|
||||
}
|
||||
</td>
|
||||
<td>@invoice.InvoiceId</td>
|
||||
<td>
|
||||
@if (invoice.Details.Archived)
|
||||
{
|
||||
<span class="badge bg-warning">archived</span>
|
||||
}
|
||||
@if (invoice.CanMarkStatus)
|
||||
{
|
||||
<div id="pavpill_@invoice.InvoiceId" class="badge badge-@invoice.Status.Status.ToModernStatus().ToString().ToLower()">
|
||||
<span class="dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@invoice.Status.Status.ToModernStatus().ToString() @* @invoice.Status.ToString() *@
|
||||
@if (invoice.Status.ExceptionStatus != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format("({0})", @invoice.Status.ExceptionStatus.ToString())
|
||||
;
|
||||
}
|
||||
</span>
|
||||
<div class="dropdown-menu pull-right">
|
||||
@if (invoice.CanMarkInvalid)
|
||||
{
|
||||
<button class="dropdown-item small cursorPointer changeInvoiceState" data-invoice-id="@invoice.InvoiceId" data-new-state="invalid">
|
||||
Mark as invalid <span class="fa fa-times"></span>
|
||||
</button>
|
||||
}
|
||||
@if (invoice.CanMarkSettled)
|
||||
{
|
||||
<button class="dropdown-item small cursorPointer changeInvoiceState" data-invoice-id="@invoice.InvoiceId" data-new-state="settled">
|
||||
Mark as settled <span class="fa fa-check-circle"></span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge badge-@invoice.Status.Status.ToModernStatus().ToString().ToLower()">
|
||||
@invoice.Status.Status.ToModernStatus().ToString() @* @invoice.Status.ToString().ToLower() *@
|
||||
@if (invoice.Status.ExceptionStatus != InvoiceExceptionStatus.None)
|
||||
{
|
||||
@String.Format("({0})", @invoice.Status.ExceptionStatus.ToString())
|
||||
;
|
||||
}
|
||||
</span>
|
||||
}
|
||||
@foreach (var paymentType in invoice.Details.Payments.Select(payment => payment.GetPaymentMethodId()?.PaymentType).Distinct().Where(type => type != null && !string.IsNullOrEmpty(type.GetBadge())))
|
||||
{
|
||||
<span class="badge">@paymentType.GetBadge()</span>
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">@invoice.AmountCurrency</td>
|
||||
<td style="text-align:right">
|
||||
@if (invoice.ShowCheckout)
|
||||
{
|
||||
<span>
|
||||
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId" class="invoice-checkout-link" id="invoice-checkout-@invoice.InvoiceId">Checkout</a>
|
||||
<a asp-action="Checkout" asp-route-invoiceId="@invoice.InvoiceId" class="showInvoice only-for-js" data-invoice-id="@invoice.InvoiceId">[^]</a>
|
||||
@if (!invoice.CanMarkStatus)
|
||||
{
|
||||
<span>-</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
|
||||
<a asp-action="Invoice" class="invoice-details-link" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
|
||||
<a class="only-for-js invoice-details-toggle" href="#">
|
||||
<span title="Invoice Details Toggle" class="fa fa-1x fa-angle-double-down"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="invoice_details_@invoice.InvoiceId" class="invoice-details-row" style="display:none;">
|
||||
<td colspan="99" class="border-top-0">
|
||||
<div style="margin-left: 15px; margin-bottom: 0;">
|
||||
@* Leaving this as partial because it abstracts complexity of Invoice Payments *@
|
||||
<partial name="ListInvoicesPaymentsPartial" model="(invoice.Details, true)"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<vc:pager view-model="Model"/>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no invoices matching your criteria.
|
||||
</p>
|
||||
}
|
||||
|
|
|
@ -3,92 +3,90 @@
|
|||
ViewData["Title"] = "Refund";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="row">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">@Model.Title</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="RefundStep" value="@Model.RefundStep"/>
|
||||
<input type="hidden" asp-for="Title" value="@Model.Title"/>
|
||||
@switch (Model.RefundStep)
|
||||
{
|
||||
case RefundSteps.SelectPaymentMethod:
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedPaymentMethod" class="form-label"></label>
|
||||
<select asp-items="Model.AvailablePaymentMethods" asp-for="SelectedPaymentMethod" class="form-select"></select>
|
||||
<span asp-validation-for="SelectedPaymentMethod" class="text-danger"></span>
|
||||
<div class="row">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">@Model.Title</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="RefundStep" value="@Model.RefundStep"/>
|
||||
<input type="hidden" asp-for="Title" value="@Model.Title"/>
|
||||
@switch (Model.RefundStep)
|
||||
{
|
||||
case RefundSteps.SelectPaymentMethod:
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedPaymentMethod" class="form-label"></label>
|
||||
<select asp-items="Model.AvailablePaymentMethods" asp-for="SelectedPaymentMethod" class="form-select"></select>
|
||||
<span asp-validation-for="SelectedPaymentMethod" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="ok" type="submit" class="btn btn-primary btn-lg w-100">Next</button>
|
||||
</div>
|
||||
break;
|
||||
case RefundSteps.SelectRate:
|
||||
<input type="hidden" asp-for="SelectedPaymentMethod"/>
|
||||
<input type="hidden" asp-for="CryptoAmountThen"/>
|
||||
<input type="hidden" asp-for="FiatAmount"/>
|
||||
<input type="hidden" asp-for="CryptoAmountNow"/>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="RateThenText" asp-for="SelectedRefundOption" type="radio" value="RateThen" class="form-check-input"/>
|
||||
<label for="RateThenText" class="form-check-label">@Model.RateThenText</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="ok" type="submit" class="btn btn-primary btn-lg w-100">Next</button>
|
||||
<small class="form-text text-muted">The crypto currency price, at the rate the invoice got paid.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="CurrentRateText" asp-for="SelectedRefundOption" type="radio" value="CurrentRate" class="form-check-input"/>
|
||||
<label for="CurrentRateText" class="form-check-label">@Model.CurrentRateText</label>
|
||||
</div>
|
||||
break;
|
||||
case RefundSteps.SelectRate:
|
||||
<input type="hidden" asp-for="SelectedPaymentMethod"/>
|
||||
<input type="hidden" asp-for="CryptoAmountThen"/>
|
||||
<input type="hidden" asp-for="FiatAmount"/>
|
||||
<input type="hidden" asp-for="CryptoAmountNow"/>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="RateThenText" asp-for="SelectedRefundOption" type="radio" value="RateThen" class="form-check-input"/>
|
||||
<label for="RateThenText" class="form-check-label">@Model.RateThenText</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">The crypto currency price, at the rate the invoice got paid.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="CurrentRateText" asp-for="SelectedRefundOption" type="radio" value="CurrentRate" class="form-check-input"/>
|
||||
<label for="CurrentRateText" class="form-check-label">@Model.CurrentRateText</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">The crypto currency price, at the current rate.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="FiatText" asp-for="SelectedRefundOption" type="radio" value="Fiat" class="form-check-input"/>
|
||||
<label for="FiatText" class="form-check-label">@Model.FiatText</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">The invoice currency, at the rate when the refund will be sent.</small>
|
||||
<small class="form-text text-muted">The crypto currency price, at the current rate.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="FiatText" asp-for="SelectedRefundOption" type="radio" value="Fiat" class="form-check-input"/>
|
||||
<label for="FiatText" class="form-check-label">@Model.FiatText</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">The invoice currency, at the rate when the refund will be sent.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="CustomText" asp-for="SelectedRefundOption" type="radio" value="Custom" class="form-check-input"/>
|
||||
<label for="CustomText" class="form-check-label">Custom</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">The specified amount with the specified currency, at the rate when the refund will be sent. </small>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input id="CustomText" asp-for="SelectedRefundOption" type="radio" value="Custom" class="form-check-input"/>
|
||||
<label for="CustomText" class="form-check-label">Custom</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="ok" type="submit" class="btn btn-primary btn-lg w-100">Create refund</button>
|
||||
</div>
|
||||
break;
|
||||
case RefundSteps.SelectCustomAmount:
|
||||
<small class="form-text text-muted">The specified amount with the specified currency, at the rate when the refund will be sent. </small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="ok" type="submit" class="btn btn-primary btn-lg w-100">Create refund</button>
|
||||
</div>
|
||||
break;
|
||||
case RefundSteps.SelectCustomAmount:
|
||||
|
||||
<input type="hidden" asp-for="SelectedPaymentMethod"/>
|
||||
<input type="hidden" asp-for="CryptoAmountThen"/>
|
||||
<input type="hidden" asp-for="FiatAmount"/>
|
||||
<input type="hidden" asp-for="CryptoAmountNow"/>
|
||||
<input type="hidden" asp-for="SelectedRefundOption"/>
|
||||
<input type="hidden" asp-for="SelectedPaymentMethod"/>
|
||||
<input type="hidden" asp-for="CryptoAmountThen"/>
|
||||
<input type="hidden" asp-for="FiatAmount"/>
|
||||
<input type="hidden" asp-for="CryptoAmountNow"/>
|
||||
<input type="hidden" asp-for="SelectedRefundOption"/>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomAmount" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="CustomAmount" type="number" step="any" asp-format="{0}" class="form-control"/>
|
||||
<input asp-for="CustomCurrency" type="text" class="form-control"/>
|
||||
</div>
|
||||
<span asp-validation-for="CustomAmount" class="text-danger w-100"></span>
|
||||
<span asp-validation-for="CustomCurrency" class="text-danger w-100"></span>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomAmount" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="CustomAmount" type="number" step="any" asp-format="{0}" class="form-control"/>
|
||||
<input asp-for="CustomCurrency" type="text" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="ok" type="submit" class="btn btn-primary btn-lg w-100">Next</button>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<span asp-validation-for="CustomAmount" class="text-danger w-100"></span>
|
||||
<span asp-validation-for="CustomCurrency" class="text-danger w-100"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button id="ok" type="submit" class="btn btn-primary btn-lg w-100">Next</button>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -4,31 +4,29 @@
|
|||
ViewData["Title"] = "Confirm Lightning Payout";
|
||||
var cryptoCode = Context.GetRouteValue("cryptoCode");
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ul class="list-group">
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div data-bs-toggle="tooltip" class="text-break" title="@item.Destination">@item.Destination</div>
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ul class="list-group">
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<div data-bs-toggle="tooltip" class="text-break" title="@item.Destination">@item.Destination</div>
|
||||
|
||||
<span class="text-capitalize badge bg-secondary">@item.Amount @cryptoCode</span>
|
||||
</li>
|
||||
<span class="text-capitalize badge bg-secondary">@item.Amount @cryptoCode</span>
|
||||
</li>
|
||||
|
||||
<form method="post" class="list-group-item justify-content-center" id="pay-invoices-form">
|
||||
<button type="submit" class="btn btn-primary xmx-2" style="min-width:25%;" id="Pay">Pay</button>
|
||||
<button type="button" class="btn btn-secondary mx-2" onclick="history.back(); return false;" style="min-width:25%;">Go back</button>
|
||||
</form>
|
||||
}
|
||||
<form method="post" class="list-group-item justify-content-center" id="pay-invoices-form">
|
||||
<button type="submit" class="btn btn-primary xmx-2" style="min-width:25%;" id="Pay">Pay</button>
|
||||
<button type="button" class="btn btn-secondary mx-2" onclick="history.back(); return false;" style="min-width:25%;">Go back</button>
|
||||
</form>
|
||||
}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
<script>
|
||||
|
|
|
@ -4,22 +4,19 @@
|
|||
Layout = "_Layout";
|
||||
ViewData["Title"] = "Lightning Payout Result";
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
@foreach (var item in Model)
|
||||
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<div class="alert alert-@(item.Result == PayResult.Ok ? "success" : "danger") d-flex justify-content-between align-items-center mb-3" role="alert">
|
||||
@if (item.Result == PayResult.Ok)
|
||||
{
|
||||
<div class="alert alert-@(item.Result == PayResult.Ok ? "success" : "danger") d-flex justify-content-between align-items-center mb-3" role="alert">
|
||||
@if (item.Result == PayResult.Ok)
|
||||
{
|
||||
<div class="text-break me-3" title="@item.Destination">@item.Destination</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-break me-3" title="@item.Destination">@item.Destination: @item.Message (@item.Result)</div>
|
||||
}
|
||||
<span class="badge fs-5">@(item.Result == PayResult.Ok ? "Sent" : "Failed")</span>
|
||||
</div>
|
||||
<div class="text-break me-3" title="@item.Destination">@item.Destination</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-break me-3" title="@item.Destination">@item.Destination: @item.Message (@item.Result)</div>
|
||||
}
|
||||
<span class="badge fs-5">@(item.Result == PayResult.Ok ? "Sent" : "Failed")</span>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
|
|
@ -18,168 +18,165 @@
|
|||
<input type="hidden" asp-for="ApplicationName" value="@Model.ApplicationName"/>
|
||||
<input type="hidden" asp-for="SelectiveStores" value="@Model.SelectiveStores"/>
|
||||
<input type="hidden" asp-for="ApplicationIdentifier" value="@Model.ApplicationIdentifier"/>
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>Authorization Request</h2>
|
||||
<p class="my-3">@(Model.ApplicationName ?? "An application") is requesting access to your account.</p>
|
||||
@if (Model.RedirectUrl != null)
|
||||
{
|
||||
<p class="mb-1 alert alert-info">
|
||||
If authorized, the generated API key will be provided to <strong>@Model.RedirectUrl.AbsoluteUri</strong>
|
||||
</p>
|
||||
}
|
||||
<
|
||||
<div class="row">
|
||||
<div class="col-lg-12 section-heading">
|
||||
<h2>Authorization Request</h2>
|
||||
<p class="my-3">@(Model.ApplicationName ?? "An application") is requesting access to your account.</p>
|
||||
@if (Model.RedirectUrl != null)
|
||||
{
|
||||
<p class="mb-1 alert alert-info">
|
||||
If authorized, the generated API key will be provided to <strong>@Model.RedirectUrl.AbsoluteUri</strong>
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 list-group px-2">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="list-group-item ">
|
||||
<div class="form-group">
|
||||
<label asp-for="Label" class="form-label"></label>
|
||||
<input asp-for="Label" class="form-control"/>
|
||||
<span asp-validation-for="Label" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 list-group px-2">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="list-group-item ">
|
||||
<div class="form-group">
|
||||
<label asp-for="Label" class="form-label"></label>
|
||||
<input asp-for="Label" class="form-control"/>
|
||||
<span asp-validation-for="Label" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
@if (!permissions.Any())
|
||||
@if (!permissions.Any())
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
<p class="text-center">There are no associated permissions to the API key being requested by the application.<br/>The application cannot do anything with your BTCPay account other than validating your account exists.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@for (int i = 0; i < Model.PermissionValues.Count; i++)
|
||||
{
|
||||
<input type="hidden" asp-for="PermissionValues[i].Forbidden"/>
|
||||
<input type="hidden" asp-for="PermissionValues[i].Permission"/>
|
||||
<input type="hidden" asp-for="PermissionValues[i].StoreMode" value="@Model.PermissionValues[i].StoreMode"/>
|
||||
@if (Model.PermissionValues[i].Forbidden && !Model.Strict)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@if (Policies.IsStorePolicy(Model.PermissionValues[i].Permission))
|
||||
{
|
||||
@if (Model.PermissionValues[i].StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
<p class="text-center">There are no associated permissions to the API key being requested by the application.<br/>The application cannot do anything with your BTCPay account other than validating your account exists.</p>
|
||||
<div class="form-check d-flex">
|
||||
@if (Model.Strict || Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="hidden" asp-for="PermissionValues[i].Value"/>
|
||||
<input type="checkbox" checked="@Model.PermissionValues[i].Value" disabled class="form-check-input"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-input"/>
|
||||
}
|
||||
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label m-0 me-4 ms-2">@Model.PermissionValues[i].Title</label>
|
||||
@if (Model.SelectiveStores)
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 me-4" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">select specific stores...</button>
|
||||
}
|
||||
|
||||
@if (Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<br/>
|
||||
<span class="text-danger">
|
||||
This permission is not available for your account.
|
||||
</span>
|
||||
}
|
||||
|
||||
<span asp-validation-for="PermissionValues[i].Value" class="text-danger"></span>
|
||||
<span class="form-text text-muted">@Model.PermissionValues[i].Description</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@for (int i = 0; i < Model.PermissionValues.Count; i++)
|
||||
else if (Model.SelectiveStores)
|
||||
{
|
||||
<input type="hidden" asp-for="PermissionValues[i].Forbidden"/>
|
||||
<input type="hidden" asp-for="PermissionValues[i].Permission"/>
|
||||
<input type="hidden" asp-for="PermissionValues[i].StoreMode" value="@Model.PermissionValues[i].StoreMode"/>
|
||||
@if (Model.PermissionValues[i].Forbidden && !Model.Strict)
|
||||
<div class="list-group-item ">
|
||||
<h5 class="mb-1">@Model.PermissionValues[i].Title</h5>
|
||||
<span class="form-text text-muted">@Model.PermissionValues[i].Description</span>
|
||||
<button type="submit" class="btn btn-link" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">Give permission to all stores instead</button>
|
||||
</div>
|
||||
@if (!Model.Stores.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@if (Policies.IsStorePolicy(Model.PermissionValues[i].Permission))
|
||||
<div class="list-group-item alert-warning">
|
||||
You currently have no stores configured.
|
||||
</div>
|
||||
}
|
||||
@for (var index = 0; index < Model.PermissionValues[i].SpecificStores.Count; index++)
|
||||
{
|
||||
@if (Model.PermissionValues[i].StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
<div class="form-check d-flex">
|
||||
@if (Model.Strict || Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="hidden" asp-for="PermissionValues[i].Value"/>
|
||||
<input type="checkbox" checked="@Model.PermissionValues[i].Value" disabled class="form-check-input"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-input"/>
|
||||
}
|
||||
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label m-0 me-4 ms-2">@Model.PermissionValues[i].Title</label>
|
||||
@if (Model.SelectiveStores)
|
||||
{
|
||||
<button type="submit" class="btn btn-link p-0 me-4" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">select specific stores...</button>
|
||||
}
|
||||
<div class="list-group-item transaction-output-form p-0 ps-lg-2">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-10 py-2">
|
||||
<div class="form-group my-0">
|
||||
@if (Model.PermissionValues[i].SpecificStores[index] == null)
|
||||
{
|
||||
<select asp-for="PermissionValues[i].SpecificStores[index]" class="form-select" asp-items="@(new SelectList(Model.Stores.Where(data => !Model.PermissionValues[i].SpecificStores.Contains(data.Id)), nameof(StoreData.Id), nameof(StoreData.StoreName)))"></select>
|
||||
}
|
||||
else
|
||||
{
|
||||
var store = Model.Stores.SingleOrDefault(data => data.Id == Model.PermissionValues[i].SpecificStores[index]);
|
||||
<select asp-for="PermissionValues[i].SpecificStores[index]" class="form-select" asp-items="@(new SelectList(new[] {store}, nameof(StoreData.Id), nameof(StoreData.StoreName), store.Id))"></select>
|
||||
}
|
||||
|
||||
@if (Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<br/>
|
||||
<span class="text-danger">
|
||||
This permission is not available for your account.
|
||||
</span>
|
||||
}
|
||||
|
||||
<span asp-validation-for="PermissionValues[i].Value" class="text-danger"></span>
|
||||
<span class="form-text text-muted">@Model.PermissionValues[i].Description</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (Model.SelectiveStores)
|
||||
{
|
||||
<div class="list-group-item ">
|
||||
<h5 class="mb-1">@Model.PermissionValues[i].Title</h5>
|
||||
<span class="form-text text-muted">@Model.PermissionValues[i].Description</span>
|
||||
<button type="submit" class="btn btn-link" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">Give permission to all stores instead</button>
|
||||
</div>
|
||||
@if (!Model.Stores.Any())
|
||||
{
|
||||
<div class="list-group-item alert-warning">
|
||||
You currently have no stores configured.
|
||||
</div>
|
||||
}
|
||||
@for (var index = 0; index < Model.PermissionValues[i].SpecificStores.Count; index++)
|
||||
{
|
||||
<div class="list-group-item transaction-output-form p-0 ps-lg-2">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-10 py-2">
|
||||
<div class="form-group my-0">
|
||||
@if (Model.PermissionValues[i].SpecificStores[index] == null)
|
||||
{
|
||||
<select asp-for="PermissionValues[i].SpecificStores[index]" class="form-select" asp-items="@(new SelectList(Model.Stores.Where(data => !Model.PermissionValues[i].SpecificStores.Contains(data.Id)), nameof(StoreData.Id), nameof(StoreData.StoreName)))"></select>
|
||||
}
|
||||
else
|
||||
{
|
||||
var store = Model.Stores.SingleOrDefault(data => data.Id == Model.PermissionValues[i].SpecificStores[index]);
|
||||
<select asp-for="PermissionValues[i].SpecificStores[index]" class="form-select" asp-items="@(new SelectList(new[] {store}, nameof(StoreData.Id), nameof(StoreData.StoreName), store.Id))"></select>
|
||||
}
|
||||
|
||||
<span asp-validation-for="PermissionValues[i].SpecificStores[index]" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-2 pull-right">
|
||||
<button type="submit" title="Remove Store Permission" name="command" value="@($"{Model.PermissionValues[i].Permission}:remove-store:{index}")"
|
||||
class="d-block d-lg-none d-xl-none btn btn-danger mb-2 ms-2">
|
||||
Remove
|
||||
</button>
|
||||
<button type="submit" title="Remove Store Permission" name="command" value="@($"{Model.PermissionValues[i].Permission}:remove-store:{index}")"
|
||||
class="d-none d-lg-block remove-btn text-decoration-none h-100 align-middle btn text-danger btn-link fa fa-times rounded-0 pull-right">
|
||||
</button>
|
||||
</div>
|
||||
<span asp-validation-for="PermissionValues[i].SpecificStores[index]" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (Model.PermissionValues[i].SpecificStores.Count < Model.Stores.Length)
|
||||
{
|
||||
<div class="list-group-item">
|
||||
<button type="submit" name="command" value="@($"{Model.PermissionValues[i].Permission}:add-store")" class="ms-1 btn btn-secondary">Add another store </button>
|
||||
<div class="col-sm-12 col-md-12 col-lg-2 pull-right">
|
||||
<button type="submit" title="Remove Store Permission" name="command" value="@($"{Model.PermissionValues[i].Permission}:remove-store:{index}")"
|
||||
class="d-block d-lg-none d-xl-none btn btn-danger mb-2 ms-2">
|
||||
Remove
|
||||
</button>
|
||||
<button type="submit" title="Remove Store Permission" name="command" value="@($"{Model.PermissionValues[i].Permission}:remove-store:{index}")"
|
||||
class="d-none d-lg-block remove-btn text-decoration-none h-100 align-middle btn text-danger btn-link fa fa-times rounded-0 pull-right">
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
<div class="form-check">
|
||||
@if (Model.Strict || Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="hidden" asp-for="PermissionValues[i].Value"/>
|
||||
<input type="checkbox" checked="@Model.PermissionValues[i].Value" disabled class="form-check-input"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-input"/>
|
||||
}
|
||||
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label">@Model.PermissionValues[i].Title</label>
|
||||
@if (Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<br/>
|
||||
<span class="text-danger">
|
||||
This permission is not available for your account.
|
||||
</span>
|
||||
}
|
||||
<span asp-validation-for="PermissionValues[i].Value" class="text-danger"></span>
|
||||
<span class="form-text text-muted">@Model.PermissionValues[i].Description</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (Model.PermissionValues[i].SpecificStores.Count < Model.Stores.Length)
|
||||
{
|
||||
<div class="list-group-item">
|
||||
<button type="submit" name="command" value="@($"{Model.PermissionValues[i].Permission}:add-store")" class="ms-1 btn btn-secondary">Add another store </button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-4">
|
||||
<div class="col-lg-12 text-center">
|
||||
<button class="btn btn-primary mx-2" name="command" id="consent-yes" type="submit" value="Authorize">Authorize app</button>
|
||||
<button class="btn btn-secondary mx-2" name="command" id="consent-no" type="submit" value="Cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
<div class="form-check">
|
||||
@if (Model.Strict || Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="hidden" asp-for="PermissionValues[i].Value"/>
|
||||
<input type="checkbox" checked="@Model.PermissionValues[i].Value" disabled class="form-check-input"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-input"/>
|
||||
}
|
||||
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label">@Model.PermissionValues[i].Title</label>
|
||||
@if (Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<br/>
|
||||
<span class="text-danger">
|
||||
This permission is not available for your account.
|
||||
</span>
|
||||
}
|
||||
<span asp-validation-for="PermissionValues[i].Value" class="text-danger"></span>
|
||||
<span class="form-text text-muted">@Model.PermissionValues[i].Description</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row my-4">
|
||||
<div class="col-lg-12 text-center">
|
||||
<button class="btn btn-primary mx-2" name="command" id="consent-yes" type="submit" value="Authorize">Authorize app</button>
|
||||
<button class="btn btn-secondary mx-2" name="command" id="consent-no" type="submit" value="Cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
<nav id="sideNav" class="nav flex-column mb-4">
|
||||
<a id="@ManageNavPages.Index.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Index)" asp-controller="Manage" asp-action="Index">Profile</a>
|
||||
<a id="@ManageNavPages.ChangePassword.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.ChangePassword)" asp-controller="Manage" asp-action="ChangePassword">Password</a>
|
||||
<a id="@ManageNavPages.TwoFactorAuthentication.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.TwoFactorAuthentication)" asp-controller="Manage" asp-action="TwoFactorAuthentication">Two-Factor Authentication</a>
|
||||
<a id="@ManageNavPages.APIKeys.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.APIKeys)" asp-controller="Manage" asp-action="APIKeys">API Keys</a>
|
||||
<a id="@ManageNavPages.Notifications.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Notifications)" asp-controller="Manage" asp-action="NotificationSettings">Notifications</a>
|
||||
<nav id="SectionNav" class="nav">
|
||||
<a id="SectionNav-@ManageNavPages.Index.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Index)" asp-controller="Manage" asp-action="Index">Profile</a>
|
||||
<a id="SectionNav-@ManageNavPages.ChangePassword.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.ChangePassword)" asp-controller="Manage" asp-action="ChangePassword">Password</a>
|
||||
<a id="SectionNav-@ManageNavPages.TwoFactorAuthentication.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.TwoFactorAuthentication)" asp-controller="Manage" asp-action="TwoFactorAuthentication">Two-Factor Authentication</a>
|
||||
<a id="SectionNav-@ManageNavPages.APIKeys.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.APIKeys)" asp-controller="Manage" asp-action="APIKeys">API Keys</a>
|
||||
<a id="SectionNav-@ManageNavPages.Notifications.ToString()" class="nav-link @ViewData.IsActivePage(ManageNavPages.Notifications)" asp-controller="Manage" asp-action="NotificationSettings">Notifications</a>
|
||||
<vc:ui-extension-point location="user-nav" model="@Model"/>
|
||||
</nav>
|
||||
|
|
|
@ -3,99 +3,95 @@
|
|||
ViewData["Title"] = "Notifications";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between mb-2">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<a id="NotificationSettings" asp-controller="Manage" asp-action="NotificationSettings" class="btn btn-secondary">
|
||||
<span class="fa fa-cog"></span>
|
||||
Settings
|
||||
</a>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between mb-2">
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<a id="NotificationSettings" asp-controller="Manage" asp-action="NotificationSettings" class="btn btn-secondary">
|
||||
<span class="fa fa-cog"></span>
|
||||
Settings
|
||||
</a>
|
||||
</div>
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<form method="post" asp-action="MassAction">
|
||||
<div class="row button-row">
|
||||
<div class="col-lg-6">
|
||||
<span class="dropdown" style="display:none;" id="MassAction">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button type="submit" class="dropdown-item" name="command" value="mark-seen"><i class="fa fa-eye"></i> Mark seen</button>
|
||||
<button type="submit" class="dropdown-item" name="command" value="mark-unseen"><i class="fa fa-eye-slash"></i> Mark unseen</button>
|
||||
<button type="submit" class="dropdown-item" name="command" value="delete"><i class="fa fa-trash-o"></i> Delete</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<form method="post" asp-action="MassAction">
|
||||
<div class="row button-row">
|
||||
<div class="col-lg-6">
|
||||
<span class="dropdown" style="display:none;" id="MassAction">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button type="submit" class="dropdown-item" name="command" value="mark-seen"><i class="fa fa-eye"></i> Mark seen</button>
|
||||
<button type="submit" class="dropdown-item" name="command" value="mark-unseen"><i class="fa fa-eye-slash"></i> Mark unseen</button>
|
||||
<button type="submit" class="dropdown-item" name="command" value="delete"><i class="fa fa-trash-o"></i> Delete</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30px" class="only-for-js">
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<input name="selectedItems" id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
||||
}
|
||||
</th>
|
||||
<th width="190px">
|
||||
Date
|
||||
<a href="#" id="switchTimeFormat">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>Message</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="30px" class="only-for-js">
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<tr data-guid="@item.Id" class="notification-row @(item.Seen ? "seen" : "")">
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@item.Id" />
|
||||
</td>
|
||||
<td class="toggleRowCheckbox">
|
||||
<span class="switchTimeFormat" data-switch="@item.Created.ToTimeAgo()">
|
||||
@item.Created.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td class="toggleRowCheckbox">
|
||||
@item.Body
|
||||
</td>
|
||||
<td class="text-end fw-normal">
|
||||
@if (!String.IsNullOrEmpty(item.ActionLink))
|
||||
{
|
||||
<a href="@item.ActionLink" class="btn btn-link p-0" rel="noreferrer noopener">Details</a>
|
||||
<span class="d-none d-md-inline-block"> - </span>
|
||||
}
|
||||
<button class="btn btn-link p-0 btn-toggle-seen" type="submit" name="command" value="flip-individual:@(item.Id)">
|
||||
<span>Mark </span><span class="seen-text"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<input name="selectedItems" id="selectAllCheckbox" type="checkbox" class="form-check-input" />
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</th>
|
||||
<th width="190px">
|
||||
Date
|
||||
<a href="#" id="switchTimeFormat">
|
||||
<span class="fa fa-clock-o" title="Switch date format"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th>Message</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
<tr data-guid="@item.Id" class="notification-row @(item.Seen ? "seen" : "")">
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@item.Id" />
|
||||
</td>
|
||||
<td class="toggleRowCheckbox">
|
||||
<span class="switchTimeFormat" data-switch="@item.Created.ToTimeAgo()">
|
||||
@item.Created.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td class="toggleRowCheckbox">
|
||||
@item.Body
|
||||
</td>
|
||||
<td class="text-end fw-normal">
|
||||
@if (!String.IsNullOrEmpty(item.ActionLink))
|
||||
{
|
||||
<a href="@item.ActionLink" class="btn btn-link p-0" rel="noreferrer noopener">Details</a>
|
||||
<span class="d-none d-md-inline-block"> - </span>
|
||||
}
|
||||
<button class="btn btn-link p-0 btn-toggle-seen" type="submit" name="command" value="flip-individual:@(item.Id)">
|
||||
<span>Mark </span><span class="seen-text"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<vc:pager view-model="Model" />
|
||||
<vc:pager view-model="Model" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no notifications.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no notifications.
|
||||
</p>
|
||||
}
|
||||
|
||||
<style>
|
||||
.notification-row.loading {
|
||||
|
|
|
@ -15,134 +15,114 @@
|
|||
<partial name="_ValidationScriptsPartial" />
|
||||
<bundle name="wwwroot/bundles/payment-request-admin-bundle.min.js" asp-append-version="true"></bundle>
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
<form method="post" action="@Url.Action("EditPaymentRequest", "PaymentRequest", new { id = Model.Id}, Context.Request.Scheme)">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<input type="hidden" name="Id" value="@Model.Id" />
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Stores" class="form-label"></label>
|
||||
@if (string.IsNullOrEmpty(Model.Id))
|
||||
{
|
||||
<select asp-for="StoreId" asp-items="Model.Stores" class="form-select"></select>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="hidden" asp-for="StoreId" value="@Model.StoreId" />
|
||||
<input type="text" class="form-control" value="@Model.Stores.Single(item => item.Value == Model.StoreId).Text" readonly />
|
||||
}
|
||||
<span asp-validation-for="StoreId" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
<input type="email" asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
<p id="PaymentRequestEmailHelpBlock" class="form-text text-muted">
|
||||
Receive updates for this payment request.
|
||||
</p>
|
||||
</div>
|
||||
<h4 class="mt-5 mb-4">Request Details</h4>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required></label>
|
||||
<input asp-for="Title" class="form-control" required />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
<partial name="_StatusMessage" />
|
||||
<h2 class="mb-4">@ViewData["Title"]</h2>
|
||||
<form method="post" action="@Url.Action("EditPaymentRequest", "PaymentRequest", new { storeId = Model.StoreId, payReqId = Model.Id }, Context.Request.Scheme)">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required></label>
|
||||
<input asp-for="Title" class="form-control" required />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Amount" class="form-label" data-required></label>
|
||||
<input type="number" step="any" asp-for="Amount" class="form-control" required />
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label" data-required></label>
|
||||
<input asp-for="Currency" class="form-control" required />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Amount" class="form-label" data-required></label>
|
||||
<input type="number" step="any" asp-for="Amount" class="form-control" required />
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Currency" class="form-label" data-required></label>
|
||||
<input asp-for="Currency" class="form-control" required />
|
||||
<span asp-validation-for="Currency" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-check">
|
||||
<input asp-for="AllowCustomPaymentAmounts" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="AllowCustomPaymentAmounts" class="form-check-label"></label>
|
||||
<span asp-validation-for="AllowCustomPaymentAmounts" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ExpiryDate" class="form-label"></label>
|
||||
<div class="input-group ">
|
||||
<input asp-for="ExpiryDate"
|
||||
value="@(Model.ExpiryDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker" min="today" placeholder="No expiry date has been set for this payment request" />
|
||||
<button id="ClearExpiryDate" class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<span asp-validation-for="ExpiryDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group form-check">
|
||||
<input asp-for="AllowCustomPaymentAmounts" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="AllowCustomPaymentAmounts" class="form-check-label"></label>
|
||||
<span asp-validation-for="AllowCustomPaymentAmounts" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ExpiryDate" class="form-label"></label>
|
||||
<div class="input-group ">
|
||||
<input asp-for="ExpiryDate"
|
||||
value="@(Model.ExpiryDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker" min="today" placeholder="No expiry date has been set for this payment request" />
|
||||
<button id="ClearExpiryDate" class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label"></label>
|
||||
<textarea asp-for="Description" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/#2-bootstrap-themes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="ExpiryDate" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="form-label"></label>
|
||||
<input type="email" asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
<p id="PaymentRequestEmailHelpBlock" class="form-text text-muted">
|
||||
Receive updates for this payment request.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label"></label>
|
||||
<textarea asp-for="Description" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-2">Additional Options</h4>
|
||||
<div class="form-group">
|
||||
<div class="accordion" id="additional">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="additional-custom-css-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#additional-custom-css" aria-expanded="false" aria-controls="additional-custom-css">
|
||||
Custom CSS
|
||||
<vc:icon symbol="caret-down" />
|
||||
</button>
|
||||
</h2>
|
||||
<div id="additional-custom-css" class="accordion-collapse collapse" aria-labelledby="additional-custom-css-header">
|
||||
<div class="accordion-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSSLink" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Development/Theme/#2-bootstrap-themes" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<input asp-for="CustomCSSLink" class="form-control" />
|
||||
<span asp-validation-for="CustomCSSLink" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="EmbeddedCSS" class="form-label"></label>
|
||||
<textarea asp-for="EmbeddedCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="EmbeddedCSS" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-4">
|
||||
<button type="submit" class="btn btn-primary" id="SaveButton">Save</button>
|
||||
@if (!string.IsNullOrEmpty(Model.Id))
|
||||
{
|
||||
<a class="btn btn-secondary" target="_blank" asp-action="ViewPaymentRequest" asp-route-id="@Context.GetRouteValue("id")" id="@Model.Id" name="ViewAppButton">View</a>
|
||||
<a class="btn btn-secondary"
|
||||
target="_blank"
|
||||
asp-action="ListInvoices"
|
||||
asp-controller="Invoice"
|
||||
asp-route-searchterm="@($"orderid:{PaymentRequestRepository.GetOrderIdForPaymentRequest(Model.Id)}")">Invoices</a>
|
||||
<a class="btn btn-secondary" asp-route-id="@Context.GetRouteValue("id")" asp-action="ClonePaymentRequest" id="@Model.Id">Clone</a>
|
||||
@if (!Model.Archived)
|
||||
{
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" title="Archive this payment request so that it does not appear in the payment request list by default" asp-route-id="@Context.GetRouteValue("id")" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival">Archive</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" title="Unarchive this payment request" asp-route-id="@Context.GetRouteValue("id")" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival">Unarchive</a>
|
||||
}
|
||||
}
|
||||
<a asp-action="GetPaymentRequests" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="form-group mt-4">
|
||||
<button type="submit" class="btn btn-primary" id="SaveButton">Save</button>
|
||||
@if (!string.IsNullOrEmpty(Model.Id))
|
||||
{
|
||||
<a class="btn btn-secondary" target="_blank" asp-action="ViewPaymentRequest" asp-route-payReqId="@Model.Id" id="ViewAppButton">View</a>
|
||||
<a class="btn btn-secondary"
|
||||
target="_blank"
|
||||
asp-action="ListInvoices"
|
||||
asp-controller="Invoice"
|
||||
asp-route-searchterm="@($"orderid:{PaymentRequestRepository.GetOrderIdForPaymentRequest(Model.Id)}")">Invoices</a>
|
||||
<a class="btn btn-secondary" asp-route-payReqId="@Model.Id" asp-action="ClonePaymentRequest" id="@Model.Id">Clone</a>
|
||||
@if (!Model.Archived)
|
||||
{
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" title="Archive this payment request so that it does not appear in the payment request list by default" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival" asp-route-payReqId="@Model.Id">Archive</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="btn btn-secondary" data-bs-toggle="tooltip" title="Unarchive this payment request" asp-controller="PaymentRequest" asp-action="TogglePaymentRequestArchival" asp-route-payReqId="@Model.Id">Unarchive</a>
|
||||
}
|
||||
}
|
||||
<a asp-action="GetPaymentRequests" asp-route-storeId="@Model.StoreId" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
|
|
|
@ -4,98 +4,95 @@
|
|||
Layout = "_Layout";
|
||||
ViewData["Title"] = "Payment Requests";
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/PaymentRequests/" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
<a asp-action="EditPaymentRequest" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreatePaymentRequest">
|
||||
<span class="fa fa-plus"></span>
|
||||
Create a payment request
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/PaymentRequests/" class="ms-1" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
</small>
|
||||
</h2>
|
||||
<a asp-action="EditPaymentRequest" asp-route-storeId="@Context.GetStoreData().Id" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreatePaymentRequest">
|
||||
<span class="fa fa-plus"></span>
|
||||
Create a payment request
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-3 ms-auto">
|
||||
<form asp-action="GetPaymentRequests" method="get">
|
||||
<input type="hidden" asp-for="Count"/>
|
||||
<input type="hidden" asp-for="TimezoneOffset" />
|
||||
<div class="input-group">
|
||||
<input asp-for="SearchTerm" class="form-control" style="width:300px;"/>
|
||||
<button type="submit" class="btn btn-secondary" title="Search invoice">
|
||||
<span class="fa fa-search"></span> Search
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" asp-action="GetPaymentRequests" asp-route-count="@Model.Count" asp-route-searchTerm="includearchived:true">Include Archived Payment Reqs</a>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="?searchTerm=">Unfiltered</a>
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="SearchTerm" class="text-danger"></span>
|
||||
</form>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-3 ms-auto">
|
||||
<form asp-action="GetPaymentRequests" method="get">
|
||||
<input type="hidden" asp-for="Count"/>
|
||||
<input type="hidden" asp-for="TimezoneOffset" />
|
||||
<div class="input-group">
|
||||
<input asp-for="SearchTerm" class="form-control" style="width:300px;"/>
|
||||
<button type="submit" class="btn btn-secondary" title="Search invoice">
|
||||
<span class="fa fa-search"></span> Search
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" asp-action="GetPaymentRequests" asp-route-storeId="@Context.GetStoreData().Id" asp-route-count="@Model.Count" asp-route-searchTerm="includearchived:true">Include Archived Payment Reqs</a>
|
||||
<div role="separator" class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="?searchTerm=">Unfiltered</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Expiry</th>
|
||||
<th class="text-end">Price</th>
|
||||
<th class="text-end">Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Title</td>
|
||||
<td>@(item.ExpiryDate?.ToString("g") ?? "No Expiry")</td>
|
||||
<td class="text-end">@item.Amount @item.Currency</td>
|
||||
<td class="text-end">@item.Status</td>
|
||||
<td class="text-end">
|
||||
<a asp-action="EditPaymentRequest" asp-route-id="@item.Id">Edit</a>
|
||||
<span> - </span>
|
||||
<a asp-action="ViewPaymentRequest" asp-route-id="@item.Id">View</a>
|
||||
<span> - </span>
|
||||
<a target="_blank" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@($"orderid:{PaymentRequestRepository.GetOrderIdForPaymentRequest(item.Id)}")">Invoices</a>
|
||||
<span> - </span>
|
||||
<a target="_blank" asp-action="PayPaymentRequest" asp-route-id="@item.Id">Pay</a>
|
||||
<span> - </span>
|
||||
<a target="_blank" asp-action="ClonePaymentRequest" asp-route-id="@item.Id">Clone</a>
|
||||
<span> - </span>
|
||||
<a asp-action="TogglePaymentRequestArchival" asp-route-id="@item.Id">@(item.Archived ? "Unarchive" : "Archive")</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<vc:pager view-model="Model"></vc:pager>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no payment requests matching your criteria.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<span asp-validation-for="SearchTerm" class="text-danger"></span>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Expiry</th>
|
||||
<th class="text-end">Price</th>
|
||||
<th class="text-end">Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Title</td>
|
||||
<td>@(item.ExpiryDate?.ToString("g") ?? "No Expiry")</td>
|
||||
<td class="text-end">@item.Amount @item.Currency</td>
|
||||
<td class="text-end">@item.Status</td>
|
||||
<td class="text-end">
|
||||
<a asp-action="EditPaymentRequest" asp-route-storeId="@item.StoreId" asp-route-payReqId="@item.Id">Edit</a>
|
||||
<span> - </span>
|
||||
<a asp-action="ViewPaymentRequest" asp-route-payReqId="@item.Id">View</a>
|
||||
<span> - </span>
|
||||
<a target="_blank" asp-controller="Invoice" asp-action="ListInvoices" asp-route-searchterm="@($"orderid:{PaymentRequestRepository.GetOrderIdForPaymentRequest(item.Id)}")">Invoices</a>
|
||||
<span> - </span>
|
||||
<a target="_blank" asp-action="PayPaymentRequest" asp-route-payReqId="@item.Id">Pay</a>
|
||||
<span> - </span>
|
||||
<a target="_blank" asp-action="ClonePaymentRequest" asp-route-storeId="@item.StoreId" asp-route-payReqId="@item.Id">Clone</a>
|
||||
<span> - </span>
|
||||
<a asp-action="TogglePaymentRequestArchival" asp-route-storeId="@item.StoreId" asp-route-payReqId="@item.Id">@(item.Archived ? "Unarchive" : "Archive")</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<vc:pager view-model="Model" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no payment requests matching your criteria.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app" class="min-vh-100 d-flex flex-column">
|
||||
<nav id="mainNav" class="navbar sticky-top py-3 py-lg-4 d-print-block">
|
||||
<nav class="btcpay-header navbar sticky-top py-3 py-lg-4 d-print-block">
|
||||
<div class="container">
|
||||
<div class="row align-items-center" style="width:calc(100% + 30px)">
|
||||
<div class="col-12 col-md-8 col-lg-9">
|
||||
|
@ -107,7 +107,7 @@
|
|||
{
|
||||
@if (Model.AllowCustomPaymentAmounts && !Model.AnyPendingInvoice)
|
||||
{
|
||||
<form method="get" asp-action="PayPaymentRequest" asp-route-id="@Model.Id" class="d-print-none">
|
||||
<form method="get" asp-action="PayPaymentRequest" asp-route-payReqId="@Model.Id" class="d-print-none">
|
||||
<div class="row">
|
||||
<div class="col col-12 col-sm-6 col-md-12">
|
||||
<div class="input-group">
|
||||
|
@ -123,12 +123,12 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
<a class="btn btn-primary d-inline-block d-print-none w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-id="@Model.Id" data-test="pay-button">
|
||||
<a class="btn btn-primary d-inline-block d-print-none w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-payReqId="@Model.Id" data-test="pay-button">
|
||||
Pay Invoice
|
||||
</a>
|
||||
if (Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments && Model.AllowCustomPaymentAmounts)
|
||||
{
|
||||
<form method="get" asp-action="CancelUnpaidPendingInvoice" asp-route-id="@Model.Id" class="mt-2 d-print-none">
|
||||
<form method="get" asp-action="CancelUnpaidPendingInvoice" asp-route-payReqId="@Model.Id" class="mt-2 d-print-none">
|
||||
<button class="btn btn-outline-secondary w-100 text-nowrap" type="submit">Cancel Invoice</button>
|
||||
</form>
|
||||
}
|
||||
|
|
|
@ -2,19 +2,16 @@
|
|||
var allErrors = ViewData.ModelState.Values.SelectMany(v => v.Errors.Select(b => b.ErrorMessage));
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h2>Pay Button request failed</h2>
|
||||
Please fix following errors:
|
||||
<ul>
|
||||
@foreach (var error in allErrors)
|
||||
{
|
||||
<li>@error</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h2>Pay Button request failed</h2>
|
||||
Please fix following errors:
|
||||
<ul>
|
||||
@foreach (var error in allErrors)
|
||||
{
|
||||
<li>@error</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<div class="min-vh-100 d-flex flex-column">
|
||||
@if (Model.IsPending)
|
||||
{
|
||||
<nav id="mainNav" class="navbar sticky-top py-3 py-lg-4 d-print-none">
|
||||
<nav class="btcpay-header navbar sticky-top py-3 py-lg-4 d-print-none">
|
||||
<div class="container">
|
||||
<form asp-action="ClaimPullPayment" asp-route-pullPaymentId="@Model.Id" class="w-100">
|
||||
<div class="row align-items-center" style="width:calc(100% + 30px)">
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
@using BTCPayServer.Configuration
|
||||
@inject BTCPayServerOptions BTCPayServerOptions
|
||||
<nav id="sideNav" class="nav flex-column mb-4">
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Users" class="nav-link @ViewData.IsActivePage(ServerNavPages.Users)" asp-action="ListUsers">Users</a>
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Emails" class="nav-link @ViewData.IsActivePage(ServerNavPages.Emails)" asp-action="Emails">Email Server</a>
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Policies" class="nav-link @ViewData.IsActivePage(ServerNavPages.Policies)" asp-action="Policies">Policies</a>
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Services" class="nav-link @ViewData.IsActivePage(ServerNavPages.Services)" asp-action="Services">Services</a>
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Theme" class="nav-link @ViewData.IsActivePage(ServerNavPages.Theme)" asp-action="Theme">Theme</a>
|
||||
|
||||
<nav id="SectionNav" class="nav">
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Users" class="nav-link @ViewData.IsActivePage(ServerNavPages.Users)" asp-action="ListUsers">Users</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Emails" class="nav-link @ViewData.IsActivePage(ServerNavPages.Emails)" asp-action="Emails">Email Server</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Policies" class="nav-link @ViewData.IsActivePage(ServerNavPages.Policies)" asp-action="Policies">Policies</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Services" class="nav-link @ViewData.IsActivePage(ServerNavPages.Services)" asp-action="Services">Services</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Theme" class="nav-link @ViewData.IsActivePage(ServerNavPages.Theme)" asp-action="Theme">Theme</a>
|
||||
@if (BTCPayServerOptions.DockerDeployment)
|
||||
{
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Maintenance" class="nav-link @ViewData.IsActivePage(ServerNavPages.Maintenance)" asp-action="Maintenance">Maintenance</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Maintenance" class="nav-link @ViewData.IsActivePage(ServerNavPages.Maintenance)" asp-action="Maintenance">Maintenance</a>
|
||||
}
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Logs" class="nav-link @ViewData.IsActivePage(ServerNavPages.Logs)" asp-action="LogsView">Logs</a>
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Files" class="nav-link @ViewData.IsActivePage(ServerNavPages.Files)" asp-action="Files">Files</a>
|
||||
<a asp-controller="Server" id="Server-@ServerNavPages.Plugins" class="nav-link @ViewData.IsActivePage(ServerNavPages.Plugins)" asp-action="ListPlugins">Plugins (experimental)</a>
|
||||
<vc:ui-extension-point location="server-nav" model="@Model"/>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Logs" class="nav-link @ViewData.IsActivePage(ServerNavPages.Logs)" asp-action="LogsView">Logs</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Files" class="nav-link @ViewData.IsActivePage(ServerNavPages.Files)" asp-action="Files">Files</a>
|
||||
<a asp-controller="Server" id="SectionNav-@ServerNavPages.Plugins" class="nav-link @ViewData.IsActivePage(ServerNavPages.Plugins)" asp-action="ListPlugins">Plugins (experimental)</a>
|
||||
<vc:ui-extension-point location="server-nav" model="@Model"/>
|
||||
</nav>
|
||||
|
|
|
@ -1,224 +1,129 @@
|
|||
@using BTCPayServer.Views.Server
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using BTCPayServer.Views.Apps
|
||||
@using BTCPayServer.Views.Invoice
|
||||
@using BTCPayServer.Views.Manage
|
||||
@using BTCPayServer.Views.PaymentRequest
|
||||
@using BTCPayServer.Views.Wallets
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
@inject RoleManager<IdentityRole> RoleManager
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
|
||||
@inject ISettingsRepository SettingsRepository
|
||||
@inject LinkGenerator linkGenerator
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment _env
|
||||
@inject UserManager<ApplicationUser> _userManager
|
||||
@inject ISettingsRepository _settingsRepository
|
||||
@inject LinkGenerator _linkGenerator
|
||||
|
||||
@{
|
||||
var theme = await SettingsRepository.GetTheme();
|
||||
}
|
||||
|
||||
@functions {
|
||||
// The .NET function for inserting classes requires this to be async
|
||||
// ReSharper disable once CSharpWarnings::CS1998
|
||||
#pragma warning disable CS1998
|
||||
private async Task Logo(string classes = "")
|
||||
var logoSrc = $"{ViewContext.HttpContext.Request.PathBase}/img/logo.svg";
|
||||
var notificationDisabled = (await _settingsRepository.GetPolicies()).DisableInstantNotifications;
|
||||
if (!notificationDisabled)
|
||||
{
|
||||
<a href="~/" class="navbar-brand py-2 js-scroll-trigger @classes">
|
||||
<svg class="logo" viewBox="0 0 192 84" xmlns="http://www.w3.org/2000/svg"><g><path d="M5.206 83.433a4.86 4.86 0 01-4.859-4.861V5.431a4.86 4.86 0 119.719 0v73.141a4.861 4.861 0 01-4.86 4.861" fill="#CEDC21" class="logo-brand-light"/><path d="M5.209 83.433a4.862 4.862 0 01-2.086-9.253L32.43 60.274 2.323 38.093a4.861 4.861 0 015.766-7.826l36.647 26.999a4.864 4.864 0 01-.799 8.306L7.289 82.964a4.866 4.866 0 01-2.08.469" fill="#51B13E" class="logo-brand-medium"/><path d="M5.211 54.684a4.86 4.86 0 01-2.887-8.774L32.43 23.73 3.123 9.821a4.861 4.861 0 014.166-8.784l36.648 17.394a4.86 4.86 0 01.799 8.305l-36.647 27a4.844 4.844 0 01-2.878.948" fill="#CEDC21" class="logo-brand-light"/><path d="M10.066 31.725v20.553L24.01 42.006z" fill="#1E7A44" class="logo-brand-dark"/><path d="M10.066 5.431A4.861 4.861 0 005.206.57 4.86 4.86 0 00.347 5.431v61.165h9.72V5.431h-.001z" fill="#CEDC21" class="logo-brand-light"/><path d="M74.355 41.412c3.114.884 4.84 3.704 4.84 7.238 0 5.513-3.368 8.082-7.955 8.082H60.761V27.271h9.259c4.504 0 7.997 2.146 7.997 7.743 0 2.821-1.179 5.43-3.662 6.398m-4.293-.716c3.324 0 6.018-1.179 6.018-5.724 0-4.586-2.776-5.808-6.145-5.808h-7.197v11.531h7.324v.001zm1.052 14.099c3.366 0 6.06-1.768 6.06-6.145 0-4.713-3.072-6.144-6.901-6.144h-7.534v12.288h8.375v.001zM98.893 27.271v1.81h-8.122v27.651h-1.979V29.081h-8.123v-1.81zM112.738 26.85c5.01 0 9.554 2.524 10.987 8.543h-1.895c-1.348-4.923-5.303-6.732-9.134-6.732-6.944 0-10.605 5.681-10.605 13.341 0 8.08 3.661 13.256 10.646 13.256 4.125 0 7.828-1.85 9.26-7.279h1.895c-1.264 6.271-6.229 9.174-11.154 9.174-7.87 0-12.583-5.808-12.583-15.15 0-8.966 4.969-15.153 12.583-15.153M138.709 27.271c5.091 0 8.795 3.326 8.795 9.764 0 6.06-3.704 9.722-8.795 9.722h-7.746v9.976h-1.935V27.271h9.681zm0 17.549c3.745 0 6.816-2.397 6.816-7.827 0-5.429-2.947-7.869-6.816-7.869h-7.746V44.82h7.746zM147.841 56.732v-.255l11.741-29.29h.885l11.615 29.29v.255h-2.062l-3.322-8.501H153.27l-3.324 8.501h-2.105zm12.164-26.052l-6.059 15.697h12.078l-6.019-15.697zM189.551 27.271h2.104v.293l-9.176 16.92v12.248h-2.02V44.484l-9.216-16.961v-.252h2.147l3.997 7.492 4.043 7.786h.04l4.081-7.786z" class="logo-brand-text"/></g></svg>
|
||||
@if (Env.NetworkType != NBitcoin.ChainName.Mainnet)
|
||||
{
|
||||
<span class="badge bg-warning" style="font-size:10px;">@Env.NetworkType.ToString()</span>
|
||||
}
|
||||
</a>
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
notificationDisabled = user?.DisabledNotifications == "all";
|
||||
}
|
||||
#pragma warning restore CS1998
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"@(Env.IsDeveloping ? " data-devenv" : "")>
|
||||
<html lang="en"@(_env.IsDeveloping ? " data-devenv" : "")>
|
||||
<head>
|
||||
<partial name="LayoutHead" />
|
||||
@await RenderSectionAsync("PageHeadContent", false)
|
||||
</head>
|
||||
<body id="page-top">
|
||||
@{
|
||||
if (ViewBag.AlwaysShrinkNavBar == null)
|
||||
{
|
||||
ViewBag.AlwaysShrinkNavBar = true;
|
||||
}
|
||||
var additionalStyle = ViewBag.AlwaysShrinkNavBar ? "navbar-shrink always-shrinked" : "";
|
||||
}
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg fixed-top py-2 @additionalStyle" id="mainNav">
|
||||
<div class="container">
|
||||
@* Logo on Mobile *@
|
||||
@{ await Logo("d-lg-none"); }
|
||||
<button class="navbar-toggler navbar-toggler-right border-0 shadow-none p-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<svg class="navbar-toggler-icon" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg"><path stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" d="M4 7h22M4 15h22M4 23h22"/></svg>
|
||||
</button>
|
||||
<div id="navbarResponsive" class="offcanvas offcanvas-fade border-0" data-bs-scroll="true" tabindex="-1">
|
||||
<div class="container">
|
||||
<div class="offcanvas-header">
|
||||
<div class="offcanvas-title" id="offcanvasLabel">
|
||||
@* Logo in Offcanvas *@
|
||||
@{ await Logo(); }
|
||||
</div>
|
||||
<button type="button" class="btn-close shadow-none m-0" data-bs-dismiss="offcanvas" aria-label="Close" style="padding:.8125rem;">
|
||||
<vc:icon symbol="close"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="offcanvas-body d-lg-flex w-100 align-items-center justify-content-between">
|
||||
@* Logo on Desktop *@
|
||||
@{ await Logo("d-none d-lg-inline-block"); }
|
||||
@if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
<ul class="navbar-nav">
|
||||
@if (User.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Server" asp-action="ListUsers" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(ServerNavPages))" id="ServerSettings">Server settings</a></li>
|
||||
}
|
||||
<li class="nav-item"><a asp-area="" asp-controller="UserStores" asp-action="ListStores" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(StoreNavPages))" id="Stores">Stores</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Apps" asp-action="ListApps" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(AppsNavPages))" id="Apps">Apps</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Wallets" asp-action="ListWallets" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(WalletsNavPages))" id="Wallets">Wallets</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Invoice" asp-action="ListInvoices" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(InvoiceNavPages))" id="Invoices">Invoices</a></li>
|
||||
<li class="nav-item"><a asp-area="" asp-controller="PaymentRequest" asp-action="GetPaymentRequests" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(PaymentRequestsNavPages))" id="PaymentRequests">Payment Requests</a></li>
|
||||
<vc:ui-extension-point location="header-nav" model="@Model"/>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Manage" asp-action="Index" title="My settings" class="nav-link js-scroll-trigger @ViewData.IsActiveCategory(typeof(ManageNavPages))" id="MySettings"><span class="d-lg-none d-sm-block">Account</span><i class="fa fa-user d-lg-inline-block d-none"></i></a>
|
||||
</li>
|
||||
<vc:notifications-dropdown/>
|
||||
@if (!theme.CustomTheme)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<vc:theme-switch responsive="lg" css-class="nav-link" />
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp- asp-action="Logout" title="Logout" class="nav-link js-scroll-trigger" id="Logout"><span class="d-lg-none d-sm-block">Logout</span><i class="fa fa-sign-out d-lg-inline-block d-none"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
else if (Env.IsSecure)
|
||||
{
|
||||
<ul class="navbar-nav">
|
||||
@if (!(await SettingsRepository.GetPolicies()).LockSubscription)
|
||||
{
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Register" class="nav-link js-scroll-trigger" id="Register">Register</a></li>
|
||||
}
|
||||
<li class="nav-item"><a asp-area="" asp-controller="Account" asp-action="Login" class="nav-link js-scroll-trigger" id="Login">Log in</a></li>
|
||||
<vc:ui-extension-point location="header-nav" model="@Model"/>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="badUrl" class="alert alert-danger alert-dismissible" style="display:none; position:absolute; top:75px;" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
<span>BTCPay is expecting you to access this website from <b>@(Env.ExpectedProtocol)://@(Env.ExpectedHost)/</b>. If you use a reverse proxy, please set the <b>X-Forwarded-Proto</b> header to <b id="browserScheme">@(Env.ExpectedProtocol)</b> (<a href="https://docs.btcpayserver.org/FAQ/Deployment/#cause-3-btcpay-is-expecting-you-to-access-this-website-from" target="_blank" class="alert-link" rel="noreferrer noopener">More information</a>)</span>
|
||||
</div>
|
||||
@if (!Env.IsSecure)
|
||||
<body class="d-flex flex-column flex-lg-row min-vh-100">
|
||||
<header id="mainMenu" class="btcpay-header d-flex flex-column">
|
||||
<div id="mainMenuHead" class="d-flex flex-lg-wrap align-items-center justify-content-between py-2 px-3 py-lg-3 px-lg-4">
|
||||
<a href="~/" class="navbar-brand py-2 js-scroll-trigger">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" role="img" alt="BTCPay Server" class="logo"><use href="@logoSrc#small" class="logo-small" /><use href="@logoSrc#large" class="logo-large" /></svg>
|
||||
@if (_env.NetworkType != NBitcoin.ChainName.Mainnet)
|
||||
{
|
||||
<div id="insecureEnv" class="alert alert-danger alert-dismissible" style="position:absolute; top:75px;" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
<span>
|
||||
Your access to BTCPay Server is over an unsecured network. If you are using the docker deployment method with NGINX and HTTPS is not available, you probably did not configure your DNS settings correctly. <br/>
|
||||
We disabled the register and login link so you don't leak your credentials.
|
||||
</span>
|
||||
</div>
|
||||
<span class="badge bg-warning ms-1 ms-sm-0" style="font-size:10px;">@_env.NetworkType.ToString()</span>
|
||||
}
|
||||
</a>
|
||||
<vc:store-selector />
|
||||
<vc:notifications-dropdown />
|
||||
<button id="mainMenuToggle" class="mainMenuButton" type="button" data-bs-toggle="offcanvas" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span>Menu</span>
|
||||
</button>
|
||||
</div>
|
||||
<vc:main-nav />
|
||||
</header>
|
||||
<main id="mainContent">
|
||||
@if (_env.Context.Request.Host.ToString() != _env.ExpectedHost || _env.Context.Request.Scheme != _env.ExpectedProtocol)
|
||||
{
|
||||
<div id="badUrl" class="alert alert-danger alert-dismissible m-3" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close"/>
|
||||
</button>
|
||||
<span>
|
||||
BTCPay is expecting you to access this website from <strong>@(_env.ExpectedProtocol)://@(_env.ExpectedHost)/</strong>.
|
||||
If you use a reverse proxy, please set the <strong>X-Forwarded-Proto</strong> header to <strong id="browserScheme">@(_env.ExpectedProtocol)</strong>
|
||||
(<a href="https://docs.btcpayserver.org/FAQ/Deployment/#cause-3-btcpay-is-expecting-you-to-access-this-website-from" target="_blank" class="alert-link" rel="noreferrer noopener">More information</a>)
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@RenderBody()
|
||||
|
||||
}
|
||||
@if (!_env.IsSecure)
|
||||
{
|
||||
<div id="insecureEnv" class="alert alert-danger alert-dismissible" style="position:absolute; top:75px;" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close"/>
|
||||
</button>
|
||||
<span>
|
||||
Your access to BTCPay Server is over an unsecured network. If you are using the docker deployment method with NGINX and HTTPS is not available, you probably did not configure your DNS settings correctly. <br/>
|
||||
We disabled the register and login link so you don't leak your credentials.
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
<section>
|
||||
<div class="container">
|
||||
@RenderBody()
|
||||
</div>
|
||||
</section>
|
||||
@if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
<footer class="btcpay-footer">
|
||||
<div class="container">
|
||||
<div class="d-flex flex-column justify-content-between flex-lg-row py-1">
|
||||
<div class="d-flex justify-content-center justify-content-lg-start mb-2 mb-lg-0">
|
||||
<div class="d-flex flex-column justify-content-between flex-xl-row py-1">
|
||||
<div class="d-flex justify-content-center justify-content-xl-start mb-2 mb-xl-0">
|
||||
<a href="https://github.com/btcpayserver/btcpayserver" class="d-flex align-items-center me-4" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="github" />
|
||||
<vc:icon symbol="github"/>
|
||||
<span style="margin-left:.4rem">Github</span>
|
||||
</a>
|
||||
<a href="https://chat.btcpayserver.org/" class="d-flex align-items-center me-4" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="mattermost" />
|
||||
<vc:icon symbol="mattermost"/>
|
||||
<span style="margin-left:.4rem">Mattermost</span>
|
||||
</a>
|
||||
<a href="https://twitter.com/BtcpayServer" class="d-flex align-items-center" target="_blank" rel="noreferrer noopener">
|
||||
<vc:icon symbol="twitter" />
|
||||
<vc:icon symbol="twitter"/>
|
||||
<span style="margin-left:.4rem">Twitter</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-center text-lg-start">@Env.ToString()</div>
|
||||
<div class="text-center text-xl-start">@_env.ToString()</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
}
|
||||
|
||||
<partial name="LayoutFoot" />
|
||||
<partial name="LayoutFoot"/>
|
||||
|
||||
@await RenderSectionAsync("PageFootContent", false)
|
||||
<partial name="LayoutPartials/SyncModal" />
|
||||
<partial name="LayoutPartials/SyncModal"/>
|
||||
|
||||
@{
|
||||
var notificationDisabled = (await SettingsRepository.GetPolicies()).DisableInstantNotifications;
|
||||
if (!notificationDisabled)
|
||||
{
|
||||
var user = await UserManager.GetUserAsync(User);
|
||||
notificationDisabled = user?.DisabledNotifications == "all";
|
||||
}
|
||||
}
|
||||
|
||||
<script type="text/javascript">
|
||||
const expectedDomain = @Safe.Json(Env.ExpectedHost);
|
||||
const expectedProtocol = @Safe.Json(Env.ExpectedProtocol);
|
||||
if (window.location.host !== expectedDomain || window.location.protocol !== expectedProtocol + ":") {
|
||||
document.getElementById("badUrl").style.display = "block";
|
||||
document.getElementById("browserScheme").innerText = window.location.protocol.substr(0, window.location.protocol.length -1);
|
||||
}
|
||||
</script>
|
||||
@if (!notificationDisabled)
|
||||
{
|
||||
<script>
|
||||
var supportsWebSockets = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
|
||||
if (supportsWebSockets) {
|
||||
|
||||
var loc = window.location, ws_uri;
|
||||
if (loc.protocol === "https:") {
|
||||
ws_uri = "wss:";
|
||||
} else {
|
||||
ws_uri = "ws:";
|
||||
<script>
|
||||
if ('WebSocket' in window && window.WebSocket.CLOSING === 2) {
|
||||
const { host, protocol } = window.location;
|
||||
const wsUri = `${protocol === "https:" ? "wss:" : "ws:"}//${host}@_linkGenerator.GetPathByAction("SubscribeUpdates", "Notifications")`;
|
||||
const newDataEndpoint = "@_linkGenerator.GetPathByAction("GetNotificationDropdownUI", "Notifications")";
|
||||
try {
|
||||
socket = new WebSocket(wsUri);
|
||||
socket.onmessage = e => {
|
||||
if (e.data === "ping") return;
|
||||
$.get(newDataEndpoint, data => {
|
||||
$("#Notifications").replaceWith($(data));
|
||||
});
|
||||
};
|
||||
socket.onerror = e => {
|
||||
console.error("Error while connecting to websocket for notifications (callback)", e);
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error("Error while connecting to websocket for notifications", e);
|
||||
}
|
||||
}
|
||||
ws_uri += "//" + loc.host;
|
||||
ws_uri += "@linkGenerator.GetPathByAction("SubscribeUpdates", "Notifications")";
|
||||
var newDataEndpoint = "@linkGenerator.GetPathByAction("GetNotificationDropdownUI", "Notifications")";
|
||||
|
||||
try {
|
||||
socket = new WebSocket(ws_uri);
|
||||
socket.onmessage = function (e) {
|
||||
if (e.data === "ping")
|
||||
return;
|
||||
$.get(newDataEndpoint, function (data) {
|
||||
$("#notifications-nav-item").replaceWith($(data));
|
||||
});
|
||||
};
|
||||
socket.onerror = function (e) {
|
||||
console.error("Error while connecting to websocket for notifications (callback)", e);
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error("Error while connecting to websocket for notifications", e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
ViewBag.ShowMenu = ViewBag.ShowMenu ?? true;
|
||||
ViewBag.ShowMainTitle = ViewBag.ShowMainTitle ?? true;
|
||||
ViewBag.ShowBreadcrumb = ViewBag.ShowBreadcrumb ?? false;
|
||||
if (!ViewData.ContainsKey("NavPartialName"))
|
||||
{
|
||||
ViewData["NavPartialName"] = "_Nav";
|
||||
|
@ -17,42 +16,17 @@
|
|||
@await RenderSectionAsync("PageFootContent", false)
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
@if (ViewBag.ShowBreadcrumb)
|
||||
{
|
||||
<nav aria-label="breadcrumb" class="mt-n3 mb-4">
|
||||
<ol class="breadcrumb px-0">
|
||||
@if (!string.IsNullOrEmpty(ViewData["CategoryTitle"] as string))
|
||||
{
|
||||
<li class="breadcrumb-item" aria-current="page">@ViewData["CategoryTitle"]</li>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(ViewData["MainTitle"] as string))
|
||||
{
|
||||
<li class="breadcrumb-item" aria-current="page">@ViewData["MainTitle"]</li>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(ViewData["PageTitle"] as string))
|
||||
{
|
||||
<li class="breadcrumb-item" aria-current="page">@ViewData["PageTitle"]</li>
|
||||
}
|
||||
</ol>
|
||||
</nav>
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(ViewData["MainTitle"] as string))
|
||||
{
|
||||
<h2 class="mb-5">@ViewData["MainTitle"]</h2>
|
||||
}
|
||||
<div class="row">
|
||||
@if (ViewBag.ShowMenu)
|
||||
{
|
||||
<div class="col-md-3 ms-n3 ms-md-0">
|
||||
<partial name="@ViewData["NavPartialName"].ToString()" />
|
||||
</div>
|
||||
}
|
||||
<div class="col-md-9">
|
||||
<partial name="_StatusMessage" />
|
||||
@RenderBody()
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@if (!string.IsNullOrEmpty(ViewData["MainTitle"] as string))
|
||||
{
|
||||
<h2 class="mb-5">@ViewData["MainTitle"]</h2>
|
||||
}
|
||||
@if (ViewBag.ShowMenu)
|
||||
{
|
||||
<nav class="nav">
|
||||
<partial name="@ViewData["NavPartialName"].ToString()" />
|
||||
</nav>
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
@RenderBody()
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
{
|
||||
<h4 class="mt-5 mb-3">Other actions</h4>
|
||||
<div id="danger-zone">
|
||||
<a id="delete-store" class="btn btn-outline-danger mb-5" asp-action="DeleteStore" asp-route-storeId="@Model.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@Model.StoreName</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store.">Delete this store</a>
|
||||
<a id="DeleteStore" class="btn btn-outline-danger mb-5" asp-action="DeleteStore" asp-route-storeId="@Model.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@Model.StoreName</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete this store</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
@using BTCPayServer.Client
|
||||
<nav id="sideNav" class="nav flex-column mb-4">
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.PaymentMethods))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PaymentMethods)" asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Context.GetRouteValue("storeId")">Payment Methods</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.Rates))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Rates)" asp-controller="Stores" asp-action="Rates" asp-route-storeId="@Context.GetRouteValue("storeId")">Rates</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.CheckoutAppearance))" class="nav-link @ViewData.IsActivePage(StoreNavPages.CheckoutAppearance)" asp-controller="Stores" asp-action="CheckoutAppearance" asp-route-storeId="@Context.GetRouteValue("storeId")">Checkout Appearance</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.GeneralSettings))" class="nav-link @ViewData.IsActivePage(StoreNavPages.GeneralSettings)" asp-controller="Stores" asp-action="GeneralSettings" asp-route-storeId="@Context.GetRouteValue("storeId")">General Settings</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.Tokens))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Tokens)" asp-controller="Stores" asp-action="ListTokens" asp-route-storeId="@Context.GetRouteValue("storeId")">Access Tokens</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.Users))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Users)" asp-controller="Stores" asp-action="StoreUsers" asp-route-storeId="@Context.GetRouteValue("storeId")">Users</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.PayButton))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@Context.GetRouteValue("storeId")">Pay Button</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.Integrations))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Integrations)" asp-controller="Stores" asp-action="Integrations" asp-route-storeId="@Context.GetRouteValue("storeId")">Integrations</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="Nav-@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Webhooks)" asp-controller="Stores" asp-action="Webhooks" asp-route-storeId="@Context.GetRouteValue("storeId")">Webhooks</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.PullPayments))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@Context.GetRouteValue("storeId")">Pull Payments</a>
|
||||
<a id="Nav-@(nameof(StoreNavPages.Payouts))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" asp-action="Payouts" asp-controller="StorePullPayments" asp-route-storeId="@Context.GetRouteValue("storeId")">Payouts</a>
|
||||
<vc:ui-extension-point location="store-nav" model="@Model"/>
|
||||
<nav id="SectionNav" class="nav">
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.PaymentMethods))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PaymentMethods)" asp-controller="Stores" asp-action="PaymentMethods" asp-route-storeId="@Context.GetRouteValue("storeId")">Payment Methods</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Rates))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Rates)" asp-controller="Stores" asp-action="Rates" asp-route-storeId="@Context.GetRouteValue("storeId")">Rates</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.CheckoutAppearance))" class="nav-link @ViewData.IsActivePage(StoreNavPages.CheckoutAppearance)" asp-controller="Stores" asp-action="CheckoutAppearance" asp-route-storeId="@Context.GetRouteValue("storeId")">Checkout Appearance</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.GeneralSettings))" class="nav-link @ViewData.IsActivePage(StoreNavPages.GeneralSettings)" asp-controller="Stores" asp-action="GeneralSettings" asp-route-storeId="@Context.GetRouteValue("storeId")">General Settings</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Tokens))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Tokens)" asp-controller="Stores" asp-action="ListTokens" asp-route-storeId="@Context.GetRouteValue("storeId")">Access Tokens</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Users))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Users)" asp-controller="Stores" asp-action="StoreUsers" asp-route-storeId="@Context.GetRouteValue("storeId")">Users</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.PayButton))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@Context.GetRouteValue("storeId")">Pay Button</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Integrations))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Integrations)" asp-controller="Stores" asp-action="Integrations" asp-route-storeId="@Context.GetRouteValue("storeId")">Integrations</a>
|
||||
<a permission="@Policies.CanModifyStoreSettings" id="SectionNav-@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Webhooks)" asp-controller="Stores" asp-action="Webhooks" asp-route-storeId="@Context.GetRouteValue("storeId")">Webhooks</a>
|
||||
<vc:ui-extension-point location="store-nav" model="@Model" />
|
||||
</nav>
|
||||
|
|
|
@ -7,26 +7,22 @@
|
|||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form asp-action="CreateStore">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Name" class="form-label" data-required></label>
|
||||
<input asp-for="Name" class="form-control" required />
|
||||
<span asp-validation-for="Name" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
|
||||
<a asp-action="ListStores" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</form>
|
||||
<h2 class="mb-4">@ViewData["PageTitle"]</h2>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<form asp-action="CreateStore">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Name" class="form-label" data-required></label>
|
||||
<input asp-for="Name" class="form-control" required />
|
||||
<span asp-validation-for="Name" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Create" class="btn btn-primary" id="Create" />
|
||||
<a asp-action="ListStores" class="btn btn-link px-0 ms-3">Back to list</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -7,92 +7,89 @@
|
|||
var sortByAsc = "Sort by ascending...";
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-2">
|
||||
<h2 class="mb-0">@ViewData["PageTitle"]</h2>
|
||||
<a asp-action="CreateStore" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateStore"><span class="fa fa-plus"></span> Create a new store</a>
|
||||
</div>
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-2">
|
||||
<h2 class="mb-0">@ViewData["PageTitle"]</h2>
|
||||
<a asp-action="CreateStore" class="btn btn-primary mt-3 mt-sm-0" role="button" id="CreateStore"><span class="fa fa-plus"></span> Create a new store</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Stores.Any())
|
||||
{
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a asp-action="ListStores"
|
||||
asp-route-sortOrder="@(storeNameSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="StoreName"
|
||||
class="text-nowrap"
|
||||
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)">
|
||||
Name
|
||||
<span class="fa @(storeNameSortOrder == "asc" ? "fa-sort-alpha-desc" : storeNameSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a asp-action="ListStores"
|
||||
asp-route-sortOrder="@(storeWebsiteSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="StoreWebsite"
|
||||
class="text-nowrap"
|
||||
title="@(storeWebsiteSortOrder == "desc" ? sortByDesc : sortByAsc)">
|
||||
Website
|
||||
<span class="fa @(storeWebsiteSortOrder == "asc" ? "fa-sort-alpha-desc" : storeWebsiteSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th style="text-align:right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var store in Model.Stores)
|
||||
{
|
||||
<tr id="store-@store.Id">
|
||||
<td>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id">@store.Name</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
@store.Name
|
||||
}
|
||||
@if (store.HintWalletWarning)
|
||||
{
|
||||
<span class="fa fa-warning text-warning" title="Wallet not setup for this store" id="warninghint_@store.Id"></span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(store.WebSite))
|
||||
{
|
||||
<a href="@store.WebSite" rel="noreferrer noopener">@store.WebSite</a>
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">
|
||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchTerm="storeid:@store.Id">Invoices</a><span> - </span>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id" id="update-store-@store.Id">Settings</a><span> - </span>
|
||||
<a asp-action="DeleteStore" asp-controller="Stores" asp-route-storeId="@store.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@store.Name</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete</a><span> - </span>
|
||||
}
|
||||
<a asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@store.Id" >Pull Payments</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no stores yet.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Stores.Any())
|
||||
{
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a asp-action="ListStores"
|
||||
asp-route-sortOrder="@(storeNameSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="StoreName"
|
||||
class="text-nowrap"
|
||||
title="@(storeNameSortOrder == "desc" ? sortByDesc : sortByAsc)">
|
||||
Name
|
||||
<span class="fa @(storeNameSortOrder == "asc" ? "fa-sort-alpha-desc" : storeNameSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a asp-action="ListStores"
|
||||
asp-route-sortOrder="@(storeWebsiteSortOrder ?? "asc")"
|
||||
asp-route-sortOrderColumn="StoreWebsite"
|
||||
class="text-nowrap"
|
||||
title="@(storeWebsiteSortOrder == "desc" ? sortByDesc : sortByAsc)">
|
||||
Website
|
||||
<span class="fa @(storeWebsiteSortOrder == "asc" ? "fa-sort-alpha-desc" : storeWebsiteSortOrder == "desc" ? "fa-sort-alpha-asc" : "fa-sort")" />
|
||||
</a>
|
||||
</th>
|
||||
<th style="text-align:right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var store in Model.Stores)
|
||||
{
|
||||
<tr id="store-@store.Id">
|
||||
<td>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id">@store.Name</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
@store.Name
|
||||
}
|
||||
@if (store.HintWalletWarning)
|
||||
{
|
||||
<span class="fa fa-warning text-warning" title="Wallet not setup for this store" id="warninghint_@store.Id"></span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(store.WebSite))
|
||||
{
|
||||
<a href="@store.WebSite" rel="noreferrer noopener">@store.WebSite</a>
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">
|
||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchTerm="storeid:@store.Id">Invoices</a><span> - </span>
|
||||
<a asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@store.Id">Pull Payments</a>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<span> - </span>
|
||||
<a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@store.Id" id="update-store-@store.Id">Settings</a><span> - </span>
|
||||
<a asp-action="DeleteStore" asp-controller="Stores" asp-route-storeId="@store.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The store <strong>@store.Name</strong> will be permanently deleted. This action will also delete all invoices, apps and data associated with the store." data-confirm-input="DELETE">Delete</a><span> - </span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no stores yet.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<partial name="_Confirm" model="@(new ConfirmModel("Delete store", "The store will be permanently deleted. This action will also delete all invoices, apps and data associated with the store.", "Delete"))" />
|
||||
|
|
|
@ -3,72 +3,68 @@
|
|||
ViewData.SetActivePageAndTitle(WalletsNavPages.Index, "Wallets");
|
||||
}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<partial name="_StatusMessage" />
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
<div class="d-sm-flex justify-content-between mb-2">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/Wallet/" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
@if (Model.Wallets.Any())
|
||||
<div class="d-sm-flex justify-content-between mb-2">
|
||||
<h2 class="mb-0">
|
||||
@ViewData["Title"]
|
||||
<small>
|
||||
<a href="https://docs.btcpayserver.org/Wallet/" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</small>
|
||||
</h2>
|
||||
@if (Model.Wallets.Any())
|
||||
{
|
||||
<div class="mt-2 mt-sm-1">
|
||||
@foreach (var balance in Model.BalanceForCryptoCode)
|
||||
{
|
||||
<div class="mt-2 mt-sm-1">
|
||||
@foreach (var balance in Model.BalanceForCryptoCode)
|
||||
{
|
||||
<div class="fs-5 fw-semibold">@($"{balance.Value.ShowMoney(balance.Key)} {balance.Key.CryptoCode}")</div>
|
||||
}
|
||||
<div class="text-muted text-sm-end fs-6">Total Balance</div>
|
||||
</div>
|
||||
<div class="fs-5 fw-semibold">@($"{balance.Value.ShowMoney(balance.Key)} {balance.Key.CryptoCode}")</div>
|
||||
}
|
||||
<div class="text-muted text-sm-end fs-6">Total Balance</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Wallets.Any())
|
||||
{
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Store Name</th>
|
||||
<th>Crypto Code</th>
|
||||
<th>Balance</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var wallet in Model.Wallets)
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@if (Model.Wallets.Any())
|
||||
{
|
||||
<table class="table table-hover table-responsive-md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Store Name</th>
|
||||
<th>Crypto Code</th>
|
||||
<th>Balance</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var wallet in Model.Wallets)
|
||||
{
|
||||
<tr>
|
||||
@if (wallet.IsOwner)
|
||||
{
|
||||
<tr>
|
||||
@if (wallet.IsOwner)
|
||||
{
|
||||
<td><a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@wallet.StoreId">@wallet.StoreName</a></td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>@wallet.StoreName</td>
|
||||
}
|
||||
<td>@wallet.CryptoCode</td>
|
||||
<td>@wallet.Balance</td>
|
||||
<td class="text-end">
|
||||
<a asp-action="WalletTransactions" asp-route-walletId="@wallet.Id">Manage</a>
|
||||
</td>
|
||||
</tr>
|
||||
<td><a asp-action="PaymentMethods" asp-controller="Stores" asp-route-storeId="@wallet.StoreId">@wallet.StoreName</a></td>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no wallets yet. You can add wallets in the store setup.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
else
|
||||
{
|
||||
<td>@wallet.StoreName</td>
|
||||
}
|
||||
<td>@wallet.CryptoCode</td>
|
||||
<td>@wallet.Balance</td>
|
||||
<td class="text-end">
|
||||
<a asp-action="WalletTransactions" asp-route-walletId="@wallet.Id">Manage</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-secondary mt-3">
|
||||
There are no wallets yet. You can add wallets in the store setup.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -4,18 +4,17 @@
|
|||
var wallet = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
|
||||
var network = BtcPayNetworkProvider.GetNetwork<BTCPayNetwork>(wallet.CryptoCode);
|
||||
}
|
||||
<nav id="sideNav" class="nav flex-column mb-4">
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Transactions)" asp-action="WalletTransactions" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletTransactions">Transactions</a>
|
||||
<nav id="SectionNav" class="nav">
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Transactions)" asp-action="WalletTransactions" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Transactions">Transactions</a>
|
||||
@if (!network.ReadonlyWallet)
|
||||
{
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Send)" asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletSend">Send</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Send)" asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Send">Send</a>
|
||||
}
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Receive)" asp-action="WalletReceive" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletReceive">Receive</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-action="WalletRescan" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletRescan">Rescan</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Receive)" asp-action="WalletReceive" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Receive">Receive</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-action="WalletRescan" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-Rescan">Rescan</a>
|
||||
@if (!network.ReadonlyWallet)
|
||||
{
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PSBT)" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")">PSBT</a>
|
||||
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PSBT)" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" id="SectionNav-PSBT">PSBT</a>
|
||||
}
|
||||
<a class="nav-link" asp-controller="Stores" asp-action="WalletSettings" asp-route-storeId="@wallet.StoreId" asp-route-cryptoCode="@wallet.CryptoCode" id="WalletSettings">Settings</a>
|
||||
<vc:ui-extension-point location="wallet-nav" model="@Model" />
|
||||
</nav>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"wwwroot/vendor/font-awesome/css/font-awesome.css",
|
||||
"wwwroot/vendor/flatpickr/flatpickr.css",
|
||||
"wwwroot/main/fonts/OpenSans.css",
|
||||
"wwwroot/main/layout.css",
|
||||
"wwwroot/main/site.css"
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
delegate('click', '.payment-method', function(e) {
|
||||
closePaymentMethodDialog(e.target.dataset.paymentMethod);
|
||||
delegate('click', '.payment-method', e => {
|
||||
const el = e.target.closest('.payment-method')
|
||||
closePaymentMethodDialog(el.dataset.paymentMethod);
|
||||
return false;
|
||||
})
|
||||
|
|
|
@ -14,7 +14,19 @@
|
|||
<symbol id="scan-qr" viewBox="0 0 32 32"><path d="M20 .875h10c.621 0 1.125.504 1.125 1.125v10m0 8v10c0 .621-.504 1.125-1.125 1.125H20m-8 0H2A1.125 1.125 0 01.875 30V20m0-8V2C.875 1.379 1.379.875 2 .875h10" stroke="currentColor" stroke-width="1.75" fill="none" fill-rule="evenodd"/></symbol>
|
||||
<symbol id="seed" viewBox="0 0 32 32"><rect x="0.875" y="2.875" width="30.25" height="26.25" rx="1.125" fill="none" stroke="currentColor" stroke-width="1.75"/><rect x="5" y="7" width="9" height="4" rx="0.5" fill="currentColor"/><rect x="5" y="14" width="9" height="4" rx="0.5" fill="currentColor"/><rect x="18" y="7" width="9" height="4" rx="0.5" fill="currentColor"/><rect x="18" y="14" width="9" height="4" rx="0.5" fill="currentColor"/><rect x="5" y="21" width="9" height="4" rx="0.5" fill="currentColor"/><rect x="18" y="21" width="9" height="4" rx="0.5" fill="currentColor"/></symbol>
|
||||
<symbol id="warning" viewBox="0 0 24 24"><path d="M12.337 3.101a.383.383 0 00-.674 0l-9.32 17.434a.383.383 0 00.338.564h18.638a.384.384 0 00.337-.564L12.337 3.101zM9.636 2.018c1.01-1.89 3.719-1.89 4.728 0l9.32 17.434a2.681 2.681 0 01-2.365 3.945H2.681a2.68 2.68 0 01-2.364-3.945L9.636 2.018zm3.896 15.25a1.532 1.532 0 11-3.064 0 1.532 1.532 0 013.064 0zm-.383-8.044a1.15 1.15 0 00-2.298 0v3.83a1.15 1.15 0 002.298 0v-3.83z" fill="currentColor"/></symbol>
|
||||
<symbol id="github" viewBox="0 0 25 24"><path clip-rule="evenodd" d="M12.75.3c-6.6 0-12 5.4-12 12 0 5.325 3.45 9.825 8.175 11.4.6.075.825-.225.825-.6v-2.025C6.375 21.825 5.7 19.5 5.7 19.5c-.525-1.35-1.35-1.725-1.35-1.725-1.125-.75.075-.75.075-.75 1.2.075 1.875 1.2 1.875 1.2 1.05 1.8 2.775 1.275 3.525.975a2.59 2.59 0 0 1 .75-1.575c-2.7-.3-5.475-1.35-5.475-5.925 0-1.275.45-2.4 1.2-3.225-.15-.3-.525-1.5.15-3.15 0 0 .975-.3 3.3 1.2.975-.3 1.95-.375 3-.375s2.025.15 3 .375c2.325-1.575 3.3-1.275 3.3-1.275.675 1.65.225 2.85.15 3.15.75.825 1.2 1.875 1.2 3.225 0 4.575-2.775 5.625-5.475 5.925.45.375.825 1.125.825 2.25v3.3c0 .3.225.675.825.6a12.015 12.015 0 0 0 8.175-11.4c0-6.6-5.4-12-12-12z" fill="currentColor" fill-rule="evenodd"></path></symbol>
|
||||
<symbol id="twitter" viewBox="0 0 37 37"><path d="M36 18c0 9.945-8.055 18-18 18S0 27.945 0 18 8.055 0 18 0s18 8.055 18 18zm-21.294 9.495c7.983 0 12.348-6.615 12.348-12.348 0-.189 0-.378-.009-.558a8.891 8.891 0 0 0 2.169-2.25 8.808 8.808 0 0 1-2.493.684 4.337 4.337 0 0 0 1.908-2.403 8.788 8.788 0 0 1-2.754 1.053 4.319 4.319 0 0 0-3.168-1.368 4.34 4.34 0 0 0-4.338 4.338c0 .342.036.675.117.99a12.311 12.311 0 0 1-8.946-4.536 4.353 4.353 0 0 0-.585 2.178 4.32 4.32 0 0 0 1.935 3.609 4.263 4.263 0 0 1-1.962-.54v.054a4.345 4.345 0 0 0 3.483 4.257 4.326 4.326 0 0 1-1.962.072 4.333 4.333 0 0 0 4.05 3.015 8.724 8.724 0 0 1-6.426 1.791 12.091 12.091 0 0 0 6.633 1.962z" fill="currentColor"></path></symbol>
|
||||
<symbol id="github" viewBox="0 0 25 24"><path clip-rule="evenodd" d="M12.75.3c-6.6 0-12 5.4-12 12 0 5.325 3.45 9.825 8.175 11.4.6.075.825-.225.825-.6v-2.025C6.375 21.825 5.7 19.5 5.7 19.5c-.525-1.35-1.35-1.725-1.35-1.725-1.125-.75.075-.75.075-.75 1.2.075 1.875 1.2 1.875 1.2 1.05 1.8 2.775 1.275 3.525.975a2.59 2.59 0 0 1 .75-1.575c-2.7-.3-5.475-1.35-5.475-5.925 0-1.275.45-2.4 1.2-3.225-.15-.3-.525-1.5.15-3.15 0 0 .975-.3 3.3 1.2.975-.3 1.95-.375 3-.375s2.025.15 3 .375c2.325-1.575 3.3-1.275 3.3-1.275.675 1.65.225 2.85.15 3.15.75.825 1.2 1.875 1.2 3.225 0 4.575-2.775 5.625-5.475 5.925.45.375.825 1.125.825 2.25v3.3c0 .3.225.675.825.6a12.015 12.015 0 0 0 8.175-11.4c0-6.6-5.4-12-12-12z" fill="currentColor" fill-rule="evenodd"/></symbol>
|
||||
<symbol id="twitter" viewBox="0 0 37 37"><path d="M36 18c0 9.945-8.055 18-18 18S0 27.945 0 18 8.055 0 18 0s18 8.055 18 18zm-21.294 9.495c7.983 0 12.348-6.615 12.348-12.348 0-.189 0-.378-.009-.558a8.891 8.891 0 0 0 2.169-2.25 8.808 8.808 0 0 1-2.493.684 4.337 4.337 0 0 0 1.908-2.403 8.788 8.788 0 0 1-2.754 1.053 4.319 4.319 0 0 0-3.168-1.368 4.34 4.34 0 0 0-4.338 4.338c0 .342.036.675.117.99a12.311 12.311 0 0 1-8.946-4.536 4.353 4.353 0 0 0-.585 2.178 4.32 4.32 0 0 0 1.935 3.609 4.263 4.263 0 0 1-1.962-.54v.054a4.345 4.345 0 0 0 3.483 4.257 4.326 4.326 0 0 1-1.962.072 4.333 4.333 0 0 0 4.05 3.015 8.724 8.724 0 0 1-6.426 1.791 12.091 12.091 0 0 0 6.633 1.962z" fill="currentColor"/></symbol>
|
||||
<symbol id="mattermost" viewBox="0 0 206 206"><path fill="currentColor" d="m163.012 19.596 1.082 21.794c17.667 19.519 24.641 47.161 15.846 73.14-13.129 38.782-56.419 59.169-96.693 45.535-40.272-13.633-62.278-56.124-49.15-94.905 8.825-26.066 31.275-43.822 57.276-48.524L105.422.038C61.592-1.15 20.242 26.056 5.448 69.76c-18.178 53.697 10.616 111.963 64.314 130.142 53.698 18.178 111.964-10.617 130.143-64.315 14.77-43.633-1.474-90.283-36.893-115.99"/><path fill="currentColor" d="m137.097 53.436-.596-17.531-.404-15.189s.084-7.322-.17-9.043a2.776 2.776 0 0 0-.305-.914l-.05-.109-.06-.094a2.378 2.378 0 0 0-1.293-1.07 2.382 2.382 0 0 0-1.714.078l-.033.014-.18.092a2.821 2.821 0 0 0-.75.518c-1.25 1.212-5.63 7.08-5.63 7.08l-9.547 11.82-11.123 13.563-19.098 23.75s-8.763 10.938-6.827 24.4c1.937 13.464 11.946 20.022 19.71 22.65 7.765 2.63 19.7 3.5 29.417-6.019 9.716-9.518 9.397-23.53 9.397-23.53l-.744-30.466z"/></symbol>
|
||||
<symbol id="notifications" viewBox="0 0 20 20"><path d="M2.78 17.046h-.578c-.867 0-1.618-.455-1.964-1.137-.462-.852-.231-1.932.52-2.557a3.126 3.126 0 0 0 1.155-2.216V7.67C1.913 3.466 5.553 0 10 0c4.448 0 8.087 3.466 8.087 7.67v3.694c.116.795.52 1.477 1.155 1.988.751.625.982 1.648.52 2.557-.346.682-1.155 1.136-1.964 1.136H2.78Zm.057-1.705H17.74c.174 0 .405-.114.463-.227.115-.228 0-.398-.116-.455-.924-.795-1.56-1.875-1.733-3.125V7.67c0-3.295-2.83-5.965-6.354-5.965S3.646 4.375 3.646 7.67v3.523a4.879 4.879 0 0 1-1.79 3.41c-.116.113-.174.283-.116.454.115.17.289.284.462.284h.635Zm9.878 2.84C12.31 19.262 11.27 20 10 20c-1.27 0-2.31-.739-2.715-1.818h5.43Z" fill="currentColor"/></symbol>
|
||||
<symbol id="crowdfund" viewBox="0 0 24 24"><path d="M8 13.854a.4.4 0 1 0 .4.692l-.4-.692Zm8-3.708a.4.4 0 1 0-.4-.692l.4.692Zm-.4 4.4a.4.4 0 1 0 .4-.692l-.4.692ZM8.4 9.454a.4.4 0 1 0-.4.692l.4-.692ZM11.6 7.6v8.8h.8V7.6h-.8ZM13.2 6A1.2 1.2 0 0 1 12 7.2V8a2 2 0 0 0 2-2h-.8ZM12 7.2A1.2 1.2 0 0 1 10.8 6H10a2 2 0 0 0 2 2v-.8ZM10.8 6A1.2 1.2 0 0 1 12 4.8V4a2 2 0 0 0-2 2h.8ZM12 4.8A1.2 1.2 0 0 1 13.2 6h.8a2 2 0 0 0-2-2v.8ZM13.2 18a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2h-.8ZM12 19.2a1.2 1.2 0 0 1-1.2-1.2H10a2 2 0 0 0 2 2v-.8ZM10.8 18a1.2 1.2 0 0 1 1.2-1.2V16a2 2 0 0 0-2 2h.8Zm1.2-1.2a1.2 1.2 0 0 1 1.2 1.2h.8a2 2 0 0 0-2-2v.8Zm-3.6-2.254 7.6-4.4-.4-.692-7.6 4.4.4.692ZM8 15a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2H8Zm-1.2 1.2A1.2 1.2 0 0 1 5.6 15h-.8a2 2 0 0 0 2 2v-.8ZM5.6 15a1.2 1.2 0 0 1 1.2-1.2V13a2 2 0 0 0-2 2h.8Zm1.2-1.2A1.2 1.2 0 0 1 8 15h.8a2 2 0 0 0-2-2v.8ZM18.4 9a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2h-.8Zm-1.2 1.2A1.2 1.2 0 0 1 16 9h-.8a2 2 0 0 0 2 2v-.8ZM16 9a1.2 1.2 0 0 1 1.2-1.2V7a2 2 0 0 0-2 2h.8Zm1.2-1.2A1.2 1.2 0 0 1 18.4 9h.8a2 2 0 0 0-2-2v.8ZM16 13.854l-7.6-4.4-.4.692 7.6 4.4.4-.692ZM18.4 15a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2h-.8Zm-1.2 1.2A1.2 1.2 0 0 1 16 15h-.8a2 2 0 0 0 2 2v-.8ZM16 15a1.2 1.2 0 0 1 1.2-1.2V13a2 2 0 0 0-2 2h.8Zm1.2-1.2a1.2 1.2 0 0 1 1.2 1.2h.8a2 2 0 0 0-2-2v.8ZM8 9a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2H8Zm-1.2 1.2A1.2 1.2 0 0 1 5.6 9h-.8a2 2 0 0 0 2 2v-.8ZM5.6 9a1.2 1.2 0 0 1 1.2-1.2V7a2 2 0 0 0-2 2h.8Zm1.2-1.2A1.2 1.2 0 0 1 8 9h.8a2 2 0 0 0-2-2v.8Z" fill="#343A40"/><path d="M8 13.854a.4.4 0 1 0 .4.692l-.4-.692Zm8-3.708a.4.4 0 1 0-.4-.692l.4.692Zm-.4 4.4a.4.4 0 1 0 .4-.692l-.4.692ZM8.4 9.454a.4.4 0 1 0-.4.692l.4-.692ZM11.6 7.6v8.8h.8V7.6h-.8ZM13.2 6A1.2 1.2 0 0 1 12 7.2V8a2 2 0 0 0 2-2h-.8ZM12 7.2A1.2 1.2 0 0 1 10.8 6H10a2 2 0 0 0 2 2v-.8ZM10.8 6A1.2 1.2 0 0 1 12 4.8V4a2 2 0 0 0-2 2h.8ZM12 4.8A1.2 1.2 0 0 1 13.2 6h.8a2 2 0 0 0-2-2v.8ZM13.2 18a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2h-.8ZM12 19.2a1.2 1.2 0 0 1-1.2-1.2H10a2 2 0 0 0 2 2v-.8ZM10.8 18a1.2 1.2 0 0 1 1.2-1.2V16a2 2 0 0 0-2 2h.8Zm1.2-1.2a1.2 1.2 0 0 1 1.2 1.2h.8a2 2 0 0 0-2-2v.8Zm-3.6-2.254 7.6-4.4-.4-.692-7.6 4.4.4.692ZM8 15a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2H8Zm-1.2 1.2A1.2 1.2 0 0 1 5.6 15h-.8a2 2 0 0 0 2 2v-.8ZM5.6 15a1.2 1.2 0 0 1 1.2-1.2V13a2 2 0 0 0-2 2h.8Zm1.2-1.2A1.2 1.2 0 0 1 8 15h.8a2 2 0 0 0-2-2v.8ZM18.4 9a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2h-.8Zm-1.2 1.2A1.2 1.2 0 0 1 16 9h-.8a2 2 0 0 0 2 2v-.8ZM16 9a1.2 1.2 0 0 1 1.2-1.2V7a2 2 0 0 0-2 2h.8Zm1.2-1.2A1.2 1.2 0 0 1 18.4 9h.8a2 2 0 0 0-2-2v.8ZM16 13.854l-7.6-4.4-.4.692 7.6 4.4.4-.692ZM18.4 15a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2h-.8Zm-1.2 1.2A1.2 1.2 0 0 1 16 15h-.8a2 2 0 0 0 2 2v-.8ZM16 15a1.2 1.2 0 0 1 1.2-1.2V13a2 2 0 0 0-2 2h.8Zm1.2-1.2a1.2 1.2 0 0 1 1.2 1.2h.8a2 2 0 0 0-2-2v.8ZM8 9a1.2 1.2 0 0 1-1.2 1.2v.8a2 2 0 0 0 2-2H8Zm-1.2 1.2A1.2 1.2 0 0 1 5.6 9h-.8a2 2 0 0 0 2 2v-.8ZM5.6 9a1.2 1.2 0 0 1 1.2-1.2V7a2 2 0 0 0-2 2h.8Zm1.2-1.2A1.2 1.2 0 0 1 8 9h.8a2 2 0 0 0-2-2v.8Z" stroke="currentColor" stroke-width=".4" /></symbol>
|
||||
<symbol id="pointofsale" viewBox="0 0 24 24"><path d="M18.475 12v-.075h-.008V7.024a.64.64 0 0 0-.267-.497.995.995 0 0 0-.608-.202H6.4a.995.995 0 0 0-.608.202.64.64 0 0 0-.267.497v9.952a.64.64 0 0 0 .267.497c.16.125.376.202.608.202h11.2a.995.995 0 0 0 .608-.202.64.64 0 0 0 .267-.497V12Zm-6.4-.725h4.65v1.45h-4.65v-1.45Zm4.65 3.2v1.45h-9.45v-1.45h9.45Zm-6.4-6.4v4.65h-3.05v-4.65h3.05Zm9.6-1.051v9.952c0 1.185-1.033 2.149-2.325 2.149H6.4c-1.276 0-2.325-.972-2.325-2.15V7.025c0-1.17 1.049-2.15 2.325-2.15h11.2c1.276 0 2.325.973 2.325 2.15Zm-3.2 2.5h-4.65v-1.45h4.65v1.45Z" fill="currentColor" /></symbol>
|
||||
<symbol id="account" viewBox="0 0 24 24"><path d="M6.5 16.74a7.83 7.83 0 0 0 11 0 4.16 4.16 0 0 0-3.58-4.17c-.55.37-1.22.59-1.9.59-.68 0-1.35-.22-1.9-.59-2.03.2-3.6 2-3.62 4.17ZM12 5c1.8 0 3.26 1.55 3.26 3.46 0 1.92-1.46 3.49-3.26 3.49-1.8 0-3.26-1.55-3.26-3.47C8.74 6.58 10.2 5 12 5Z" fill="currentColor"/></symbol>
|
||||
<symbol id="settings" viewBox="0 0 24 24"><path fill="none" d="M19.2 13.37a.62.62 0 0 0 .43-.6v-1.6c0-.27-.18-.5-.43-.59l-1.67-.55-.24-.57.76-1.6a.63.63 0 0 0-.12-.7L16.8 6.01a.62.62 0 0 0-.72-.12l-1.56.79-.57-.24-.6-1.66a.62.62 0 0 0-.58-.42h-1.6a.63.63 0 0 0-.6.44l-.53 1.66-.59.24-1.59-.76a.63.63 0 0 0-.71.12L6.02 7.2a.63.63 0 0 0-.12.73l.78 1.56-.23.57-1.66.58a.62.62 0 0 0-.42.6v1.59c0 .27.18.5.43.59l1.67.55.24.57-.76 1.6a.63.63 0 0 0 .12.7l1.13 1.14c.19.19.48.24.72.11l1.56-.78.57.24.6 1.66c.08.25.32.41.58.41h1.59c.27 0 .51-.17.6-.43l.53-1.66.59-.24 1.59.76c.24.12.52.07.71-.12l1.13-1.13c.2-.2.24-.49.12-.73l-.79-1.55.25-.57 1.66-.58ZM12 14.39A2.37 2.37 0 0 1 9.62 12a2.37 2.37 0 1 1 4.76 0A2.37 2.37 0 0 1 12 14.39Z" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round"/></symbol>
|
||||
<symbol id="server-settings" viewBox="0 0 24 24"><rect x="4.75" y="4.75" width="14.5" height="14.5" rx="3.25" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="m8 8 1.6 1.6L8 11.2" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/></symbol>
|
||||
<symbol id="new" viewBox="0 0 24 24"><path d="M17 11H13V7C13 6.45 12.55 6 12 6C11.45 6 11 6.45 11 7V11H7C6.45 11 6 11.45 6 12C6 12.55 6.45 13 7 13H11V17C11 17.55 11.45 18 12 18C12.55 18 13 17.55 13 17V13H17C17.55 13 18 12.55 18 12C18 11.45 17.55 11 17 11Z" fill="currentColor"/></symbol>
|
||||
<symbol id="wallet-onchain" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="m16.05 12.26 1.08-1.08a3.05 3.05 0 0 0-4.31-4.32l-2.7 2.7a2.28 2.28 0 0 0 0 3.23l.54.54 1.08-1.07a1.52 1.52 0 0 1 0-2.15l2.15-2.16a1.52 1.52 0 0 1 2.6 1.08 1.52 1.52 0 0 1-.44 1.07l-1.08 1.08 1.08 1.08Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M7.97 11.72 6.89 12.8a3.05 3.05 0 0 0 4.3 4.31l2.7-2.7a2.28 2.28 0 0 0 0-3.23l-.54-.54-1.07 1.08a1.52 1.52 0 0 1 0 2.15l-2.16 2.16a1.52 1.52 0 0 1-2.6-1.08 1.52 1.52 0 0 1 .45-1.08l1.08-1.08-1.08-1.07Z" fill="currentColor"/></symbol>
|
||||
<symbol id="wallet-lightning" viewBox="0 0 24 24"><path d="M17.57 10.7c-.1-.23-.27-.34-.5-.34h-4.3l.5-3.76a.48.48 0 0 0-.33-.55.52.52 0 0 0-.66.17l-5.45 6.54a.59.59 0 0 0-.05.6c.1.17.27.28.49.28h4.3l-.49 3.76c-.05.22.11.5.33.55.06.05.17.05.22.05a.5.5 0 0 0 .44-.22l5.45-6.54c.1-.17.16-.39.05-.55Z" fill="currentColor"/></symbol>
|
||||
<symbol id="payment-1" viewBox="0 0 24 24"><path d="M7.2 11.2h9.6v1.6H7.2v-1.6Zm0 4.8h5.6v-1.6H7.2V16ZM20 7.02v9.96c0 1.23-1.07 2.22-2.4 2.22H6.4c-1.31 0-2.4-1-2.4-2.22V7.02C4 5.81 5.09 4.8 6.4 4.8h11.2c1.31 0 2.4 1 2.4 2.22ZM18.4 12V7.02c0-.33-.38-.62-.8-.62H6.4c-.43 0-.8.29-.8.62v9.96c0 .33.37.62.8.62h11.2c.43 0 .8-.29.8-.62V12ZM7.2 9.6h9.6V8H7.2v1.6Z" fill="currentColor"/></symbol>
|
||||
<symbol id="payment-2" viewBox="0 0 24 24"><path d="M12 20a8 8 0 1 1 0-16 8 8 0 0 1 0 16Zm0-15.19a7.2 7.2 0 0 0 0 14.38A7.2 7.2 0 0 0 12 4.8Z" fill="currentColor" stroke="currentColor" stroke-width=".6"/><path d="M9.48 14.85a.44.44 0 0 1-.3-.14c-.14-.16-.14-.43.05-.57l5.02-4.31c.16-.14.43-.14.57.05.14.17.14.44-.05.57l-5.05 4.29c-.05.08-.16.1-.24.1Z" fill="currentColor" stroke="currentColor" stroke-width=".6"/><path d="M14.39 14.28a.4.4 0 0 1-.41-.4l.1-3.42-3.08-.17a.4.4 0 0 1-.38-.43c0-.22.19-.4.43-.38l3.47.19c.22 0 .38.19.38.4l-.13 3.83c.02.19-.17.38-.38.38Z" fill="currentColor" stroke="currentColor" stroke-width=".6"/></symbol>
|
||||
<symbol id="invoice" viewBox="0 0 24 24"><path d="M17.1 20H6.9c-.83 0-1.53-.7-1.53-1.52V5.52c0-.82.7-1.52 1.52-1.52h10.22c.83 0 1.52.7 1.52 1.52v12.96c0 .82-.7 1.52-1.52 1.52ZM6.9 5.3c-.14 0-.23.1-.23.22v12.96c0 .13.1.22.22.22h10.22c.13 0 .22-.1.22-.22V5.52c0-.13-.09-.22-.22-.22H6.89Z" fill="currentColor"/><path d="M12.24 7.95H8.11c-.09 0-.13-.05-.13-.15v-1c0-.05.04-.1.09-.1h4.13c.04 0 .08.05.08.1v1c.05.1 0 .15-.04.15ZM16.2 17.6H8.1c-.08 0-.12-.08-.12-.12V9.87a.1.1 0 0 1 .09-.09h8.08a.1.1 0 0 1 .09.09v7.44c0 .11.06.3-.04.3Z" fill="currentColor"/></symbol>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 23 KiB |
|
@ -1,10 +1,17 @@
|
|||
<svg class="logo" viewBox="0 0 192 84" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<path d="M5.206 83.433a4.86 4.86 0 01-4.859-4.861V5.431a4.86 4.86 0 119.719 0v73.141a4.861 4.861 0 01-4.86 4.861" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M5.209 83.433a4.862 4.862 0 01-2.086-9.253L32.43 60.274 2.323 38.093a4.861 4.861 0 015.766-7.826l36.647 26.999a4.864 4.864 0 01-.799 8.306L7.289 82.964a4.866 4.866 0 01-2.08.469" fill="#51B13E" class="logo-brand-medium"/>
|
||||
<path d="M5.211 54.684a4.86 4.86 0 01-2.887-8.774L32.43 23.73 3.123 9.821a4.861 4.861 0 014.166-8.784l36.648 17.394a4.86 4.86 0 01.799 8.305l-36.647 27a4.844 4.844 0 01-2.878.948" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M10.066 31.725v20.553L24.01 42.006z" fill="#1E7A44" class="logo-brand-dark"/>
|
||||
<path d="M10.066 5.431A4.861 4.861 0 005.206.57 4.86 4.86 0 00.347 5.431v61.165h9.72V5.431h-.001z" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M74.355 41.412c3.114.884 4.84 3.704 4.84 7.238 0 5.513-3.368 8.082-7.955 8.082H60.761V27.271h9.259c4.504 0 7.997 2.146 7.997 7.743 0 2.821-1.179 5.43-3.662 6.398m-4.293-.716c3.324 0 6.018-1.179 6.018-5.724 0-4.586-2.776-5.808-6.145-5.808h-7.197v11.531h7.324v.001zm1.052 14.099c3.366 0 6.06-1.768 6.06-6.145 0-4.713-3.072-6.144-6.901-6.144h-7.534v12.288h8.375v.001zM98.893 27.271v1.81h-8.122v27.651h-1.979V29.081h-8.123v-1.81zM112.738 26.85c5.01 0 9.554 2.524 10.987 8.543h-1.895c-1.348-4.923-5.303-6.732-9.134-6.732-6.944 0-10.605 5.681-10.605 13.341 0 8.08 3.661 13.256 10.646 13.256 4.125 0 7.828-1.85 9.26-7.279h1.895c-1.264 6.271-6.229 9.174-11.154 9.174-7.87 0-12.583-5.808-12.583-15.15 0-8.966 4.969-15.153 12.583-15.153M138.709 27.271c5.091 0 8.795 3.326 8.795 9.764 0 6.06-3.704 9.722-8.795 9.722h-7.746v9.976h-1.935V27.271h9.681zm0 17.549c3.745 0 6.816-2.397 6.816-7.827 0-5.429-2.947-7.869-6.816-7.869h-7.746V44.82h7.746zM147.841 56.732v-.255l11.741-29.29h.885l11.615 29.29v.255h-2.062l-3.322-8.501H153.27l-3.324 8.501h-2.105zm12.164-26.052l-6.059 15.697h12.078l-6.019-15.697zM189.551 27.271h2.104v.293l-9.176 16.92v12.248h-2.02V44.484l-9.216-16.961v-.252h2.147l3.997 7.492 4.043 7.786h.04l4.081-7.786z" fill="#FFFFFF" class="logo-brand-text"/>
|
||||
</g>
|
||||
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="small" viewBox="0 0 46 84">
|
||||
<path d="M5.206 83.433a4.86 4.86 0 01-4.859-4.861V5.431a4.86 4.86 0 119.719 0v73.141a4.861 4.861 0 01-4.86 4.861" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M5.209 83.433a4.862 4.862 0 01-2.086-9.253L32.43 60.274 2.323 38.093a4.861 4.861 0 015.766-7.826l36.647 26.999a4.864 4.864 0 01-.799 8.306L7.289 82.964a4.866 4.866 0 01-2.08.469" fill="#51B13E" class="logo-brand-medium"/>
|
||||
<path d="M5.211 54.684a4.86 4.86 0 01-2.887-8.774L32.43 23.73 3.123 9.821a4.861 4.861 0 014.166-8.784l36.648 17.394a4.86 4.86 0 01.799 8.305l-36.647 27a4.844 4.844 0 01-2.878.948" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M10.066 31.725v20.553L24.01 42.006z" fill="#1E7A44" class="logo-brand-dark"/>
|
||||
<path d="M10.066 5.431A4.861 4.861 0 005.206.57 4.86 4.86 0 00.347 5.431v61.165h9.72V5.431h-.001z" fill="#CEDC21" class="logo-brand-light"/>
|
||||
</symbol>
|
||||
<symbol id="large" viewBox="0 0 192 84">
|
||||
<path d="M5.206 83.433a4.86 4.86 0 01-4.859-4.861V5.431a4.86 4.86 0 119.719 0v73.141a4.861 4.861 0 01-4.86 4.861" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M5.209 83.433a4.862 4.862 0 01-2.086-9.253L32.43 60.274 2.323 38.093a4.861 4.861 0 015.766-7.826l36.647 26.999a4.864 4.864 0 01-.799 8.306L7.289 82.964a4.866 4.866 0 01-2.08.469" fill="#51B13E" class="logo-brand-medium"/>
|
||||
<path d="M5.211 54.684a4.86 4.86 0 01-2.887-8.774L32.43 23.73 3.123 9.821a4.861 4.861 0 014.166-8.784l36.648 17.394a4.86 4.86 0 01.799 8.305l-36.647 27a4.844 4.844 0 01-2.878.948" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M10.066 31.725v20.553L24.01 42.006z" fill="#1E7A44" class="logo-brand-dark"/>
|
||||
<path d="M10.066 5.431A4.861 4.861 0 005.206.57 4.86 4.86 0 00.347 5.431v61.165h9.72V5.431h-.001z" fill="#CEDC21" class="logo-brand-light"/>
|
||||
<path d="M74.355 41.412c3.114.884 4.84 3.704 4.84 7.238 0 5.513-3.368 8.082-7.955 8.082H60.761V27.271h9.259c4.504 0 7.997 2.146 7.997 7.743 0 2.821-1.179 5.43-3.662 6.398m-4.293-.716c3.324 0 6.018-1.179 6.018-5.724 0-4.586-2.776-5.808-6.145-5.808h-7.197v11.531h7.324v.001zm1.052 14.099c3.366 0 6.06-1.768 6.06-6.145 0-4.713-3.072-6.144-6.901-6.144h-7.534v12.288h8.375v.001zM98.893 27.271v1.81h-8.122v27.651h-1.979V29.081h-8.123v-1.81zM112.738 26.85c5.01 0 9.554 2.524 10.987 8.543h-1.895c-1.348-4.923-5.303-6.732-9.134-6.732-6.944 0-10.605 5.681-10.605 13.341 0 8.08 3.661 13.256 10.646 13.256 4.125 0 7.828-1.85 9.26-7.279h1.895c-1.264 6.271-6.229 9.174-11.154 9.174-7.87 0-12.583-5.808-12.583-15.15 0-8.966 4.969-15.153 12.583-15.153M138.709 27.271c5.091 0 8.795 3.326 8.795 9.764 0 6.06-3.704 9.722-8.795 9.722h-7.746v9.976h-1.935V27.271h9.681zm0 17.549c3.745 0 6.816-2.397 6.816-7.827 0-5.429-2.947-7.869-6.816-7.869h-7.746V44.82h7.746zM147.841 56.732v-.255l11.741-29.29h.885l11.615 29.29v.255h-2.062l-3.322-8.501H153.27l-3.324 8.501h-2.105zm12.164-26.052l-6.059 15.697h12.078l-6.019-15.697zM189.551 27.271h2.104v.293l-9.176 16.92v12.248h-2.02V44.484l-9.216-16.961v-.252h2.147l3.997 7.492 4.043 7.786h.04l4.081-7.786z" fill="currentColor" class="logo-brand-text"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
|
|
@ -3521,7 +3521,7 @@ fieldset:disabled .btn {
|
|||
}
|
||||
|
||||
.dropdown-item.active, .dropdown-item:active {
|
||||
color: var(--btcpay-body-text);
|
||||
color: var(--btcpay-body-text-active);
|
||||
text-decoration: none;
|
||||
background-color: var(--btcpay-body-bg-active);
|
||||
}
|
||||
|
@ -3567,7 +3567,7 @@ fieldset:disabled .btn {
|
|||
}
|
||||
|
||||
.dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active {
|
||||
color: var(--btcpay-body-text);
|
||||
color: var(--btcpay-body-text-active);
|
||||
background-color: var(--btcpay-body-bg-active);
|
||||
}
|
||||
|
||||
|
@ -10295,7 +10295,7 @@ html[data-devenv]:before {
|
|||
z-index: 1000;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--btcpay-secondary);
|
||||
background: var(--btcpay-bg-tile);
|
||||
color: var(--btcpay-secondary-text);
|
||||
opacity: .7;
|
||||
padding: 4px 5px 3px 7px;
|
||||
|
|
430
BTCPayServer/wwwroot/main/layout.css
Normal file
430
BTCPayServer/wwwroot/main/layout.css
Normal file
|
@ -0,0 +1,430 @@
|
|||
/* Breakpoints:
|
||||
XS <576px
|
||||
SM ≥576px
|
||||
MD ≥768px
|
||||
LG ≥992px
|
||||
XL ≥1200px */
|
||||
|
||||
:root {
|
||||
--mobile-header-height: 4rem;
|
||||
--desktop-header-height: 8rem;
|
||||
--sidebar-width: 15%;
|
||||
--sidebar-min-width: 250px;
|
||||
--sidebar-max-width: 350px;
|
||||
}
|
||||
|
||||
/* Main Menu */
|
||||
#mainMenu {
|
||||
--button-width: 40px;
|
||||
--button-height: 40px;
|
||||
--button-padding: 7px;
|
||||
|
||||
height: var(--header-height);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#mainMenuHead .mainMenuButton {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--button-width);
|
||||
height: var(--button-height);
|
||||
padding: var(--button-padding);
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
color: var(--btcpay-body-text-muted);
|
||||
}
|
||||
|
||||
#mainNav {
|
||||
height: calc(100vh - var(--mobile-header-height));
|
||||
overflow-y: auto;
|
||||
padding-top: var(--btcpay-space-m);
|
||||
}
|
||||
|
||||
#mainNav .nav-item i.fa,
|
||||
#mainNav .nav-item svg.icon {
|
||||
font-size: 1.125rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
margin-right: var(--btcpay-space-xs);
|
||||
}
|
||||
|
||||
#mainNav .nav-item i.fa {
|
||||
padding: .15rem 0 0 var(--btcpay-space-xs);
|
||||
}
|
||||
|
||||
#mainNav .accordion-button {
|
||||
padding: var(--btcpay-space-s) 0;
|
||||
text-transform: uppercase;
|
||||
color: var(--btcpay-body-text-muted);
|
||||
font-weight: var(--btcpay-font-weight-semibold);
|
||||
}
|
||||
|
||||
#mainNav .accordion-item {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: var(--btcpay-font-weight-semibold);
|
||||
color: var(--btcpay-header-link);
|
||||
transition-property: color;
|
||||
transition-duration: var(--btcpay-transition-duration-fast);
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link:focus,
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link:hover {
|
||||
color: var(--btcpay-header-link-accent);
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link.active,
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link.active:focus,
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link.active:hover {
|
||||
color: var(--btcpay-header-link-active);
|
||||
}
|
||||
|
||||
#mainNavSettings {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.navbar-brand,
|
||||
.navbar-brand:hover,
|
||||
.navbar-brand:focus {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.btcpay-header {
|
||||
color: var(--btcpay-header-text);
|
||||
background: var(--btcpay-header-bg);
|
||||
}
|
||||
|
||||
#mainContent {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#mainContent > section {
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#StoreSelector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
#StoreSelector hr {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
#StoreSelectorDropdown,
|
||||
#StoreSelectorToggle {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#StoreSelectorToggle {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#StoreSelectorMenu {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
@media (max-width: 575px) {
|
||||
.logo {
|
||||
width: 1.125rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.logo-large {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.logo {
|
||||
width: 4.6rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.logo-small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Theme Switch */
|
||||
.btcpay-theme-switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch svg {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch svg[class="d-inline-block"] + span {
|
||||
margin-left: var(--btcpay-space-xs);
|
||||
}
|
||||
|
||||
.btcpay-theme-switch path {
|
||||
stroke-width: .5px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch:hover .btcpay-theme-switch-light,
|
||||
.btcpay-theme-switch:hover .btcpay-theme-switch-dark {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch-dark {
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
:root[data-btcpay-theme="dark"] .btcpay-theme-switch-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-btcpay-theme="dark"]) .btcpay-theme-switch-dark {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.btcpay-theme-switch-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root[data-btcpay-theme="dark"] .btcpay-theme-switch-light {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-btcpay-theme="light"]) .btcpay-theme-switch-light {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
#Notifications {
|
||||
flex: 0 0 var(--button-width);
|
||||
}
|
||||
|
||||
#NotificationsBadge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
min-width: 1.75em;
|
||||
}
|
||||
|
||||
#NotificationsHandle svg {
|
||||
width: 1.4rem;
|
||||
height: 1.4rem;
|
||||
color: var(--btcpay-body-text-muted);
|
||||
}
|
||||
|
||||
#NotificationsHandle:hover svg {
|
||||
color: var(--btcpay-header-text);
|
||||
}
|
||||
|
||||
#NotificationsDropdown {
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: var(--btcpay-bg-tile);
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.08);
|
||||
padding: 0;
|
||||
z-index: 2000;
|
||||
top: var(--btcpay-space-xs) !important;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.btcpay-footer {
|
||||
font-size: var(--btcpay-font-size-s);
|
||||
overflow: hidden;
|
||||
padding: .5em 0;
|
||||
color: var(--btcpay-footer-text);
|
||||
background: var(--btcpay-footer-bg);
|
||||
}
|
||||
|
||||
.btcpay-footer a {
|
||||
color: var(--btcpay-footer-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btcpay-footer a:focus,
|
||||
.btcpay-footer a:hover {
|
||||
color: var(--btcpay-footer-link-accent);
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
#mainMenu {
|
||||
--header-height: var(--mobile-header-height);
|
||||
}
|
||||
|
||||
#mainNav {
|
||||
position: fixed;
|
||||
top: var(--mobile-header-height);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: var(--sidebar-width);
|
||||
min-width: var(--sidebar-min-width);
|
||||
max-width: var(--sidebar-max-width);
|
||||
z-index: 1045;
|
||||
color: var(--btcpay-body-text);
|
||||
visibility: hidden;
|
||||
background-color: inherit;
|
||||
background-clip: padding-box;
|
||||
outline: 0;
|
||||
transform: translateX(-100%);
|
||||
transition: transform var(--btcpay-transition-duration-fast) ease-in-out;
|
||||
}
|
||||
|
||||
#mainNav.show {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.offcanvas-backdrop {
|
||||
top: var(--mobile-header-height);
|
||||
transition-duration: var(--btcpay-transition-duration-fast);
|
||||
}
|
||||
|
||||
.offcanvas-backdrop.show {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#StoreSelector {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#StoreSelectorDropdown {
|
||||
max-width: 40vw;
|
||||
}
|
||||
|
||||
#Notifications {
|
||||
margin-left: var(--btcpay-space-s);
|
||||
}
|
||||
|
||||
#mainMenuToggle {
|
||||
--line-thickness: 2px;
|
||||
--transition-easing: ease-in-out;
|
||||
--transition-duration: var(--btcpay-transition-duration-fast);
|
||||
|
||||
flex: 0 0 var(--button-width);
|
||||
margin-right: calc(var(--button-padding) * -1);
|
||||
margin-left: var(--btcpay-space-s);
|
||||
}
|
||||
|
||||
#mainMenuToggle span {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: calc(var(--button-width) - var(--button-padding) * 2);
|
||||
height: calc(var(--button-height) - (var(--button-padding) * 2) - (var(--line-thickness) * 4));
|
||||
border-top: var(--line-thickness) solid;
|
||||
border-bottom: var(--line-thickness) solid;
|
||||
color: var(--btcpay-body-text-muted);
|
||||
font-size: 0;
|
||||
transition: all var(--transition-duration) var(--transition-easing);
|
||||
}
|
||||
|
||||
#mainMenuToggle span:before,
|
||||
#mainMenuToggle span:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: var(--line-thickness);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
background: currentColor;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: transform var(--transition-duration) var(--transition-easing);
|
||||
}
|
||||
|
||||
#mainMenuToggle:hover span {
|
||||
color: var(--btcpay-header-text);
|
||||
}
|
||||
|
||||
#mainMenuToggle[aria-expanded="true"] span {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
#mainMenuToggle[aria-expanded="true"] span:before {
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
}
|
||||
|
||||
#mainMenuToggle[aria-expanded="true"] span:after {
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
|
||||
#mainContent > section {
|
||||
padding: var(--btcpay-space-l) 0 var(--btcpay-space-xl);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
#mainMenu {
|
||||
--header-height: var(--desktop-header-height);
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: var(--sidebar-width);
|
||||
min-width: var(--sidebar-min-width);
|
||||
max-width: var(--sidebar-max-width);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#mainNav {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
#Notifications {
|
||||
order: 1;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#StoreSelector {
|
||||
order: 2;
|
||||
margin-top: var(--btcpay-space-s);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mainMenuToggle,
|
||||
.offcanvas-backdrop{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#NotificationsDropdown {
|
||||
inset: calc(var(--button-height) * -1 - var(--btcpay-space-s)) auto auto calc(var(--button-width) + var(--btcpay-space-s)) !important;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#mainContent {
|
||||
margin-left: clamp(var(--sidebar-min-width), var(--sidebar-width), var(--sidebar-max-width));
|
||||
}
|
||||
|
||||
#mainContent > section {
|
||||
padding: var(--btcpay-space-xl) var(--btcpay-space-l);
|
||||
}
|
||||
|
||||
#mainContent > section > .container {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btcpay-footer {
|
||||
padding-left: var(--btcpay-space-l);
|
||||
padding-right: var(--btcpay-space-l);
|
||||
}
|
||||
}
|
|
@ -12,30 +12,18 @@ p {
|
|||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
hr.primary {
|
||||
width: 50px;
|
||||
height: 3px;
|
||||
background: var(--btcpay-primary);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
hr.light {
|
||||
background: var(--btcpay-white);
|
||||
}
|
||||
|
||||
.no-gutter > [class*='col-'] {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.logo-brand-text {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.hide-when-js,
|
||||
.input-group-clear {
|
||||
display: none;
|
||||
|
@ -76,148 +64,6 @@ hr.light {
|
|||
}
|
||||
}
|
||||
|
||||
/* Navigation bar */
|
||||
@media (max-width: 575px) {
|
||||
.offcanvas-header,
|
||||
.offcanvas-body {
|
||||
padding-left: var(--btcpay-gutter-x, 0.75rem);
|
||||
padding-right: var(--btcpay-gutter-x, 0.75rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.offcanvas-header {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.offcanvas-fade {
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.offcanvas-fade .offcanvas-header {
|
||||
background-color: var(--btcpay-header-bg);
|
||||
}
|
||||
|
||||
.offcanvas-fade .offcanvas-body {
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s linear, transform 0.15s linear;
|
||||
}
|
||||
|
||||
.offcanvas-fade.show .offcanvas-body {
|
||||
opacity: 1;
|
||||
transform: translate(0, .5rem);
|
||||
}
|
||||
}
|
||||
|
||||
#mainNav {
|
||||
color: var(--btcpay-header-text);
|
||||
background: var(--btcpay-header-bg);
|
||||
transition-property: background, color;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link {
|
||||
font-weight: var(--btcpay-font-weight-bold);
|
||||
color: var(--btcpay-header-link);
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link:focus,
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link:hover {
|
||||
color: var(--btcpay-header-link-accent);
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link.active,
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link.active:focus,
|
||||
#mainNav .navbar-nav > li.nav-item > .nav-link.active:hover {
|
||||
color: var(--btcpay-header-link-active);
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
#mainNav .navbar-nav > li.nav-item {
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
|
||||
#mainNav .navbar-nav:last-child > li.nav-item {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-brand,
|
||||
.navbar-brand:hover,
|
||||
.navbar-brand:focus {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
color: inherit;
|
||||
border-color: inherit;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* Theme Switch */
|
||||
.btcpay-theme-switch {
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch:not(.nav-link) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch svg {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch svg[class="d-inline-block"] + span {
|
||||
margin-left: var(--btcpay-space-xs);
|
||||
}
|
||||
|
||||
.btcpay-theme-switch path {
|
||||
stroke-width: .5px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch:hover .btcpay-theme-switch-light,
|
||||
.btcpay-theme-switch:hover .btcpay-theme-switch-dark {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.btcpay-theme-switch-dark {
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
:root[data-btcpay-theme="dark"] .btcpay-theme-switch-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-btcpay-theme="dark"]) .btcpay-theme-switch-dark {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.btcpay-theme-switch-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:root[data-btcpay-theme="dark"] .btcpay-theme-switch-light {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-btcpay-theme="light"]) .btcpay-theme-switch-light {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Info icons in main headline */
|
||||
h2 small .fa-question-circle-o {
|
||||
position: relative;
|
||||
|
@ -225,48 +71,39 @@ h2 small .fa-question-circle-o {
|
|||
font-size: var(--btcpay-font-size-l);
|
||||
}
|
||||
|
||||
/* Admin Sidebar Navigation */
|
||||
.col-md-3 .nav-pills {
|
||||
margin-left: -1rem;
|
||||
/* Section Navigation */
|
||||
#SectionNav {
|
||||
--border-size: 2px;
|
||||
|
||||
width: 100%;
|
||||
margin-top: calc(var(--btcpay-space-s) * -1);
|
||||
margin-bottom: var(--btcpay-space-l);
|
||||
border-bottom: var(--border-size) solid var(--btcpay-body-border-light);
|
||||
}
|
||||
|
||||
#sideNav .nav-link {
|
||||
margin: .3rem 0;
|
||||
border-left: 2px solid transparent;
|
||||
padding: .2rem 1rem;
|
||||
#SectionNav .nav-link {
|
||||
color: var(--btcpay-nav-link);
|
||||
margin-right: var(--btcpay-space-l);
|
||||
margin-bottom: calc(var(--border-size) * -1);
|
||||
border-bottom: var(--border-size) solid transparent;
|
||||
padding: var(--btcpay-space-m) 0;
|
||||
font-weight: var(--btcpay-font-weight-semibold);
|
||||
}
|
||||
|
||||
#sideNav .nav-link.active,
|
||||
#sideNav .show > .nav-link {
|
||||
#SectionNav .nav-link:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#SectionNav .nav-link:hover {
|
||||
color: var(--btcpay-nav-link-accent);
|
||||
}
|
||||
|
||||
#SectionNav .nav-link.active {
|
||||
color: var(--btcpay-nav-link-active);
|
||||
border-left-color: var(--btcpay-nav-border-active);
|
||||
border-bottom-color: var(--btcpay-nav-border-active);
|
||||
background: var(--btcpay-nav-bg-active);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.btcpay-footer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
padding: .5em 0;
|
||||
color: var(--btcpay-footer-text);
|
||||
background: var(--btcpay-footer-bg);
|
||||
}
|
||||
|
||||
.btcpay-footer a {
|
||||
color: var(--btcpay-footer-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btcpay-footer a:focus,
|
||||
.btcpay-footer a:hover {
|
||||
color: var(--btcpay-footer-link-accent);
|
||||
}
|
||||
|
||||
/* Prevent layout from breaking on hyperlinks with very long URLs as the visible text */
|
||||
.invoice-details a {
|
||||
word-break: break-word;
|
||||
|
@ -383,161 +220,3 @@ svg.icon {
|
|||
svg.icon-note {
|
||||
color: var(--btcpay-neutral-500);
|
||||
}
|
||||
|
||||
/* Custom notification dropdown styling */
|
||||
.notification-dropdown {
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 16px rgba(0, 0, 0, 0.08);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.notification-dropdown {
|
||||
width: 420px;
|
||||
top: 64px;
|
||||
right: -32px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification:hover {
|
||||
background-color: var(--btcpay-body-bg);
|
||||
}
|
||||
|
||||
.notification-badge {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
min-width: 1.5em;
|
||||
padding: .25em;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
#NotificationsDropdownToggle {
|
||||
position: relative;
|
||||
}
|
||||
.notification-badge {
|
||||
position: absolute;
|
||||
top: .125rem;
|
||||
left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 5rem 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
section {
|
||||
padding: 6rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Homepage */
|
||||
header.masthead {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: auto;
|
||||
margin-top: 4rem;
|
||||
padding: var(--btcpay-space-xxl) var(--btcpay-space-m);
|
||||
}
|
||||
|
||||
header.masthead::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../img/bg.png");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
header.masthead .header-content {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header.masthead .header-content .header-content-inner {
|
||||
max-width: 1000px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
header.masthead .header-content .header-content-inner h1 {
|
||||
font-size: var(--btcpay-font-size-xl);
|
||||
font-weight: var(--btcpay-font-weight-bold);
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
header.masthead .header-content .header-content-inner hr {
|
||||
margin: var(--btcpay-space-m) auto;
|
||||
}
|
||||
|
||||
header.masthead .header-content .header-content-inner p {
|
||||
font-size: var(--btcpay-font-size-l);
|
||||
margin: var(--btcpay-space-l) 0;
|
||||
max-width: 38rem;
|
||||
}
|
||||
|
||||
#services img {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.social-row {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.social-row > div {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.social-row img {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.social-row span {
|
||||
display: block;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.service-box {
|
||||
max-width: 400px;
|
||||
margin: 50px auto 0;
|
||||
}
|
||||
|
||||
.service-box p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.call-to-action {
|
||||
padding: 50px 0;
|
||||
}
|
||||
|
||||
.call-to-action h2 {
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
header.masthead .header-content .header-content-inner h1 {
|
||||
font-size: var(--btcpay-font-size-xxl);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
header.masthead {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.service-box {
|
||||
margin: 20px auto 0;
|
||||
}
|
||||
}
|
||||
|
||||
.social-link {
|
||||
height: 1rem;
|
||||
padding: 0 0.5rem 0 1rem;
|
||||
}
|
||||
|
|
|
@ -99,12 +99,44 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||
});
|
||||
});
|
||||
|
||||
delegate('click', '.btcpay-theme-switch', function (e) {
|
||||
e.preventDefault();
|
||||
const current = document.documentElement.getAttribute(THEME_ATTR) || COLOR_MODES[0];
|
||||
const mode = current === COLOR_MODES[0] ? COLOR_MODES[1] : COLOR_MODES[0];
|
||||
setColorMode(mode);
|
||||
// Theme Switch
|
||||
delegate('click', '.btcpay-theme-switch', e => {
|
||||
e.preventDefault()
|
||||
const current = document.documentElement.getAttribute(THEME_ATTR) || COLOR_MODES[0]
|
||||
const mode = current === COLOR_MODES[0] ? COLOR_MODES[1] : COLOR_MODES[0]
|
||||
setColorMode(mode)
|
||||
e.target.closest('.btcpay-theme-switch').blur()
|
||||
})
|
||||
|
||||
// Offcanvas navigation
|
||||
const mainMenuToggle = document.getElementById('mainMenuToggle')
|
||||
if (mainMenuToggle) {
|
||||
delegate('show.bs.offcanvas', '#mainNav', () => {
|
||||
mainMenuToggle.setAttribute('aria-expanded', 'true')
|
||||
})
|
||||
delegate('hide.bs.offcanvas', '#mainNav', () => {
|
||||
mainMenuToggle.setAttribute('aria-expanded', 'false')
|
||||
})
|
||||
}
|
||||
|
||||
// Menu collapses
|
||||
const mainNav = document.getElementById('mainNav')
|
||||
if (mainNav) {
|
||||
const COLLAPSED_KEY = 'btcpay-nav-collapsed'
|
||||
delegate('show.bs.collapse', '#mainNav', (e) => {
|
||||
const { id } = e.target
|
||||
const navCollapsed = window.localStorage.getItem(COLLAPSED_KEY)
|
||||
const collapsed = navCollapsed ? JSON.parse(navCollapsed).filter(i => i !== id ) : []
|
||||
window.localStorage.setItem(COLLAPSED_KEY, JSON.stringify(collapsed))
|
||||
})
|
||||
delegate('hide.bs.collapse', '#mainNav', (e) => {
|
||||
const { id } = e.target
|
||||
const navCollapsed = window.localStorage.getItem(COLLAPSED_KEY)
|
||||
const collapsed = navCollapsed ? JSON.parse(navCollapsed) : []
|
||||
if (!collapsed.includes(id)) collapsed.push(id)
|
||||
window.localStorage.setItem(COLLAPSED_KEY, JSON.stringify(collapsed))
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
function switchTimeFormat() {
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
--btcpay-nav-link: var(--btcpay-neutral-500);
|
||||
--btcpay-nav-link-accent: var(--btcpay-neutral-300);
|
||||
--btcpay-nav-link-active: var(--btcpay-white);
|
||||
--btcpay-footer-bg: var(--btcpay-bg-dark);
|
||||
--btcpay-footer-text: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link-accent: var(--btcpay-neutral-200);
|
||||
|
|
|
@ -148,10 +148,10 @@
|
|||
--btcpay-neutral-800: var(--btcpay-neutral-light-800);
|
||||
--btcpay-neutral-900: var(--btcpay-neutral-light-900);
|
||||
--btcpay-font-size-base: var(--btcpay-font-size-m);
|
||||
--btcpay-bg-tile: var(--btcpay-white);
|
||||
--btcpay-bg-tile: var(--btcpay-neutral-100);
|
||||
--btcpay-bg-dark: var(--btcpay-brand-dark);
|
||||
--btcpay-body-bg: var(--btcpay-neutral-100);
|
||||
--btcpay-body-bg-light: var(--btcpay-white);
|
||||
--btcpay-body-bg: var(--btcpay-white);
|
||||
--btcpay-body-bg-light: var(--btcpay-neutral-100);
|
||||
--btcpay-body-bg-medium: var(--btcpay-neutral-200);
|
||||
--btcpay-body-bg-striped: var(--btcpay-neutral-200);
|
||||
--btcpay-body-bg-hover: var(--btcpay-white);
|
||||
|
@ -170,11 +170,11 @@
|
|||
--btcpay-body-shadow: rgba(25, 135, 84, 0.33);
|
||||
--btcpay-wizard-bg: var(--btcpay-body-bg);
|
||||
--btcpay-wizard-text: var(--btcpay-body-text);
|
||||
--btcpay-header-bg: var(--btcpay-white);
|
||||
--btcpay-header-bg: var(--btcpay-neutral-100);
|
||||
--btcpay-header-text: var(--btcpay-body-text);
|
||||
--btcpay-header-link: var(--btcpay-header-text);
|
||||
--btcpay-header-link-accent: var(--btcpay-primary);
|
||||
--btcpay-header-link-active: var(--btcpay-primary-accent);
|
||||
--btcpay-header-link-active: var(--btcpay-primary);
|
||||
--btcpay-nav-link: var(--btcpay-neutral-600);
|
||||
--btcpay-nav-link-accent: var(--btcpay-neutral-700);
|
||||
--btcpay-nav-link-active: var(--btcpay-neutral-900);
|
||||
|
@ -198,10 +198,10 @@
|
|||
--btcpay-form-shadow-focus: var(--btcpay-primary-shadow);
|
||||
--btcpay-form-shadow-valid: var(--btcpay-success-shadow);
|
||||
--btcpay-form-shadow-invalid: var(--btcpay-danger-shadow);
|
||||
--btcpay-footer-bg: var(--btcpay-brand-dark);
|
||||
--btcpay-footer-text: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link: var(--btcpay-neutral-400);
|
||||
--btcpay-footer-link-accent: var(--btcpay-neutral-100);
|
||||
--btcpay-footer-bg: var(--btcpay-body-bg);
|
||||
--btcpay-footer-text: var(--btcpay-neutral-500);
|
||||
--btcpay-footer-link: var(--btcpay-neutral-500);
|
||||
--btcpay-footer-link-accent: var(--btcpay-neutral-600);
|
||||
--btcpay-code-text: var(--btcpay-body-text);
|
||||
--btcpay-code-bg: transparent;
|
||||
--btcpay-pre-text: var(--btcpay-white);
|
||||
|
@ -384,7 +384,6 @@
|
|||
--btcpay-dark-rgb: 33, 38, 45;
|
||||
}
|
||||
|
||||
header.masthead::before,
|
||||
.service-box img {
|
||||
filter: hue-rotate(318deg);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue