Store branding: Unify public pages (#4568)

Closes #3842.
This commit is contained in:
d11n 2023-01-30 09:23:49 +01:00 committed by GitHub
parent 14313291d5
commit f821e35cb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 611 additions and 646 deletions

View file

@ -180,14 +180,25 @@ namespace BTCPayServer.Controllers
[AllowAnonymous]
public async Task<IActionResult> ViewPaymentRequest(string payReqId)
{
var result = await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId());
if (result == null)
var vm = await _PaymentRequestService.GetPaymentRequest(payReqId, GetUserId());
if (vm == null)
{
return NotFound();
}
result.HubPath = PaymentRequestHub.GetHubPath(Request);
return View(result);
var store = await _storeRepository.FindStore(vm.StoreId);
if (store == null)
{
return NotFound();
}
var storeBlob = store.GetStoreBlob();
vm.StoreName = store.StoreName;
vm.BrandColor = storeBlob.BrandColor;
vm.LogoFileId = storeBlob.LogoFileId;
vm.CssFileId = storeBlob.CssFileId;
vm.HubPath = PaymentRequestHub.GetHubPath(Request);
return View(vm);
}
[HttpGet("{payReqId}/form")]

View file

@ -15,6 +15,7 @@ using BTCPayServer.Models.WalletViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Services;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@ -29,18 +30,21 @@ namespace BTCPayServer.Controllers
private readonly PullPaymentHostedService _pullPaymentHostedService;
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
private readonly StoreRepository _storeRepository;
public UIPullPaymentController(ApplicationDbContextFactory dbContextFactory,
CurrencyNameTable currencyNameTable,
PullPaymentHostedService pullPaymentHostedService,
BTCPayNetworkJsonSerializerSettings serializerSettings,
IEnumerable<IPayoutHandler> payoutHandlers)
IEnumerable<IPayoutHandler> payoutHandlers,
StoreRepository storeRepository)
{
_dbContextFactory = dbContextFactory;
_currencyNameTable = currencyNameTable;
_pullPaymentHostedService = pullPaymentHostedService;
_serializerSettings = serializerSettings;
_payoutHandlers = payoutHandlers;
_storeRepository = storeRepository;
}
[AllowAnonymous]
@ -53,6 +57,11 @@ namespace BTCPayServer.Controllers
return NotFound();
var blob = pp.GetBlob();
var store = await _storeRepository.FindStore(pp.StoreId);
if (store is null)
return NotFound();
var storeBlob = store.GetStoreBlob();
var payouts = (await ctx.Payouts.GetPayoutInPeriod(pp)
.OrderByDescending(o => o.Date)
.ToListAsync())
@ -68,6 +77,8 @@ namespace BTCPayServer.Controllers
ViewPullPaymentModel vm = new(pp, DateTimeOffset.UtcNow)
{
BrandColor = storeBlob.BrandColor,
CssFileId = storeBlob.CssFileId,
AmountFormatted = _currencyNameTable.FormatCurrency(blob.Limit, blob.Currency),
AmountCollected = totalPaid,
AmountCollectedFormatted = _currencyNameTable.FormatCurrency(totalPaid, blob.Currency),

View file

@ -140,6 +140,10 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
public DateTime? ExpiryDate { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string LogoFileId { get; set; }
public string CssFileId { get; set; }
public string BrandColor { get; set; }
public string StoreName { get; set; }
public string EmbeddedCSS { get; set; }
public string CustomCSSLink { get; set; }

View file

@ -89,6 +89,8 @@ namespace BTCPayServer.Models
public DateTime? ExpiryDate { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string BrandColor { get; set; }
public string CssFileId { get; set; }
public string EmbeddedCSS { get; set; }
public string CustomCSSLink { get; set; }
public List<PayoutLine> Payouts { get; set; } = new();

View file

@ -93,7 +93,6 @@ namespace BTCPayServer.Plugins.Crowdfund.Controllers
[RateLimitsFilter(ZoneLimits.PublicInvoices, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> ContributeToCrowdfund(string appId, ContributeToCrowdfund request, CancellationToken cancellationToken)
{
var app = await _appService.GetApp(appId, AppType.Crowdfund, true);
if (app == null)

View file

@ -17,6 +17,10 @@ namespace BTCPayServer.Plugins.Crowdfund.Models
public string Title { get; set; }
public string Description { get; set; }
public string MainImageUrl { get; set; }
public string CssFileId { get; set; }
public string LogoFileId { get; set; }
public string StoreName { get; set; }
public string BrandColor { get; set; }
public string EmbeddedCSS { get; set; }
public string CustomCSSLink { get; set; }
public DateTime? StartDate { get; set; }

View file

@ -78,6 +78,10 @@ namespace BTCPayServer.Plugins.PointOfSale.Controllers
return View($"PointOfSale/Public/{viewType}", new ViewPointOfSaleViewModel
{
Title = settings.Title,
StoreName = store.StoreName,
BrandColor = storeBlob.BrandColor,
CssFileId = storeBlob.CssFileId,
LogoFileId = storeBlob.LogoFileId,
Step = step.ToString(CultureInfo.InvariantCulture),
ViewType = (PosViewType)viewType,
ShowCustomAmount = settings.ShowCustomAmount,

View file

@ -41,8 +41,11 @@ namespace BTCPayServer.Plugins.PointOfSale.Models
public bool SymbolSpace { get; set; }
}
public string LogoFileId { get; set; }
public string CssFileId { get; set; }
public string BrandColor { get; set; }
public string StoreName { get; set; }
public CurrencyInfoData CurrencyInfo { get; set; }
public PosViewType ViewType { get; set; }
public bool ShowCustomAmount { get; set; }
public bool ShowDiscount { get; set; }

View file

@ -134,6 +134,9 @@ namespace BTCPayServer.Services.Apps
perks = newPerksOrder.ToArray();
}
var store = appData.StoreData;
var storeBlob = store.GetStoreBlob();
return new ViewCrowdfundViewModel
{
Title = settings.Title,
@ -142,6 +145,10 @@ namespace BTCPayServer.Services.Apps
CustomCSSLink = settings.CustomCSSLink,
MainImageUrl = settings.MainImageUrl,
EmbeddedCSS = settings.EmbeddedCSS,
StoreName = store.StoreName,
CssFileId = storeBlob.CssFileId,
LogoFileId = storeBlob.LogoFileId,
BrandColor = storeBlob.BrandColor,
StoreId = appData.StoreDataId,
AppId = appData.Id,
StartDate = settings.StartDate?.ToUniversalTime(),

View file

@ -1,16 +1,15 @@
@model BTCPayServer.Plugins.Crowdfund.Models.ViewCrowdfundViewModel
@using BTCPayServer.Plugins.Crowdfund.Models
@inject BTCPayServer.Services.ThemeSettings Theme
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@{
ViewData["Title"] = Model.Title;
Layout = null;
}
<!DOCTYPE html>
<html class="h-100">
<html class="h-100" @(Env.IsDeveloping ? " data-devenv" : "")>
<head>
<partial name="LayoutHead"/>
<link href="~/vendor/font-awesome/css/font-awesome.min.css" asp-append-version="true" rel="stylesheet" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, Model.CustomCSSLink, Model.EmbeddedCSS)" />
<link href="~/vendor/bootstrap-vue/bootstrap-vue.css" asp-append-version="true" rel="stylesheet" />
<link href="~/crowdfund/styles/main.css" asp-append-version="true" rel="stylesheet" />
@ -25,7 +24,7 @@
<vc:ui-extension-point location="crowdfund-head" model="@Model"></vc:ui-extension-point>
</head>
<body>
<body class="min-vh-100">
@if (!Model.Enabled)
{
<div class="alert alert-warning text-center sticky-top mb-0 rounded-0" role="alert">
@ -36,258 +35,261 @@
{
<canvas id="fireworks" class="d-none"></canvas>
}
<div class="container px-0 py-3" id="app" @(Model.SimpleDisplay ? "" : "v-cloak")>
<div class="row h-100 w-100 py-sm-0 py-md-4 mx-0">
<div class="card w-100 p-0 mx-0">
@if (!string.IsNullOrEmpty(Model.MainImageUrl))
{
<img v-if="srvModel.mainImageUrl" src="@Model.MainImageUrl" :src="srvModel.mainImageUrl" alt="@Model.Title" :alt="srvModel.title" class="card-img-top" id="crowdfund-main-image" asp-append-version="true"/>
}
<div class="d-flex flex-column justify-content-between py-4 px-3 text-center" id="crowdfund-header-container">
<h1 v-text="srvModel.title" class="my-3">@Model.Title</h1>
@if (!Model.Started && Model.StartDate.HasValue)
<div class="public-page-wrap flex-column container" id="app" @(Model.SimpleDisplay ? "" : "v-cloak")>
<div class="row h-100 w-100 py-sm-0 py-md-4 mx-0">
<div class="card w-100 p-0 mx-0">
@if (!string.IsNullOrEmpty(Model.MainImageUrl))
{
<h6 class="text-muted fst-italic" v-if="!started && srvModel.startDate" v-b-tooltip :title="startDate" v-text="`starts in ${startDiff}`" data-test="time-state">
starts @TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)
</h6>
}
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
{
<h6 class="text-muted fst-italic" v-if="started && !ended && srvModel.endDate" v-b-tooltip :title="endDate" v-text="`ends in ${endDiff}`" data-test="time-state">
ends @TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)
</h6>
}
else if (Model.Started && !Model.Ended && !Model.EndDate.HasValue)
{
<h6 class="text-muted fst-italic" v-if="started && !ended && !srvModel.endDate" v-b-tooltip title="No set end date" data-test="time-state">
currently active!
</h6>
<img v-if="srvModel.mainImageUrl" src="@Model.MainImageUrl" :src="srvModel.mainImageUrl" alt="@Model.Title" :alt="srvModel.title" class="card-img-top" id="crowdfund-main-image" asp-append-version="true"/>
}
<div class="d-flex flex-column justify-content-between py-4 px-3 text-center" id="crowdfund-header-container">
<h1 v-text="srvModel.title" class="my-3">@Model.Title</h1>
@if (!Model.Started && Model.StartDate.HasValue)
{
<h6 class="text-muted fst-italic" v-if="!started && srvModel.startDate" v-b-tooltip :title="startDate" v-text="`starts in ${startDiff}`" data-test="time-state">
starts @TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)
</h6>
}
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
{
<h6 class="text-muted fst-italic" v-if="started && !ended && srvModel.endDate" v-b-tooltip :title="endDate" v-text="`ends in ${endDiff}`" data-test="time-state">
ends @TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)
</h6>
}
else if (Model.Started && !Model.Ended && !Model.EndDate.HasValue)
{
<h6 class="text-muted fst-italic" v-if="started && !ended && !srvModel.endDate" v-b-tooltip title="No set end date" data-test="time-state">
currently active!
</h6>
}
@if (Model.TargetAmount.HasValue)
{
<span v-if="srvModel.targetAmount" class="mt-3" id="crowdfund-header-target">
<h3 class="d-inline-block">
<span class="badge bg-info px-3" v-text="`${targetAmount} ${targetCurrency}`">@Math.Round(Model.TargetAmount.GetValueOrDefault(0)) @Model.TargetCurrency</span>
</h3>
@if (Model.ResetEveryAmount > 0 && !Model.NeverReset)
{
<span v-if="srvModel.resetEvery !== 'Never'"
class="h5 ms-2"
v-b-tooltip
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')">
Dynamic
</span>
}
@if (Model.EnforceTargetAmount)
{
<span v-if="srvModel.enforceTargetAmount" class="h5 ms-2">
Hardcap Goal
<span class="fa fa-question-circle" v-b-tooltip title="No contributions allowed after the goal has been reached"></span>
</span>
}
else
{
<span v-if="!srvModel.enforceTargetAmount" class="h5 ms-2">
Softcap Goal
<span class="fa fa-question-circle" v-b-tooltip title="Contributions allowed even after goal is reached"></span>
</span>
}
</span>
}
</div>
@if (Model.TargetAmount.HasValue)
{
<span v-if="srvModel.targetAmount" class="mt-3" id="crowdfund-header-target">
<h3 class="d-inline-block">
<span class="badge bg-info px-3" v-text="`${targetAmount} ${targetCurrency}`">@Math.Round(Model.TargetAmount.GetValueOrDefault(0)) @Model.TargetCurrency</span>
</h3>
@if (Model.ResetEveryAmount > 0 && !Model.NeverReset)
{
<span v-if="srvModel.resetEvery !== 'Never'"
class="h5 ms-2"
v-b-tooltip
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')">
Dynamic
</span>
}
@if (Model.EnforceTargetAmount)
{
<span v-if="srvModel.enforceTargetAmount" class="h5 ms-2">
Hardcap Goal
<span class="fa fa-question-circle" v-b-tooltip title="No contributions allowed after the goal has been reached"></span>
</span>
}
else
{
<span v-if="!srvModel.enforceTargetAmount" class="h5 ms-2">
Softcap Goal
<span class="fa fa-question-circle" v-b-tooltip title="Contributions allowed even after goal is reached"></span>
</span>
}
</span>
<div class="progress rounded-pill mx-3" v-if="srvModel.targetAmount" id="crowdfund-progress-bar">
<div class="progress-bar bg-primary"
role="progressbar"
style="width:@(Model.Info.ProgressPercentage + "%")"
:aria-valuenow="srvModel.info.progressPercentage"
v-bind:style="{ width: srvModel.info.progressPercentage + '%' }"
aria-valuemin="0"
id="crowdfund-progress-bar-confirmed-bar"
v-b-tooltip
:title="parseFloat(srvModel.info.progressPercentage).toFixed(2) + '% contributions'"
aria-valuemax="100">
</div>
<div class="progress-bar bg-warning"
role="progressbar"
id="crowdfund-progress-bar-pending-bar"
style="width:@(Model.Info.PendingProgressPercentage + "%")"
:aria-valuenow="srvModel.info.pendingProgressPercentage"
v-bind:style="{ width: srvModel.info.pendingProgressPercentage + '%' }"
v-b-tooltip
:title="parseFloat(srvModel.info.pendingProgressPercentage).toFixed(2) + '% contributions pending confirmation'"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
}
</div>
@if (Model.TargetAmount.HasValue)
{
<div class="progress rounded-pill mx-3" v-if="srvModel.targetAmount" id="crowdfund-progress-bar">
<div class="progress-bar bg-primary"
role="progressbar"
style="width:@(Model.Info.ProgressPercentage + "%")"
:aria-valuenow="srvModel.info.progressPercentage"
v-bind:style="{ width: srvModel.info.progressPercentage + '%' }"
aria-valuemin="0"
id="crowdfund-progress-bar-confirmed-bar"
v-b-tooltip
:title="parseFloat(srvModel.info.progressPercentage).toFixed(2) + '% contributions'"
aria-valuemax="100">
</div>
<div class="progress-bar bg-warning"
role="progressbar"
id="crowdfund-progress-bar-pending-bar"
style="width:@(Model.Info.PendingProgressPercentage + "%")"
:aria-valuenow="srvModel.info.pendingProgressPercentage"
v-bind:style="{ width: srvModel.info.pendingProgressPercentage + '%' }"
v-b-tooltip
:title="parseFloat(srvModel.info.pendingProgressPercentage).toFixed(2) + '% contributions pending confirmation'"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
}
<div class="card-body">
<div class="row py-2 text-center crowdfund-stats">
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-raised-amount">
<h3 v-text="`${raisedAmount} ${targetCurrency}`">@Math.Round(Model.Info.CurrentAmount + Model.Info.CurrentPendingAmount, Model.CurrencyData.Divisibility) @Model.TargetCurrency</h3>
<h5 class="text-muted fst-italic mb-0">Raised</h5>
<b-tooltip target="crowdfund-body-raised-amount" v-if="paymentStats && paymentStats.length > 0" class="only-for-js">
<ul class="p-0 text-uppercase">
<li v-for="stat of paymentStats" class="list-unstyled">
{{stat.label}} <span v-if="stat.lightning" class="fa fa-bolt"></span> {{stat.value}}
</li>
</ul>
</b-tooltip>
</div>
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-goal-raised">
<h3 v-text="`${percentageRaisedAmount}%`">@Math.Round(Model.Info.PendingProgressPercentage.GetValueOrDefault(0) + Model.Info.ProgressPercentage.GetValueOrDefault(0))%</h3>
<h5 class="text-muted fst-italic mb-0">Of Goal</h5>
<b-tooltip target="crowdfund-body-goal-raised" v-if="srvModel.resetEvery !== 'Never'" class="only-for-js">
Goal resets every {{srvModel.resetEveryAmount}} {{srvModel.resetEvery}} {{srvModel.resetEveryAmount>1?'s': ''}}
</b-tooltip>
</div>
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-total-contributors">
<h3 v-text="new Intl.NumberFormat().format(srvModel.info.totalContributors)">@Model.Info.TotalContributors</h3>
<h5 class="text-muted fst-italic mb-0">Contributors</h5>
</div>
@if (Model.StartDate.HasValue || Model.EndDate.HasValue)
{
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-campaign-dates">
@if (!Model.Started && Model.StartDate.HasValue)
{
<div v-if="startDiff">
<h3 v-text="startDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)</h3>
<h5 class="text-muted fst-italic mb-0" v-text="'Left to start'">Start Date</h5>
</div>
}
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
{
<div v-if="!startDiff && endDiff">
<h3 v-text="endDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)</h3>
<h5 class="text-muted fst-italic mb-0" v-text="'Left'">End Date</h5>
</div>
}
else if (Model.Ended)
{
<div v-if="ended">
<h3 class="mb-0">Campaign not active</h3>
</div>
}
<b-tooltip v-if="startDate || endDate" target="crowdfund-body-campaign-dates" class="only-for-js">
<ul class="p-0">
@if (Model.StartDate.HasValue)
{
<li v-if="startDate" class="list-unstyled">
{{started? "Started" : "Starts"}} {{startDate}}
</li>
}
@if (Model.EndDate.HasValue)
{
<li v-if="endDate" class="list-unstyled">
{{ended? "Ended" : "Ends"}} {{endDate}}
</li>
}
<div class="card-body">
<div class="row py-2 text-center crowdfund-stats">
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-raised-amount">
<h3 v-text="`${raisedAmount} ${targetCurrency}`">@Math.Round(Model.Info.CurrentAmount + Model.Info.CurrentPendingAmount, Model.CurrencyData.Divisibility) @Model.TargetCurrency</h3>
<h5 class="text-muted fst-italic mb-0">Raised</h5>
<b-tooltip target="crowdfund-body-raised-amount" v-if="paymentStats && paymentStats.length > 0" class="only-for-js">
<ul class="p-0 text-uppercase">
<li v-for="stat of paymentStats" class="list-unstyled">
{{stat.label}} <span v-if="stat.lightning" class="fa fa-bolt"></span> {{stat.value}}
</li>
</ul>
</b-tooltip>
</div>
}
</div>
<div class="card-title text-center" id="crowdfund-body-header">
@if (!string.IsNullOrEmpty(Model.Tagline))
{
<h2 class="h3 my-4 fw-normal" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2>
}
<button v-if="active" id="crowdfund-body-header-cta" class="btn btn-lg btn-primary mb-4 py-2 px-5 only-for-js" v-on:click="contributeModalOpen = true">Contribute</button>
</div>
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-goal-raised">
<h3 v-text="`${percentageRaisedAmount}%`">@Math.Round(Model.Info.PendingProgressPercentage.GetValueOrDefault(0) + Model.Info.ProgressPercentage.GetValueOrDefault(0))%</h3>
<h5 class="text-muted fst-italic mb-0">Of Goal</h5>
<b-tooltip target="crowdfund-body-goal-raised" v-if="srvModel.resetEvery !== 'Never'" class="only-for-js">
Goal resets every {{srvModel.resetEveryAmount}} {{srvModel.resetEvery}} {{srvModel.resetEveryAmount>1?'s': ''}}
</b-tooltip>
</div>
<hr class="w-100"/>
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-total-contributors">
<h3 v-text="new Intl.NumberFormat().format(srvModel.info.totalContributors)">@Model.Info.TotalContributors</h3>
<h5 class="text-muted fst-italic mb-0">Contributors</h5>
</div>
@if (Model.StartDate.HasValue || Model.EndDate.HasValue)
{
<div class="col-sm border-end p-3 text-center" id="crowdfund-body-campaign-dates">
@if (!Model.Started && Model.StartDate.HasValue)
{
<div v-if="startDiff">
<h3 v-text="startDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.StartDate.Value, TimeZoneInfo.Local)</h3>
<h5 class="text-muted fst-italic mb-0" v-text="'Left to start'">Start Date</h5>
</div>
}
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
{
<div v-if="!startDiff && endDiff">
<h3 v-text="endDiff">@TimeZoneInfo.ConvertTimeFromUtc(Model.EndDate.Value, TimeZoneInfo.Local)</h3>
<h5 class="text-muted fst-italic mb-0" v-text="'Left'">End Date</h5>
</div>
}
else if (Model.Ended)
{
<div v-if="ended">
<h3 class="mb-0">Campaign not active</h3>
</div>
}
<b-tooltip v-if="startDate || endDate" target="crowdfund-body-campaign-dates" class="only-for-js">
<ul class="p-0">
@if (Model.StartDate.HasValue)
{
<li v-if="startDate" class="list-unstyled">
{{started? "Started" : "Starts"}} {{startDate}}
</li>
}
@if (Model.EndDate.HasValue)
{
<li v-if="endDate" class="list-unstyled">
{{ended? "Ended" : "Ends"}} {{endDate}}
</li>
}
</ul>
</b-tooltip>
</div>
}
</div>
<template v-if="srvModel.disqusEnabled && srvModel.disqusShortname">
<b-tabs>
<b-tab title="Details" active>
<div class="row mt-3">
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
@Safe.Raw(Model.Description)
<div class="card-title text-center" id="crowdfund-body-header">
@if (!string.IsNullOrEmpty(Model.Tagline))
{
<h2 class="h3 my-4 fw-normal" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2>
}
<button v-if="active" id="crowdfund-body-header-cta" class="btn btn-lg btn-primary mb-4 py-2 px-5 only-for-js" v-on:click="contributeModalOpen = true">Contribute</button>
</div>
<hr class="w-100"/>
<template v-if="srvModel.disqusEnabled && srvModel.disqusShortname">
<b-tabs>
<b-tab title="Details" active>
<div class="row mt-3">
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
@Safe.Raw(Model.Description)
</div>
</div>
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
<contribute :target-currency="srvModel.targetCurrency"
:display-perks-ranking="srvModel.displayPerksRanking"
:perks-value="srvModel.perksValue"
:active="active"
:loading="loading"
:in-modal="false"
:perks="perks">
</contribute>
</div>
</div>
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
<contribute :target-currency="srvModel.targetCurrency"
:display-perks-ranking="srvModel.displayPerksRanking"
:perks-value="srvModel.perksValue"
:active="active"
:loading="loading"
:in-modal="false"
:perks="perks">
</contribute>
</b-tab>
<b-tab title="Discussion">
<div id="disqus_thread" class="mt-3"></div>
</b-tab>
</b-tabs>
</template>
<template v-else>
<div class="row mt-2">
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
@Safe.Raw(Model.Description)
</div>
</div>
</b-tab>
<b-tab title="Discussion">
<div id="disqus_thread" class="mt-3"></div>
</b-tab>
</b-tabs>
</template>
<template v-else>
<div class="row mt-2">
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container">
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
@Safe.Raw(Model.Description)
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
<contribute :target-currency="srvModel.targetCurrency"
:loading="loading"
:display-perks-ranking="srvModel.displayPerksRanking"
:perks-value="srvModel.perksValue"
:active="active"
:in-modal="false"
:perks="perks">
</contribute>
</div>
</div>
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
<contribute :target-currency="srvModel.targetCurrency"
:loading="loading"
:display-perks-ranking="srvModel.displayPerksRanking"
:perks-value="srvModel.perksValue"
:active="active"
:in-modal="false"
:perks="perks">
</contribute>
</template>
<noscript>
<div class="row">
<div class="col-md-8 col-sm-12">
<div class="card-text overflow-hidden">@Safe.Raw(Model.Description)</div>
</div>
<div class="col-md-4 col-sm-12">
<partial
name="Crowdfund/Public/ContributeForm"
model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })">
</partial>
</div>
</div>
</div>
</template>
<noscript>
<div class="row">
<div class="col-md-8 col-sm-12">
<div class="card-text overflow-hidden">@Safe.Raw(Model.Description)</div>
</div>
<div class="col-md-4 col-sm-12">
<partial
name="Crowdfund/Public/ContributeForm"
model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })">
</partial>
</div>
</div>
</noscript>
</div>
<div class="card-footer text-muted d-flex flex-wrap align-items-center">
<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" />
}
<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">
<label class="form-check-label" for="cbAnime">Animations</label>
</noscript>
</div>
<div class="form-check me-3 my-0 only-for-js" v-if="srvModel.soundsEnabled|| sound">
<input class="form-check-input" type="checkbox" id="cbSounds" v-model="sound">
<label class="form-check-label" for="cbSounds">Sounds</label>
<div class="card-footer text-muted d-flex flex-wrap align-items-center">
<div class="me-3" v-text="`Updated ${lastUpdated}`">Updated @Model.Info.LastUpdated</div>
<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">
<label class="form-check-label" for="cbAnime">Animations</label>
</div>
<div class="form-check me-3 my-0 only-for-js" v-if="srvModel.soundsEnabled|| sound">
<input class="form-check-input" type="checkbox" id="cbSounds" v-model="sound">
<label class="form-check-label" for="cbSounds">Sounds</label>
</div>
</div>
</div>
</div>
</div>
<b-modal title="Contribute" v-model="contributeModalOpen" size="lg" ok-only="true" ok-variant="secondary" ok-title="Close" ref="modalContribute">
<contribute v-if="contributeModalOpen"
:target-currency="srvModel.targetCurrency"
:active="active"
:perks="srvModel.perks"
:loading="loading"
:in-modal="true">
</contribute>
</b-modal>
</div>
</div>
<b-modal title="Contribute" v-model="contributeModalOpen" size="lg" ok-only="true" ok-variant="secondary" ok-title="Close" ref="modalContribute">
<contribute v-if="contributeModalOpen"
:target-currency="srvModel.targetCurrency"
:active="active"
:perks="srvModel.perks"
:loading="loading"
:in-modal="true">
</contribute>
</b-modal>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>
<template id="perks-template">

View file

@ -1,4 +1,4 @@
@model (string BrandColor, string CssFileId)
@model (string BrandColor, string CssFileId, string CustomCSSLink, string EmbeddedCSS)
@using BTCPayServer.Abstractions.Extensions
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.Contracts
@ -24,3 +24,12 @@
{
<link href="@cssUrl" asp-append-version="true" rel="stylesheet" />
}
@* Deprecated, but added for backwards-compatibility *@
@if (!string.IsNullOrEmpty(Model.CustomCSSLink))
{
<link href="@Model.CustomCSSLink" asp-append-version="true" rel="stylesheet" />
}
@if (!string.IsNullOrEmpty(Model.EmbeddedCSS))
{
@Safe.Raw(Model.EmbeddedCSS)
}

View file

@ -1,7 +1,7 @@
@using BTCPayServer.Plugins.PointOfSale.Models
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{
Layout = "PointOfSale/Public/_LayoutPos";
Layout = "PointOfSale/Public/_Layout";
var customTipPercentages = Model.CustomTipPercentages;
var anyInventoryItems = Model.Items.Any(item => item.Inventory.HasValue);
}
@ -189,35 +189,28 @@
<!-- Page Content -->
<div id="content">
<div class="p-2 p-sm-4">
<div class="row">
<partial name="_StatusMessage" />
<div class="col-sm-4 col-lg-2 order-sm-last text-end mb-2">
<a class="js-cart btn btn-lg btn-outline-primary" href="#">
<i class="fa fa-shopping-basket"></i>&nbsp;
<span class="badge bg-light rounded-pill">
<span id="js-cart-items">0</span>
</span>
<partial name="_StatusMessage" />
<div class="d-flex gap-3 mb-4">
<div class="flex-fill position-relative">
<input type="text" class="js-search form-control form-control-lg" placeholder="Find product">
<a class="js-search-reset btn btn-lg btn-link text-black" href="#">
<i class="fa fa-times-circle fa-lg"></i>
</a>
</div>
<div class="col-sm-8 col-lg-10 mb-2">
<div class="mb-2 position-relative">
<input type="text" class="js-search form-control form-control-lg" placeholder="Find product">
<a class="js-search-reset btn btn-lg btn-link text-black" href="#">
<i class="fa fa-times-circle fa-lg"></i>
</a>
</div>
</div>
<a class="js-cart btn btn-lg btn-outline-primary text-nowrap" href="#">
<i class="fa fa-shopping-basket"></i>&nbsp;
<span class="badge bg-light rounded-pill">
<span id="js-cart-items">0</span>
</span>
</a>
</div>
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="row">
<div class="overflow-hidden col-12">@Safe.Raw(Model.Description)</div>
</div>
<div class="lead text-center mt-3">@Safe.Raw(Model.Description)</div>
}
</div>
<div id="js-pos-list" class="text-center mx-auto px-4">
<div class="card-deck my-3">
<div id="js-pos-list" class="text-center mx-auto px-2 px-sm-4 py-4 py-sm-2">
<div class="card-deck">
@for (var index = 0; index < Model.Items.Length; index++)
{
var item = Model.Items[index];
@ -275,8 +268,8 @@
<!-- Sidebar -->
<nav id="sidebar">
<div class="bg-primary p-3 clearfix">
<h3 class="text-white m-0 pull-left">Cart</h3>
<div class="d-flex align-items-center pt-4 p-2">
<h3 class="text-white m-0 me-auto">Cart</h3>
<a class="js-cart btn btn-sm bg-white text-black pull-right ms-5" href="#">
<i class="fa fa-times fa-lg"></i>
</a>
@ -302,12 +295,14 @@
<thead></thead>
</table>
<button id="js-cart-confirm" data-bs-toggle="modal" data-bs-target="#cartModal" class="btn btn-primary btn-lg w-100 mb-3 p-3" disabled="disabled" type="submit">
<b>Confirm</b>
<button id="js-cart-confirm" data-bs-toggle="modal" data-bs-target="#cartModal" class="btn btn-primary btn-lg mx-2 mb-3 p-3" disabled="disabled" type="submit">
Confirm
</button>
<div class="text-center mb-5 pb-5">
<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>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</nav>
</div>

View file

@ -1,18 +1,26 @@
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{
Layout = "PointOfSale/Public/_LayoutPos";
Layout = "PointOfSale/Public/_Layout";
}
<partial name="_StatusMessage" />
<div class="public-page-wrap flex-column">
<partial name="_StatusMessage" />
<partial name="_StoreHeader" model="(string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title, Model.LogoFileId)" />
@if (Context.Request.Query.ContainsKey("simple"))
{
<partial name="PointOfSale/Public/MinimalLight" model="Model" />
}
else
{
<noscript>
@if (Context.Request.Query.ContainsKey("simple"))
{
<partial name="PointOfSale/Public/MinimalLight" model="Model" />
</noscript>
<partial name="PointOfSale/Public/VueLight" model="Model" />
}
}
else
{
<noscript>
<partial name="PointOfSale/Public/MinimalLight" model="Model" />
</noscript>
<partial name="PointOfSale/Public/VueLight" model="Model" />
}
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>

View file

@ -1,11 +1,4 @@
<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" asp-append-version="true">
} else {
<h1 class="mb-0">@Model.Title</h1>
}
</div>
<div class="container p-0 l-pos-wrapper my-0 mx-auto">
<div class="py-5 px-3">
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
<div class="input-group">
@ -14,9 +7,5 @@
<button class="btn btn-primary" type="submit">Pay</button>
</div>
</form>
<div class="text-center mt-5 mb-3 py-2">
<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>

View file

@ -14,12 +14,10 @@
margin-bottom: 0;
}
}
.card { display: inline-block; width: 300px; page-break-inside: avoid; margin: 2em 1em 0 1em; }
</style>
@{
var store = await StoreRepository.FindStore(Model.StoreId);
Layout = "PointOfSale/Public/_LayoutPos";
Layout = "PointOfSale/Public/_Layout";
Context.Request.Query.TryGetValue("cryptocode", out var cryptoCodeValues);
var cryptoCode = cryptoCodeValues.FirstOrDefault() ?? "BTC";
var supported = store.GetSupportedPaymentMethods(BTCPayNetworkProvider).OfType<LNURLPaySupportedPaymentMethod>().OrderBy(method => method.CryptoCode == cryptoCode).FirstOrDefault();
@ -38,16 +36,25 @@
</a>
</div>
}
<div class="container d-flex">
<div class="justify-content-center align-self-center text-center mx-auto px-2 py-5 w-100 m-auto">
<h1 class="mb-4">@Model.Title</h1>
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="row">
<div class="overflow-hidden col-12">@Safe.Raw(Model.Description)</div>
</div>
}
else
{
<div class="alert alert-info alert-dismissible d-flex align-items-center justify-content-center sticky-top mb-0 rounded-0 d-print-none fade show" role="alert">
<button type="button" class="btn btn-info me-4 border border-light" onclick="window.print()">
<i class="fa fa-print"></i>&nbsp;Print
</button>
This view is intended for printing only —
<a asp-route-viewType="static" class="alert-link">Regular version</a>
</div>
}
<div class="container public-page-wrap flex-column">
<partial name="_StatusMessage" />
<partial name="_StoreHeader" model="(string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title, Model.LogoFileId)" />
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="lead text-center">@Safe.Raw(Model.Description)</div>
}
<main class="flex-grow-1 justify-content-center align-self-center mx-auto py-3">
@if (supported is not null)
{
if (Model.ShowCustomAmount)
@ -67,17 +74,10 @@
}
}).ToArray();
}
<div class="alert alert-info alert-dismissible d-flex align-items-center justify-content-center sticky-top mb-0 rounded-0 d-print-none fade show" role="alert">
<button type="button" class="btn btn-info me-4 border border-light" onclick="window.print()">
<i class="fa fa-print"></i>&nbsp;Print
</button>
This view is intended for printing only —
<a asp-route-viewType="static" class="alert-link">Regular version</a>
</div>
}
<div class="container text-center">
@for (int x = 0; x < Model.Items.Length; x++)
<div class="card-deck mx-auto">
@for (var x = 0; x < Model.Items.Length; x++)
{
var item = Model.Items[x];
<div class="card" data-id="@x">
@ -115,12 +115,19 @@
ItemCode = item.Id
}, Context.Request.Scheme, Context.Request.Host.ToString()));
var lnUrl = LNURL.EncodeUri(lnurlEndpoint, "payRequest", supported.UseBech32Scheme);
<a href="@lnUrl" rel="noreferrer noopener"><vc:qr-code data="@lnUrl.ToString().ToUpperInvariant()" /></a>
<a href="@lnUrl" rel="noreferrer noopener" class="d-block mx-auto text-center">
<vc:qr-code data="@lnUrl.ToString().ToUpperInvariant()" />
</a>
}
}
</div>
</div>
}
</div>
</div>
</main>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>

View file

@ -1,28 +1,25 @@
@using BTCPayServer.Plugins.PointOfSale.Models
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{
Layout = "PointOfSale/Public/_LayoutPos";
Layout = "PointOfSale/Public/_Layout";
var anyInventoryItems = Model.Items.Any(item => item.Inventory.HasValue);
}
<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 m-auto">
<partial name="_StatusMessage" />
<h1 class="mb-4">@Model.Title</h1>
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="row">
<div class="overflow-hidden col-12">@Safe.Raw(Model.Description)</div>
</div>
}
<div class="card-deck my-3 mx-auto">
<div class="container public-page-wrap flex-column">
<partial name="_StatusMessage" />
<partial name="_StoreHeader" model="(string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title, Model.LogoFileId)" />
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="lead text-center">@Safe.Raw(Model.Description)</div>
}
<main class="flex-grow-1 justify-content-center align-self-center text-center mx-auto py-3">
<div class="card-deck mx-auto">
@for (var x = 0; x < Model.Items.Length; x++)
{
var item = Model.Items[x];
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? item.Price.Type != ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
buttonText = buttonText.Replace("{0}",item.Price.Formatted).Replace("{Price}",item.Price.Formatted);
buttonText = buttonText.Replace("{0}", item.Price.Formatted).Replace("{Price}", item.Price.Formatted);
<div class="card px-0" data-id="@x">
@if (!string.IsNullOrWhiteSpace(item.Image))
{
@ -33,21 +30,21 @@
@if (!item.Inventory.HasValue || item.Inventory.Value > 0)
{
@if (item.Price.Type != ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup)
{
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
<input type="hidden" name="choicekey" value="@item.Id"/>
@{PayFormInputContent(item.BuyButtonText ?? Model.CustomButtonText, item.Price.Type, item.Price.Value, item.Price.Value);}
</form>
}
else
{
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false">
<input type="hidden" name="requiresRefundEmail" value="@Model.RequiresRefundEmail.ToString()" />
<button type="submit" name="choiceKey" class="js-add-cart btn btn-primary" value="@item.Id">
@Safe.Raw(buttonText)
</button>
</form>
}
{
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
<input type="hidden" name="choicekey" value="@item.Id" />
@{PayFormInputContent(item.BuyButtonText ?? Model.CustomButtonText, item.Price.Type, item.Price.Value, item.Price.Value);}
</form>
}
else
{
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false">
<input type="hidden" name="requiresRefundEmail" value="@Model.RequiresRefundEmail.ToString()" />
<button type="submit" name="choiceKey" class="js-add-cart btn btn-primary" value="@item.Id">
@Safe.Raw(buttonText)
</button>
</form>
}
}
@if (item.Inventory.HasValue)
{
@ -85,7 +82,12 @@
</div>
}
</div>
</div>
</main>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>
@functions {

View file

@ -1,18 +1,9 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
<div id="app" class="l-pos-wrapper" v-cloak>
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy v-on:submit="handleFormSubmit">
<div class="l-pos-header bg-primary py-3 px-3">
@if (!string.IsNullOrEmpty(Model.CustomLogoLink))
{
<img src="@Model.CustomLogoLink" height="40" asp-append-version="true" />
}
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 ref="display" class="l-pos-display pb-3 px-1"><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"
@ -23,14 +14,13 @@
<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" id="pay-button" type="submit" v-bind:disabled="payButtonLoading">
<div class="d-flex align-items-center justify-content-center mt-4 gap-3">
<div class="btn btn-outline-secondary btn-lg flex-fill" v-on:click="clearTotal">Clear</div>
<button class="btn btn-primary btn-lg flex-fill" id="pay-button" type="submit" v-bind:disabled="payButtonLoading">
<div v-if="payButtonLoading" class="spinner-border spinner-border-sm" id="pay-button-spinner" role="status">
<span class="sr-only">Loading...</span>
</div>
<b>Pay</b>
Pay
</button>
</div>
@ -81,29 +71,20 @@
v-on:click="removeTip"
><i class="fa fa-times"></i></a>
</div>
<div class="row">
<div class="d-flex align-items-center justify-content-center mt-2 gap-3">
@if (Model.CustomTipPercentages != null && Model.CustomTipPercentages.Length > 0)
{
@for (int i = 0; i < Model.CustomTipPercentages.Length; i++)
@foreach (var percentage in Model.CustomTipPercentages)
{
var percentage = Model.CustomTipPercentages[i];
<div class="@(Model.CustomTipPercentages.Length > 3 ? "col-4" : "col")">
<button
class="js-cart-tip-btn btn btn-lg btn-light w-100 border mb-2"
data-tip="@percentage"
v-on:click="tipClicked(@percentage)"
>
@percentage%
</button>
</div>
<button
class="js-cart-tip-btn btn btn-lg btn-light w-100 border mb-2"
data-tip="@percentage"
v-on:click="tipClicked(@percentage)"
>
@percentage%
</button>
}
}
</div>
}
<div class="text-center mt-4 mb-3 py-2">
<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>

View file

@ -1,17 +1,15 @@
@inject BTCPayServer.Services.ThemeSettings Theme
@inject IWebHostEnvironment WebHostEnvironment
@using BTCPayServer.Services.Apps
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.TagHelpers
@using Microsoft.AspNetCore.Hosting
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@using System.IO
@inject IWebHostEnvironment WebHostEnvironment
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{
ViewData["Title"] = Model.Title;
ViewData["Title"] = string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title;
Layout = null;
async Task<string> GetDynamicManifest(string title)
@ -33,69 +31,53 @@
return $"data:application/manifest+json,{Safe.Json(jObject)}";
}
}
<!DOCTYPE html>
<html class="h-100">
<html class="h-100" lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head>
<title>@Model.Title</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<partial name="LayoutHead"/>
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, Model.CustomCSSLink, Model.EmbeddedCSS)" />
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-icon" href="~/img/icons/icon-512x512.png">
<link rel="apple-touch-startup-image" href="~/img/splash.png">
<link rel="manifest" href="@(await GetDynamicManifest(Model.Title))">
<link href="~/main/bootstrap/bootstrap.css" asp-append-version="true" rel="stylesheet" />
<link href="~/vendor/font-awesome/css/font-awesome.css" asp-append-version="true" rel="stylesheet" />
<link href="~/vendor/flatpickr/flatpickr.css" asp-append-version="true" rel="stylesheet" />
<link href="~/main/fonts/OpenSans.css" asp-append-version="true" rel="stylesheet" />
<link href="~/main/layout.css" asp-append-version="true" rel="stylesheet" />
<link href="~/main/site.css" asp-append-version="true" rel="stylesheet" />
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true"/>
<link rel="manifest" href="@(await GetDynamicManifest(ViewData["Title"]!.ToString()))">
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" asp-append-version="true" />
}
<link href="~/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" asp-append-version="true" />
@if (Model.ViewType == PosViewType.Cart)
{
<link rel="stylesheet" href="~/cart/css/style.css" asp-append-version="true">
<script type="text/javascript">
var srvModel = @Safe.Json(Model);
</script>
<script>var srvModel = @Safe.Json(Model);</script>
<script src="~/vendor/jquery/jquery.min.js" asp-append-version="true"></script>
<script src="~/vendor/bootstrap/bootstrap.bundle.min.js" asp-append-version="true"></script>
<script src="~/cart/js/cart.js" asp-append-version="true"></script>
<script src="~/cart/js/cart.jquery.js" asp-append-version="true"></script>
}
@if (Model.ViewType == PosViewType.Light)
{
<link href="~/light-pos/styles/main.css" asp-append-version="true" rel="stylesheet" />
<script type="text/javascript">
var srvModel = @Safe.Json(Model);
</script>
<script>var srvModel = @Safe.Json(Model);</script>
<script src="~/vendor/jquery/jquery.min.js" asp-append-version="true"></script>
<script src="~/vendor/bootstrap/bootstrap.bundle.min.js" asp-append-version="true"></script>
<script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
<script src="~/light-pos/app.js" asp-append-version="true"></script>
}
<style>
.lead :last-child {
margin-bottom: 0;
}
.card-deck {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 1.5rem;
}
.card {
page-break-inside: avoid;
}
.card:only-of-type {
max-width: 320px;
margin: auto !important;
}
.js-cart-item-minus .fa,
.js-cart-item-plus .fa {
background: #fff;
@ -112,7 +94,7 @@
@Safe.Raw($"<style>{Model.EmbeddedCSS}</style>");
}
</head>
<body class="h-100">
<body class="min-vh-100">
@RenderBody()
</body>
</html>

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 84" role="img" alt="BTCPay Server" class="ms-1"><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="currentColor" 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="currentColor" 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="currentColor" class="logo-brand-light"/><path d="M10.066 31.725v20.553L24.01 42.006z" fill="currentColor" 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="currentColor" 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"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,16 @@
@inject IFileService FileService
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Extensions
@model (string Title, string LogoFileId)
@{
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
: null;
}
<header class="store-header">
@if (!string.IsNullOrEmpty(logoUrl))
{
<img src="@logoUrl" alt="@Model.Title" class="store-logo"/>
}
<h1 class="store-name">@Model.Title</h1>
</header>

View file

@ -1,7 +1,5 @@
@using BTCPayServer.Components.ThemeSwitch
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.Services.ThemeSettings Theme
@model BTCPayServer.Forms.Models.FormViewModel
@{
Layout = null;
@ -14,43 +12,37 @@
<partial name="LayoutHead"/>
<meta name="robots" content="noindex,nofollow">
</head>
<body>
<div class="min-vh-100 d-flex flex-column">
<main class="flex-grow-1 py-5">
<div class="container" style="max-width:720px;">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) {{"Margin", "mb-4"}})"/>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
}
<partial name="_FormTopMessages" model="@Model.Form"/>
<div class="d-flex flex-column justify-content-center gap-4">
<h1 class="h3 text-center">@ViewData["Title"]</h1>
<div class="bg-tile p-3 p-sm-4 rounded">
<form asp-action="SubmitForm" asp-route-formId="@Model.FormData.Id">
@if (!string.IsNullOrEmpty(Model.RedirectUrl))
{
<input type="hidden" asp-for="RedirectUrl" value="@Model.RedirectUrl"/>
}
<partial name="_Form" model="@Model.Form"/>
<input type="submit" class="btn btn-primary" name="command" value="Submit"/>
</form>
<body class="min-vh-100">
<div class="public-page-wrap flex-column">
<main class="flex-grow-1">
<div class="container" style="max-width:720px;">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) {{"Margin", "mb-4"}})"/>
@if (!ViewContext.ModelState.IsValid)
{
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
}
<partial name="_FormTopMessages" model="@Model.Form"/>
<div class="d-flex flex-column justify-content-center gap-4">
<h1 class="h3 text-center">@ViewData["Title"]</h1>
<div class="bg-tile p-3 p-sm-4 rounded">
<form asp-action="SubmitForm" asp-route-formId="@Model.FormData.Id">
@if (!string.IsNullOrEmpty(Model.RedirectUrl))
{
<input type="hidden" asp-for="RedirectUrl" value="@Model.RedirectUrl"/>
}
<partial name="_Form" model="@Model.Form"/>
<input type="submit" class="btn btn-primary" name="command" value="Submit"/>
</form>
</div>
</div>
</div>
</div>
</main>
<footer class="pt-2 pb-4 d-print-none">
<div class="container d-flex flex-wrap align-items-center justify-content-center">
<span class="text-muted mx-2">
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a>
</span>
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" responsive="none"/>
}
</div>
</footer>
</div>
<partial name="LayoutFoot"/>
</main>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>
<partial name="LayoutFoot"/>
</body>
</html>

View file

@ -1,11 +1,10 @@
@inject LanguageService LangService
@inject BTCPayServerEnvironment Env
@inject IFileService FileService
@inject IEnumerable<IUIExtension> UiExtensions
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@using BTCPayServer.Services
@using BTCPayServer.Abstractions.Contracts
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject LanguageService LangService
@inject BTCPayServerEnvironment Env
@inject IEnumerable<IUIExtension> UiExtensions
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@model PaymentModel
@{
Layout = null;
@ -13,9 +12,6 @@
var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
var paymentMethodCount = Model.AvailableCryptos.Count;
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
: null;
}
@functions {
private string PaymentMethodName(PaymentModel.AvailableCrypto pm)
@ -36,17 +32,11 @@
<partial name="LayoutHead"/>
<meta name="robots" content="noindex,nofollow">
<link href="~/checkout-v2/checkout.css" asp-append-version="true" rel="stylesheet" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId)" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, "", "")" />
</head>
<body class="min-vh-100">
<div id="Checkout-v2" class="wrap gap-4" v-cloak v-waitForT>
<header class="store-header">
@if (!string.IsNullOrEmpty(logoUrl))
{
<img src="@logoUrl" alt="@Model.StoreName" class="store-logo"/>
}
<h1 class="store-name">@Model.StoreName</h1>
</header>
<div id="Checkout-v2" class="public-page-wrap" v-cloak v-waitForT>
<partial name="_StoreHeader" model="(Model.StoreName, Model.LogoFileId)" />
<main class="shadow-lg">
<nav v-if="isModal">
<button type="button" v-if="isModal" id="close" v-on:click="close">
@ -164,13 +154,10 @@
{
<checkout-cheating invoice-id="@Model.InvoiceId" :btc-due="srvModel.btcDue" :is-paid="isPaid" :payment-method-id="pmId"></checkout-cheating>
}
<footer>
<div>
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
{{$t("powered_by")}}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 84" role="img" alt="BTCPay Server" class="ms-1"><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="currentColor" 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="currentColor" 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="currentColor" class="logo-brand-light"/><path d="M10.066 31.725v20.553L24.01 42.006z" fill="currentColor" 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="currentColor" 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"/></svg>
</a>
</div>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
{{$t("powered_by")}} <partial name="_StoreFooterLogo" />
</a>
@* TODO: Re-add this once checkout v2 has been translated
<select asp-for="DefaultLang" asp-items="@LangService.GetLanguageSelectListItems()" class="form-select w-auto" v-on:change="changeLanguage"></select>
*@

View file

@ -2,24 +2,20 @@
@using BTCPayServer.Client
@using BTCPayServer.Client.Models
@using BTCPayServer.Services.Rates
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject BTCPayServer.Services.ThemeSettings Theme
@inject CurrencyNameTable CurrencyNameTable
@using BTCPayServer.Abstractions.Contracts
@inject IFileService FileService
@{
Layout = null;
ViewData["Title"] = $"Receipt from {Model.StoreName}";
var isProcessing = Model.Status == InvoiceStatus.Processing;
var isSettled = Model.Status == InvoiceStatus.Settled;
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
: null;
}
<!DOCTYPE html>
<html lang="en" @(env.IsDeveloping ? " data-devenv" : "")>
<html lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head>
<partial name="LayoutHead" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, "", "")" />
<meta name="robots" content="noindex,nofollow">
@if (isProcessing)
{
@ -32,32 +28,25 @@
#posData td > table:last-child { margin-bottom: 0 !important; }
#posData table > tbody > tr:first-child > td > h4 { margin-top: 0 !important; }
</style>
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId)" />
</head>
<body>
<div class="min-vh-100 d-flex flex-column">
<main class="flex-grow-1 py-5">
<body class="min-vh-100">
<div class="public-page-wrap flex-column">
<main class="flex-grow-1">
<div class="container" style="max-width:720px;">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) { { "Margin", "mb-4" } })"/>
<div class="d-flex flex-column justify-content-center gap-4">
<header class="store-header">
@if (!string.IsNullOrEmpty(logoUrl))
{
<img src="@logoUrl" alt="@Model.StoreName" class="store-logo" />
}
<h1 class="store-name">@Model.StoreName</h1>
</header>
<partial name="_StoreHeader" model="(Model.StoreName, Model.LogoFileId)" />
<div id="InvoiceSummary" class="bg-tile p-3 p-sm-4 rounded d-flex flex-wrap align-items-center">
@if (isProcessing)
{
<div class="lead text-center text-muted py-5 px-4 fw-semibold" id="invoice-processing">
<div class="lead text-center p-4 fw-semibold" id="invoice-processing">
The invoice has detected a payment but is still waiting to be settled.
</div>
}
else if (!isSettled)
{
<div class="lead text-center text-muted py-5 px-4 fw-semibold" id="invoice-unsettled">
<div class="lead text-center p-4 fw-semibold" id="invoice-unsettled">
The invoice is not settled.
</div>
}
@ -173,21 +162,15 @@
</div>
</div>
</main>
<footer class="pt-2 pb-4 d-print-none">
<p class="container text-center" permission="@Policies.CanViewInvoices">
<footer class="store-footer">
<p permission="@Policies.CanViewInvoices">
<a asp-action="Invoice" asp-route-invoiceId="@Model.InvoiceId">
Admin details
</a>
</p>
<div class="container d-flex flex-wrap align-items-center justify-content-center">
<span class="text-muted mx-2">
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a>
</span>
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" />
}
</div>
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>
<partial name="LayoutFoot"/>

View file

@ -1,10 +1,8 @@
@using BTCPayServer.Services.Invoices
@using BTCPayServer.Client.Models
@using BTCPayServer.Client
@using BTCPayServer.Components.ThemeSwitch
@model BTCPayServer.Models.PaymentRequestViewModels.ViewPaymentRequestViewModel
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.Services.ThemeSettings Theme
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@{
ViewData["Title"] = Model.Title;
Layout = null;
@ -34,17 +32,11 @@
}
<!DOCTYPE html>
<html lang="en" @(env.IsDeveloping ? " data-devenv" : "")>
<html lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head>
<partial name="LayoutHead" />
<link href="~/vendor/font-awesome/css/font-awesome.min.css" asp-append-version="true" rel="stylesheet" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, Model.CustomCSSLink, Model.EmbeddedCSS)" />
<link href="~/vendor/bootstrap-vue/bootstrap-vue.css" asp-append-version="true" rel="stylesheet" />
<link href="~/main/site.css" asp-append-version="true" rel="stylesheet" />
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" asp-append-version="true" />
}
<script type="text/javascript">
var srvModel = @Safe.Json(Model);
</script>
@ -57,7 +49,6 @@
<script src="~/payment-request/helpers/math.js" asp-append-version="true"></script>
<script src="~/payment-request/services/listener.js" asp-append-version="true"></script>
<script src="~/modal/btcpay.js" asp-append-version="true"></script>
@Safe.Raw(Model.EmbeddedCSS)
<style>
.invoice { margin-top: var(--btcpay-space-s); }
.invoice + .invoice { margin-top: var(--btcpay-space-m); }
@ -69,8 +60,8 @@
}
</style>
</head>
<body>
<div id="app" class="min-vh-100 d-flex flex-column">
<body class="min-vh-100">
<div id="app" class="d-flex flex-column min-vh-100 pb-l">
<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)">
@ -378,21 +369,15 @@
</div>
</div>
</main>
<footer class="pt-2 pb-4 d-print-none">
<p class="container text-center" permission="@Policies.CanModifyStoreSettings">
<footer class="store-footer">
<p permission="@Policies.CanModifyStoreSettings">
<a asp-controller="UIPaymentRequest" asp-action="EditPaymentRequest" asp-route-storeId="@Model.StoreId" asp-route-payReqId="@Model.Id">
Edit payment request
</a>
</p>
<div class="container d-flex flex-wrap align-items-center justify-content-center">
<span class="text-muted mx-2">
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a>
</span>
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" />
}
</div>
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>
<partial name="LayoutFoot"/>

View file

@ -1,10 +1,11 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@model BTCPayServer.Controllers.ShowLightningNodeInfoViewModel
@{
Layout = null;
ViewData["Title"] = $"{Model.StoreName} {Model.CryptoCode} Lightning Node";
}
<!DOCTYPE html>
<html>
<html lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head>
<partial name="LayoutHead" />
<link href="~/main/qrcode.css" rel="stylesheet" asp-append-version="true" />

View file

@ -1,5 +1,4 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject BTCPayServer.Services.ThemeSettings Theme
@inject BTCPayNetworkProvider BtcPayNetworkProvider
@using BTCPayServer.Client
@using BTCPayServer.Components.ThemeSwitch
@ -43,21 +42,14 @@
<html lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head>
<partial name="LayoutHead" />
<link href="~/vendor/font-awesome/css/font-awesome.min.css" asp-append-version="true" rel="stylesheet" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, Model.CustomCSSLink, Model.EmbeddedCSS)" />
<link href="~/vendor/bootstrap-vue/bootstrap-vue.css" asp-append-version="true" rel="stylesheet" />
<link href="~/main/site.css" asp-append-version="true" rel="stylesheet" />
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" asp-append-version="true" />
}
@Safe.Raw(Model.EmbeddedCSS)
<style>
.no-marker > ul { list-style-type: none; }
</style>
</head>
<body>
<div class="min-vh-100 d-flex flex-column">
<body class="min-vh-100">
<div id="app" class="d-flex flex-column min-vh-100 pb-l">
@if (Model.IsPending)
{
<nav class="btcpay-header navbar sticky-top py-3 py-lg-4 d-print-none">
@ -215,24 +207,15 @@
</div>
</div>
</main>
<footer class="pt-2 pb-4 d-print-none">
<p class="container text-center" permission="@Policies.CanViewStoreSettings">
<a asp-action="EditPullPayment"
asp-controller="UIPullPayment"
asp-route-storeId="@Model.StoreId"
asp-route-pullPaymentId="@Model.Id">
<footer class="store-footer">
<p permission="@Policies.CanViewStoreSettings">
<a asp-action="EditPullPayment" asp-controller="UIPullPayment" asp-route-storeId="@Model.StoreId" asp-route-pullPaymentId="@Model.Id">
Edit pull payment
</a>
</p>
<div class="container d-flex flex-wrap align-items-center justify-content-center">
<span class="text-muted mx-2">
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a>
</span>
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" />
}
</div>
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div>
<partial name="LayoutFoot" />

View file

@ -51,10 +51,6 @@
background-color: #dee2e6;
}
#js-cart-confirm {
border-radius: 0;
}
.js-search-reset {
position: absolute;
right: 0px;
@ -74,8 +70,10 @@
}
#sidebar {
width: 400px;
position: fixed;
width: 400px;
display: flex;
flex-direction: column;
top: 0;
right: 0;
height: 100vh;
@ -85,6 +83,7 @@
color: var(--btcpay-white);
background: var(--btcpay-bg-dark);
transition: all 0.3s;
padding-bottom: var(--btcpay-space-l);
-webkit-overflow-scrolling: touch;
}

View file

@ -8,12 +8,8 @@
--border-radius: var(--btcpay-border-radius-l);
--wrap-max-width: 400px;
}
.wrap {
display: flex;
.public-page-wrap {
flex-direction: column;
min-height: 100vh;
margin: 0 auto;
padding: var(--btcpay-space-l); var(--btcpay-space-m);
max-width: var(--wrap-max-width);
}
main {
@ -222,27 +218,8 @@ section dl > div dd {
#result #expired .top .icn .icon {
color: var(--btcpay-body-text-muted);
}
footer {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--btcpay-space-m);
margin-top: auto;
padding: var(--section-padding) var(--section-padding) 0;
}
footer,
footer a,
#DefaultLang {
color: var(--btcpay-body-text-muted);
}
footer a {
transition-duration: unset;
}
footer a svg {
height: 2rem;
width: 4rem;
}
#DefaultLang {
background-color: transparent;
box-shadow: none;
border: none;
@ -250,25 +227,11 @@ footer a svg {
cursor: pointer;
margin-left: -4.5rem; /* Adjust for visual center */
}
footer a:hover,
#DefaultLang:hover {
color: var(--btcpay-body-text-hover);
}
footer a:hover .logo-brand-light {
color: var(--btcpay-brand-secondary);
}
footer a:hover .logo-brand-medium {
color: var(--btcpay-brand-primary);
}
footer a:hover .logo-brand-dark {
color: var(--btcpay-brand-tertiary);
}
@media (max-width: 400px) {
.wrap {
padding-left: 0;
padding-right: 0;
}
main {
border-radius: 0;
}

View file

@ -25,10 +25,6 @@
line-height: 80px;
}
.l-pos-keypad {
margin-left: 2%;
}
.l-pos-keypad .btn {
width: 32%;
margin-right: 1%;
@ -40,14 +36,6 @@
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;
}

View file

@ -205,29 +205,6 @@ h2 small .fa-question-circle-o {
margin-top: .5rem;
}
/* Store header */
.store-header {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--btcpay-space-s);
}
.store-logo {
--logo-size: 3rem;
--logo-bg: transparent;
--logo-radius: 50%;
width: var(--logo-size);
height: var(--logo-size);
background: var(--logo-bg);
border-radius: var(--logo-radius);
}
.store-name {
font-size: 1.3rem;
}
/* Print */
@media print {
.table td,
@ -626,3 +603,73 @@ svg.icon-note {
input:checked + .btcpay-list-select-item {
border-color: var(--btcpay-form-border-focus);
}
/* Public pages */
.public-page-wrap {
display: flex;
gap: 1.5rem;
min-height: 100vh;
margin: 0 auto;
padding: var(--btcpay-space-l) var(--btcpay-space-m);
}
@media (max-width: 400px) {
.public-page-wrap {
padding-left: 0;
padding-right: 0;
}
}
.store-header {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--btcpay-space-s);
}
.store-logo {
--logo-size: 3rem;
--logo-bg: transparent;
--logo-radius: 50%;
width: var(--logo-size);
height: var(--logo-size);
background: var(--logo-bg);
border-radius: var(--logo-radius);
}
.store-name {
font-size: 1.3rem;
}
.store-footer {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--btcpay-space-m);
margin-top: auto;
color: var(--btcpay-body-text-muted);
padding: 1.5rem 1.5rem 0;
}
.store-footer,
.store-footer a {
color: var(--btcpay-body-text-muted);
}
.store-footer a {
transition-duration: unset;
}
.store-footer a svg {
height: 2rem;
width: 4rem;
}
.store-footer a:hover {
color: var(--btcpay-body-text-hover);
}
.store-footer a:hover .logo-brand-light {
color: var(--btcpay-brand-secondary);
}
.store-footer a:hover .logo-brand-medium {
color: var(--btcpay-brand-primary);
}
.store-footer a:hover .logo-brand-dark {
color: var(--btcpay-brand-tertiary);
}