diff --git a/Groceries/Components/Modal.razor b/Groceries/Components/Modal.razor new file mode 100644 index 0000000..ab6fbfa --- /dev/null +++ b/Groceries/Components/Modal.razor @@ -0,0 +1,13 @@ +@inherits LayoutComponentBase + + +
+
+

+ +

+ +
+ @Body +
+
diff --git a/Groceries/HttpRequestExtensions.cs b/Groceries/HttpRequestExtensions.cs index 91ba443..0c1b877 100644 --- a/Groceries/HttpRequestExtensions.cs +++ b/Groceries/HttpRequestExtensions.cs @@ -2,6 +2,42 @@ namespace Groceries; public static class HttpRequestExtensions { + public static Uri GetUri(this HttpRequest request) + { + var builder = new UriBuilder + { + Scheme = request.Scheme, + Host = request.Host.Host, + Port = request.Host.Port.GetValueOrDefault(-1), + Path = request.Path.ToUriComponent(), + Query = request.QueryString.ToUriComponent(), + }; + return builder.Uri; + } + + private static Uri GetOrigin(this HttpRequest request) + { + var builder = new UriBuilder + { + Scheme = request.Scheme, + Host = request.Host.Host, + Port = request.Host.Port.GetValueOrDefault(-1), + }; + return builder.Uri; + } + + private static bool IsSameOrigin(this HttpRequest request, Uri uri) + { + var origin = request.GetOrigin(); + return origin.IsBaseOf(uri); + } + + public static Uri? GetRefererIfSameOrigin(this HttpRequest request) + { + var referer = request.GetTypedHeaders().Referer; + return referer != null && request.IsSameOrigin(referer) ? referer : null; + } + public static bool IsTurboFrameRequest(this HttpRequest request, string frameId) { return request.Headers.TryGetValue("Turbo-Frame", out var values) && values.Contains(frameId); diff --git a/Groceries/Stores/EditStore.cshtml b/Groceries/Stores/EditStore.cshtml deleted file mode 100644 index 4a932a8..0000000 --- a/Groceries/Stores/EditStore.cshtml +++ /dev/null @@ -1,36 +0,0 @@ -@using Microsoft.AspNetCore.Http -@using Microsoft.AspNetCore.Http.Extensions; - -@model Groceries.Data.Store -@{ - ViewBag.Title = "Edit Store"; - - var returnUrl = Url.Action("Index", new { page = 1 }); - if (Context.Request.GetTypedHeaders().Referer is Uri referer && referer.Host == Context.Request.Host.Host) - { - var requestUrl = new UriBuilder - { - Scheme = Context.Request.Scheme, - Host = Context.Request.Host.Host, - Port = Context.Request.Host.Port.GetValueOrDefault(-1), - Path = Context.Request.Path.ToString(), - Query = Context.Request.QueryString.ToString(), - }.Uri; - - if (referer != requestUrl) - { - returnUrl = referer.PathAndQuery; - } - } -} - -

Edit Store

- -
- - -
- - Cancel -
- diff --git a/Groceries/Stores/EditStoreModal.razor b/Groceries/Stores/EditStoreModal.razor new file mode 100644 index 0000000..d30c98b --- /dev/null +++ b/Groceries/Stores/EditStoreModal.razor @@ -0,0 +1,17 @@ +@using Groceries.Data + +@layout Modal + +Edit Store + + + +
+ + +
+ +@code { + [Parameter] + public required Store Store { get; set; } +} diff --git a/Groceries/Stores/EditStorePage.razor b/Groceries/Stores/EditStorePage.razor new file mode 100644 index 0000000..90dfb8b --- /dev/null +++ b/Groceries/Stores/EditStorePage.razor @@ -0,0 +1,32 @@ +@using Groceries.Data + +@layout Layout + +@inject IHttpContextAccessor HttpContextAccessor + +Groceries – Edit Store + +

Edit Store

+ + +
+ + Cancel +
+
+ +@code { + [Parameter] + public required Store Store { get; set; } + + private string returnUrl = "/stores?page=1"; + + protected override void OnInitialized() + { + var request = HttpContextAccessor.HttpContext!.Request; + if (request.GetRefererIfSameOrigin() is Uri referer && referer != request.GetUri()) + { + returnUrl = referer.PathAndQuery; + } + } +} diff --git a/Groceries/Stores/EditStore_Modal.cshtml b/Groceries/Stores/EditStore_Modal.cshtml deleted file mode 100644 index a21e4a7..0000000 --- a/Groceries/Stores/EditStore_Modal.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@model Groceries.Data.Store -@{ - Layout = "_Modal"; - ViewBag.Title = "Edit Store"; -} - -
- - - -
- - -
diff --git a/Groceries/Stores/NewStore.cshtml b/Groceries/Stores/NewStore.cshtml deleted file mode 100644 index eb9f6c6..0000000 --- a/Groceries/Stores/NewStore.cshtml +++ /dev/null @@ -1,35 +0,0 @@ -@using Microsoft.AspNetCore.Http -@using Microsoft.AspNetCore.Http.Extensions; - -@{ - ViewBag.Title = "New Store"; - - var returnUrl = Url.Action("Index", new { page = 1 }); - if (Context.Request.GetTypedHeaders().Referer is Uri referer && referer.Host == Context.Request.Host.Host) - { - var requestUrl = new UriBuilder - { - Scheme = Context.Request.Scheme, - Host = Context.Request.Host.Host, - Port = Context.Request.Host.Port.GetValueOrDefault(-1), - Path = Context.Request.Path.ToString(), - Query = Context.Request.QueryString.ToString(), - }.Uri; - - if (referer != requestUrl) - { - returnUrl = referer.PathAndQuery; - } - } -} - -

New Store

- -
- - -
- - Cancel -
- diff --git a/Groceries/Stores/NewStoreModal.razor b/Groceries/Stores/NewStoreModal.razor new file mode 100644 index 0000000..4752601 --- /dev/null +++ b/Groceries/Stores/NewStoreModal.razor @@ -0,0 +1,10 @@ +@layout Modal + +New Store + + + +
+ + +
diff --git a/Groceries/Stores/NewStorePage.razor b/Groceries/Stores/NewStorePage.razor new file mode 100644 index 0000000..d34ef34 --- /dev/null +++ b/Groceries/Stores/NewStorePage.razor @@ -0,0 +1,27 @@ +@layout Layout + +@inject IHttpContextAccessor HttpContextAccessor + +Groceries – New Store + +

New Store

+ + +
+ + Cancel +
+
+ +@code { + private string returnUrl = "/stores?page=1"; + + protected override void OnInitialized() + { + var request = HttpContextAccessor.HttpContext!.Request; + if (request.GetRefererIfSameOrigin() is Uri referer && referer != request.GetUri()) + { + returnUrl = referer.PathAndQuery; + } + } +} diff --git a/Groceries/Stores/NewStore_Modal.cshtml b/Groceries/Stores/NewStore_Modal.cshtml deleted file mode 100644 index a563269..0000000 --- a/Groceries/Stores/NewStore_Modal.cshtml +++ /dev/null @@ -1,13 +0,0 @@ -@{ - Layout = "_Modal"; - ViewBag.Title = "New Store"; -} - -
- - - -
- - -
diff --git a/Groceries/Stores/StoreForm.razor b/Groceries/Stores/StoreForm.razor new file mode 100644 index 0000000..5166e47 --- /dev/null +++ b/Groceries/Stores/StoreForm.razor @@ -0,0 +1,52 @@ +@using Groceries.Data +@using Microsoft.EntityFrameworkCore + +@inject AppDbContext DbContext + +
+
+ + +
+ +
+ +
+ +
+
+ +
+ + +
+ + @ChildContent +
+ +@code { + [Parameter] + public Store? Store { get; set; } + + [Parameter] + public RenderFragment? ChildContent { get; set; } + + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary? AdditionalAttributes { get; set; } + + private Retailer[] retailers = []; + + protected override async Task OnInitializedAsync() + { + retailers = await DbContext.Retailers + .OrderBy(retailer => retailer.Name) + .ToArrayAsync(); + } +} diff --git a/Groceries/Stores/StoresController.cs b/Groceries/Stores/StoresController.cs index 109c4be..db2888e 100644 --- a/Groceries/Stores/StoresController.cs +++ b/Groceries/Stores/StoresController.cs @@ -22,15 +22,15 @@ public class StoresController : Controller } [HttpGet("new")] - public IActionResult NewStore() + public IResult NewStore() { return Request.IsTurboFrameRequest("modal") - ? View($"{nameof(NewStore)}_Modal") - : View(); + ? new RazorComponentResult() + : new RazorComponentResult(); } [HttpPost("new")] - public async Task NewStore(Guid retailerId, string name, string? address) + public async Task NewStore(Guid retailerId, string name, string? address) { var store = new Store(retailerId, name, address); dbContext.Stores.Add(store); @@ -38,28 +38,28 @@ public class StoresController : Controller await dbContext.SaveChangesAsync(HttpContext.RequestAborted); return Request.IsTurboFrameRequest("modal") - ? NoContent() - : RedirectToAction(nameof(Index), new { page = 1 }); + ? Results.NoContent() + : Results.LocalRedirect("/stores?page=1"); } [HttpGet("edit/{id}")] - public async Task EditStore(Guid id) + public async Task EditStore(Guid id) { var store = await dbContext.Stores .SingleOrDefaultAsync(store => store.Id == id, HttpContext.RequestAborted); if (store == null) { - return NotFound(); + return Results.NotFound(); } return Request.IsTurboFrameRequest("modal") - ? View($"{nameof(EditStore)}_Modal", store) - : View(store); + ? new RazorComponentResult(new { Store = store }) + : new RazorComponentResult(new { Store = store }); } [HttpPost("edit/{id}")] - public async Task EditStore(Guid id, Guid retailerId, string name, string? address) + public async Task EditStore(Guid id, Guid retailerId, string name, string? address, string? returnUrl) { var store = new Store(id, retailerId, name, address); dbContext.Stores.Update(store); @@ -67,7 +67,7 @@ public class StoresController : Controller await dbContext.SaveChangesAsync(HttpContext.RequestAborted); return Request.IsTurboFrameRequest("modal") - ? NoContent() - : RedirectToAction(nameof(EditStore)); + ? Results.NoContent() + : Results.LocalRedirect($"/stores/edit/{id}"); } } diff --git a/Groceries/Stores/_StoreForm.cshtml b/Groceries/Stores/_StoreForm.cshtml deleted file mode 100644 index b9d7f7f..0000000 --- a/Groceries/Stores/_StoreForm.cshtml +++ /dev/null @@ -1,35 +0,0 @@ -@using Groceries.Data -@using Microsoft.EntityFrameworkCore - -@model Store? -@inject AppDbContext dbContext -@{ - var retailers = await dbContext.Retailers - .OrderBy(retailer => retailer.Name) - .ToListAsync(); -} - -
- - -
- -
- -
- -
-
- -
- - -
diff --git a/Groceries/_Imports.razor b/Groceries/_Imports.razor index 2f5b3ae..104a483 100644 --- a/Groceries/_Imports.razor +++ b/Groceries/_Imports.razor @@ -1,3 +1,4 @@ @using Groceries.Components @using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Sections @using Microsoft.AspNetCore.Components.Web