diff --git a/WebApiAdaptor/Components/Pages/Home.razor b/WebApiAdaptor/Components/Pages/Home.razor deleted file mode 100644 index 7591484..0000000 --- a/WebApiAdaptor/Components/Pages/Home.razor +++ /dev/null @@ -1,16 +0,0 @@ -@page "/" -@using Syncfusion.Blazor.Grids -@using Syncfusion.Blazor.Data -@using Syncfusion.Blazor -@using WebApiAdaptor.Models - - - - - - - - - - - \ No newline at end of file diff --git a/WebApiAdaptor/Controllers/GridController.cs b/WebApiAdaptor/Controllers/GridController.cs deleted file mode 100644 index 7430243..0000000 --- a/WebApiAdaptor/Controllers/GridController.cs +++ /dev/null @@ -1,177 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Syncfusion.Blazor.Data; -using WebApiAdaptor.Models; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace WebApiAdaptor.Controllers -{ - [ApiController] - [Route("api/[controller]")] - public class GridController : ControllerBase - { - [HttpGet] - public object GetOrderData() - { - var queryString = Request.Query; - List data = OrdersDetails.GetAllRecords().ToList(); - -#nullable enable - string? sort = queryString["$orderby"]; - string? filterQuery = queryString["$filter"]; -#nullable disable - - // Sorting - if (!string.IsNullOrEmpty(sort)) - { - var sortConditions = sort.Split(','); - IOrderedEnumerable? orderedData = null; - - foreach (var sortCondition in sortConditions) - { - var sortParts = sortCondition.Trim().Split(' '); - var sortBy = sortParts[0]; - var descending = sortParts.Length > 1 && sortParts[1].ToLower() == "desc"; - - Func keySelector = item => - item.GetType().GetProperty(sortBy)?.GetValue(item, null); - - orderedData = orderedData == null - ? (descending ? data.OrderByDescending(keySelector) : data.OrderBy(keySelector)) - : (descending ? orderedData.ThenByDescending(keySelector) : orderedData.ThenBy(keySelector)); - } - - data = orderedData?.ToList() ?? data; - } - - // Filtering - if (!string.IsNullOrEmpty(filterQuery)) - { - var filterConditions = filterQuery.Split(new[] { " and " }, StringSplitOptions.RemoveEmptyEntries); - - foreach (var condition in filterConditions) - { - if (condition.Contains("substringof")) - { - var conditionParts = condition.Split('(', ')', '\''); - var searchValue = conditionParts[3]?.ToLower() ?? ""; - - data = data.Where(order => - order != null && - (order.OrderID.ToString().Contains(searchValue) || - (order.CustomerID?.ToLower().Contains(searchValue) ?? false) || - (order.ShipCity?.ToLower().Contains(searchValue) ?? false) || - (order.ShipCountry?.ToLower().Contains(searchValue) ?? false)) - ).ToList(); - } - else - { - string filterfield = ""; - string filtervalue = ""; - var filterParts = condition.Split('(', ')', '\''); - - if (filterParts.Length < 6) - { - var filterValueParts = filterParts[1].Split(); - filterfield = filterValueParts[0]; - filtervalue = filterValueParts.Length > 2 ? filterValueParts[2].Trim('\'') : ""; - } - else - { - filterfield = filterParts[3]; - filtervalue = filterParts[5]; - } - - switch (filterfield) - { - case "OrderID": - data = data.Where(item => item != null && item.OrderID.ToString() == filtervalue).ToList(); - break; - case "CustomerID": - data = data.Where(item => item != null && item.CustomerID?.ToLower().StartsWith(filtervalue.ToLower()) == true).ToList(); - break; - case "ShipCity": - data = data.Where(item => item != null && item.ShipCity?.ToLower().StartsWith(filtervalue.ToLower()) == true).ToList(); - break; - case "ShipCountry": - data = data.Where(item => item != null && item.ShipCountry?.ToLower().StartsWith(filtervalue.ToLower()) == true).ToList(); - break; - } - } - } - } - - // Paging - int skip = Convert.ToInt32(queryString["$skip"]); - int take = Convert.ToInt32(queryString["$top"]); - int totalRecordsCount = data.Count; - - return take != 0 - ? new { Items = data.Skip(skip).Take(take).ToList(), Count = totalRecordsCount } - : new { Items = data, Count = totalRecordsCount }; - } - - // POST: api/Orders - /// - /// Inserts a new data item into the data collection. - /// - /// It holds new record detail which is need to be inserted. - /// Returns void - [HttpPost] - public void Post([FromBody] OrdersDetails newRecord) - { - // Insert a new record into the OrdersDetails model - OrdersDetails.GetAllRecords().Insert(0, newRecord); - } - - // PUT: api/Grid/5 - /// - /// Update a existing data item from the data collection. - /// - /// It holds updated record detail which is need to be updated. - /// Returns void - [HttpPut] - public void Put([FromBody] OrdersDetails updatedOrder) - { - var id = updatedOrder.OrderID; - // Find the existing order by ID - var existingOrder = OrdersDetails.GetAllRecords().FirstOrDefault(o => o.OrderID == id); - if (existingOrder != null) - { - // If the order exists, update its properties - existingOrder.OrderID = updatedOrder.OrderID; - existingOrder.CustomerID = updatedOrder.CustomerID; - existingOrder.ShipCity = updatedOrder.ShipCity; - existingOrder.ShipCountry = updatedOrder.ShipCountry; - } - } - - // DELETE api/Grid/5 - [HttpDelete("{id}")] - public void Delete(int id) - { - // Find the order to remove by ID - var orderToRemove = OrdersDetails.GetAllRecords().FirstOrDefault(order => order.OrderID == id); - // If the order exists, remove it - if (orderToRemove != null) - { - OrdersDetails.GetAllRecords().Remove(orderToRemove); - } - } - - public class CRUDModel where T : class - { -#nullable enable - public string? action { get; set; } - public string? keyColumn { get; set; } - public object? key { get; set; } - public T? value { get; set; } - public List? added { get; set; } - public List? changed { get; set; } - public List? deleted { get; set; } - public IDictionary? @params { get; set; } -#nullable disable - } - } -} diff --git a/WebApiAdaptor/Models/OrdersDetails.cs b/WebApiAdaptor/Models/OrdersDetails.cs deleted file mode 100644 index d033945..0000000 --- a/WebApiAdaptor/Models/OrdersDetails.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace WebApiAdaptor.Models -{ - public class OrdersDetails - { - public static List order = new List(); - public OrdersDetails() - { - - } - public OrdersDetails( - int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, - DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, - DateTime ShippedDate, string ShipAddress) - { - this.OrderID = OrderID; - CustomerID = CustomerId; - EmployeeID = EmployeeId; - this.Freight = Freight; - this.ShipCity = ShipCity; - this.Verified = Verified; - this.OrderDate = OrderDate; - this.ShipName = ShipName; - this.ShipCountry = ShipCountry; - this.ShippedDate = ShippedDate; - this.ShipAddress = ShipAddress; - } - - public static List GetAllRecords() - { - if (order.Count() == 0) - { - int code = 10000; - for (int i = 1; i < 10; i++) - { - order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6")); - order.Add(new OrdersDetails(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123")); - order.Add(new OrdersDetails(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo")); - order.Add(new OrdersDetails(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7")); - order.Add(new OrdersDetails(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S.")); - code += 5; - } - } - return order; - } - - public int? OrderID { get; set; } - public string? CustomerID { get; set; } - public int? EmployeeID { get; set; } - public double? Freight { get; set; } - public string? ShipCity { get; set; } - public bool? Verified { get; set; } - public DateTime OrderDate { get; set; } - public string? ShipName { get; set; } - public string? ShipCountry { get; set; } - public DateTime ShippedDate { get; set; } - public string? ShipAddress { get; set; } - } -} diff --git a/WebApiAdaptor/WebApiAdaptor.sln b/WebApiAdaptor/WebApiAdaptor.sln deleted file mode 100644 index 87acf2c..0000000 --- a/WebApiAdaptor/WebApiAdaptor.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 d17.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiAdaptor", "WebApiAdaptor.csproj", "{48726450-1D03-46B6-846E-FF37AC0994CA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {48726450-1D03-46B6-846E-FF37AC0994CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48726450-1D03-46B6-846E-FF37AC0994CA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48726450-1D03-46B6-846E-FF37AC0994CA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48726450-1D03-46B6-846E-FF37AC0994CA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/WebApiAdaptor/WebApiAdaptor.slnx b/WebApiAdaptor/WebApiAdaptor.slnx new file mode 100644 index 0000000..3ad159a --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor.slnx @@ -0,0 +1,3 @@ + + + diff --git a/WebApiAdaptor/Components/App.razor b/WebApiAdaptor/WebApiAdaptor/Components/App.razor similarity index 87% rename from WebApiAdaptor/Components/App.razor rename to WebApiAdaptor/WebApiAdaptor/Components/App.razor index b88fe6a..0aed304 100644 --- a/WebApiAdaptor/Components/App.razor +++ b/WebApiAdaptor/WebApiAdaptor/Components/App.razor @@ -5,19 +5,21 @@ + - + - - + + + diff --git a/WebApiAdaptor/Components/Layout/MainLayout.razor b/WebApiAdaptor/WebApiAdaptor/Components/Layout/MainLayout.razor similarity index 100% rename from WebApiAdaptor/Components/Layout/MainLayout.razor rename to WebApiAdaptor/WebApiAdaptor/Components/Layout/MainLayout.razor diff --git a/WebApiAdaptor/Components/Layout/MainLayout.razor.css b/WebApiAdaptor/WebApiAdaptor/Components/Layout/MainLayout.razor.css similarity index 100% rename from WebApiAdaptor/Components/Layout/MainLayout.razor.css rename to WebApiAdaptor/WebApiAdaptor/Components/Layout/MainLayout.razor.css diff --git a/WebApiAdaptor/Components/Layout/NavMenu.razor b/WebApiAdaptor/WebApiAdaptor/Components/Layout/NavMenu.razor similarity index 100% rename from WebApiAdaptor/Components/Layout/NavMenu.razor rename to WebApiAdaptor/WebApiAdaptor/Components/Layout/NavMenu.razor diff --git a/WebApiAdaptor/Components/Layout/NavMenu.razor.css b/WebApiAdaptor/WebApiAdaptor/Components/Layout/NavMenu.razor.css similarity index 100% rename from WebApiAdaptor/Components/Layout/NavMenu.razor.css rename to WebApiAdaptor/WebApiAdaptor/Components/Layout/NavMenu.razor.css diff --git a/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor b/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor new file mode 100644 index 0000000..e740b0c --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor @@ -0,0 +1,31 @@ + + + +
+ +

+ Rejoining the server... +

+

+ Rejoin failed... trying again in seconds. +

+

+ Failed to rejoin.
Please retry or reload the page. +

+ +

+ The session has been paused by the server. +

+

+ Failed to resume the session.
Please retry or reload the page. +

+ +
+
diff --git a/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor.css b/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor.css new file mode 100644 index 0000000..3ad3773 --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor.css @@ -0,0 +1,157 @@ +.components-reconnect-first-attempt-visible, +.components-reconnect-repeated-attempt-visible, +.components-reconnect-failed-visible, +.components-pause-visible, +.components-resume-failed-visible, +.components-rejoining-animation { + display: none; +} + +#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible, +#components-reconnect-modal.components-reconnect-show .components-rejoining-animation, +#components-reconnect-modal.components-reconnect-paused .components-pause-visible, +#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible, +#components-reconnect-modal.components-reconnect-retrying, +#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible, +#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation, +#components-reconnect-modal.components-reconnect-failed, +#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible { + display: block; +} + + +#components-reconnect-modal { + background-color: white; + width: 20rem; + margin: 20vh auto; + padding: 2rem; + border: 0; + border-radius: 0.5rem; + box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3); + opacity: 0; + transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete; + animation: components-reconnect-modal-fadeOutOpacity 0.5s both; + &[open] + +{ + animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s; + animation-fill-mode: both; +} + +} + +#components-reconnect-modal::backdrop { + background-color: rgba(0, 0, 0, 0.4); + animation: components-reconnect-modal-fadeInOpacity 0.5s ease-in-out; + opacity: 1; +} + +@keyframes components-reconnect-modal-slideUp { + 0% { + transform: translateY(30px) scale(0.95); + } + + 100% { + transform: translateY(0); + } +} + +@keyframes components-reconnect-modal-fadeInOpacity { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes components-reconnect-modal-fadeOutOpacity { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +.components-reconnect-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +#components-reconnect-modal p { + margin: 0; + text-align: center; +} + +#components-reconnect-modal button { + border: 0; + background-color: #6b9ed2; + color: white; + padding: 4px 24px; + border-radius: 4px; +} + + #components-reconnect-modal button:hover { + background-color: #3b6ea2; + } + + #components-reconnect-modal button:active { + background-color: #6b9ed2; + } + +.components-rejoining-animation { + position: relative; + width: 80px; + height: 80px; +} + + .components-rejoining-animation div { + position: absolute; + border: 3px solid #0087ff; + opacity: 1; + border-radius: 50%; + animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite; + } + + .components-rejoining-animation div:nth-child(2) { + animation-delay: -0.5s; + } + +@keyframes components-rejoining-animation { + 0% { + top: 40px; + left: 40px; + width: 0; + height: 0; + opacity: 0; + } + + 4.9% { + top: 40px; + left: 40px; + width: 0; + height: 0; + opacity: 0; + } + + 5% { + top: 40px; + left: 40px; + width: 0; + height: 0; + opacity: 1; + } + + 100% { + top: 0px; + left: 0px; + width: 80px; + height: 80px; + opacity: 0; + } +} diff --git a/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor.js b/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor.js new file mode 100644 index 0000000..a44de78 --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Components/Layout/ReconnectModal.razor.js @@ -0,0 +1,63 @@ +// Set up event handlers +const reconnectModal = document.getElementById("components-reconnect-modal"); +reconnectModal.addEventListener("components-reconnect-state-changed", handleReconnectStateChanged); + +const retryButton = document.getElementById("components-reconnect-button"); +retryButton.addEventListener("click", retry); + +const resumeButton = document.getElementById("components-resume-button"); +resumeButton.addEventListener("click", resume); + +function handleReconnectStateChanged(event) { + if (event.detail.state === "show") { + reconnectModal.showModal(); + } else if (event.detail.state === "hide") { + reconnectModal.close(); + } else if (event.detail.state === "failed") { + document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible); + } else if (event.detail.state === "rejected") { + location.reload(); + } +} + +async function retry() { + document.removeEventListener("visibilitychange", retryWhenDocumentBecomesVisible); + + try { + // Reconnect will asynchronously return: + // - true to mean success + // - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID) + // - exception to mean we didn't reach the server (this can be sync or async) + const successful = await Blazor.reconnect(); + if (!successful) { + // We have been able to reach the server, but the circuit is no longer available. + // We'll reload the page so the user can continue using the app as quickly as possible. + const resumeSuccessful = await Blazor.resumeCircuit(); + if (!resumeSuccessful) { + location.reload(); + } else { + reconnectModal.close(); + } + } + } catch (err) { + // We got an exception, server is currently unavailable + document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible); + } +} + +async function resume() { + try { + const successful = await Blazor.resumeCircuit(); + if (!successful) { + location.reload(); + } + } catch { + reconnectModal.classList.replace("components-reconnect-paused", "components-reconnect-resume-failed"); + } +} + +async function retryWhenDocumentBecomesVisible() { + if (document.visibilityState === "visible") { + await retry(); + } +} diff --git a/WebApiAdaptor/Components/Pages/Counter.razor b/WebApiAdaptor/WebApiAdaptor/Components/Pages/Counter.razor similarity index 100% rename from WebApiAdaptor/Components/Pages/Counter.razor rename to WebApiAdaptor/WebApiAdaptor/Components/Pages/Counter.razor diff --git a/WebApiAdaptor/Components/Pages/Error.razor b/WebApiAdaptor/WebApiAdaptor/Components/Pages/Error.razor similarity index 100% rename from WebApiAdaptor/Components/Pages/Error.razor rename to WebApiAdaptor/WebApiAdaptor/Components/Pages/Error.razor diff --git a/WebApiAdaptor/WebApiAdaptor/Components/Pages/Home.razor b/WebApiAdaptor/WebApiAdaptor/Components/Pages/Home.razor new file mode 100644 index 0000000..2463998 --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Components/Pages/Home.razor @@ -0,0 +1,40 @@ +@page "/" +@using WebApiAdaptor.Models + + + + + + + + + + + + + + + + + diff --git a/WebApiAdaptor/WebApiAdaptor/Components/Pages/NotFound.razor b/WebApiAdaptor/WebApiAdaptor/Components/Pages/NotFound.razor new file mode 100644 index 0000000..917ada1 --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Components/Pages/NotFound.razor @@ -0,0 +1,5 @@ +@page "/not-found" +@layout MainLayout + +

Not Found

+

Sorry, the content you are looking for does not exist.

\ No newline at end of file diff --git a/WebApiAdaptor/Components/Pages/Weather.razor b/WebApiAdaptor/WebApiAdaptor/Components/Pages/Weather.razor similarity index 95% rename from WebApiAdaptor/Components/Pages/Weather.razor rename to WebApiAdaptor/WebApiAdaptor/Components/Pages/Weather.razor index dd36b18..2a2a6cf 100644 --- a/WebApiAdaptor/Components/Pages/Weather.razor +++ b/WebApiAdaptor/WebApiAdaptor/Components/Pages/Weather.razor @@ -17,7 +17,7 @@ else Date Temp. (C) - Temp. (F) + Temp. (F) Summary diff --git a/WebApiAdaptor/Components/Routes.razor b/WebApiAdaptor/WebApiAdaptor/Components/Routes.razor similarity index 69% rename from WebApiAdaptor/Components/Routes.razor rename to WebApiAdaptor/WebApiAdaptor/Components/Routes.razor index f756e19..105855d 100644 --- a/WebApiAdaptor/Components/Routes.razor +++ b/WebApiAdaptor/WebApiAdaptor/Components/Routes.razor @@ -1,4 +1,4 @@ - + diff --git a/WebApiAdaptor/Components/_Imports.razor b/WebApiAdaptor/WebApiAdaptor/Components/_Imports.razor similarity index 86% rename from WebApiAdaptor/Components/_Imports.razor rename to WebApiAdaptor/WebApiAdaptor/Components/_Imports.razor index 8e62ab3..31c84e7 100644 --- a/WebApiAdaptor/Components/_Imports.razor +++ b/WebApiAdaptor/WebApiAdaptor/Components/_Imports.razor @@ -8,6 +8,7 @@ @using Microsoft.JSInterop @using WebApiAdaptor @using WebApiAdaptor.Components +@using WebApiAdaptor.Components.Layout @using Syncfusion.Blazor +@using Syncfusion.Blazor.Grids @using Syncfusion.Blazor.Data -@using Syncfusion.Blazor.Grids \ No newline at end of file diff --git a/WebApiAdaptor/WebApiAdaptor/Controllers/GridController.cs b/WebApiAdaptor/WebApiAdaptor/Controllers/GridController.cs new file mode 100644 index 0000000..5fb1777 --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Controllers/GridController.cs @@ -0,0 +1,384 @@ +using System.Reflection; +using Microsoft.AspNetCore.Mvc; +using WebApiAdaptor.Models; + +namespace WebApiAdaptor.Controllers +{ + /// + /// Provides a single endpoint that supports server-side searching, filtering, sorting, and paging + /// for integration with Syncfusion® Blazor DataGrid configured with WebApiAdaptor. + /// + [ApiController] + [Route("api/[controller]")] + public sealed class GridController : ControllerBase + { + /// + /// Returns order data with server-side data operations applied based on OData-style query parameters. + /// Supports: + /// + /// + /// $filter — substring search via substringof and simple equality/starts-with filtering. + /// + /// + /// $orderby — single or multiple field sorting with optional asc/desc. + /// + /// + /// $skip, $top — paging. + /// + /// + /// + /// Propagates a notification that operations should be canceled. + /// + /// HTTP 200 (OK) with: + /// + /// Items — processed collection for the current request. + /// Count — total record count prior to paging. + /// + /// + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task Get(CancellationToken cancellationToken) + { + // 1) Load base collection + List data = await Task.FromResult(OrderDetails.GetAllRecords().ToList()); + + // Compute the total record count after applying search, filter, and sort, + // but before applying paging. This value is required for WebApiAdaptor pagination. + + // 2) Search / Filter via $filter + string? filterQuery = Request.Query["$filter"]; + + if (!string.IsNullOrWhiteSpace(filterQuery)) + { + // Split multiple conditions joined by "and" + string[] conditions = filterQuery.Split( + " and ", + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + foreach (string condition in conditions) + { + // Handle substring search: substringof('value', FieldName) + if (condition.Contains("substringof", StringComparison.OrdinalIgnoreCase)) + { + string searchValue = ExtractSubstringValue(condition); + + if (!string.IsNullOrEmpty(searchValue)) + { + data = data + .Where(order => + (order.OrderID?.ToString()?.Contains(searchValue, StringComparison.OrdinalIgnoreCase) ?? false) || + (order.CustomerID?.Contains(searchValue, StringComparison.OrdinalIgnoreCase) ?? false) || + (order.ShipCity?.Contains(searchValue, StringComparison.OrdinalIgnoreCase) ?? false) || + (order.ShipCountry?.Contains(searchValue, StringComparison.OrdinalIgnoreCase) ?? false)) + .ToList(); + } + + continue; + } + + // Handle simple comparisons (e.g., "OrderID eq 10248") or quoted patterns + // Split tokens conservatively to extract field and value + string filterField = string.Empty; + string filterValue = string.Empty; + + string[] parts = condition.Split('(', ')', '\''); + + if (parts.Length < 6) + { + // Example without parentheses/quotes: OrderID eq 10248 + // tokens[0] = field, tokens[1] = operator, tokens[2] = value + string[] tokens = parts[0].Split(' ', StringSplitOptions.RemoveEmptyEntries); + + if (tokens.Length >= 3) + { + filterField = tokens[0]; + filterValue = tokens[2].Trim('\''); + } + } + else + { + // Example that includes parentheses/quoted values + // parts[3] -> field, parts[5] -> value + filterField = parts[3]; + filterValue = parts[5]; + } + + if (string.IsNullOrWhiteSpace(filterField)) + continue; + + // Apply a minimal set of operators/semantics suited for the sample: + // - For OrderID: equality match on numeric string + // - For text fields: StartsWith to approximate "eq 'prefix'" semantics in sample code + switch (filterField) + { + case "OrderID": + data = data + .Where(item => + item is not null && + (item.OrderID?.ToString()?.Equals(filterValue, StringComparison.OrdinalIgnoreCase) ?? false)) + .ToList(); + break; + + case "CustomerID": + data = data + .Where(item => + item is not null && + (item.CustomerID?.StartsWith(filterValue, StringComparison.OrdinalIgnoreCase) ?? false)) + .ToList(); + break; + + case "ShipCity": + data = data + .Where(item => + item is not null && + (item.ShipCity?.StartsWith(filterValue, StringComparison.OrdinalIgnoreCase) ?? false)) + .ToList(); + break; + + case "ShipCountry": + data = data + .Where(item => + item is not null && + (item.ShipCountry?.StartsWith(filterValue, StringComparison.OrdinalIgnoreCase) ?? false)) + .ToList(); + break; + + // Extend with additional fields when required. + } + } + } + + // 3) Sort via $orderby + string? orderByQuery = Request.Query["$orderby"]; + if (!string.IsNullOrWhiteSpace(orderByQuery)) + { + IEnumerable sortClauses = orderByQuery + .Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(c => c.Trim()); + + IOrderedEnumerable? ordered = null; + + foreach (string clause in sortClauses) + { + // Each clause: "FieldName [asc|desc]" + string[] parts = clause.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (parts.Length == 0) + continue; + + string fieldName = parts[0]; + bool isDesc = parts.Length > 1 && parts[1].Equals("desc", StringComparison.OrdinalIgnoreCase); + + PropertyInfo? prop = typeof(OrderDetails).GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance); + if (prop is null) + continue; + + Func keySelector = item => prop.GetValue(item, null); + + ordered = ordered is null + ? (isDesc ? data.OrderByDescending(keySelector) : data.OrderBy(keySelector)) + : (isDesc ? ordered.ThenByDescending(keySelector) : ordered.ThenBy(keySelector)); + } + + if (ordered is not null) + { + data = ordered.ToList(); + } + } + + // Compute total after search/filter/sort, before paging (required by WebApiAdaptor) + int totalRecordsCount = data.Count; + + // 4) Paging via $skip / $top + string? skipQueryValue = Request.Query["$skip"]; + string? topQueryValue = Request.Query["$top"]; + + _ = int.TryParse(skipQueryValue, out int skip); + _ = int.TryParse(topQueryValue, out int top); + + if (skip < 0) skip = 0; + if (top < 0) top = 0; + + IEnumerable items = top > 0 + ? data.Skip(skip).Take(top) + : data; + + return Ok(new + { + Items = items.ToList(), + Count = totalRecordsCount + }); + } + + /// + /// Extracts the search value from an OData substringof expression. + /// + /// Expression such as substringof('abc', CustomerID). + /// Extracted search value, or an empty string when the pattern is invalid. + private static string ExtractSubstringValue(string condition) + { + int firstQuote = condition.IndexOf('\''); + if (firstQuote < 0) + return string.Empty; + + int secondQuote = condition.IndexOf('\'', firstQuote + 1); + if (secondQuote <= firstQuote) + return string.Empty; + + return condition.Substring(firstQuote + 1, secondQuote - firstQuote - 1); + } + + + + // ------------------------------------------------------------- + // GET BY ID (Required for CreatedAtAction in Insert Operation) + // ------------------------------------------------------------- + + /// + /// Retrieves an entity by its primary key. + /// + /// Represents the primary key of the entity to retrieve. + /// Propagates a notification that operations should be canceled. + /// + /// Returns with the entity when found; otherwise . + /// + /// Returns the matched entity. + /// No entity exists with the specified identifier. + [HttpGet("{id:int}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetById(int id, CancellationToken cancellationToken) + { + var entity = await Task.FromResult( + OrderDetails.GetAllRecords().FirstOrDefault(o => o.OrderID == id)); + + return entity is null ? NotFound() : Ok(entity); + } + + + // ------------------------------------------------------------- + // INSERT OPERATION (HTTP POST) + // ------------------------------------------------------------ + + /// + /// Inserts a new entity into the in-memory collection. + /// + /// Represents the entity to insert. + /// Propagates a notification that operations should be canceled. + /// + /// Returns with the created entity on success; + /// otherwise for validation failures. + /// + /// + /// This sample uses an in-memory collection for demonstration. Replace with persistent storage as required. + /// + /// Entity created successfully; Location header contains the resource URL. + /// Validation failed or a duplicate key exists. + [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public async Task Post( + [FromBody] OrderDetails? newRecord, + CancellationToken cancellationToken) + { + if (newRecord is null) + return BadRequest("Request body is required."); + + if (newRecord.OrderID == 0) + return BadRequest("OrderID must be a non-zero value."); + + var store = OrderDetails.GetAllRecords(); + + if (store.Any(o => o.OrderID == newRecord.OrderID)) + return BadRequest($"An entity with OrderID '{newRecord.OrderID}' already exists."); + + // Insert at the beginning to mirror original samples. + store.Insert(0, newRecord); + + // Return 201 with a canonical resource location. + return await Task.FromResult( + CreatedAtAction(nameof(GetById), new { id = newRecord.OrderID }, newRecord)); + } + + // ------------------------------------------------------------- + // UPDATE OPERATION (HTTP PUT) + // ------------------------------------------------------------ + + /// + /// Updates an existing entity in the in-memory collection. + /// + /// Represents the entity with updated values. + /// Propagates a notification that operations should be canceled. + /// + /// Returns when the update succeeds; + /// when the input is invalid; + /// when no entity matches the provided key. + /// + /// + /// If editing of the primary key is not intended, omit assignments that modify OrderID. + /// + /// Entity updated successfully. + /// Validation failed. + /// No entity exists with the specified identifier. + [HttpPut] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Put( + [FromBody] OrderDetails? updatedRecord, + CancellationToken cancellationToken) + { + if (updatedRecord is null) + return BadRequest("Request body is required."); + + if (updatedRecord.OrderID == 0) + return BadRequest("OrderID must be provided for update."); + + var store = OrderDetails.GetAllRecords(); + + var existing = store.FirstOrDefault(o => o.OrderID == updatedRecord.OrderID); + if (existing is null) + return NotFound($"No entity found with OrderID '{updatedRecord.OrderID}'."); + + // Apply updates to the supported editable fields. + existing.CustomerID = updatedRecord.CustomerID; + existing.ShipCity = updatedRecord.ShipCity; + existing.ShipCountry = updatedRecord.ShipCountry; + + // Assign only when primary key modification is intended + existing.OrderID = updatedRecord.OrderID; + + return await Task.FromResult(NoContent()); + } + + // ------------------------------------------------------------- + // DELETE OPERATION (HTTP DELETE) + // ------------------------------------------------------------ + + /// + /// Deletes an entity by its primary key from the in-memory collection. + /// + /// Represents the identifier of the entity to delete. + /// Propagates a notification that operations should be canceled. + /// + /// Returns when the entity is deleted; + /// otherwise when the entity does not exist. + /// + /// Entity deleted successfully. + /// No entity exists with the specified identifier. + [HttpDelete("{id:int}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Delete(int id, CancellationToken cancellationToken) + { + var store = OrderDetails.GetAllRecords(); + + var toRemove = store.FirstOrDefault(o => o.OrderID == id); + if (toRemove is null) + return NotFound($"No entity found with OrderID '{id}'."); + + _ = store.Remove(toRemove); + + return await Task.FromResult(NoContent()); + } + } +} \ No newline at end of file diff --git a/WebApiAdaptor/WebApiAdaptor/Models/OrderDetails.cs b/WebApiAdaptor/WebApiAdaptor/Models/OrderDetails.cs new file mode 100644 index 0000000..70140f8 --- /dev/null +++ b/WebApiAdaptor/WebApiAdaptor/Models/OrderDetails.cs @@ -0,0 +1,148 @@ +namespace WebApiAdaptor.Models +{ + /// + /// Represents an order entity used in Web API samples that integrate with the + /// Syncfusion® Blazor DataGrid via WebApiAdaptor. + /// + /// + /// This type is intended for demonstration purposes and uses an in-memory + /// backing store. Replace with a persistent data store for production. + /// + public sealed class OrderDetails + { + // In-memory backing store (shared for the app lifetime). + private static readonly List _orders = new(); + + // ✅ Static constructor seeds once before the first use of this type. + static OrderDetails() => Seed(_orders); + + /// + /// Initializes a new instance of the class. + /// + public OrderDetails() + { + } + + /// + /// Initializes a new instance of the class with all fields. + /// + /// Order identifier. + /// Customer identifier. + /// Employee identifier. + /// Freight charge associated with the order. + /// Indicates whether the order is verified. + /// Order creation date. + /// Destination city. + /// Recipient or shipping name. + /// Destination country. + /// Shipment date. + /// Destination address. + public OrderDetails( + int orderId, + string customerId, + int employeeId, + double freight, + bool verified, + DateTime orderDate, + string shipCity, + string shipName, + string shipCountry, + DateTime shippedDate, + string shipAddress) + { + OrderID = orderId; + CustomerID = customerId; + EmployeeID = employeeId; + Freight = freight; + Verified = verified; + OrderDate = orderDate; + ShipCity = shipCity; + ShipName = shipName; + ShipCountry = shipCountry; + ShippedDate = shippedDate; + ShipAddress = shipAddress; + } + + /// + /// Returns the in-memory collection of orders. + /// + /// A containing entities. + public static List GetAllRecords() => _orders; + + /// + /// Seeds the provided collection with deterministic demo data. + /// + /// A target list to populate. + private static void Seed(List target) + { + var code = 10000; + + for (var i = 1; i < 10; i++) + { + target.Add(new OrderDetails( + code + 1, "ALFKI", i + 0, 2.3 * i, false, + new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", + new DateTime(1996, 07, 16), "Kirchgasse 6")); + + target.Add(new OrderDetails( + code + 2, "ANATR", i + 2, 3.3 * i, true, + new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", + new DateTime(1996, 09, 11), "Avda. Azteca 123")); + + target.Add(new OrderDetails( + code + 3, "ANTON", i + 1, 4.3 * i, true, + new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", + new DateTime(1996, 10, 07), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo")); + + target.Add(new OrderDetails( + code + 4, "BLONP", i + 3, 5.3 * i, false, + new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", + new DateTime(1996, 12, 30), "Magazinweg 7")); + + target.Add(new OrderDetails( + code + 5, "BOLID", i + 4, 6.3 * i, true, + new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", + new DateTime(1997, 12, 03), "1029 - 12th Ave. S.")); + + code += 5; + } + } + + // ======================= + // Public properties + // ======================= + + /// Gets or sets the order identifier. + public int? OrderID { get; set; } + + /// Gets or sets the customer identifier. + public string? CustomerID { get; set; } + + /// Gets or sets the employee identifier. + public int? EmployeeID { get; set; } + + /// Gets or sets the freight charge. + public double? Freight { get; set; } + + /// Gets or sets the destination city. + public string? ShipCity { get; set; } + + /// Gets or sets a value indicating whether the order is verified. + public bool? Verified { get; set; } + + /// Gets or sets the order date. + public DateTime OrderDate { get; set; } + + /// Gets or sets the recipient or shipping name. + public string? ShipName { get; set; } + + /// Gets or sets the destination country. + public string? ShipCountry { get; set; } + + /// Gets or sets the shipment date. + public DateTime ShippedDate { get; set; } + + /// Gets or sets the destination address. + public string? ShipAddress { get; set; } + } +} \ No newline at end of file diff --git a/WebApiAdaptor/Program.cs b/WebApiAdaptor/WebApiAdaptor/Program.cs similarity index 84% rename from WebApiAdaptor/Program.cs rename to WebApiAdaptor/WebApiAdaptor/Program.cs index 5e7d986..2ec545c 100644 --- a/WebApiAdaptor/Program.cs +++ b/WebApiAdaptor/WebApiAdaptor/Program.cs @@ -6,10 +6,9 @@ // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); - -builder.Services.AddControllers(); - builder.Services.AddSyncfusionBlazor(); +// Register controller services. +builder.Services.AddControllers(); var app = builder.Build(); // Configure the HTTP request pipeline. @@ -19,12 +18,12 @@ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } - +app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseHttpsRedirection(); -app.MapControllers(); app.UseAntiforgery(); - +// Map controller endpoints. +app.MapControllers(); app.MapStaticAssets(); app.MapRazorComponents() .AddInteractiveServerRenderMode(); diff --git a/WebApiAdaptor/Properties/launchSettings.json b/WebApiAdaptor/WebApiAdaptor/Properties/launchSettings.json similarity index 81% rename from WebApiAdaptor/Properties/launchSettings.json rename to WebApiAdaptor/WebApiAdaptor/Properties/launchSettings.json index d81ba2f..d490344 100644 --- a/WebApiAdaptor/Properties/launchSettings.json +++ b/WebApiAdaptor/WebApiAdaptor/Properties/launchSettings.json @@ -5,7 +5,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "http://localhost:5198", + "applicationUrl": "http://localhost:5028", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -14,7 +14,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:7167;http://localhost:5198", + "applicationUrl": "https://localhost:7041;http://localhost:5028", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/WebApiAdaptor/WebApiAdaptor.csproj b/WebApiAdaptor/WebApiAdaptor/WebApiAdaptor.csproj similarity index 59% rename from WebApiAdaptor/WebApiAdaptor.csproj rename to WebApiAdaptor/WebApiAdaptor/WebApiAdaptor.csproj index dc91d0c..ae7cd00 100644 --- a/WebApiAdaptor/WebApiAdaptor.csproj +++ b/WebApiAdaptor/WebApiAdaptor/WebApiAdaptor.csproj @@ -1,15 +1,15 @@ - + - net9.0 + net10.0 enable enable + true - - - + + diff --git a/WebApiAdaptor/WebApiAdaptor.csproj.user b/WebApiAdaptor/WebApiAdaptor/WebApiAdaptor.csproj.user similarity index 100% rename from WebApiAdaptor/WebApiAdaptor.csproj.user rename to WebApiAdaptor/WebApiAdaptor/WebApiAdaptor.csproj.user diff --git a/WebApiAdaptor/appsettings.Development.json b/WebApiAdaptor/WebApiAdaptor/appsettings.Development.json similarity index 100% rename from WebApiAdaptor/appsettings.Development.json rename to WebApiAdaptor/WebApiAdaptor/appsettings.Development.json diff --git a/WebApiAdaptor/appsettings.json b/WebApiAdaptor/WebApiAdaptor/appsettings.json similarity index 100% rename from WebApiAdaptor/appsettings.json rename to WebApiAdaptor/WebApiAdaptor/appsettings.json diff --git a/WebApiAdaptor/wwwroot/app.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/app.css similarity index 100% rename from WebApiAdaptor/wwwroot/app.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/app.css diff --git a/WebApiAdaptor/wwwroot/favicon.png b/WebApiAdaptor/WebApiAdaptor/wwwroot/favicon.png similarity index 100% rename from WebApiAdaptor/wwwroot/favicon.png rename to WebApiAdaptor/WebApiAdaptor/wwwroot/favicon.png diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js diff --git a/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map b/WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map similarity index 100% rename from WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map rename to WebApiAdaptor/WebApiAdaptor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map