mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
App: Light PoS on top of the existing PoS (#1909)
* Initial commit * review fixes * Fix padding of logo, readjust display font size when clearing numbers Co-authored-by: Kukks <evilkukka@gmail.com>
This commit is contained in:
@@ -65,6 +65,8 @@ namespace BTCPayServer.Controllers
|
||||
var numberFormatInfo = _AppService.Currencies.GetNumberFormatInfo(settings.Currency) ?? _AppService.Currencies.GetNumberFormatInfo("USD");
|
||||
double step = Math.Pow(10, -(numberFormatInfo.CurrencyDecimalDigits));
|
||||
viewType ??= settings.EnableShoppingCart ? PosViewType.Cart : settings.DefaultView;
|
||||
var store = await _AppService.GetStore(app);
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
|
||||
return View("PointOfSale/" + viewType, new ViewPointOfSaleViewModel()
|
||||
{
|
||||
@@ -91,6 +93,7 @@ namespace BTCPayServer.Controllers
|
||||
CustomTipText = settings.CustomTipText,
|
||||
CustomTipPercentages = settings.CustomTipPercentages,
|
||||
CustomCSSLink = settings.CustomCSSLink,
|
||||
CustomLogoLink = storeBlob.CustomLogo,
|
||||
AppId = appId,
|
||||
Description = settings.Description,
|
||||
EmbeddedCSS = settings.EmbeddedCSS
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace BTCPayServer.Models.AppViewModels
|
||||
public int[] CustomTipPercentages { get; set; }
|
||||
|
||||
public string CustomCSSLink { get; set; }
|
||||
public string CustomLogoLink { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string EmbeddedCSS { get; set; }
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace BTCPayServer.Services.Apps
|
||||
public enum PosViewType
|
||||
{
|
||||
Static,
|
||||
Cart
|
||||
Cart,
|
||||
Light
|
||||
}
|
||||
}
|
||||
|
||||
16
BTCPayServer/Views/AppsPublic/PointOfSale/Light.cshtml
Normal file
16
BTCPayServer/Views/AppsPublic/PointOfSale/Light.cshtml
Normal file
@@ -0,0 +1,16 @@
|
||||
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
|
||||
@{
|
||||
Layout = "_LayoutPos";
|
||||
}
|
||||
|
||||
@if (Context.Request.Query.ContainsKey("simple"))
|
||||
{
|
||||
@await Html.PartialAsync("/Views/AppsPublic/PointOfSale/MinimalLight.cshtml", Model)
|
||||
}
|
||||
else
|
||||
{
|
||||
<noscript>
|
||||
@await Html.PartialAsync("/Views/AppsPublic/PointOfSale/MinimalLight.cshtml", Model)
|
||||
</noscript>
|
||||
@await Html.PartialAsync("/Views/AppsPublic/PointOfSale/VueLight.cshtml", Model)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<div class="container p-0 l-pos-wrapper">
|
||||
<div class="l-pos-header bg-primary py-3 px-3">
|
||||
@if (!string.IsNullOrEmpty(Model.CustomLogoLink)) {
|
||||
<img src="@Model.CustomLogoLink" height="40">
|
||||
} else {
|
||||
<h1 class="mb-0">@Model.Title</h1>
|
||||
}
|
||||
</div>
|
||||
<div class="py-5 px-3">
|
||||
<form method="post" asp-controller="AppsPublic" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" 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" 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 class="text-center mt-5 mb-3 py-2 bg-dark">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
40
BTCPayServer/Views/AppsPublic/PointOfSale/VueLight.cshtml
Normal file
40
BTCPayServer/Views/AppsPublic/PointOfSale/VueLight.cshtml
Normal file
@@ -0,0 +1,40 @@
|
||||
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
|
||||
<div id="app" class="l-pos-wrapper" v-cloak>
|
||||
<form method="post" asp-controller="AppsPublic" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
|
||||
<div class="l-pos-header bg-primary py-3 px-3">
|
||||
@if (!string.IsNullOrEmpty(Model.CustomLogoLink))
|
||||
{
|
||||
|
||||
<img src="@Model.CustomLogoLink" height="40"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h1 class="mb-0">@Model.Title</h1>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div ref="display" class="l-pos-display pt-5 pb-3 px-3"><div class="text-muted">{{srvModel.currencyCode}}</div><span ref="amount" v-bind:style="{fontSize: fontSize + 'px'}">{{ payTotal }}</span></div>
|
||||
<div class="l-pos-keypad">
|
||||
<template
|
||||
v-for="(key, index) in keys"
|
||||
:key="index">
|
||||
<div v-if="key !== ''" class="btn"
|
||||
v-bind:class="{ 'btn-primary' : (isNaN(key) === false) || key === '.', 'btn-dark' : isNaN(key) && key !== '.' }"
|
||||
v-on:click="buttonClicked(key)">{{ key }}</div>
|
||||
<div v-else class="btn btn-empty"></div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="l-pos-controls mt-2">
|
||||
<div class="btn btn-outline-secondary btn-lg mb-0" v-on:click="clearTotal">Clear</div><button class="btn btn-primary btn-lg mb-0" type="submit"><b>Pay</b></button>
|
||||
</div>
|
||||
|
||||
<input class="form-control" type="hidden" name="amount" v-model="payTotalNumeric">
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-4 mb-3 py-2 bg-dark">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -36,6 +36,14 @@
|
||||
</script>
|
||||
<bundle name="wwwroot/bundles/cart-bundle.min.js" asp-append-version="true" />
|
||||
}
|
||||
@if (Model.ViewType == PosViewType.Light)
|
||||
{
|
||||
<bundle name="wwwroot/bundles/light-pos-bundle.min.css" asp-append-version="true" />
|
||||
<script type="text/javascript">
|
||||
var srvModel = @Safe.Json(Model);
|
||||
</script>
|
||||
<bundle name="wwwroot/bundles/light-pos-bundle.min.js" asp-append-version="true" />
|
||||
}
|
||||
<style>
|
||||
.card-deck {
|
||||
display: grid;
|
||||
|
||||
@@ -140,6 +140,19 @@
|
||||
"wwwroot/crowdfund/**/*.css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outputFileName": "wwwroot/bundles/light-pos-bundle.min.js",
|
||||
"inputFiles": [
|
||||
"wwwroot/vendor/vuejs/vue.min.js",
|
||||
"wwwroot/light-pos/**/*.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outputFileName": "wwwroot/bundles/light-pos-bundle.min.css",
|
||||
"inputFiles": [
|
||||
"wwwroot/light-pos/**/*.css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outputFileName": "wwwroot/bundles/payment-request-admin-bundle.min.js",
|
||||
"inputFiles": [
|
||||
|
||||
99
BTCPayServer/wwwroot/light-pos/app.js
Normal file
99
BTCPayServer/wwwroot/light-pos/app.js
Normal file
@@ -0,0 +1,99 @@
|
||||
var app = null;
|
||||
|
||||
function addLoadEvent(func) {
|
||||
var oldonload = window.onload;
|
||||
if (typeof window.onload != 'function') {
|
||||
window.onload = func;
|
||||
} else {
|
||||
window.onload = function() {
|
||||
if (oldonload) {
|
||||
oldonload();
|
||||
}
|
||||
func();
|
||||
}
|
||||
}
|
||||
}
|
||||
addLoadEvent(function (ev) {
|
||||
app = new Vue({
|
||||
el: '#app',
|
||||
data: function(){
|
||||
var displayFontSize = 80;
|
||||
|
||||
return {
|
||||
srvModel: window.srvModel,
|
||||
payTotal: '0',
|
||||
payTotalNumeric: 0,
|
||||
fontSize: displayFontSize,
|
||||
defaultFontSize: displayFontSize,
|
||||
keys: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '0', 'C']
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Currency: function(){
|
||||
return this.srvModel.Currency.toUpperCase();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
payTotal: function() {
|
||||
// This must be timeouted because the updated width is not available yet
|
||||
this.$nextTick(function(){
|
||||
var displayWidth = this.getWidth(this.$refs.display),
|
||||
amountWidth = this.getWidth(this.$refs.amount),
|
||||
gamma = displayWidth / amountWidth || 0,
|
||||
isAmountWider = displayWidth < amountWidth;
|
||||
|
||||
if (isAmountWider) {
|
||||
// Font size will get smaller
|
||||
this.fontSize = Math.floor(this.fontSize * gamma);
|
||||
} else if (!isAmountWider && this.fontSize < this.defaultFontSize) {
|
||||
// Font size will get larger up to the max size
|
||||
this.fontSize = Math.min(this.fontSize * gamma, this.defaultFontSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getWidth: function(el) {
|
||||
var styles = window.getComputedStyle(el),
|
||||
width = parseFloat(el.clientWidth),
|
||||
padL = parseFloat(styles.paddingLeft),
|
||||
padR = parseFloat(styles.paddingRight);
|
||||
|
||||
return width - padL - padR;
|
||||
},
|
||||
clearTotal: function() {
|
||||
this.payTotal = '0';
|
||||
this.payTotalNumeric = 0;
|
||||
},
|
||||
buttonClicked: function(key) {
|
||||
var payTotal = this.payTotal;
|
||||
|
||||
if (key === 'C') {
|
||||
payTotal = payTotal.substring(0, payTotal.length - 1);
|
||||
payTotal = payTotal === '' ? '0' : payTotal;
|
||||
} else if (key === '.') {
|
||||
// Only add decimal point if it doesn't exist yet
|
||||
if (payTotal.indexOf('.') === -1) {
|
||||
payTotal += key;
|
||||
}
|
||||
} else { // Is a digit
|
||||
if (payTotal === '0') {
|
||||
payTotal = '';
|
||||
}
|
||||
payTotal += key;
|
||||
|
||||
var divsibility = this.srvModel.currencyInfo.divisibility;
|
||||
var decimalIndex = payTotal.indexOf('.')
|
||||
if (decimalIndex !== -1 && (payTotal.length - decimalIndex-1 > divsibility)) {
|
||||
payTotal = payTotal.replace(".", "");
|
||||
payTotal = payTotal.substr(0, payTotal.length - divsibility) + "." + payTotal.substr(payTotal.length - divsibility);
|
||||
}
|
||||
}
|
||||
|
||||
this.payTotal = payTotal;
|
||||
this.payTotalNumeric = parseFloat(payTotal);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
54
BTCPayServer/wwwroot/light-pos/styles/main.css
Normal file
54
BTCPayServer/wwwroot/light-pos/styles/main.css
Normal file
@@ -0,0 +1,54 @@
|
||||
[v-cloak] > * {
|
||||
display: none
|
||||
}
|
||||
|
||||
[v-cloak]::before {
|
||||
content: "loading…"
|
||||
}
|
||||
|
||||
.l-pos-wrapper {
|
||||
max-width: 450px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.l-pos-header {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.l-pos-display {
|
||||
font-size: 1.4rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.l-pos-display span {
|
||||
display: inline-block;
|
||||
height: 80px;
|
||||
line-height: 80px;
|
||||
}
|
||||
|
||||
.l-pos-keypad {
|
||||
margin-left: 2%;
|
||||
}
|
||||
|
||||
.l-pos-keypad .btn {
|
||||
width: 32%;
|
||||
margin-right: 1%;
|
||||
margin-bottom: 1%;
|
||||
border-radius: 0;
|
||||
padding-top: 4%;
|
||||
padding-bottom: 4%;
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.l-pos-controls .btn {
|
||||
width: 47%;
|
||||
border-radius: 0;
|
||||
margin-right: 0%;
|
||||
margin-left: 2%;
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 40px;
|
||||
filter: contrast(0) brightness(200%);
|
||||
}
|
||||
Reference in New Issue
Block a user