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
30 changed files with 611 additions and 646 deletions

View File

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

View File

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

View File

@@ -140,6 +140,10 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
public DateTime? ExpiryDate { get; set; } public DateTime? ExpiryDate { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Description { 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 EmbeddedCSS { get; set; }
public string CustomCSSLink { get; set; } public string CustomCSSLink { get; set; }

View File

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

View File

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

View File

@@ -17,6 +17,10 @@ namespace BTCPayServer.Plugins.Crowdfund.Models
public string Title { get; set; } public string Title { get; set; }
public string Description { get; set; } public string Description { get; set; }
public string MainImageUrl { 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 EmbeddedCSS { get; set; }
public string CustomCSSLink { get; set; } public string CustomCSSLink { get; set; }
public DateTime? StartDate { 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 return View($"PointOfSale/Public/{viewType}", new ViewPointOfSaleViewModel
{ {
Title = settings.Title, Title = settings.Title,
StoreName = store.StoreName,
BrandColor = storeBlob.BrandColor,
CssFileId = storeBlob.CssFileId,
LogoFileId = storeBlob.LogoFileId,
Step = step.ToString(CultureInfo.InvariantCulture), Step = step.ToString(CultureInfo.InvariantCulture),
ViewType = (PosViewType)viewType, ViewType = (PosViewType)viewType,
ShowCustomAmount = settings.ShowCustomAmount, ShowCustomAmount = settings.ShowCustomAmount,

View File

@@ -41,8 +41,11 @@ namespace BTCPayServer.Plugins.PointOfSale.Models
public bool SymbolSpace { get; set; } 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 CurrencyInfoData CurrencyInfo { get; set; }
public PosViewType ViewType { get; set; } public PosViewType ViewType { get; set; }
public bool ShowCustomAmount { get; set; } public bool ShowCustomAmount { get; set; }
public bool ShowDiscount { get; set; } public bool ShowDiscount { get; set; }

View File

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

View File

@@ -1,16 +1,15 @@
@model BTCPayServer.Plugins.Crowdfund.Models.ViewCrowdfundViewModel @model BTCPayServer.Plugins.Crowdfund.Models.ViewCrowdfundViewModel
@using BTCPayServer.Plugins.Crowdfund.Models @using BTCPayServer.Plugins.Crowdfund.Models
@inject BTCPayServer.Services.ThemeSettings Theme @inject BTCPayServer.Services.BTCPayServerEnvironment Env
@{ @{
ViewData["Title"] = Model.Title; ViewData["Title"] = Model.Title;
Layout = null; Layout = null;
} }
<!DOCTYPE html> <!DOCTYPE html>
<html class="h-100"> <html class="h-100" @(Env.IsDeveloping ? " data-devenv" : "")>
<head> <head>
<partial name="LayoutHead"/> <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="~/vendor/bootstrap-vue/bootstrap-vue.css" asp-append-version="true" rel="stylesheet" />
<link href="~/crowdfund/styles/main.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> <vc:ui-extension-point location="crowdfund-head" model="@Model"></vc:ui-extension-point>
</head> </head>
<body> <body class="min-vh-100">
@if (!Model.Enabled) @if (!Model.Enabled)
{ {
<div class="alert alert-warning text-center sticky-top mb-0 rounded-0" role="alert"> <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> <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="public-page-wrap flex-column container" id="app" @(Model.SimpleDisplay ? "" : "v-cloak")>
<div class="card w-100 p-0 mx-0"> <div class="row h-100 w-100 py-sm-0 py-md-4 mx-0">
@if (!string.IsNullOrEmpty(Model.MainImageUrl)) <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)
{ {
<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"> <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"/>
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>
} }
<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) @if (Model.TargetAmount.HasValue)
{ {
<span v-if="srvModel.targetAmount" class="mt-3" id="crowdfund-header-target"> <div class="progress rounded-pill mx-3" v-if="srvModel.targetAmount" id="crowdfund-progress-bar">
<h3 class="d-inline-block"> <div class="progress-bar bg-primary"
<span class="badge bg-info px-3" v-text="`${targetAmount} ${targetCurrency}`">@Math.Round(Model.TargetAmount.GetValueOrDefault(0)) @Model.TargetCurrency</span> role="progressbar"
</h3> style="width:@(Model.Info.ProgressPercentage + "%")"
@if (Model.ResetEveryAmount > 0 && !Model.NeverReset) :aria-valuenow="srvModel.info.progressPercentage"
{ v-bind:style="{ width: srvModel.info.progressPercentage + '%' }"
<span v-if="srvModel.resetEvery !== 'Never'" aria-valuemin="0"
class="h5 ms-2" id="crowdfund-progress-bar-confirmed-bar"
v-b-tooltip v-b-tooltip
:title="'Goal resets every ' + srvModel.resetEveryAmount + ' ' + srvModel.resetEvery + ((srvModel.resetEveryAmount>1)?'s': '')"> :title="parseFloat(srvModel.info.progressPercentage).toFixed(2) + '% contributions'"
Dynamic aria-valuemax="100">
</span> </div>
} <div class="progress-bar bg-warning"
@if (Model.EnforceTargetAmount) role="progressbar"
{ id="crowdfund-progress-bar-pending-bar"
<span v-if="srvModel.enforceTargetAmount" class="h5 ms-2"> style="width:@(Model.Info.PendingProgressPercentage + "%")"
Hardcap Goal :aria-valuenow="srvModel.info.pendingProgressPercentage"
<span class="fa fa-question-circle" v-b-tooltip title="No contributions allowed after the goal has been reached"></span> v-bind:style="{ width: srvModel.info.pendingProgressPercentage + '%' }"
</span> v-b-tooltip
} :title="parseFloat(srvModel.info.pendingProgressPercentage).toFixed(2) + '% contributions pending confirmation'"
else aria-valuemin="0"
{ aria-valuemax="100">
<span v-if="!srvModel.enforceTargetAmount" class="h5 ms-2"> </div>
Softcap Goal </div>
<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) <div class="card-body">
{ <div class="row py-2 text-center crowdfund-stats">
<div class="progress rounded-pill mx-3" v-if="srvModel.targetAmount" id="crowdfund-progress-bar"> <div class="col-sm border-end p-3 text-center" id="crowdfund-body-raised-amount">
<div class="progress-bar bg-primary" <h3 v-text="`${raisedAmount} ${targetCurrency}`">@Math.Round(Model.Info.CurrentAmount + Model.Info.CurrentPendingAmount, Model.CurrencyData.Divisibility) @Model.TargetCurrency</h3>
role="progressbar" <h5 class="text-muted fst-italic mb-0">Raised</h5>
style="width:@(Model.Info.ProgressPercentage + "%")" <b-tooltip target="crowdfund-body-raised-amount" v-if="paymentStats && paymentStats.length > 0" class="only-for-js">
:aria-valuenow="srvModel.info.progressPercentage" <ul class="p-0 text-uppercase">
v-bind:style="{ width: srvModel.info.progressPercentage + '%' }" <li v-for="stat of paymentStats" class="list-unstyled">
aria-valuemin="0" {{stat.label}} <span v-if="stat.lightning" class="fa fa-bolt"></span> {{stat.value}}
id="crowdfund-progress-bar-confirmed-bar" </li>
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>
}
</ul> </ul>
</b-tooltip> </b-tooltip>
</div> </div>
}
</div>
<div class="card-title text-center" id="crowdfund-body-header"> <div class="col-sm border-end p-3 text-center" id="crowdfund-body-goal-raised">
@if (!string.IsNullOrEmpty(Model.Tagline)) <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>
<h2 class="h3 my-4 fw-normal" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2> <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': ''}}
<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> </b-tooltip>
</div> </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"> <div class="card-title text-center" id="crowdfund-body-header">
<b-tabs> @if (!string.IsNullOrEmpty(Model.Tagline))
<b-tab title="Details" active> {
<div class="row mt-3"> <h2 class="h3 my-4 fw-normal" v-if="srvModel.tagline" v-text="srvModel.tagline">@Model.Tagline</h2>
<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"> <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>
@Safe.Raw(Model.Description) </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> </div>
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container"> </b-tab>
<contribute :target-currency="srvModel.targetCurrency" <b-tab title="Discussion">
:display-perks-ranking="srvModel.displayPerksRanking" <div id="disqus_thread" class="mt-3"></div>
:perks-value="srvModel.perksValue" </b-tab>
:active="active" </b-tabs>
:loading="loading" </template>
:in-modal="false" <template v-else>
:perks="perks"> <div class="row mt-2">
</contribute> <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> </div>
</b-tab> <div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container">
<b-tab title="Discussion"> <contribute :target-currency="srvModel.targetCurrency"
<div id="disqus_thread" class="mt-3"></div> :loading="loading"
</b-tab> :display-perks-ranking="srvModel.displayPerksRanking"
</b-tabs> :perks-value="srvModel.perksValue"
</template> :active="active"
<template v-else> :in-modal="false"
<div class="row mt-2"> :perks="perks">
<div class="col-md-8 col-sm-12" id="crowdfund-body-description-container"> </contribute>
<div class="card-text overflow-hidden" v-html="srvModel.description" id="crowdfund-body-description">
@Safe.Raw(Model.Description)
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-12" id="crowdfund-body-contribution-container"> </template>
<contribute :target-currency="srvModel.targetCurrency" <noscript>
:loading="loading" <div class="row">
:display-perks-ranking="srvModel.displayPerksRanking" <div class="col-md-8 col-sm-12">
:perks-value="srvModel.perksValue" <div class="card-text overflow-hidden">@Safe.Raw(Model.Description)</div>
:active="active" </div>
:in-modal="false" <div class="col-md-4 col-sm-12">
:perks="perks"> <partial
</contribute> name="Crowdfund/Public/ContributeForm"
model="@(new ContributeToCrowdfund { ViewCrowdfundViewModel = Model, RedirectToCheckout = true })">
</partial>
</div>
</div> </div>
</div> </noscript>
</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>
</div> </div>
<div class="form-check me-3 my-0 only-for-js" v-if="srvModel.soundsEnabled|| sound"> <div class="card-footer text-muted d-flex flex-wrap align-items-center">
<input class="form-check-input" type="checkbox" id="cbSounds" v-model="sound"> <div class="me-3" v-text="`Updated ${lastUpdated}`">Updated @Model.Info.LastUpdated</div>
<label class="form-check-label" for="cbSounds">Sounds</label> <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>
</div> </div>
</div> <b-modal title="Contribute" v-model="contributeModalOpen" size="lg" ok-only="true" ok-variant="secondary" ok-title="Close" ref="modalContribute">
<b-modal title="Contribute" v-model="contributeModalOpen" size="lg" ok-only="true" ok-variant="secondary" ok-title="Close" ref="modalContribute"> <contribute v-if="contributeModalOpen"
<contribute v-if="contributeModalOpen" :target-currency="srvModel.targetCurrency"
:target-currency="srvModel.targetCurrency" :active="active"
:active="active" :perks="srvModel.perks"
:perks="srvModel.perks" :loading="loading"
:loading="loading" :in-modal="true">
:in-modal="true"> </contribute>
</contribute> </b-modal>
</b-modal>
<footer class="store-footer">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</div> </div>
<template id="perks-template"> <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 BTCPayServer.Abstractions.Extensions
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Abstractions.Contracts @using BTCPayServer.Abstractions.Contracts
@@ -24,3 +24,12 @@
{ {
<link href="@cssUrl" asp-append-version="true" rel="stylesheet" /> <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 @using BTCPayServer.Plugins.PointOfSale.Models
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel @model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{ @{
Layout = "PointOfSale/Public/_LayoutPos"; Layout = "PointOfSale/Public/_Layout";
var customTipPercentages = Model.CustomTipPercentages; var customTipPercentages = Model.CustomTipPercentages;
var anyInventoryItems = Model.Items.Any(item => item.Inventory.HasValue); var anyInventoryItems = Model.Items.Any(item => item.Inventory.HasValue);
} }
@@ -189,35 +189,28 @@
<!-- Page Content --> <!-- Page Content -->
<div id="content"> <div id="content">
<div class="p-2 p-sm-4"> <div class="p-2 p-sm-4">
<div class="row"> <partial name="_StatusMessage" />
<partial name="_StatusMessage" /> <div class="d-flex gap-3 mb-4">
<div class="flex-fill position-relative">
<div class="col-sm-4 col-lg-2 order-sm-last text-end mb-2"> <input type="text" class="js-search form-control form-control-lg" placeholder="Find product">
<a class="js-cart btn btn-lg btn-outline-primary" href="#"> <a class="js-search-reset btn btn-lg btn-link text-black" href="#">
<i class="fa fa-shopping-basket"></i>&nbsp; <i class="fa fa-times-circle fa-lg"></i>
<span class="badge bg-light rounded-pill">
<span id="js-cart-items">0</span>
</span>
</a> </a>
</div> </div>
<div class="col-sm-8 col-lg-10 mb-2"> <a class="js-cart btn btn-lg btn-outline-primary text-nowrap" href="#">
<div class="mb-2 position-relative"> <i class="fa fa-shopping-basket"></i>&nbsp;
<input type="text" class="js-search form-control form-control-lg" placeholder="Find product"> <span class="badge bg-light rounded-pill">
<a class="js-search-reset btn btn-lg btn-link text-black" href="#"> <span id="js-cart-items">0</span>
<i class="fa fa-times-circle fa-lg"></i> </span>
</a> </a>
</div>
</div>
</div> </div>
@if (!string.IsNullOrEmpty(Model.Description)) @if (!string.IsNullOrEmpty(Model.Description))
{ {
<div class="row"> <div class="lead text-center mt-3">@Safe.Raw(Model.Description)</div>
<div class="overflow-hidden col-12">@Safe.Raw(Model.Description)</div>
</div>
} }
</div> </div>
<div id="js-pos-list" class="text-center mx-auto px-4"> <div id="js-pos-list" class="text-center mx-auto px-2 px-sm-4 py-4 py-sm-2">
<div class="card-deck my-3"> <div class="card-deck">
@for (var index = 0; index < Model.Items.Length; index++) @for (var index = 0; index < Model.Items.Length; index++)
{ {
var item = Model.Items[index]; var item = Model.Items[index];
@@ -275,8 +268,8 @@
<!-- Sidebar --> <!-- Sidebar -->
<nav id="sidebar"> <nav id="sidebar">
<div class="bg-primary p-3 clearfix"> <div class="d-flex align-items-center pt-4 p-2">
<h3 class="text-white m-0 pull-left">Cart</h3> <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="#"> <a class="js-cart btn btn-sm bg-white text-black pull-right ms-5" href="#">
<i class="fa fa-times fa-lg"></i> <i class="fa fa-times fa-lg"></i>
</a> </a>
@@ -302,12 +295,14 @@
<thead></thead> <thead></thead>
</table> </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"> <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">
<b>Confirm</b> Confirm
</button> </button>
<div class="text-center mb-5 pb-5"> <footer class="store-footer">
<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> <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
</div> Powered by <partial name="_StoreFooterLogo" />
</a>
</footer>
</nav> </nav>
</div> </div>

View File

@@ -1,18 +1,26 @@
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel @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")) @if (Context.Request.Query.ContainsKey("simple"))
{ {
<partial name="PointOfSale/Public/MinimalLight" model="Model" />
}
else
{
<noscript>
<partial name="PointOfSale/Public/MinimalLight" model="Model" /> <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="container p-0 l-pos-wrapper my-0 mx-auto">
<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="py-5 px-3"> <div class="py-5 px-3">
<form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy> <form method="post" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
<div class="input-group"> <div class="input-group">
@@ -14,9 +7,5 @@
<button class="btn btn-primary" type="submit">Pay</button> <button class="btn btn-primary" type="submit">Pay</button>
</div> </div>
</form> </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>
</div> </div>

View File

@@ -14,12 +14,10 @@
margin-bottom: 0; margin-bottom: 0;
} }
} }
.card { display: inline-block; width: 300px; page-break-inside: avoid; margin: 2em 1em 0 1em; }
</style> </style>
@{ @{
var store = await StoreRepository.FindStore(Model.StoreId); var store = await StoreRepository.FindStore(Model.StoreId);
Layout = "PointOfSale/Public/_LayoutPos"; Layout = "PointOfSale/Public/_Layout";
Context.Request.Query.TryGetValue("cryptocode", out var cryptoCodeValues); Context.Request.Query.TryGetValue("cryptocode", out var cryptoCodeValues);
var cryptoCode = cryptoCodeValues.FirstOrDefault() ?? "BTC"; var cryptoCode = cryptoCodeValues.FirstOrDefault() ?? "BTC";
var supported = store.GetSupportedPaymentMethods(BTCPayNetworkProvider).OfType<LNURLPaySupportedPaymentMethod>().OrderBy(method => method.CryptoCode == cryptoCode).FirstOrDefault(); var supported = store.GetSupportedPaymentMethods(BTCPayNetworkProvider).OfType<LNURLPaySupportedPaymentMethod>().OrderBy(method => method.CryptoCode == cryptoCode).FirstOrDefault();
@@ -38,16 +36,25 @@
</a> </a>
</div> </div>
} }
<div class="container d-flex"> else
<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> <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">
@if (!string.IsNullOrEmpty(Model.Description)) <button type="button" class="btn btn-info me-4 border border-light" onclick="window.print()">
{ <i class="fa fa-print"></i>&nbsp;Print
<div class="row"> </button>
<div class="overflow-hidden col-12">@Safe.Raw(Model.Description)</div> This view is intended for printing only —
</div>
} <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 (supported is not null)
{ {
if (Model.ShowCustomAmount) if (Model.ShowCustomAmount)
@@ -67,17 +74,10 @@
} }
}).ToArray(); }).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"> <div class="card-deck mx-auto">
@for (int x = 0; x < Model.Items.Length; x++) @for (var x = 0; x < Model.Items.Length; x++)
{ {
var item = Model.Items[x]; var item = Model.Items[x];
<div class="card" data-id="@x"> <div class="card" data-id="@x">
@@ -115,12 +115,19 @@
ItemCode = item.Id ItemCode = item.Id
}, Context.Request.Scheme, Context.Request.Host.ToString())); }, Context.Request.Scheme, Context.Request.Host.ToString()));
var lnUrl = LNURL.EncodeUri(lnurlEndpoint, "payRequest", supported.UseBech32Scheme); 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>
} }
</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> </div>

View File

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

View File

@@ -1,18 +1,9 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel @model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
<div id="app" class="l-pos-wrapper" v-cloak> <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"> <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"> <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>
@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 class="l-pos-keypad"> <div class="l-pos-keypad">
<template <template
v-for="(key, index) in keys" v-for="(key, index) in keys"
@@ -23,14 +14,13 @@
<div v-else class="btn btn-empty"></div> <div v-else class="btn btn-empty"></div>
</template> </template>
</div> </div>
<div class="d-flex align-items-center justify-content-center mt-4 gap-3">
<div class="l-pos-controls mt-2"> <div class="btn btn-outline-secondary btn-lg flex-fill" v-on:click="clearTotal">Clear</div>
<div class="btn btn-outline-secondary btn-lg mb-0" v-on:click="clearTotal">Clear</div> <button class="btn btn-primary btn-lg flex-fill" id="pay-button" type="submit" v-bind:disabled="payButtonLoading">
<button class="btn btn-primary btn-lg mb-0" 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"> <div v-if="payButtonLoading" class="spinner-border spinner-border-sm" id="pay-button-spinner" role="status">
<span class="sr-only">Loading...</span> <span class="sr-only">Loading...</span>
</div> </div>
<b>Pay</b> Pay
</button> </button>
</div> </div>
@@ -81,29 +71,20 @@
v-on:click="removeTip" v-on:click="removeTip"
><i class="fa fa-times"></i></a> ><i class="fa fa-times"></i></a>
</div> </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) @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]; <button
class="js-cart-tip-btn btn btn-lg btn-light w-100 border mb-2"
<div class="@(Model.CustomTipPercentages.Length > 3 ? "col-4" : "col")"> data-tip="@percentage"
<button v-on:click="tipClicked(@percentage)"
class="js-cart-tip-btn btn btn-lg btn-light w-100 border mb-2" >
data-tip="@percentage" @percentage%
v-on:click="tipClicked(@percentage)" </button>
>
@percentage%
</button>
</div>
} }
} }
</div> </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> </div>

View File

@@ -1,17 +1,15 @@
@inject BTCPayServer.Services.ThemeSettings Theme
@inject IWebHostEnvironment WebHostEnvironment
@using BTCPayServer.Services.Apps @using BTCPayServer.Services.Apps
@using BTCPayServer.Abstractions.Extensions @using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.TagHelpers @using BTCPayServer.Abstractions.TagHelpers
@using Microsoft.AspNetCore.Hosting @using Microsoft.AspNetCore.Hosting
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq @using Newtonsoft.Json.Linq
@using System.IO @using System.IO
@inject IWebHostEnvironment WebHostEnvironment
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel @model BTCPayServer.Plugins.PointOfSale.Models.ViewPointOfSaleViewModel
@{ @{
ViewData["Title"] = Model.Title; ViewData["Title"] = string.IsNullOrEmpty(Model.Title) ? Model.StoreName : Model.Title;
Layout = null; Layout = null;
async Task<string> GetDynamicManifest(string title) async Task<string> GetDynamicManifest(string title)
@@ -33,69 +31,53 @@
return $"data:application/manifest+json,{Safe.Json(jObject)}"; return $"data:application/manifest+json,{Safe.Json(jObject)}";
} }
} }
<!DOCTYPE html> <!DOCTYPE html>
<html class="h-100"> <html class="h-100" lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head> <head>
<title>@Model.Title</title> <partial name="LayoutHead"/>
<meta charset="utf-8" /> <partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, Model.CustomCSSLink, Model.EmbeddedCSS)" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-icon" href="~/img/icons/icon-512x512.png"> <link rel="apple-touch-icon" href="~/img/icons/icon-512x512.png">
<link rel="apple-touch-startup-image" href="~/img/splash.png"> <link rel="apple-touch-startup-image" href="~/img/splash.png">
<link rel="manifest" href="@(await GetDynamicManifest(Model.Title))"> <link rel="manifest" href="@(await GetDynamicManifest(ViewData["Title"]!.ToString()))">
<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"/>
@if (Model.CustomCSSLink != null) @if (Model.CustomCSSLink != null)
{ {
<link href="@Model.CustomCSSLink" rel="stylesheet" asp-append-version="true" /> <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) @if (Model.ViewType == PosViewType.Cart)
{ {
<link rel="stylesheet" href="~/cart/css/style.css" asp-append-version="true"> <link rel="stylesheet" href="~/cart/css/style.css" asp-append-version="true">
<script type="text/javascript"> <script>var srvModel = @Safe.Json(Model);</script>
var srvModel = @Safe.Json(Model);
</script>
<script src="~/vendor/jquery/jquery.min.js" asp-append-version="true"></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/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.js" asp-append-version="true"></script>
<script src="~/cart/js/cart.jquery.js" asp-append-version="true"></script> <script src="~/cart/js/cart.jquery.js" asp-append-version="true"></script>
} }
@if (Model.ViewType == PosViewType.Light) @if (Model.ViewType == PosViewType.Light)
{ {
<link href="~/light-pos/styles/main.css" asp-append-version="true" rel="stylesheet" /> <link href="~/light-pos/styles/main.css" asp-append-version="true" rel="stylesheet" />
<script>var srvModel = @Safe.Json(Model);</script>
<script type="text/javascript">
var srvModel = @Safe.Json(Model);
</script>
<script src="~/vendor/jquery/jquery.min.js" asp-append-version="true"></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/bootstrap/bootstrap.bundle.min.js" asp-append-version="true"></script>
<script src="~/vendor/vuejs/vue.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> <script src="~/light-pos/app.js" asp-append-version="true"></script>
} }
<style> <style>
.lead :last-child {
margin-bottom: 0;
}
.card-deck { .card-deck {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 1.5rem; grid-gap: 1.5rem;
} }
.card {
page-break-inside: avoid;
}
.card:only-of-type { .card:only-of-type {
max-width: 320px; max-width: 320px;
margin: auto !important; margin: auto !important;
} }
.js-cart-item-minus .fa, .js-cart-item-minus .fa,
.js-cart-item-plus .fa { .js-cart-item-plus .fa {
background: #fff; background: #fff;
@@ -112,7 +94,7 @@
@Safe.Raw($"<style>{Model.EmbeddedCSS}</style>"); @Safe.Raw($"<style>{Model.EmbeddedCSS}</style>");
} }
</head> </head>
<body class="h-100"> <body class="min-vh-100">
@RenderBody() @RenderBody()
</body> </body>
</html> </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 @using Microsoft.AspNetCore.Mvc.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env @inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.Services.ThemeSettings Theme
@model BTCPayServer.Forms.Models.FormViewModel @model BTCPayServer.Forms.Models.FormViewModel
@{ @{
Layout = null; Layout = null;
@@ -14,43 +12,37 @@
<partial name="LayoutHead"/> <partial name="LayoutHead"/>
<meta name="robots" content="noindex,nofollow"> <meta name="robots" content="noindex,nofollow">
</head> </head>
<body> <body class="min-vh-100">
<div class="min-vh-100 d-flex flex-column"> <div class="public-page-wrap flex-column">
<main class="flex-grow-1 py-5"> <main class="flex-grow-1">
<div class="container" style="max-width:720px;"> <div class="container" style="max-width:720px;">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) {{"Margin", "mb-4"}})"/> <partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) {{"Margin", "mb-4"}})"/>
@if (!ViewContext.ModelState.IsValid) @if (!ViewContext.ModelState.IsValid)
{ {
<div asp-validation-summary="ModelOnly" class="text-danger"></div> <div asp-validation-summary="ModelOnly" class="text-danger"></div>
} }
<partial name="_FormTopMessages" model="@Model.Form"/> <partial name="_FormTopMessages" model="@Model.Form"/>
<div class="d-flex flex-column justify-content-center gap-4"> <div class="d-flex flex-column justify-content-center gap-4">
<h1 class="h3 text-center">@ViewData["Title"]</h1> <h1 class="h3 text-center">@ViewData["Title"]</h1>
<div class="bg-tile p-3 p-sm-4 rounded"> <div class="bg-tile p-3 p-sm-4 rounded">
<form asp-action="SubmitForm" asp-route-formId="@Model.FormData.Id"> <form asp-action="SubmitForm" asp-route-formId="@Model.FormData.Id">
@if (!string.IsNullOrEmpty(Model.RedirectUrl)) @if (!string.IsNullOrEmpty(Model.RedirectUrl))
{ {
<input type="hidden" asp-for="RedirectUrl" value="@Model.RedirectUrl"/> <input type="hidden" asp-for="RedirectUrl" value="@Model.RedirectUrl"/>
} }
<partial name="_Form" model="@Model.Form"/> <partial name="_Form" model="@Model.Form"/>
<input type="submit" class="btn btn-primary" name="command" value="Submit"/> <input type="submit" class="btn btn-primary" name="command" value="Submit"/>
</form> </form>
</div>
</div> </div>
</div> </div>
</div> </main>
</main> <footer class="store-footer">
<footer class="pt-2 pb-4 d-print-none"> <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
<div class="container d-flex flex-wrap align-items-center justify-content-center"> Powered by <partial name="_StoreFooterLogo" />
<span class="text-muted mx-2"> </a>
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a> </footer>
</span> </div>
@if (!Theme.CustomTheme) <partial name="LayoutFoot"/>
{
<vc:theme-switch css-class="text-muted mx-2" responsive="none"/>
}
</div>
</footer>
</div>
<partial name="LayoutFoot"/>
</body> </body>
</html> </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.Services
@using BTCPayServer.Abstractions.Contracts @using BTCPayServer.Abstractions.Contracts
@using Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Mvc.TagHelpers
@inject LanguageService LangService
@inject BTCPayServerEnvironment Env
@inject IEnumerable<IUIExtension> UiExtensions
@inject PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary
@model PaymentModel @model PaymentModel
@{ @{
Layout = null; Layout = null;
@@ -13,9 +12,6 @@
var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method"); var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
var paymentMethodCount = Model.AvailableCryptos.Count; var paymentMethodCount = Model.AvailableCryptos.Count;
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
: null;
} }
@functions { @functions {
private string PaymentMethodName(PaymentModel.AvailableCrypto pm) private string PaymentMethodName(PaymentModel.AvailableCrypto pm)
@@ -36,17 +32,11 @@
<partial name="LayoutHead"/> <partial name="LayoutHead"/>
<meta name="robots" content="noindex,nofollow"> <meta name="robots" content="noindex,nofollow">
<link href="~/checkout-v2/checkout.css" asp-append-version="true" rel="stylesheet" /> <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> </head>
<body class="min-vh-100"> <body class="min-vh-100">
<div id="Checkout-v2" class="wrap gap-4" v-cloak v-waitForT> <div id="Checkout-v2" class="public-page-wrap" v-cloak v-waitForT>
<header class="store-header"> <partial name="_StoreHeader" model="(Model.StoreName, Model.LogoFileId)" />
@if (!string.IsNullOrEmpty(logoUrl))
{
<img src="@logoUrl" alt="@Model.StoreName" class="store-logo"/>
}
<h1 class="store-name">@Model.StoreName</h1>
</header>
<main class="shadow-lg"> <main class="shadow-lg">
<nav v-if="isModal"> <nav v-if="isModal">
<button type="button" v-if="isModal" id="close" v-on:click="close"> <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> <checkout-cheating invoice-id="@Model.InvoiceId" :btc-due="srvModel.btcDue" :is-paid="isPaid" :payment-method-id="pmId"></checkout-cheating>
} }
<footer> <footer class="store-footer">
<div> <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
<a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener"> {{$t("powered_by")}} <partial name="_StoreFooterLogo" />
{{$t("powered_by")}} </a>
<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>
@* TODO: Re-add this once checkout v2 has been translated @* 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> <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
@using BTCPayServer.Client.Models @using BTCPayServer.Client.Models
@using BTCPayServer.Services.Rates @using BTCPayServer.Services.Rates
@inject BTCPayServer.Services.BTCPayServerEnvironment env @inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject BTCPayServer.Services.ThemeSettings Theme @inject BTCPayServer.Services.ThemeSettings Theme
@inject CurrencyNameTable CurrencyNameTable @inject CurrencyNameTable CurrencyNameTable
@using BTCPayServer.Abstractions.Contracts
@inject IFileService FileService
@{ @{
Layout = null; Layout = null;
ViewData["Title"] = $"Receipt from {Model.StoreName}"; ViewData["Title"] = $"Receipt from {Model.StoreName}";
var isProcessing = Model.Status == InvoiceStatus.Processing; var isProcessing = Model.Status == InvoiceStatus.Processing;
var isSettled = Model.Status == InvoiceStatus.Settled; var isSettled = Model.Status == InvoiceStatus.Settled;
var logoUrl = !string.IsNullOrEmpty(Model.LogoFileId)
? await FileService.GetFileUrl(Context.Request.GetAbsoluteRootUri(), Model.LogoFileId)
: null;
} }
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" @(env.IsDeveloping ? " data-devenv" : "")> <html lang="en" @(Env.IsDeveloping ? " data-devenv" : "")>
<head> <head>
<partial name="LayoutHead" /> <partial name="LayoutHead" />
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId, "", "")" />
<meta name="robots" content="noindex,nofollow"> <meta name="robots" content="noindex,nofollow">
@if (isProcessing) @if (isProcessing)
{ {
@@ -32,32 +28,25 @@
#posData td > table:last-child { margin-bottom: 0 !important; } #posData td > table:last-child { margin-bottom: 0 !important; }
#posData table > tbody > tr:first-child > td > h4 { margin-top: 0 !important; } #posData table > tbody > tr:first-child > td > h4 { margin-top: 0 !important; }
</style> </style>
<partial name="LayoutHeadStoreBranding" model="@(Model.BrandColor, Model.CssFileId)" />
</head> </head>
<body> <body class="min-vh-100">
<div class="min-vh-100 d-flex flex-column"> <div class="public-page-wrap flex-column">
<main class="flex-grow-1 py-5"> <main class="flex-grow-1">
<div class="container" style="max-width:720px;"> <div class="container" style="max-width:720px;">
<partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) { { "Margin", "mb-4" } })"/> <partial name="_StatusMessage" model="@(new ViewDataDictionary(ViewData) { { "Margin", "mb-4" } })"/>
<div class="d-flex flex-column justify-content-center gap-4"> <div class="d-flex flex-column justify-content-center gap-4">
<header class="store-header"> <partial name="_StoreHeader" model="(Model.StoreName, Model.LogoFileId)" />
@if (!string.IsNullOrEmpty(logoUrl))
{
<img src="@logoUrl" alt="@Model.StoreName" class="store-logo" />
}
<h1 class="store-name">@Model.StoreName</h1>
</header>
<div id="InvoiceSummary" class="bg-tile p-3 p-sm-4 rounded d-flex flex-wrap align-items-center"> <div id="InvoiceSummary" class="bg-tile p-3 p-sm-4 rounded d-flex flex-wrap align-items-center">
@if (isProcessing) @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. The invoice has detected a payment but is still waiting to be settled.
</div> </div>
} }
else if (!isSettled) 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. The invoice is not settled.
</div> </div>
} }
@@ -173,21 +162,15 @@
</div> </div>
</div> </div>
</main> </main>
<footer class="pt-2 pb-4 d-print-none"> <footer class="store-footer">
<p class="container text-center" permission="@Policies.CanViewInvoices"> <p permission="@Policies.CanViewInvoices">
<a asp-action="Invoice" asp-route-invoiceId="@Model.InvoiceId"> <a asp-action="Invoice" asp-route-invoiceId="@Model.InvoiceId">
Admin details Admin details
</a> </a>
</p> </p>
<div class="container d-flex flex-wrap align-items-center justify-content-center"> <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
<span class="text-muted mx-2"> Powered by <partial name="_StoreFooterLogo" />
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a> </a>
</span>
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" />
}
</div>
</footer> </footer>
</div> </div>
<partial name="LayoutFoot"/> <partial name="LayoutFoot"/>

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,10 +25,6 @@
line-height: 80px; line-height: 80px;
} }
.l-pos-keypad {
margin-left: 2%;
}
.l-pos-keypad .btn { .l-pos-keypad .btn {
width: 32%; width: 32%;
margin-right: 1%; margin-right: 1%;
@@ -40,14 +36,6 @@
font-size: 1.3rem; font-size: 1.3rem;
} }
.l-pos-controls .btn {
width: 47%;
border-radius: 0;
margin-right: 0%;
margin-left: 2%;
margin-bottom: 1%;
}
.logo { .logo {
height: 40px; height: 40px;
} }

View File

@@ -205,29 +205,6 @@ h2 small .fa-question-circle-o {
margin-top: .5rem; 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 */ /* Print */
@media print { @media print {
.table td, .table td,
@@ -626,3 +603,73 @@ svg.icon-note {
input:checked + .btcpay-list-select-item { input:checked + .btcpay-list-select-item {
border-color: var(--btcpay-form-border-focus); 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);
}