Tweaking UI for custom amounts (#398)

* Tweaking appearance of custom amount card

* Allowing POS items to have custom amounts, good for donations/tips

* Prepending currency symbol in POS

* Fixing regression, thanks unit test
This commit is contained in:
Rockstar Developer
2018-11-15 21:31:38 -06:00
committed by Nicolas Dorier
parent e9b2088f7d
commit 479303dd9e
3 changed files with 95 additions and 53 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -45,6 +46,7 @@ namespace BTCPayServer.Controllers
Title = settings.Title,
Step = step.ToString(CultureInfo.InvariantCulture),
ShowCustomAmount = settings.ShowCustomAmount,
CurrencySymbol = currency.Symbol,
Items = _AppsHelper.Parse(settings.Template, settings.Currency)
});
}
@@ -140,6 +142,7 @@ namespace BTCPayServer.Controllers
}
}
public ViewPointOfSaleViewModel.Item[] Parse(string template, string currency)
{
if (string.IsNullOrWhiteSpace(template))
@@ -150,40 +153,50 @@ namespace BTCPayServer.Controllers
var root = (YamlMappingNode)stream.Documents[0].RootNode;
return root
.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlMappingNode })
.Select(kv => new PosHolder { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlMappingNode })
.Where(kv => kv.Value != null)
.Select(c => new ViewPointOfSaleViewModel.Item()
{
Description = c.Value.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(kv => kv.Value != null)
.Where(cc => cc.Key == "description")
.FirstOrDefault()?.Value?.Value,
Description = c.GetDetailString("description"),
Id = c.Key,
Image = c.Value.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(kv => kv.Value != null)
.Where(cc => cc.Key == "image")
.FirstOrDefault()?.Value?.Value,
Title = c.Value.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(kv => kv.Value != null)
.Where(cc => cc.Key == "title")
.FirstOrDefault()?.Value?.Value ?? c.Key,
Price = c.Value.Children
.Select(kv => new { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(kv => kv.Value != null)
.Where(cc => cc.Key == "price")
Image = c.GetDetailString("image"),
Title = c.GetDetailString("title") ?? c.Key,
Price = c.GetDetail("price")
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
{
Value = decimal.Parse(cc.Value.Value, CultureInfo.InvariantCulture),
Formatted = FormatCurrency(cc.Value.Value, currency)
})
.Single()
}).Single(),
Custom = c.GetDetailString("custom") == "true"
})
.ToArray();
}
private class PosHolder
{
public string Key { get; set; }
public YamlMappingNode Value { get; set; }
public IEnumerable<PosScalar> GetDetail(string field)
{
var res = Value.Children
.Where(kv => kv.Value != null)
.Select(kv => new PosScalar { Key = (kv.Key as YamlScalarNode)?.Value, Value = kv.Value as YamlScalarNode })
.Where(cc => cc.Key == field);
return res;
}
public string GetDetailString(string field)
{
return GetDetail(field).FirstOrDefault()?.Value?.Value;
}
}
private class PosScalar
{
public string Key { get; set; }
public YamlScalarNode Value { get; set; }
}
public string FormatCurrency(string price, string currency)
{
return decimal.Parse(price, CultureInfo.InvariantCulture).ToString("C", _Currencies.GetCurrencyProvider(currency));

View File

@@ -19,10 +19,13 @@ namespace BTCPayServer.Models.AppViewModels
public string Image { get; set; }
public ItemPrice Price { get; set; }
public string Title { get; set; }
public bool Custom { get; set; }
}
public bool ShowCustomAmount { get; set; }
public string Step { get; set; }
public string Title { get; set; }
public Item[] Items { get; set; }
public string CurrencySymbol { get; set; }
}
}

View File

@@ -19,44 +19,70 @@
<div class="container d-flex h-100">
<div class="justify-content-center align-self-center text-center mx-auto px-2 py-3 w-100" style="margin: auto;">
<h1 class="mb-4">@Model.Title</h1>
<form method="post" asp-antiforgery="false">
<div class="row">
@for (int i = 0; i < Model.Items.Length; i++)
{
var className = (Model.Items.Length - i) > (Model.Items.Length % 4) ? "col-sm-6 col-lg-3" : "col-sm align-self-start";
var item = Model.Items[i];
var image = item.Image;
var description = item.Description;
<div class="@className my-3 px-2">
<div class="card">
@if (image != null && image != String.Empty)
<div class="row">
@for (int i = 0; i < Model.Items.Length; i++)
{
var className = (Model.Items.Length - i) > (Model.Items.Length % 4) ? "col-sm-6 col-lg-3" : "col-md align-self-start";
var item = Model.Items[i];
var image = item.Image;
var description = item.Description;
<div class="@className my-3 px-2">
<div class="card">
@if (!String.IsNullOrWhiteSpace(image))
{
<img class="card-img-top" src="@image" alt="Card image cap">
}
<div class="card-body">
<h5 class="card-title">@item.Title</h5>
@if (!String.IsNullOrWhiteSpace(description))
{
<img class="card-img-top" src="@image" alt="Card image cap">
<p class="card-text">@description</p>
}
@if (item.Custom)
{
<form method="post" asp-antiforgery="false" data-buy>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">@Model.CurrencySymbol</span>
</div>
<input class="form-control" type="number" min="@item.Price.Value" step="@Model.Step" name="amount"
value="@item.Price.Value" placeholder="Amount">
<div class="input-group-append">
<button class="btn btn-primary" type="submit">Pay</button>
</div>
</div>
</form>
}
else
{
<form method="post" asp-antiforgery="false">
<button type="submit" name="choiceKey" class="btn btn-primary" value="@item.Id">Buy for @item.Price.Formatted</button>
</form>
}
<div class="card-body">
<h5 class="card-title">@item.Title</h5>
@if (description != null && description != String.Empty)
{
<p class="card-text">@description</p>
}
<button type="submit" name="choiceKey" class="btn btn-primary" value="@item.Id">Buy for @item.Price.Formatted</button>
</div>
</div>
</div>
}
</div>
</form>
</div>
}
</div>
@if (Model.ShowCustomAmount)
{
<div class="row mt-4">
<div class="col-md-6 offset-md-3 px-2">
<form method="post" asp-antiforgery="false" data-buy>
<div class="input-group mb-5">
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="amount"><div class="input-group-append">
<button class="btn btn-primary" type="submit">Pay</button>
</div>
<div class="row mt-2 mb-4">
<div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 px-2">
<div class="card">
<div class="card-body">
<h5 class="card-title">Custom Amount</h5>
<p class="card-text">Create invoice to pay custom amount</p>
<form method="post" asp-antiforgery="false" data-buy>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">@Model.CurrencySymbol</span>
</div>
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="Amount">
<div class="input-group-append"><button class="btn btn-primary" type="submit">Pay</button></div>
</div>
</form>
</div>
</form>
</div>
</div>
</div>
}