mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-25 23:20:33 +01:00
* Move POS assets * WIP * Refactor into common Vue mixin * Offcanvas updates * Unifications across POS views * POSData view fix * Number and test fixes * Update cart width * Fix test * More view unification * Hide cart when emptied * Validate cart * Header improvement * Increase remove icon size * Animate add to cart action * Offcanvas for mobile, sidebar for desktop * ui+pos: updates icon size + badge + label * Remove cart table headers * Use same size for Cart and Shop headlines * Update search placeholder * Bump horizontal input padding * Increase sidebar width * Bump badge font size * Fix manipulating the quantity of line items * Fix cart icon * Update cart display * updates empty button * Rounded search input * Remove cart button on desktop * Fix dark accent color * More accent fixes * Fix plus/minus alignment * Update BTCPayServer/Views/Shared/PointOfSale/Public/Cart.cshtml * Apply suggestions from code review --------- Co-authored-by: dstrukt <gfxdsign@gmail.com>
115 lines
6.5 KiB
Text
115 lines
6.5 KiB
Text
@using BTCPayServer.Plugins.PointOfSale.Models
|
|
@using BTCPayServer.Services
|
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
|
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
|
|
@inject DisplayFormatter DisplayFormatter
|
|
@{
|
|
Layout = "PointOfSale/Public/_Layout";
|
|
}
|
|
@functions {
|
|
private string GetItemPriceFormatted(ViewPointOfSaleViewModel.Item item)
|
|
{
|
|
if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Topup) return "any amount";
|
|
if (item.Price == 0) return "free";
|
|
var formatted = DisplayFormatter.Currency(item.Price ?? 0, Model.CurrencyCode, DisplayFormatter.CurrencyFormat.Symbol);
|
|
return item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Minimum ? $"{formatted} minimum" : formatted;
|
|
}
|
|
}
|
|
|
|
<div id="PosStatic" class="public-page-wrap container-xl">
|
|
<partial name="_StoreHeader" model="(string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title, Model.LogoFileId)" />
|
|
<main>
|
|
<partial name="_StatusMessage" />
|
|
@if (!string.IsNullOrEmpty(Model.Description))
|
|
{
|
|
<div class="lead">@Safe.Raw(Model.Description)</div>
|
|
}
|
|
<div class="row row-cols row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4" id="PosItems">
|
|
@for (var x = 0; x < Model.Items.Length; x++)
|
|
{
|
|
var item = Model.Items[x];
|
|
var formatted = GetItemPriceFormatted(item);
|
|
var inStock = item.Inventory is null or > 0;
|
|
var buttonText = string.IsNullOrEmpty(item.BuyButtonText)
|
|
? item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Topup ? Model.CustomButtonText : Model.ButtonText
|
|
: item.BuyButtonText;
|
|
buttonText = buttonText.Replace("{0}", formatted).Replace("{Price}", formatted);
|
|
|
|
<div class="col">
|
|
<div class="card h-100 px-0" data-id="@x">
|
|
@if (!string.IsNullOrWhiteSpace(item.Image))
|
|
{
|
|
<img class="card-img-top" src="@item.Image" alt="@Safe.Raw(item.Title)" asp-append-version="true">
|
|
}
|
|
<div class="card-body p-3 d-flex flex-column gap-2">
|
|
<h5 class="card-title m-0">@Safe.Raw(item.Title)</h5>
|
|
<div class="d-flex gap-2 align-items-center">
|
|
@if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Topup || item.Price == 0)
|
|
{
|
|
<span class="fw-semibold badge text-bg-info">@Safe.Raw(char.ToUpper(formatted[0]) + formatted[1..])</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="fw-semibold">@Safe.Raw(formatted)</span>
|
|
}
|
|
@if (item.Inventory.HasValue)
|
|
{
|
|
<span class="badge text-bg-warning">
|
|
@(item.Inventory > 0 ? $"{item.Inventory} left" : "Sold out")
|
|
</span>
|
|
}
|
|
</div>
|
|
@if (!string.IsNullOrWhiteSpace(item.Description))
|
|
{
|
|
<p class="card-text">@Safe.Raw(item.Description)</p>
|
|
}
|
|
</div>
|
|
<div class="card-footer bg-transparent border-0 pt-0 pb-3">
|
|
@if (inStock)
|
|
{
|
|
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" autocomplete="off">
|
|
<input type="hidden" name="requiresRefundEmail" value="@Model.RequiresRefundEmail.ToString()" />
|
|
<input type="hidden" name="choiceKey" value="@item.Id" />
|
|
@if (item.PriceType == ViewPointOfSaleViewModel.ItemPriceType.Minimum)
|
|
{
|
|
<div class="input-group mb-2">
|
|
<span class="input-group-text">@Model.CurrencySymbol</span>
|
|
<input class="form-control" type="number" min="@(item.Price ?? 0)" step="@Model.Step" name="amount" placeholder="Amount" value="@item.Price" required>
|
|
</div>
|
|
}
|
|
<button class="btn btn-primary w-100" type="submit">@Safe.Raw(buttonText)</button>
|
|
</form>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
@if (Model.ShowCustomAmount)
|
|
{
|
|
<div class="col">
|
|
<div class="card h-100 px-0">
|
|
<div class="card-body p-3 d-flex flex-column gap-2">
|
|
<h5 class="card-title">Custom Amount</h5>
|
|
<p class="card-text">Create invoice to pay custom amount</p>
|
|
</div>
|
|
<div class="card-footer bg-transparent border-0 pb-3">
|
|
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" autocomplete="off">
|
|
<input type="hidden" name="requiresRefundEmail" value="@Model.RequiresRefundEmail.ToString()" />
|
|
<div class="input-group mb-2">
|
|
<span class="input-group-text">@Model.CurrencySymbol</span>
|
|
<input class="form-control" type="number" min="0" step="@Model.Step" name="amount" placeholder="Amount" required>
|
|
</div>
|
|
<button class="btn btn-primary w-100" type="submit">@Safe.Raw(Model.CustomButtonText ?? Model.ButtonText)</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</main>
|
|
<footer class="store-footer">
|
|
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
|
Powered by <partial name="_StoreFooterLogo" />
|
|
</a>
|
|
</footer>
|
|
</div>
|