mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
253 lines
12 KiB
Plaintext
253 lines
12 KiB
Plaintext
@using Microsoft.AspNetCore.Routing
|
|
@using BTCPayServer.Plugins.TicketTailor
|
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
|
@model BTCPayServer.Plugins.TicketTailor.TicketTailorViewModel
|
|
@{
|
|
var appId = Context.GetRouteValue("appId");
|
|
ViewData["StoreBranding"] = Model.StoreBranding;
|
|
Layout = null;
|
|
var available = Model.Settings.BypassAvailabilityCheck || (Model.Event.Unavailable != "true" && Model.Event.TicketsAvailable == "true");
|
|
Model.Settings.SpecificTickets ??= new List<SpecificTicket>();
|
|
Context.Request.Query.TryGetValue("accessCode", out var accessCode);
|
|
if (Context.Request.Query.TryGetValue("discount", out var discount))
|
|
{
|
|
Model.DiscountCode = discount;
|
|
}
|
|
}
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<partial name="LayoutHead"/>
|
|
|
|
|
|
<style>
|
|
hr:last-child{
|
|
display: none;
|
|
}
|
|
@if (!string.IsNullOrEmpty(Model.Settings.CustomCSS))
|
|
{
|
|
@Safe.Raw(Model.Settings.CustomCSS)
|
|
}
|
|
</style>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", ()=>{
|
|
const form = document.querySelector("form");
|
|
const btn = document.querySelector("button[type='submit']");
|
|
const inputs = document.querySelectorAll("input");
|
|
const discountCode = document.querySelector("#DiscountCode");
|
|
inputs.forEach(value => value.addEventListener("input", (evt)=>{
|
|
let total = 0;
|
|
let totalQty = 0;
|
|
document.querySelectorAll("[data-price]").forEach(value1 => {
|
|
if (!!value1.value){
|
|
const qty = parseInt(value1.value);
|
|
if (qty > 0){
|
|
|
|
const price = parseFloat(value1.dataset.price).toPrecision(12);
|
|
total += price * qty;
|
|
totalQty += qty;
|
|
}
|
|
}
|
|
});
|
|
|
|
btn.classList.remove("btn-warning");
|
|
if (totalQty > 0){
|
|
btn.removeAttribute("disabled");
|
|
}
|
|
else{
|
|
btn.setAttribute("disabled", "disabled");
|
|
}
|
|
btn.textContent = `Purchase for ${total.toFixed(2)} @Model.Event.Currency.ToUpperInvariant()`
|
|
|
|
if (discountCode && discountCode.value && totalQty > 0){
|
|
const data = new FormData(form);
|
|
const xhttp = new XMLHttpRequest();
|
|
xhttp.onreadystatechange = function() {
|
|
if (this.readyState === 4 && this.status === 200) {
|
|
const response = JSON.parse(this.responseText);
|
|
if (response.error){
|
|
console.error(response.error);
|
|
btn.setAttribute("disabled", "disabled");
|
|
btn.classList.add("btn-warning");
|
|
btn.textContent = `Purchase unavailable due to ${response.error.toLowerCase()}`
|
|
return;
|
|
}
|
|
if (response.discountedAmount){
|
|
btn.innerHTML = `Purchase for ${response.total.toFixed(2)} @Model.Event.Currency.ToUpperInvariant()<br/><span class="">${response.discountedAmount.toFixed(2)} @Model.Event.Currency.ToUpperInvariant() discount</span>`
|
|
} else{
|
|
btn.textContent = `Purchase for ${response.total.toFixed(2)} @Model.Event.Currency.ToUpperInvariant()`
|
|
}
|
|
}
|
|
}
|
|
xhttp.open("POST", "@Url.Action("Purchase", new {appId, preview = true})", true);
|
|
xhttp.send(data);
|
|
}
|
|
}))
|
|
form.addEventListener("submit", ()=>{
|
|
btn.setAttribute("disabled", "disabled");
|
|
inputs.forEach(value => value.setAttribute("readonly", "readonly"));
|
|
})
|
|
})
|
|
</script>
|
|
</head>
|
|
<body class="min-vh-100">
|
|
<div id="TicketTailor" class="public-page-wrap">
|
|
<partial name="_StoreHeader" model="(Model.Event.Title, Model.StoreBranding)"/>
|
|
<main>
|
|
<partial name="_StatusMessage"/>
|
|
<div class="text-muted mb-4 text-center lead ">@Model.Event.Start.Formatted - @Model.Event.EventEnd.Formatted</div>
|
|
@if (Model.Settings.ShowDescription && !string.IsNullOrEmpty(Model.Event.Description))
|
|
{
|
|
<div class="ticket-tailor-description lead">@Safe.Raw(Model.Event.Description)</div>
|
|
}
|
|
|
|
|
|
<form method="post" asp-controller="TicketTailor" asp-action="Purchase" asp-antiforgery="false" asp-route-appId="@appId">
|
|
<input type="hidden" asp-for="AccessCode" value="@accessCode"/>
|
|
<div class="row g-2 justify-content-center mb-4" id="ticket-form-container">
|
|
<div class="col-sm-6 col-md-4">
|
|
<div class="form-floating">
|
|
<input type="text" asp-for="Name" class="form-control" required="@Model.Settings.RequireFullName"
|
|
pattern="^(\w\w+)\s(\w+)$" title="Please enter your first and last name, separated with a space.">
|
|
<label asp-for="Name">Full Name</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-md-4">
|
|
<div class="form-floating">
|
|
<input required type="email" name="email" asp-for="Email" class="form-control"/>
|
|
<label asp-for="Email">Email</label>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
<div class="row g-2 justify-content-center mb-4">
|
|
<div class="col-sm-12 col-md-8">
|
|
|
|
@{
|
|
var index = -1;
|
|
|
|
foreach (var groupedTickets in Model.Event.TicketTypes.GroupBy(type => type.GroupId).OrderBy(groupedTickets => Model.Event.TicketGroups.FirstOrDefault(ticketGroup => ticketGroup.Id == groupedTickets.Key)?.SortOrder))
|
|
{
|
|
<div class="bg-tile w-100 p-4 mb-2">
|
|
|
|
|
|
@if (!string.IsNullOrEmpty(groupedTickets.Key))
|
|
{
|
|
var group = Model.Event.TicketGroups.First(ticketGroup => ticketGroup.Id == groupedTickets.Key);
|
|
<h4 class="mb-2 text-center ">@group.Name</h4>
|
|
}
|
|
@foreach (var item in groupedTickets)
|
|
{
|
|
var availableToShow = new[] {"on_sale", "sold_out", "unavailable"}.Contains(item.Status) || !string.IsNullOrEmpty(item.AccessCode) && item.AccessCode.Equals(accessCode, StringComparison.InvariantCultureIgnoreCase);
|
|
var specific = false;
|
|
|
|
if (Model.Settings.SpecificTickets?.Any() is true)
|
|
{
|
|
var matched = Model.Settings.SpecificTickets.FirstOrDefault(ticket => ticket.TicketTypeId == item.Id);
|
|
if (matched is null || matched.Hidden)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (matched.Price is not null)
|
|
{
|
|
item.Price = matched.Price.Value;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(matched.Name))
|
|
{
|
|
item.Name = matched.Name;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(matched.Description))
|
|
{
|
|
item.Description = matched.Description;
|
|
}
|
|
|
|
availableToShow = true;
|
|
specific = true;
|
|
}
|
|
|
|
if (!availableToShow)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
index++;
|
|
|
|
<input type="hidden" asp-for="Items[index].TicketTypeId" value="@item.Id"/>
|
|
var purchasable = available && (specific || new[] {"on_sale", "locked"}.Contains(item.Status)) && item.Quantity > 0;
|
|
|
|
<div class="row justify-content-between">
|
|
<div class="col-lg-8 col-sm-12">
|
|
<h5 >@item.Name</h5>
|
|
<p>@Safe.Raw(item.Description)</p>
|
|
</div>
|
|
<div class="col-lg-4 col-sm-12">
|
|
@if (purchasable)
|
|
{
|
|
<div class="input-group">
|
|
<div class="form-floating">
|
|
<input type="number"
|
|
class="form-control" asp-for="Items[index].Quantity" max="@item.MaxPerOrder"
|
|
min="0" data-price="@item.Price"/>
|
|
|
|
<label asp-for="Items[index].Quantity">Quantity</label>
|
|
</div>
|
|
|
|
<span class="input-group-text">
|
|
@(item.Price == 0 ? "FREE" : $"{item.Price} {Model.Event.Currency.ToUpperInvariant()}")
|
|
|
|
</span>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="text-muted">Unavailable</div>
|
|
}
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<hr/>
|
|
}
|
|
|
|
</div>
|
|
}
|
|
}
|
|
|
|
|
|
</div>
|
|
@if (Model.Settings.AllowDiscountCodes)
|
|
{
|
|
<div class="col-sm-12 col-md-8 text-center">
|
|
<div class="form-floating">
|
|
<input type="text" asp-for="DiscountCode" class="form-control"/>
|
|
<label asp-for="DiscountCode">Promo code</label>
|
|
</div>
|
|
</div>
|
|
}
|
|
<div class="col-sm-12 col-md-8 text-center">
|
|
<button class="btn btn-primary btn-lg m-auto" type="submit" disabled="disabled">Purchase</button>
|
|
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
</main>
|
|
|
|
<footer class="store-footer">
|
|
<p >
|
|
<a href="@Model.Event.Url">Back to fiat ticket page</a>
|
|
</p>
|
|
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
|
|
Powered by <partial name="_StoreFooterLogo"/>
|
|
</a>
|
|
</footer>
|
|
</div>
|
|
<partial name="LayoutFoot"/>
|
|
|
|
</body>
|
|
</html> |