Files
BTCPayServerPlugins/Plugins/BTCPayServer.Plugins.TicketTailor/Views/TicketTailor/View.cshtml
2024-01-19 10:07:57 +01:00

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>