Update Invoice Views (#3264)

* updates create invoice

* updates invoice list

* formats

* updates row

* updates

* Improve invoice list markup and fix mass action form

* Responsive invoice table

* Improve spacing on invoice detail view

* Improve archive message

* Responsive status change partial

* Add test case for mass archiving

* Add mass unarchiving

Closes #3270.

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
This commit is contained in:
dstrukt
2022-01-11 00:14:34 -08:00
committed by GitHub
parent 3c5d809cf9
commit 4b941a5145
8 changed files with 362 additions and 335 deletions

View File

@@ -393,6 +393,7 @@ namespace BTCPayServer.Tests
var storeUrl = s.Driver.Url; var storeUrl = s.Driver.Url;
s.ClickOnAllSectionLinks(); s.ClickOnAllSectionLinks();
s.GoToInvoices(); s.GoToInvoices();
Assert.Contains("There are no invoices matching your criteria.", s.Driver.PageSource);
var invoiceId = s.CreateInvoice(); var invoiceId = s.CreateInvoice();
s.FindAlertMessage(); s.FindAlertMessage();
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click(); s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
@@ -415,6 +416,23 @@ namespace BTCPayServer.Tests
s.GoToInvoices(); s.GoToInvoices();
Assert.Contains(invoiceId, s.Driver.PageSource); Assert.Contains(invoiceId, s.Driver.PageSource);
// archive via list
s.Driver.FindElement(By.CssSelector($".selector[value=\"{invoiceId}\"]")).Click();
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
s.Driver.FindElement(By.Id("ActionsDropdownArchive")).Click();
Assert.Contains("1 invoice archived", s.FindAlertMessage().Text);
Assert.DoesNotContain(invoiceId, s.Driver.PageSource);
// unarchive via list
s.Driver.FindElement(By.Id("SearchOptionsToggle")).Click();
s.Driver.FindElement(By.Id("SearchOptionsIncludeArchived")).Click();
Assert.Contains(invoiceId, s.Driver.PageSource);
s.Driver.FindElement(By.CssSelector($".selector[value=\"{invoiceId}\"]")).Click();
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
s.Driver.FindElement(By.Id("ActionsDropdownUnarchive")).Click();
Assert.Contains("1 invoice unarchived", s.FindAlertMessage().Text);
Assert.Contains(invoiceId, s.Driver.PageSource);
// When logout out 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.Logout(); s.Logout();
s.Driver.Navigate().GoToUrl(storeUrl); s.Driver.Navigate().GoToUrl(storeUrl);

View File

@@ -437,8 +437,12 @@ namespace BTCPayServer.Controllers
{ {
case "archive": case "archive":
await _InvoiceRepository.MassArchive(selectedItems); await _InvoiceRepository.MassArchive(selectedItems);
TempData[WellKnownTempData.SuccessMessage] = $"{selectedItems.Length} invoice(s) archived."; TempData[WellKnownTempData.SuccessMessage] = $"{selectedItems.Length} invoice{(selectedItems.Length == 1 ? "" : "s")} archived.";
break;
case "unarchive":
await _InvoiceRepository.MassArchive(selectedItems, false);
TempData[WellKnownTempData.SuccessMessage] = $"{selectedItems.Length} invoice{(selectedItems.Length == 1 ? "" : "s")} unarchived.";
break; break;
} }
} }
@@ -763,6 +767,8 @@ namespace BTCPayServer.Controllers
invoiceQuery.Skip = model.Skip; invoiceQuery.Skip = model.Skip;
var list = await _InvoiceRepository.GetInvoices(invoiceQuery); var list = await _InvoiceRepository.GetInvoices(invoiceQuery);
model.IncludeArchived = invoiceQuery.IncludeArchived;
foreach (var invoice in list) foreach (var invoice in list)
{ {
var state = invoice.GetInvoiceState(); var state = invoice.GetInvoiceState();

View File

@@ -10,6 +10,7 @@ namespace BTCPayServer.Models.InvoicingModels
public List<InvoiceModel> Invoices { get; set; } = new List<InvoiceModel>(); public List<InvoiceModel> Invoices { get; set; } = new List<InvoiceModel>();
public string[] StoreIds { get; set; } public string[] StoreIds { get; set; }
public string StoreId { get; set; } public string StoreId { get; set; }
public bool IncludeArchived { get; set; }
} }
public class InvoiceModel public class InvoiceModel

View File

@@ -446,7 +446,7 @@ namespace BTCPayServer.Services.Invoices
} }
} }
public async Task MassArchive(string[] invoiceIds) public async Task MassArchive(string[] invoiceIds, bool archive = true)
{ {
using (var context = _applicationDbContextFactory.CreateContext()) using (var context = _applicationDbContextFactory.CreateContext())
{ {
@@ -458,7 +458,7 @@ namespace BTCPayServer.Services.Invoices
foreach (InvoiceData invoice in items) foreach (InvoiceData invoice in items)
{ {
invoice.Archived = true; invoice.Archived = archive;
} }
await context.SaveChangesAsync(); await context.SaveChangesAsync();

View File

@@ -47,7 +47,8 @@
<h4 class="mt-5 mb-4">Invoice Details</h4> <h4 class="mt-5 mb-4">Invoice Details</h4>
} }
<div class="form-group"> <div class="d-flex justify-content-between">
<div class="form-group flex-fill me-4">
<label asp-for="Amount" class="form-label"></label> <label asp-for="Amount" class="form-label"></label>
<input asp-for="Amount" class="form-control" /> <input asp-for="Amount" class="form-control" />
<span asp-validation-for="Amount" class="text-danger"></span> <span asp-validation-for="Amount" class="text-danger"></span>
@@ -57,6 +58,7 @@
<input asp-for="Currency" class="form-control" /> <input asp-for="Currency" class="form-control" />
<span asp-validation-for="Currency" class="text-danger"></span> <span asp-validation-for="Currency" class="text-danger"></span>
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label asp-for="OrderId" class="form-label"></label> <label asp-for="OrderId" class="form-label"></label>
<input asp-for="OrderId" class="form-control" /> <input asp-for="OrderId" class="form-control" />
@@ -113,7 +115,7 @@
<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> <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"> <div class="form-group">
<label asp-for="PosData" class="form-label"></label> <label asp-for="PosData" class="form-label"></label>
<input asp-for="PosData" class="form-control"/> <input asp-for="PosData" class="form-control" />
<span asp-validation-for="PosData" class="text-danger"></span> <span asp-validation-for="PosData" class="text-danger"></span>
</div> </div>
</div> </div>
@@ -122,19 +124,19 @@
<h2 class="accordion-header" id="additional-notifications-header"> <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"> <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 Invoice Notifications
<vc:icon symbol="caret-down"/> <vc:icon symbol="caret-down" />
</button> </button>
</h2> </h2>
<div id="additional-notifications" class="accordion-collapse collapse" aria-labelledby="additional-notifications-header"> <div id="additional-notifications" class="accordion-collapse collapse" aria-labelledby="additional-notifications-header">
<div class="accordion-body"> <div class="accordion-body">
<div class="form-group"> <div class="form-group">
<label asp-for="NotificationUrl" class="form-label"></label> <label asp-for="NotificationUrl" class="form-label"></label>
<input asp-for="NotificationUrl" class="form-control"/> <input asp-for="NotificationUrl" class="form-control" />
<span asp-validation-for="NotificationUrl" class="text-danger"></span> <span asp-validation-for="NotificationUrl" class="text-danger"></span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label asp-for="NotificationEmail" class="form-label"></label> <label asp-for="NotificationEmail" class="form-label"></label>
<input asp-for="NotificationEmail" class="form-control"/> <input asp-for="NotificationEmail" class="form-control" />
<span asp-validation-for="NotificationEmail" class="text-danger"></span> <span asp-validation-for="NotificationEmail" class="text-danger"></span>
<p id="InvoiceEmailHelpBlock" class="form-text text-muted"> <p id="InvoiceEmailHelpBlock" class="form-text text-muted">
Receive updates for this invoice. Receive updates for this invoice.
@@ -145,7 +147,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group mt-4"> <div class="form-group mt-4">
<input type="submit" value="Create" class="btn btn-primary" id="Create" /> <input type="submit" value="Create" class="btn btn-primary" id="Create" />
</div> </div>

View File

@@ -314,7 +314,7 @@
@if (Model.Deliveries.Count != 0) @if (Model.Deliveries.Count != 0)
{ {
<h3 class="mb-3">Webhook deliveries</h3> <h3 class="mb-3 mt-4">Webhook deliveries</h3>
<ul class="list-group mb-5"> <ul class="list-group mb-5">
@foreach (var delivery in Model.Deliveries) @foreach (var delivery in Model.Deliveries)
{ {
@@ -369,7 +369,7 @@
} }
</ul> </ul>
} }
<div class="row"> <div class="row mt-4">
<div class="col-md-12"> <div class="col-md-12">
<h3 class="mb-0">Events</h3> <h3 class="mb-0">Events</h3>
<table class="table table-hover table-responsive-md"> <table class="table table-hover table-responsive-md">

View File

@@ -11,8 +11,8 @@
<h5 class="alert-heading">Updated in v1.4.0</h5> <h5 class="alert-heading">Updated in v1.4.0</h5>
<p class="mb-2">Invoice states have been updated to match the Greenfield API:</p> <p class="mb-2">Invoice states have been updated to match the Greenfield API:</p>
<div class="row"> <div class="row">
<div class="col col-12 col-sm-6"> <div class="col-12 col-md-6">
<ul class="list-unstyled mb-sm-0"> <ul class="list-unstyled mb-md-0">
<li> <li>
<span class="badge badge-processing">Paid</span> <span class="badge badge-processing">Paid</span>
<span class="mx-1">is now shown as</span> <span class="mx-1">is now shown as</span>
@@ -30,7 +30,7 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="col col-12 col-sm-6 d-flex justify-content-sm-end align-items-sm-end"> <div class="col-12 col-md-6 d-flex justify-content-md-end align-items-md-end">
<button name="command" type="submit" value="save" class="btn btn-sm btn-outline-secondary" data-bs-dismiss="alert">Don't Show Again</button> <button name="command" type="submit" value="save" class="btn btn-sm btn-outline-secondary" data-bs-dismiss="alert">Don't Show Again</button>
</div> </div>
</div> </div>

View File

@@ -6,7 +6,7 @@
} }
@section PageHeadContent { @section PageHeadContent {
<style type="text/css"> <style>
.invoice-payments { .invoice-payments {
padding-left: 2rem; padding-left: 2rem;
} }
@@ -16,14 +16,6 @@
font-weight: bold; font-weight: bold;
} }
.wraptext200 {
max-width: 200px;
text-overflow: ellipsis;
overflow: hidden;
display: block;
white-space: nowrap;
}
.pavpill { .pavpill {
display: inline-block; display: inline-block;
padding: 0.3em 0.5em; padding: 0.3em 0.5em;
@@ -69,6 +61,13 @@
background: #329f80; background: #329f80;
color: #fff; color: #fff;
} }
/* pull mass action form up, so that it is besides the search form */
@@media (min-width: 992px) {
#MassAction {
margin-top: -4rem;
}
}
</style> </style>
} }
@@ -150,7 +149,7 @@
}); });
$("#invoices") $("#invoices")
.on("click", ".invoice-row .invoice-details-toggle", function(e) { .on("click", ".invoice-row .invoice-details-toggle", function (e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(true); e.stopPropagation(true);
@@ -167,7 +166,7 @@
} }
}); });
}) })
.on("click", ".invoice-row", function(e) { .on("click", ".invoice-row", function (e) {
const $invoiceRow = $(e.currentTarget); const $invoiceRow = $(e.currentTarget);
if (!$(e.target).is("a,.badge,.selector")) { if (!$(e.target).is("a,.badge,.selector")) {
$invoiceRow.find(".selector").trigger("click"); $invoiceRow.find(".selector").trigger("click");
@@ -196,47 +195,10 @@
</a> </a>
</div> </div>
<partial name="InvoiceStatusChangePartial"/> <partial name="InvoiceStatusChangePartial" />
<div class="row"> @* Custom Range Modal *@
<div class="col-12 col-lg-6 mb-5 mb-lg-2 ms-auto"> <div class="modal fade" id="customRangeModal" tabindex="-1" role="dialog" aria-labelledby="customRangeModalTitle" aria-hidden="true" data-bs-backdrop="static">
<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>
<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-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-dialog modal-dialog-centered" role="document" style="max-width: 550px;">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
@@ -278,12 +240,10 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div> </div>
<div class="row collapse" id="help"> <div id="help" class="row collapse">
<div class="col @(Model.Total > 0 ? "pt-3 pb-lg-5" : "")"> <div class="col-xl-8 pb-3">
<p> <p>
You can search for invoice Id, deposit address, price, order id, store id, any buyer information and any product information. 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 /> Be sure to split your search parameters with comma, for example:<br />
@@ -304,17 +264,57 @@
</ul> </ul>
</div> </div>
</div> </div>
<form class="col-lg-6 col-xl-8 mb-4" 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>
<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>
@if (Model.Total > 0) <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>
<form method="post" id="MassAction" asp-action="MassAction" class="mt-lg-n5"> <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}" id="SearchOptionsIncludeArchived">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>
<form method="post" id="MassAction" asp-action="MassAction" class="">
<div class="d-inline-flex align-items-center pb-2 float-lg-end mb-2">
<input type="hidden" name="storeId" value="@Model.StoreId" /> <input type="hidden" name="storeId" value="@Model.StoreId" />
<a href="https://docs.btcpayserver.org/Accounting/" class="ms-2 ms-lg-0 me-lg-2 order-1 order-lg-0" target="_blank" rel="noreferrer noopener">
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<span class="me-2"> <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"> <button class="btn btn-secondary dropdown-toggle mb-1" type="button" id="ActionsDropdownToggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Actions Actions
</button> </button>
<div class="dropdown-menu" aria-labelledby="ActionsDropdownToggle"> <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> <button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="archive" id="ActionsDropdownArchive"><i class="fa fa-archive"></i> Archive</button>
@if (Model.IncludeArchived)
{
<button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="unarchive" id="ActionsDropdownUnarchive"><i class="fa fa-archive"></i> Unarchive</button>
}
</div> </div>
</span> </span>
<span> <span>
@@ -326,28 +326,28 @@
<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> <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> </div>
</span> </span>
</div>
<a href="https://docs.btcpayserver.org/Accounting/" class="ms-1" target="_blank" rel="noreferrer noopener"> @if (Model.Total > 0)
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span> {
</a> <div style="clear:both"></div>
<div class="table-responsive">
<table id="invoices" class="table table-hover table-responsive-md mt-4"> <table id="invoices" class="table table-hover">
<thead> <thead>
<tr> <tr>
<th style="width:2rem;" class="only-for-js"> <th style="width:2rem;" class="only-for-js">
<input id="selectAllCheckbox" type="checkbox" class="form-check-input"/> <input id="selectAllCheckbox" type="checkbox" class="form-check-input" />
</th>
<th style="min-width:90px;" class="col-md-auto"> <th style="min-width:90px;" class="col-md-auto">
Date Date
<a id="switchTimeFormat" href="#"> <a id="switchTimeFormat" href="#">
<span class="fa fa-clock-o" title="Switch date format"></span> <span class="fa fa-clock-o" title="Switch date format"></span>
</a> </a>
</th> </th>
<th style="max-width: 180px;">OrderId</th> <th class="text-nowrap">Order Id</th>
<th>InvoiceId</th> <th class="text-nowrap">Invoice Id</th>
<th style="min-width: 150px;">Status</th> <th>Status</th>
<th style="text-align:right">Amount</th> <th class="text-end">Amount</th>
<th style="text-align:right">Actions</th> <th class="text-end">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -355,24 +355,24 @@
{ {
<tr id="invoice_@invoice.InvoiceId" class="invoice-row"> <tr id="invoice_@invoice.InvoiceId" class="invoice-row">
<td class="only-for-js"> <td class="only-for-js">
<input name="selectedItems" type="checkbox" class="selector form-check-input" value="@invoice.InvoiceId"/> <input name="selectedItems" type="checkbox" class="selector form-check-input" value="@invoice.InvoiceId" />
</td> </td>
<td> <td>
<span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()"> <span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()">
@invoice.Date.ToBrowserDate() @invoice.Date.ToBrowserDate()
</span> </span>
</td> </td>
<td style="max-width: 180px;"> <td style="max-width:120px;">
@if (invoice.RedirectUrl != string.Empty) @if (invoice.RedirectUrl != string.Empty)
{ {
<a href="@invoice.RedirectUrl" class="wraptext200" rel="noreferrer noopener">@invoice.OrderId</a> <a href="@invoice.RedirectUrl" class="wraptextAuto" rel="noreferrer noopener">@invoice.OrderId</a>
} }
else else
{ {
<span>@invoice.OrderId</span> <span>@invoice.OrderId</span>
} }
</td> </td>
<td>@invoice.InvoiceId</td> <td class="text-break">@invoice.InvoiceId</td>
<td> <td>
@if (invoice.Details.Archived) @if (invoice.Details.Archived)
{ {
@@ -421,8 +421,8 @@
<span class="badge">@paymentType.GetBadge()</span> <span class="badge">@paymentType.GetBadge()</span>
} }
</td> </td>
<td style="text-align:right">@invoice.AmountCurrency</td> <td class="text-end text-nowrap">@invoice.AmountCurrency</td>
<td style="text-align:right"> <td class="text-end text-nowrap">
@if (invoice.ShowCheckout) @if (invoice.ShowCheckout)
{ {
<span> <span>
@@ -445,20 +445,21 @@
<td colspan="99" class="border-top-0"> <td colspan="99" class="border-top-0">
<div style="margin-left: 15px; margin-bottom: 0;"> <div style="margin-left: 15px; margin-bottom: 0;">
@* Leaving this as partial because it abstracts complexity of Invoice Payments *@ @* Leaving this as partial because it abstracts complexity of Invoice Payments *@
<partial name="ListInvoicesPaymentsPartial" model="(invoice.Details, true)"/> <partial name="ListInvoicesPaymentsPartial" model="(invoice.Details, true)" />
</div> </div>
</td> </td>
</tr> </tr>
} }
</tbody> </tbody>
</table> </table>
</div>
<vc:pager view-model="Model"/> <vc:pager view-model="Model" />
</form> }
} else
else {
{
<p class="text-secondary mt-3"> <p class="text-secondary mt-3">
There are no invoices matching your criteria. There are no invoices matching your criteria.
</p> </p>
} }
</form>