start contrib perks

This commit is contained in:
Kukks 2018-12-29 11:52:07 +01:00
parent 6fced3fab2
commit 6eb36abe2e
9 changed files with 156 additions and 20 deletions

View File

@ -31,6 +31,7 @@ namespace BTCPayServer.Controllers
public string NotificationUrl { get; set; }
public string Tagline { get; set; }
public string EmbeddedCSS { get; set; }
public string PerksTemplate { get; set; }
}
@ -57,16 +58,31 @@ namespace BTCPayServer.Controllers
CustomCSSLink = settings.CustomCSSLink,
NotificationUrl = settings.NotificationUrl,
Tagline = settings.Tagline,
PerksTemplate = settings.PerksTemplate
};
return View(vm);
}
[HttpPost]
[Route("{appId}/settings/crowdfund")]
public async Task<IActionResult> UpdatePointOfSale(string appId, UpdateCrowdfundViewModel vm)
public async Task<IActionResult> UpdateCrowdfund(string appId, UpdateCrowdfundViewModel vm)
{
if (_AppsHelper.GetCurrencyData(vm.TargetCurrency, false) == null)
ModelState.AddModelError(nameof(vm.TargetCurrency), "Invalid currency");
try
{
_AppsHelper.Parse(vm.PerksTemplate, vm.TargetCurrency);
}
catch
{
ModelState.AddModelError(nameof(vm.PerksTemplate), "Invalid template");
}
if (!ModelState.IsValid)
{
return View(vm);
}
var app = await GetOwnedApp(appId, AppType.Crowdfund);
if (app == null)
return NotFound();
@ -84,7 +100,8 @@ namespace BTCPayServer.Controllers
MainImageUrl = vm.MainImageUrl,
EmbeddedCSS = vm.EmbeddedCSS,
NotificationUrl = vm.NotificationUrl,
Tagline = vm.Tagline
Tagline = vm.Tagline,
PerksTemplate = vm.PerksTemplate
});
await UpdateAppSettings(app);
_EventAggregator.Publish(new CrowdfundAppUpdated()

View File

@ -120,9 +120,17 @@ namespace BTCPayServer.Controllers
}
StatusMessage = "App successfully created";
CreatedAppId = id;
if (appType == AppType.PointOfSale)
return RedirectToAction(nameof(UpdatePointOfSale), new { appId = id });
return RedirectToAction(nameof(ListApps));
switch (appType)
{
case AppType.PointOfSale:
return RedirectToAction(nameof(UpdatePointOfSale), new { appId = id });
case AppType.Crowdfund:
return RedirectToAction(nameof(UpdateCrowdfund), new { appId = id });
default:
return RedirectToAction(nameof(ListApps));
}
}
[HttpGet]

View File

@ -113,18 +113,40 @@ namespace BTCPayServer.Controllers
return NotFound();
var settings = app.GetSettings<CrowdfundSettings>();
var store = await _AppsHelper.GetStore(app);
string title = null;
var price = 0.0m;
if (!string.IsNullOrEmpty(request.ChoiceKey))
{
var choices = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency);
var choice = choices.FirstOrDefault(c => c.Id == request.ChoiceKey);
if (choice == null)
return NotFound();
title = choice.Title;
price = choice.Price.Value;
if (request.Amount > price)
price = request.Amount;
}
else
{
price = request.Amount;
title = settings.Title;
}
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
var invoice = await _InvoiceController.CreateInvoiceCore(new Invoice()
{
OrderId = $"{CrowdfundHubStreamer.CrowdfundInvoiceOrderIdPrefix}{appId}",
Currency = settings.TargetCurrency,
ItemCode = request.ChoiceKey ?? string.Empty,
ItemDesc = title,
BuyerEmail = request.Email,
Price = request.Amount,
Price = price,
NotificationURL = settings.NotificationUrl,
FullNotifications = true,
ExtendedNotifications = true,
RedirectURL = HttpContext.Request.GetAbsoluteRoot()+ "/apps/{appId}/crowdfund",
RedirectURL = request.RedirectUrl,
}, store, HttpContext.Request.GetAbsoluteRoot());
if (request.RedirectToCheckout)
@ -195,7 +217,7 @@ namespace BTCPayServer.Controllers
OrderId = orderId,
NotificationURL = notificationUrl,
RedirectURL = redirectUrl,
FullNotifications = true
FullNotifications = true,
}, store, HttpContext.Request.GetAbsoluteRoot());
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
}

View File

@ -96,8 +96,7 @@ namespace BTCPayServer.Hubs
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
var app = await _AppsHelper.GetApp(appId, AppType.Crowdfund, true);
var result = await GetInfo(app, _InvoiceRepository, _RateFetcher,
_BtcPayNetworkProvider);
var result = await GetInfo(app);
entry.SetValue(result);
return result;
});
@ -191,26 +190,27 @@ namespace BTCPayServer.Hubs
}
private static async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, InvoiceRepository invoiceRepository,
RateFetcher rateFetcher, BTCPayNetworkProvider btcPayNetworkProvider, string statusMessage= null)
private async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, string statusMessage= null)
{
var settings = appData.GetSettings<AppsController.CrowdfundSettings>();
var invoices = await GetInvoicesForApp(appData, invoiceRepository);
var invoices = await GetInvoicesForApp(appData, _InvoiceRepository);
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(btcPayNetworkProvider);
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(_BtcPayNetworkProvider);
var currentAmount = await GetCurrentContributionAmount(
invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray(),
settings.TargetCurrency, rateFetcher, rateRules);
settings.TargetCurrency, _RateFetcher, rateRules);
var currentPendingAmount = await GetCurrentContributionAmount(
invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray(),
settings.TargetCurrency, rateFetcher, rateRules);
settings.TargetCurrency, _RateFetcher, rateRules);
var active = (settings.StartDate == null || DateTime.Now >= settings.StartDate) &&
(settings.EndDate == null || DateTime.Now <= settings.EndDate) &&
(!settings.EnforceTargetAmount || settings.TargetAmount > currentAmount);
return new ViewCrowdfundViewModel()
{
Title = settings.Title,
@ -227,6 +227,7 @@ namespace BTCPayServer.Hubs
TargetCurrency = settings.TargetCurrency,
EnforceTargetAmount = settings.EnforceTargetAmount,
StatusMessage = statusMessage,
Perks = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency),
Info = new ViewCrowdfundViewModel.CrowdfundInfo()
{
TotalContributors = invoices.Length,

View File

@ -36,6 +36,9 @@ namespace BTCPayServer.Models.AppViewModels
[Display(Name = "Do not allow additional contributions after target has been reached")]
public bool EnforceTargetAmount { get; set; }
[Display(Name = "Contribution Perks Template")]
public string PerksTemplate { get; set; }
[MaxLength(500)]
[Display(Name = "Custom bootstrap CSS file")]
public string CustomCSSLink { get; set; }

View File

@ -22,6 +22,7 @@ namespace BTCPayServer.Models.AppViewModels
public CrowdfundInfo Info { get; set; }
public string Tagline { get; set; }
public ViewPointOfSaleViewModel.Item[] Perks { get; set; }
public class CrowdfundInfo
@ -46,6 +47,8 @@ namespace BTCPayServer.Models.AppViewModels
public ViewCrowdfundViewModel ViewCrowdfundViewModel { get; set; }
[Required] public decimal Amount { get; set; }
public string Email { get; set; }
public bool RedirectToCheckout { get; set; }
public string ChoiceKey { get; set; }
public bool RedirectToCheckout { get; set; }
public string RedirectUrl { get; set; }
}
}

View File

@ -3,6 +3,24 @@
ViewData["Title"] = "Update Crowdfund";
}
<section>
<div class="modal" id="product-modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Contribution Perks Management</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="js-product-save btn btn-primary">Save Changes</button>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
@ -61,6 +79,19 @@
<input asp-for="EndDate" class="form-control" />
<span asp-validation-for="EndDate" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Contribution Perks</label>
<div class="mb-3">
<a class="js-product-add btn btn-secondary" href="#" data-toggle="modal" data-target="#product-modal"><i class="fa fa-plus fa-fw"></i> Add Product</a>
</div>
<div class="js-products bg-light row p-3">
</div>
</div>
<div class="form-group">
<label asp-for="PerksTemplate" class="control-label"></label>*
<textarea asp-for="PerksTemplate" rows="10" cols="40" class="js-product-template form-control"></textarea>
<span asp-validation-for="PerksTemplate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CustomCSSLink" class="control-label"></label>
<a href="https://docs.btcpayserver.org/development/theme#bootstrap-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
@ -104,5 +135,56 @@
<link rel="stylesheet" href="~/vendor/highlightjs/default.min.css">
<script src="~/vendor/highlightjs/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script id="template-product-item" type="text/template">
<div class="col-sm-4 col-md-3 mb-3">
<div class="card">
{image}
<div class="card-body">
<h6 class="card-title">{title}</h6>
<a href="#" class="js-product-edit btn btn-primary" data-toggle="modal" data-target="#product-modal">Edit</a>
<a href="#" class="js-product-remove btn btn-danger"><i class="fa fa-trash"></i></a>
</div>
</div>
</div>
</script>
<script id="template-product-content" type="text/template">
<div class="mb-3">
<input class="js-product-id" type="hidden" name="id" value="{id}">
<input class="js-product-index" type="hidden" name="index" value="{index}">
<div class="form-row">
<div class="col-sm-6">
<label>Title</label>*
<input type="text" class="js-product-title form-control mb-2" value="{title}" autofocus />
</div>
<div class="col-sm-3">
<label>Price</label>*
<input type="text" class="js-product-price form-control mb-2" value="{price}" />
</div>
<div class="col-sm-3">
<label>Custom price</label>
<select class="js-product-custom form-control">
{custom}
</select>
</div>
</div>
<div class="form-row">
<div class="col">
<label>Image</label>
<input type="text" class="js-product-image form-control mb-2" value="{image}" />
</div>
</div>
<div class="form-row">
<div class="col">
<label>Description</label>
<textarea rows="3" cols="40" class="js-product-description form-control mb-2">{description}</textarea>
</div>
</div>
</div>
</script>
<script src="~/products/js/products.js"></script>
<script src="~/products/js/products.jquery.js"></script>
}

View File

@ -94,7 +94,7 @@
<h2 class="text-muted" v-if="srvModel.tagline">{{srvModel.tagline}}</h2>
</div>
<div class="col-sm-12 col-md-3 col-lg-2">
<button v-if="srvModel.info.active" class="btn btn-lg btn-primary" v-on:click="contributeModalOpen = true">Contribute</button>
<button v-if="srvModel.info.active" class="btn btn-lg btn-primary w-100" v-on:click="contributeModalOpen = true">Contribute</button>
</div>

View File

@ -170,7 +170,7 @@ var resizeCanvas = function() {
window.addEventListener("resize", resizeCanvas);
// addClickListeners();
handleInactiveUser();
// handleInactiveUser();
})();
function handleInactiveUser() {