Skip to content

Commit 4565b57

Browse files
authored
#1090: Notification to the consumer when the product is back in stock (#1091)
1 parent 484cb93 commit 4565b57

File tree

15 files changed

+314
-2
lines changed

15 files changed

+314
-2
lines changed

src/Modules/SimplCommerce.Module.Catalog/Areas/Catalog/Views/Product/ProductDetail.cshtml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,31 @@
199199
<div class="out-of-stock">
200200
<span class="label label-danger">@Localizer["Out of stock"]</span>
201201
</div>
202+
203+
<div class="back-in-stock-subscribe">
204+
<form action="api/stocks/back-in-stock">
205+
<div>
206+
@if (SignInManager.IsSignedIn(User))
207+
{
208+
<p>Subscribe and we'll notify you when the product is back in stock.</p>
209+
<div class="d-flex">
210+
<input type="hidden" name="productId" value="@Model.Id" />
211+
<input type="hidden" class="form-control mr-2" name="customerEmail" value="@User.Claims.First(c => c.Type == "email").Value"/>
212+
<button type="button" class="btn btn-primary btn-back-in-stock-subscribe" id="subscribeBackInStock">Subscribe</button>
213+
</div>
214+
}
215+
else
216+
{
217+
<p>Leave your email and we'll notify you when the product is in stock</p>
218+
<div class="d-flex">
219+
<input type="hidden" name="productId" value="@Model.Id" />
220+
<input type="text" class="form-control mr-2" name="customerEmail" />
221+
<button type="button" class="btn btn-primary btn-back-in-stock-subscribe" id="subscribeBackInStock">Send</button>
222+
</div>
223+
}
224+
</div>
225+
</form>
226+
</div>
202227
}
203228
<div class="add-to-cart">
204229
<form class="inline">

src/Modules/SimplCommerce.Module.Catalog/wwwroot/product-detail.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,22 @@ $(document).ready(function () {
7171
});
7272
}
7373
});
74+
75+
$("#subscribeBackInStock").on('click', function (e) {
76+
e.preventDefault();
77+
var $form = $(this).closest("form"),
78+
productId = $(this).closest("form").find('input[name=productId]').val(),
79+
customerEmail = $(this).closest("form").find('input[name=customerEmail]').val();
80+
81+
var that = this;
82+
$.post($form.attr('action'), $form.serializeArray())
83+
.done(function (result) {
84+
$(that).closest('.back-in-stock-subscribe').html('<b>Thank you. We\'ll notify you.</b>');
85+
})
86+
.fail(function (result) {
87+
if (result.status === 409) {
88+
$(that).closest('.back-in-stock-subscribe').html('<b>You\'ve already subscribed.</b>');
89+
}
90+
});
91+
});
7492
});

src/Modules/SimplCommerce.Module.Core/Extensions/IWorkContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace SimplCommerce.Module.Core.Extensions
55
{
66
public interface IWorkContext
77
{
8+
string GetCurrentHostName();
9+
810
Task<User> GetCurrentUser();
911
}
1012
}

src/Modules/SimplCommerce.Module.Core/Extensions/WorkContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public WorkContext(UserManager<User> userManager,
3333
_configuration = configuration;
3434
}
3535

36+
public string GetCurrentHostName()
37+
{
38+
return _httpContext.Request.Host.Value;
39+
}
40+
3641
public async Task<User> GetCurrentUser()
3742
{
3843
if (_currentUser != null)

src/Modules/SimplCommerce.Module.Inventory/Areas/Inventory/Controllers/StockApiController.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,21 @@ public class StockApiController : Controller
2020
{
2121
private readonly IRepository<Stock> _stockRepository;
2222
private readonly IStockService _stockService;
23+
private readonly IStockSubscriptionService _stockSubscriptionService;
2324
private readonly IWorkContext _workContext;
2425
private readonly IRepository<Warehouse> _warehouseRepository;
2526
private readonly IRepository<StockHistory> _stockHistoryRepository;
27+
private readonly IRepository<BackInStockSubscription> _backInStockSubscriptionRepository;
2628

27-
public StockApiController(IRepository<Stock> stockRepository, IStockService stockService, IWorkContext workContext, IRepository<Warehouse> warehouseRepository, IRepository<StockHistory> stockHistoryRepository)
29+
public StockApiController(IRepository<Stock> stockRepository, IStockService stockService, IWorkContext workContext, IRepository<Warehouse> warehouseRepository, IRepository<StockHistory> stockHistoryRepository, IRepository<BackInStockSubscription> backInStockSubscriptionRepository, IStockSubscriptionService stockSubscriptionService)
2830
{
2931
_stockRepository = stockRepository;
3032
_stockService = stockService;
3133
_workContext = workContext;
3234
_warehouseRepository = warehouseRepository;
3335
_stockHistoryRepository = stockHistoryRepository;
36+
_backInStockSubscriptionRepository = backInStockSubscriptionRepository;
37+
_stockSubscriptionService = stockSubscriptionService;
3438
}
3539

3640
[HttpPost("grid")]
@@ -135,5 +139,21 @@ public async Task<IActionResult> GetStockHistory(int warehouseId, int productId)
135139

136140
return Ok(stockHistory);
137141
}
142+
143+
[AllowAnonymous]
144+
[HttpPost("back-in-stock")]
145+
public async Task<IActionResult> BackInStockSubscribe(long productId, string customerEmail)
146+
{
147+
if (await _backInStockSubscriptionRepository.Query()
148+
.Where(o => o.ProductId == productId && o.CustomerEmail == customerEmail)
149+
.AnyAsync())
150+
{
151+
return Conflict();
152+
}
153+
154+
await _stockSubscriptionService.BackInStockSubscribeAsync(productId, customerEmail);
155+
156+
return Ok();
157+
}
138158
}
139159
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@using SimplCommerce.Module.Catalog.Models
2+
@using SimplCommerce.Module.Core.Extensions
3+
@using SimplCommerce.Module.Core.Services
4+
5+
@inject IWorkContext _workContext
6+
@inject IMediaService _mediaService
7+
8+
@{
9+
Layout = null;
10+
11+
var hostName = _workContext.GetCurrentHostName();
12+
var thumbnailUrl = _mediaService.GetThumbnailUrl(Model.ThumbnailImage);
13+
}
14+
15+
@model SimplCommerce.Module.Catalog.Models.Product
16+
17+
<div style="border: 1px solid #ebebeb; width: 50%; padding: 16px 24px; margin: 0 auto;">
18+
Hi, <b>@Model.Name</b> in now available.
19+
Get in now before it out of stock again.
20+
<br />
21+
<br />
22+
<div>
23+
<img src="https://@hostName@thumbnailUrl" />
24+
<h2>@Model.Name</h2>
25+
<h4 style="color:red;">$1000</h4>
26+
</div>
27+
<a href="https://@hostName/@Model.Slug">
28+
View product
29+
</a>
30+
</div>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using MediatR;
7+
8+
namespace SimplCommerce.Module.Inventory.Event
9+
{
10+
public class BackInStock : INotification
11+
{
12+
public long ProductId { get; set; }
13+
}
14+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using MediatR;
8+
using SimplCommerce.Module.Inventory.Services;
9+
10+
namespace SimplCommerce.Module.Inventory.Event
11+
{
12+
public class BackInStockSendEmailHandler : INotificationHandler<BackInStock>
13+
{
14+
public readonly IStockSubscriptionService _stockSubscriptionService;
15+
16+
public BackInStockSendEmailHandler(IStockSubscriptionService stockSubscriptionService)
17+
{
18+
_stockSubscriptionService = stockSubscriptionService;
19+
}
20+
21+
public async Task Handle(BackInStock notification, CancellationToken cancellationToken)
22+
{
23+
await _stockSubscriptionService.BackInStockSendNotificationsAsync(notification.ProductId);
24+
}
25+
}
26+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using SimplCommerce.Infrastructure.Models;
7+
8+
namespace SimplCommerce.Module.Inventory.Models
9+
{
10+
public class BackInStockSubscription : EntityBase
11+
{
12+
public long ProductId { get; set; }
13+
public string CustomerEmail { get; set; }
14+
}
15+
}

src/Modules/SimplCommerce.Module.Inventory/ModuleInitializer.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
using SimplCommerce.Infrastructure.Modules;
55
using SimplCommerce.Module.Inventory.Services;
66
using SimplCommerce.Infrastructure;
7+
using MediatR;
8+
using SimplCommerce.Module.Catalog.Events;
9+
using SimplCommerce.Module.Core.Events;
10+
using SimplCommerce.Module.Inventory.Event;
711

812
namespace SimplCommerce.Module.Inventory
913
{
@@ -12,6 +16,9 @@ public class ModuleInitializer : IModuleInitializer
1216
public void ConfigureServices(IServiceCollection serviceCollection)
1317
{
1418
serviceCollection.AddTransient<IStockService, StockService>();
19+
serviceCollection.AddTransient<IStockSubscriptionService, StockSubscriptionService>();
20+
serviceCollection.AddTransient<INotificationHandler<BackInStock>, BackInStockSendEmailHandler>();
21+
1522

1623
GlobalConfiguration.RegisterAngularModule("simplAdmin.inventory");
1724
}

0 commit comments

Comments
 (0)