From 038a327f08bdecb7ab40d4970e69c30ed028ed6e Mon Sep 17 00:00:00 2001 From: Jeferson Almeida Date: Thu, 2 Jan 2025 21:02:24 -0300 Subject: [PATCH 1/4] #248 - Baixar o pedido do estoque - feature/sp7/#248 --- .../Configuration/MessageBusConfig.cs | 16 ++++ .../JSE.Catalogo.API/JSE.Catalogo.API.csproj | 1 + .../JSE.Catalogo.API/Models/Produto.cs | 13 ++- src/services/JSE.Catalogo.API/Program.cs | 1 + .../Services/CatalogoIntegrationHandler.cs | 84 +++++++++++++++++++ .../appsettings.Development.json | 11 ++- 6 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/services/JSE.Catalogo.API/Configuration/MessageBusConfig.cs create mode 100644 src/services/JSE.Catalogo.API/Services/CatalogoIntegrationHandler.cs diff --git a/src/services/JSE.Catalogo.API/Configuration/MessageBusConfig.cs b/src/services/JSE.Catalogo.API/Configuration/MessageBusConfig.cs new file mode 100644 index 0000000..d3fab3c --- /dev/null +++ b/src/services/JSE.Catalogo.API/Configuration/MessageBusConfig.cs @@ -0,0 +1,16 @@ +using JSE.Catalogo.API.Services; +using JSE.Core.Utils; +using JSE.MessageBus; + +namespace JSE.Catalogo.API.Configuration +{ + public static class MessageBusConfig + { + public static void AddMessageBusConfiguration(this IServiceCollection services, + IConfiguration configuration) + { + services.AddMessageBus(configuration.GetMessageQueueConnection("MessageBus")) + .AddHostedService(); + } + } +} \ No newline at end of file diff --git a/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj b/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj index 8b4546e..c66dcc2 100644 --- a/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj +++ b/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj @@ -28,6 +28,7 @@ + diff --git a/src/services/JSE.Catalogo.API/Models/Produto.cs b/src/services/JSE.Catalogo.API/Models/Produto.cs index 49af852..95212a3 100644 --- a/src/services/JSE.Catalogo.API/Models/Produto.cs +++ b/src/services/JSE.Catalogo.API/Models/Produto.cs @@ -5,11 +5,22 @@ namespace JSE.Catalogo.API.Models public class Produto : Entity, IAggregateRoot { public string Nome { get; set; } - public string Descricao { get; set; } + public string Descricao { get; set; } public bool Ativo { get; set; } public decimal Valor { get; set; } public DateTime DataCadastro { get; set; } public string Imagem { get; set; } public int QuantidadeEstoque { get; set; } + + public void RetirarEstoque(int quantidade) + { + if (QuantidadeEstoque >= quantidade) + QuantidadeEstoque -= quantidade; + } + + public bool EstaDisponivel(int quantidade) + { + return Ativo && QuantidadeEstoque >= quantidade; + } } } diff --git a/src/services/JSE.Catalogo.API/Program.cs b/src/services/JSE.Catalogo.API/Program.cs index 0929d71..01fdd54 100644 --- a/src/services/JSE.Catalogo.API/Program.cs +++ b/src/services/JSE.Catalogo.API/Program.cs @@ -13,6 +13,7 @@ .AddUserSecrets(typeof(Program).Assembly).Build(); builder.Services.AddApiConfiguration(configuration); +builder.Services.AddMessageBusConfiguration(configuration); builder.Services.AddJwtConfiguration(configuration); builder.Services.AddSwaggerConfiguration(); builder.Services.AddEndpointsApiExplorer(); diff --git a/src/services/JSE.Catalogo.API/Services/CatalogoIntegrationHandler.cs b/src/services/JSE.Catalogo.API/Services/CatalogoIntegrationHandler.cs new file mode 100644 index 0000000..cb0ae73 --- /dev/null +++ b/src/services/JSE.Catalogo.API/Services/CatalogoIntegrationHandler.cs @@ -0,0 +1,84 @@ +using JSE.Catalogo.API.Models; +using JSE.Core.DomainObjects; +using JSE.Core.Messages.Integration; +using JSE.MessageBus; + +namespace JSE.Catalogo.API.Services +{ + public class CatalogoIntegrationHandler : BackgroundService + { + private readonly IMessageBus _bus; + private readonly IServiceProvider _serviceProvider; + + public CatalogoIntegrationHandler(IServiceProvider serviceProvider, IMessageBus bus) + { + _serviceProvider = serviceProvider; + _bus = bus; + } + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + SetSubscribers(); + return Task.CompletedTask; + } + + private void SetSubscribers() + { + _bus.SubscribeAsync("PedidoAutorizado", async request => + await BaixarEstoque(request)); + } + + private async Task BaixarEstoque(PedidoAutorizadoIntegrationEvent message) + { + using (var scope = _serviceProvider.CreateScope()) + { + var produtosComEstoque = new List(); + var produtoRepository = scope.ServiceProvider.GetRequiredService(); + + var idsProdutos = string.Join(",", message.Itens.Select(c => c.Key)); + var produtos = await produtoRepository.ObterProdutosPorId(idsProdutos); + + if (produtos.Count != message.Itens.Count) + { + CancelarPedidoSemEstoque(message); + return; + } + + foreach (var produto in produtos) + { + var quantidadeProduto = message.Itens.FirstOrDefault(p => p.Key == produto.Id).Value; + + if (produto.EstaDisponivel(quantidadeProduto)) + { + produto.RetirarEstoque(quantidadeProduto); + produtosComEstoque.Add(produto); + } + } + + if (produtosComEstoque.Count != message.Itens.Count) + { + CancelarPedidoSemEstoque(message); + return; + } + + foreach (var produto in produtosComEstoque) + { + produtoRepository.Atualizar(produto); + } + + if (!await produtoRepository.UnitOfWork.Commit()) + { + throw new DomainException($"Problemas ao atualizar estoque do pedido {message.PedidoId}"); + } + + var pedidoBaixado = new PedidoBaixadoEstoqueIntegrationEvent(message.ClienteId, message.PedidoId); + await _bus.PublishAsync(pedidoBaixado); + } + } + + public async void CancelarPedidoSemEstoque(PedidoAutorizadoIntegrationEvent message) + { + var pedidoCancelado = new PedidoCanceladoIntegrationEvent(message.ClienteId, message.PedidoId); + await _bus.PublishAsync(pedidoCancelado); + } + } +} \ No newline at end of file diff --git a/src/services/JSE.Catalogo.API/appsettings.Development.json b/src/services/JSE.Catalogo.API/appsettings.Development.json index e93ace3..f0b1f7a 100644 --- a/src/services/JSE.Catalogo.API/appsettings.Development.json +++ b/src/services/JSE.Catalogo.API/appsettings.Development.json @@ -1,7 +1,4 @@ { - "ConnectionStrings": { - "DefaultConnection": "Server=.\\SQLEXPRESS;Database=JeffStoreEnterprise;User Id=sa;Password=Asd123!!;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=True;" - }, "Logging": { "LogLevel": { "Default": "Information", @@ -9,6 +6,14 @@ } }, + "ConnectionStrings": { + "DefaultConnection": "Server=.\\SQLEXPRESS;Database=JeffStoreEnterprise;User Id=sa;Password=Asd123!!;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=True;" + }, + + "MessageQueueConnection": { + "MessageBus": "host=localhost:5672;publisherConfirms=true;timeout=10" + }, + "AppSettings": { "Secret": "F9F52344-59C3-4EAC-90E6-CB47935038BE", "ExpirationHours": 2, From 3e75b4b2b4aeb1fdffec04c34aa483d01458e808 Mon Sep 17 00:00:00 2001 From: Jeferson Almeida Date: Thu, 2 Jan 2025 21:36:42 -0300 Subject: [PATCH 2/4] #249 - Capturar e cancelar pagamentos - feature/sp7/#249 --- .../PedidoAutorizadoIntegrationEvent.cs | 1 + .../Services/PedidoIntegrationHandler.cs | 69 +++++++++++++++++++ .../JSE.Pedido.Domain/Pedidos/Pedido.cs | 10 +++ 3 files changed, 80 insertions(+) create mode 100644 src/services/JSE.Pedido.API/Services/PedidoIntegrationHandler.cs diff --git a/src/building blocks/JSE.Core/Messages/Integration/PedidoAutorizadoIntegrationEvent.cs b/src/building blocks/JSE.Core/Messages/Integration/PedidoAutorizadoIntegrationEvent.cs index fbeea69..9052888 100644 --- a/src/building blocks/JSE.Core/Messages/Integration/PedidoAutorizadoIntegrationEvent.cs +++ b/src/building blocks/JSE.Core/Messages/Integration/PedidoAutorizadoIntegrationEvent.cs @@ -13,4 +13,5 @@ public PedidoAutorizadoIntegrationEvent(Guid clienteId, Guid pedidoId, IDictiona Itens = itens; } } + } \ No newline at end of file diff --git a/src/services/JSE.Pedido.API/Services/PedidoIntegrationHandler.cs b/src/services/JSE.Pedido.API/Services/PedidoIntegrationHandler.cs new file mode 100644 index 0000000..598874d --- /dev/null +++ b/src/services/JSE.Pedido.API/Services/PedidoIntegrationHandler.cs @@ -0,0 +1,69 @@ +using JSE.Core.DomainObjects; +using JSE.Core.Messages.Integration; +using JSE.MessageBus; +using JSE.Pedidos.Domain.Pedidos; + +namespace JSE.Pedidos.API.Services +{ + public class PedidoIntegrationHandler : BackgroundService + { + private readonly IMessageBus _bus; + private readonly IServiceProvider _serviceProvider; + + public PedidoIntegrationHandler(IServiceProvider serviceProvider, IMessageBus bus) + { + _serviceProvider = serviceProvider; + _bus = bus; + } + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + SetSubscribers(); + return Task.CompletedTask; + } + + private void SetSubscribers() + { + _bus.SubscribeAsync("PedidoCancelado", + async request => await CancelarPedido(request)); + + _bus.SubscribeAsync("PedidoPago", + async request => await FinalizarPedido(request)); + } + + private async Task CancelarPedido(PedidoCanceladoIntegrationEvent message) + { + using (var scope = _serviceProvider.CreateScope()) + { + var pedidoRepository = scope.ServiceProvider.GetRequiredService(); + + var pedido = await pedidoRepository.ObterPorId(message.PedidoId); + pedido.CancelarPedido(); + + pedidoRepository.Atualizar(pedido); + + if (!await pedidoRepository.UnitOfWork.Commit()) + { + throw new DomainException($"Problemas ao cancelar o pedido {message.PedidoId}"); + } + } + } + + private async Task FinalizarPedido(PedidoPagoIntegrationEvent message) + { + using (var scope = _serviceProvider.CreateScope()) + { + var pedidoRepository = scope.ServiceProvider.GetRequiredService(); + + var pedido = await pedidoRepository.ObterPorId(message.PedidoId); + pedido.FinalizarPedido(); + + pedidoRepository.Atualizar(pedido); + + if (!await pedidoRepository.UnitOfWork.Commit()) + { + throw new DomainException($"Problemas ao finalizar o pedido {message.PedidoId}"); + } + } + } + } +} \ No newline at end of file diff --git a/src/services/JSE.Pedido.Domain/Pedidos/Pedido.cs b/src/services/JSE.Pedido.Domain/Pedidos/Pedido.cs index 5c60d06..f91130a 100644 --- a/src/services/JSE.Pedido.Domain/Pedidos/Pedido.cs +++ b/src/services/JSE.Pedido.Domain/Pedidos/Pedido.cs @@ -41,6 +41,16 @@ public void AutorizarPedido() PedidoStatus = PedidoStatus.Autorizado; } + public void CancelarPedido() + { + PedidoStatus = PedidoStatus.Cancelado; + } + + public void FinalizarPedido() + { + PedidoStatus = PedidoStatus.Pago; + } + public void AtribuirVoucher(Voucher voucher) { VoucherUtilizado = true; From cc2e2c74743f2b2ccacdb855a8009d0655e18df2 Mon Sep 17 00:00:00 2001 From: Jeferson Almeida Date: Fri, 3 Jan 2025 18:30:27 -0300 Subject: [PATCH 3/4] =?UTF-8?q?#271=20-=20Pagina=C3=A7=C3=A3o=20na=20API?= =?UTF-8?q?=20de=20Cat=C3=A1logo=20-=20feature/sp8/#271?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/CatalogoController.cs | 5 ++- .../Data/Repository/ProdutoRepository.cs | 35 +++++++++++++++---- .../JSE.Catalogo.API/JSE.Catalogo.API.csproj | 1 + .../Models/IProdutoRepository.cs | 2 +- .../JSE.Catalogo.API/Models/PagedResult.cs | 11 ++++++ 5 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 src/services/JSE.Catalogo.API/Models/PagedResult.cs diff --git a/src/services/JSE.Catalogo.API/Controllers/CatalogoController.cs b/src/services/JSE.Catalogo.API/Controllers/CatalogoController.cs index 2f52f3f..edec85d 100644 --- a/src/services/JSE.Catalogo.API/Controllers/CatalogoController.cs +++ b/src/services/JSE.Catalogo.API/Controllers/CatalogoController.cs @@ -15,11 +15,10 @@ public CatalogoController(IProdutoRepository produtoRepository) _produtoRepository = produtoRepository; } - [AllowAnonymous] [HttpGet("catalogo/produtos")] - public async Task> Index() + public async Task> Index([FromQuery] int ps = 8, [FromQuery] int page = 1, [FromQuery] string q = null) { - return await _produtoRepository.ObterTodos(); + return await _produtoRepository.ObterTodos(ps, page, q); } [HttpGet("catalogo/produtos/{id}")] diff --git a/src/services/JSE.Catalogo.API/Data/Repository/ProdutoRepository.cs b/src/services/JSE.Catalogo.API/Data/Repository/ProdutoRepository.cs index 739c39d..c2d7614 100644 --- a/src/services/JSE.Catalogo.API/Data/Repository/ProdutoRepository.cs +++ b/src/services/JSE.Catalogo.API/Data/Repository/ProdutoRepository.cs @@ -1,4 +1,5 @@ -using JSE.Catalogo.API.Models; +using Dapper; +using JSE.Catalogo.API.Models; using JSE.Core.Data; using Microsoft.EntityFrameworkCore; @@ -7,7 +8,7 @@ namespace JSE.Catalogo.API.Data.Repository public class ProdutoRepository : IProdutoRepository { private readonly CatalogoContext _context; - + public ProdutoRepository(CatalogoContext context) { _context = context; @@ -15,11 +16,31 @@ public ProdutoRepository(CatalogoContext context) public IUnitOfWork UnitOfWork => _context; - public async Task> ObterTodos() + public async Task> ObterTodos(int pageSize, int pageIndex, string query = null) { - return await _context.Produtos.AsNoTracking().ToListAsync(); - } + var sql = @$"SELECT * FROM Produtos + WHERE (@Nome IS NULL OR Nome LIKE '%' + @Nome + '%') + ORDER BY [Nome] + OFFSET {pageSize * (pageIndex - 1)} ROWS + FETCH NEXT {pageSize} ROWS ONLY + SELECT COUNT(Id) FROM Produtos + WHERE (@Nome IS NULL OR Nome LIKE '%' + @Nome + '%')"; + + var multi = await _context.Database.GetDbConnection() + .QueryMultipleAsync(sql, new { Nome = query }); + var produtos = multi.Read(); + var total = multi.Read().FirstOrDefault(); + + return new PagedResult() + { + List = produtos, + TotalResults = total, + PageIndex = pageIndex, + PageSize = pageSize, + Query = query + }; + } public async Task> ObterProdutosPorId(string ids) { @@ -34,14 +55,14 @@ public async Task> ObterProdutosPorId(string ids) .Where(p => idsValue.Contains(p.Id) && p.Ativo).ToListAsync(); } - public async Task ObterPorId(Guid id) + public async Task ObterPorId(Guid id) { return await _context.Produtos.FindAsync(id); } public void Adicionar(Produto produto) { - _context.Produtos.Add(produto); + _context.Produtos.Add(produto); } public void Atualizar(Produto produto) diff --git a/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj b/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj index c66dcc2..134868a 100644 --- a/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj +++ b/src/services/JSE.Catalogo.API/JSE.Catalogo.API.csproj @@ -10,6 +10,7 @@ + diff --git a/src/services/JSE.Catalogo.API/Models/IProdutoRepository.cs b/src/services/JSE.Catalogo.API/Models/IProdutoRepository.cs index 8bf29ea..c0725a1 100644 --- a/src/services/JSE.Catalogo.API/Models/IProdutoRepository.cs +++ b/src/services/JSE.Catalogo.API/Models/IProdutoRepository.cs @@ -4,7 +4,7 @@ namespace JSE.Catalogo.API.Models { public interface IProdutoRepository : IRepository { - Task> ObterTodos(); + Task> ObterTodos(int pageSize, int pageIndex, string query = null); Task ObterPorId(Guid id); Task> ObterProdutosPorId(string ids); void Adicionar(Produto produto); diff --git a/src/services/JSE.Catalogo.API/Models/PagedResult.cs b/src/services/JSE.Catalogo.API/Models/PagedResult.cs new file mode 100644 index 0000000..f690441 --- /dev/null +++ b/src/services/JSE.Catalogo.API/Models/PagedResult.cs @@ -0,0 +1,11 @@ +namespace JSE.Catalogo.API.Models +{ + public class PagedResult where T : class + { + public IEnumerable List { get; set; } + public int TotalResults { get; set; } + public int PageIndex { get; set; } + public int PageSize { get; set; } + public string Query { get; set; } + } +} From eed808f738de7cb365cf61bcf14e4212d3658a1f Mon Sep 17 00:00:00 2001 From: Jeferson Almeida Date: Sat, 4 Jan 2025 11:02:26 -0300 Subject: [PATCH 4/4] =?UTF-8?q?#272=20-=20Pagina=C3=A7=C3=A3o=20no=20MVC?= =?UTF-8?q?=20-=20feature/sp8/#272?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/CatalogoController.cs | 6 +++-- src/web/JSE.WebApp.MVC/Models/IPagedList.cs | 12 +++++++++ .../JSE.WebApp.MVC/Models/PagedViewModel.cs | 13 ++++++++++ .../Services/CatalogoService.cs | 6 ++--- .../Services/ICatalogoService.cs | 2 +- .../Views/Catalogo/Index.cshtml | 26 +++++++++++++++++-- .../Views/Shared/_Layout.cshtml | 13 ++++++++++ 7 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/web/JSE.WebApp.MVC/Models/IPagedList.cs create mode 100644 src/web/JSE.WebApp.MVC/Models/PagedViewModel.cs diff --git a/src/web/JSE.WebApp.MVC/Controllers/CatalogoController.cs b/src/web/JSE.WebApp.MVC/Controllers/CatalogoController.cs index 0428098..d425c08 100644 --- a/src/web/JSE.WebApp.MVC/Controllers/CatalogoController.cs +++ b/src/web/JSE.WebApp.MVC/Controllers/CatalogoController.cs @@ -1,6 +1,7 @@ using JSE.WebApp.MVC.Controllers; using JSE.WebApp.MVC.Services; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing.Constraints; namespace JSE.WebApp.MVC.Controllers @@ -17,9 +18,10 @@ public CatalogoController(ICatalogoService catalogoService) [HttpGet] [Route("")] [Route("vitrine")] - public async Task Index() + public async Task Index([FromQuery] int ps = 8, [FromQuery] int page = 1, [FromQuery] string q = null) { - var produtos = await _catalogoService.ObterTodos(); + var produtos = await _catalogoService.ObterTodos(ps, page, q); + ViewBag.Pesquisa = q; return View(produtos); } diff --git a/src/web/JSE.WebApp.MVC/Models/IPagedList.cs b/src/web/JSE.WebApp.MVC/Models/IPagedList.cs new file mode 100644 index 0000000..116bc17 --- /dev/null +++ b/src/web/JSE.WebApp.MVC/Models/IPagedList.cs @@ -0,0 +1,12 @@ +namespace JSE.WebApp.MVC.Models +{ + public interface IPagedList + { + public string ReferenceAction { get; set; } + public int PageIndex { get; set; } + public int PageSize { get; set; } + public string Query { get; set; } + public int TotalResults { get; set; } + public double TotalPages { get; } + } +} diff --git a/src/web/JSE.WebApp.MVC/Models/PagedViewModel.cs b/src/web/JSE.WebApp.MVC/Models/PagedViewModel.cs new file mode 100644 index 0000000..7543f76 --- /dev/null +++ b/src/web/JSE.WebApp.MVC/Models/PagedViewModel.cs @@ -0,0 +1,13 @@ +namespace JSE.WebApp.MVC.Models +{ + public class PagedViewModel : IPagedList where T : class + { + public string ReferenceAction { get; set; } + public IEnumerable List { get; set; } + public int PageIndex { get; set; } + public int PageSize { get; set; } + public string Query { get; set; } + public int TotalResults { get; set; } + public double TotalPages => Math.Ceiling((double)TotalResults / PageSize); + } +} diff --git a/src/web/JSE.WebApp.MVC/Services/CatalogoService.cs b/src/web/JSE.WebApp.MVC/Services/CatalogoService.cs index 824442d..7f52ec8 100644 --- a/src/web/JSE.WebApp.MVC/Services/CatalogoService.cs +++ b/src/web/JSE.WebApp.MVC/Services/CatalogoService.cs @@ -25,13 +25,13 @@ public async Task ObterPorId(Guid id) return await DeserializarObjetoResponse(response); } - public async Task> ObterTodos() + public async Task> ObterTodos(int pageSize, int pageIndex, string query = null) { - var response = await _httpClient.GetAsync("/catalogo/produtos/"); + var response = await _httpClient.GetAsync($"/catalogo/produtos?ps={pageSize}&page={pageIndex}&q={query}"); TratarErrosResponse(response); - return await DeserializarObjetoResponse>(response); + return await DeserializarObjetoResponse>(response); } } } \ No newline at end of file diff --git a/src/web/JSE.WebApp.MVC/Services/ICatalogoService.cs b/src/web/JSE.WebApp.MVC/Services/ICatalogoService.cs index bd38477..3336f63 100644 --- a/src/web/JSE.WebApp.MVC/Services/ICatalogoService.cs +++ b/src/web/JSE.WebApp.MVC/Services/ICatalogoService.cs @@ -4,7 +4,7 @@ namespace JSE.WebApp.MVC.Services { public interface ICatalogoService { - Task> ObterTodos(); + Task> ObterTodos(int pageSize, int pageIndex, string query = null); Task ObterPorId(Guid id); } } \ No newline at end of file diff --git a/src/web/JSE.WebApp.MVC/Views/Catalogo/Index.cshtml b/src/web/JSE.WebApp.MVC/Views/Catalogo/Index.cshtml index c00642b..b7a1c60 100644 --- a/src/web/JSE.WebApp.MVC/Views/Catalogo/Index.cshtml +++ b/src/web/JSE.WebApp.MVC/Views/Catalogo/Index.cshtml @@ -1,5 +1,5 @@ @using JSE.WebApp.MVC.Extensions -@model IEnumerable +@model PagedViewModel @{ ViewData["Title"] = "Todos os produtos"; @@ -10,7 +10,7 @@
@{ - foreach (var produto in Model.Where(p => p.Ativo).OrderBy(p => p.Nome)) + foreach (var produto in Model.List.Where(p => p.Ativo).OrderBy(p => p.Nome)) {
@@ -43,5 +43,27 @@ }
+ +
diff --git a/src/web/JSE.WebApp.MVC/Views/Shared/_Layout.cshtml b/src/web/JSE.WebApp.MVC/Views/Shared/_Layout.cshtml index a341641..9449b85 100644 --- a/src/web/JSE.WebApp.MVC/Views/Shared/_Layout.cshtml +++ b/src/web/JSE.WebApp.MVC/Views/Shared/_Layout.cshtml @@ -50,6 +50,19 @@ +
+ +
+