+
diff --git a/TELBlazor.Components.ShowCase.Shared/Pages/Error.razor b/TELBlazor.Components.ShowCase.Shared/Pages/Error.razor index 4248f90..36c62e9 100644 --- a/TELBlazor.Components.ShowCase.Shared/Pages/Error.razor +++ b/TELBlazor.Components.ShowCase.Shared/Pages/Error.razor @@ -1,4 +1,6 @@ @page "/Error/" -This error has taken you to the ocean! ... +Error Image Finn Ocean \ No newline at end of file diff --git a/TELBlazor.Components.ShowCase.Shared/Pages/NoJSFormSubmitStubPage.razor b/TELBlazor.Components.ShowCase.Shared/Pages/NoJSFormSubmitStubPage.razor new file mode 100644 index 0000000..48d32a5 --- /dev/null +++ b/TELBlazor.Components.ShowCase.Shared/Pages/NoJSFormSubmitStubPage.razor @@ -0,0 +1,12 @@ +@page "/nojsformsubmitstubpage" + +

Welcome to nojsformsubmitstubpage

+

NoJS form submit stub page, if your here your in a nojs browser or clicked quick on a prerender hosting of the page, + testing can catch the request before it gets here and check the data being sent is correct before redirection happens. +

+

but if you're not here because of the about then something went wrong...

+

Welcome to the ocean!

+Error Image Finn Ocean + diff --git a/TELBlazor.Components.ShowCase.Shared/Pages/OptionalImplementations/TestComponentPages/SearchExperimentPage.razor b/TELBlazor.Components.ShowCase.Shared/Pages/OptionalImplementations/TestComponentPages/SearchExperimentPage.razor index 4e3352d..2f1c1d4 100644 --- a/TELBlazor.Components.ShowCase.Shared/Pages/OptionalImplementations/TestComponentPages/SearchExperimentPage.razor +++ b/TELBlazor.Components.ShowCase.Shared/Pages/OptionalImplementations/TestComponentPages/SearchExperimentPage.razor @@ -3,13 +3,23 @@ - + IsTestComponent="@IsTestComponent" + ComponentReadMeMarkdownUrl="@ComponentReadMeMarkdownUrl"> + + @code { private bool IsTestComponent => true; private string Title => "SearchExperiment"; - private string Instructions => "Try searching"; + private string Instructions => "Try searching: ⚠️ Warning: This is a test element. It is only hitting the suggestion endpoint currently for searches and for suggestions, as that is all that is available. Suggestions shown won’t necessarily lead to effective or relevant search results."; private string Description => "This is not a prototype for the new search component its a test component just for a component needing a service only"; -} + private string NoJSSearchText = "Example previous nojs search"; + private string NoJSActionUrl = "/nojsformsubmitstubpage"; + private List NoJSSearchResults = new List() {"In a nojs browser", " the user would first get a blanks components ", " they then send their data", " the Controller would provide","this static info back", "including original provideds search", "having used the same service", "serverside in its controller"}; + private List NoJSSuggestions = new List() {"Unsure if this would work ","it might do but we wouldnt", " want to spam a page reload per button press ", " so maybe there would be different suggestions in nojs" }; + private string ComponentReadMeMarkdownUrl = "https://raw.githubusercontent.com/TechnologyEnhancedLearning/TELBlazor/refs/heads/master/TELBlazor.Components/OptionalImplementations/TestComponents/SearchExperiment/README.md"; +} \ No newline at end of file diff --git a/TELBlazor.Components.ShowCase.Shared/TELBlazor.Components.ShowCase.Shared.csproj b/TELBlazor.Components.ShowCase.Shared/TELBlazor.Components.ShowCase.Shared.csproj index 46dc96c..8e19aef 100644 --- a/TELBlazor.Components.ShowCase.Shared/TELBlazor.Components.ShowCase.Shared.csproj +++ b/TELBlazor.Components.ShowCase.Shared/TELBlazor.Components.ShowCase.Shared.csproj @@ -10,6 +10,7 @@ + @@ -33,4 +34,5 @@ --> + diff --git a/TELBlazor.Components.ShowCase.Shared/Utilities/JSDisabledWarning.razor b/TELBlazor.Components.ShowCase.Shared/Utilities/JSDisabledWarning.razor new file mode 100644 index 0000000..667a1b3 --- /dev/null +++ b/TELBlazor.Components.ShowCase.Shared/Utilities/JSDisabledWarning.razor @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/TELBlazor.Components.ShowCase.Shared/_Imports.razor b/TELBlazor.Components.ShowCase.Shared/_Imports.razor index a54e1eb..62c1560 100644 --- a/TELBlazor.Components.ShowCase.Shared/_Imports.razor +++ b/TELBlazor.Components.ShowCase.Shared/_Imports.razor @@ -11,8 +11,9 @@ @using TELBlazor.Components.Core.Extensions @using Microsoft.AspNetCore.Components.Forms @using TELBlazor.Components.Components.BaseComponents -@using TELBlazor.Components.OptionalImplementations.TestComponents +@using TELBlazor.Components.OptionalImplementations.Test.TestComponents @using Microsoft.Extensions.Logging; @using Microsoft.AspNetCore.Components.Routing @using TELBlazor.Components.ShowCase.Shared.Layouts.ComponentLayouts -@using TELBlazor.Components.OptionalImplementations.TestComponents.SearchExperiment +@using TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment +@using TELBlazor.Components.ShowCase.Shared.Utilities diff --git a/TELBlazor.Components.ShowCase.Shared/packages.lock.json b/TELBlazor.Components.ShowCase.Shared/packages.lock.json index 555483d..354fe01 100644 --- a/TELBlazor.Components.ShowCase.Shared/packages.lock.json +++ b/TELBlazor.Components.ShowCase.Shared/packages.lock.json @@ -11,6 +11,12 @@ "Microsoft.AspNetCore.Components.Web": "8.0.0" } }, + "Markdig": { + "type": "Direct", + "requested": "[0.41.3, )", + "resolved": "0.41.3", + "contentHash": "i3vSTyGpBGWbJB04aJ3cPJs0T3BV2e1nduW3EUHK/i+xUupYbym75iZPss/XjqhS5JlBErwQYnx7ofK3Zcsozg==" + }, "Microsoft.AspNetCore.Components.Web": { "type": "Direct", "requested": "[8.0.14, )", diff --git a/TELBlazor.Components.ShowCase.WasmStaticClient/Program.cs b/TELBlazor.Components.ShowCase.WasmStaticClient/Program.cs index 143e35f..9387134 100644 --- a/TELBlazor.Components.ShowCase.WasmStaticClient/Program.cs +++ b/TELBlazor.Components.ShowCase.WasmStaticClient/Program.cs @@ -1,35 +1,31 @@ // Microsoft namespaces +// Still required server side even if not used so components dont fail +using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; - // Serilog core (used via appsettings, do not delete even if vs marks not in use) using Serilog; using Serilog.Configuration; using Serilog.Core; using Serilog.Events; - // Serilog extensions and sinks (used via appsettings, do not delete even if vs marks not in use) using Serilog.Extensions.Logging; using Serilog.Formatting.Compact; using Serilog.Settings.Configuration; using Serilog.Sinks.BrowserConsole; - -// Still required server side even if not used so components dont fail -using Blazored.LocalStorage; - // TELBlazor components using TELBlazor.Components.Core.Configuration; using TELBlazor.Components.Core.Services.HelperServices; - -using TELBlazor.Components.ShowCase.WasmStaticClient; +using TELBlazor.Components.OptionalImplementations.Core.DI; using TELBlazor.Components.OptionalImplementations.Core.Services.HelperServices; +using TELBlazor.Components.ShowCase.WasmStaticClient; var builder = WebAssemblyHostBuilder.CreateDefault(args); @@ -78,7 +74,7 @@ builder.Services.AddScoped(sp => levelSwitch); builder.Services.AddScoped(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - + builder.Services.AddTELBlazorComponentServicesForTestComponents(); await builder.Build().RunAsync(); } catch (Exception ex) diff --git a/TELBlazor.Components.ShowCase.WasmStaticClient/packages.lock.json b/TELBlazor.Components.ShowCase.WasmStaticClient/packages.lock.json index a5fc1e5..05c381e 100644 --- a/TELBlazor.Components.ShowCase.WasmStaticClient/packages.lock.json +++ b/TELBlazor.Components.ShowCase.WasmStaticClient/packages.lock.json @@ -76,15 +76,15 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.16, )", - "resolved": "8.0.16", - "contentHash": "0H1QaKpVibe++Zx6EYJQGhrpfz2bBPGiQ7Rpsmx8I3+oKv+ZRRIfVfmcj50KuZlhhRE6V02y5bUjP+V2oPM2ng==" + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, "Microsoft.NET.Sdk.WebAssembly.Pack": { "type": "Direct", - "requested": "[8.0.16, )", - "resolved": "8.0.16", - "contentHash": "Lu5e8iqQzLnMrD0WmHz6yw7EEauaP9Ip5RxtZgSmi1GhcymZvL1MU6vFerVHbT+FPuqHSlEyM3rXtcQE8NElAw==" + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "wufm5kIsP5PlG54bahrhJDPbN114ru7mIYOdyVQS4sCr3811SLqcn1X6rg3mxZgGxRbyfMcMBXcg5P2HV/MRYQ==" }, "Serilog": { "type": "Direct", @@ -461,11 +461,18 @@ "type": "Project", "dependencies": { "Blazored.LocalStorage": "[4.5.0, )", + "Markdig": "[0.41.3, )", "Microsoft.AspNetCore.Components.Web": "[8.0.14, )", "Serilog": "[4.2.0, )", - "TELBlazor.Components": "[1.8.0, )" + "TELBlazor.Components": "[1.9.2-local, )" } }, + "Markdig": { + "type": "CentralTransitive", + "requested": "[0.41.3, )", + "resolved": "0.41.3", + "contentHash": "i3vSTyGpBGWbJB04aJ3cPJs0T3BV2e1nduW3EUHK/i+xUupYbym75iZPss/XjqhS5JlBErwQYnx7ofK3Zcsozg==" + }, "Microsoft.AspNetCore.Components.Web": { "type": "CentralTransitive", "requested": "[8.0.14, )", diff --git a/TELBlazor.Components.ShowCase.WasmStaticClient/wwwroot/appsettings.Development.json.template b/TELBlazor.Components.ShowCase.WasmStaticClient/wwwroot/appsettings.Development.json.template index 91a07f1..9508ceb 100644 --- a/TELBlazor.Components.ShowCase.WasmStaticClient/wwwroot/appsettings.Development.json.template +++ b/TELBlazor.Components.ShowCase.WasmStaticClient/wwwroot/appsettings.Development.json.template @@ -1,5 +1,10 @@ { "APIs": { + /*Not in production there needs to be a specific soluton for security*/ + "SearchApi": { + "BaseUrl": "https://lh-openapi.dev.local", + "BearerToken": "your_token_here" + } }, "Serilog": { diff --git a/TELBlazor.Components.UnitTests/_Imports.razor b/TELBlazor.Components.UnitTests/_Imports.razor index d70747a..97cfa9b 100644 --- a/TELBlazor.Components.UnitTests/_Imports.razor +++ b/TELBlazor.Components.UnitTests/_Imports.razor @@ -28,7 +28,7 @@ // Unit tests @using TELBlazor.Components.UnitTests.DI -@using TELBlazor.Components.OptionalImplementations.TestComponents +@using TELBlazor.Components.OptionalImplementations.Test.TestComponents @using TELBlazor.Components.Core.Enums @using TELBlazor.Components.Core.Extensions diff --git a/TELBlazor.Components/Components/BaseComponents/TELButton.razor b/TELBlazor.Components/Components/BaseComponents/TELButton.razor index 057c840..82046c9 100644 --- a/TELBlazor.Components/Components/BaseComponents/TELButton.razor +++ b/TELBlazor.Components/Components/BaseComponents/TELButton.razor @@ -19,26 +19,26 @@ else { @*FormData as the model doesnt really mean anything were not using it if its blazor*@ - @foreach (var keyValuePair in FormData) - { - - } - + Model="FormData" + OnSubmit="HandleClick" + action="@NoJSActionUrl" + role="form" + tabindex="-1" + method="post"> + @foreach (var keyValuePair in FormData) + { + + } + } @@ -55,7 +55,7 @@ else /// [Parameter] public bool IsSubmitButtonForEditForm { get; set; } = false; [Parameter] public Dictionary FormData { get; set; } = new(); - [EditorRequired, Parameter] public required string NoJSActionUrl { get; set; } // URL for the form submission + [Parameter] public string NoJSActionUrl { get; set; } = null; // URL for the form submission (not required because there may be a parent form with it if its a submit for a form) [Parameter] public EventCallback OnClick { get; set; } // Not required because the submit wont use it, when its placed in a form. [Parameter] public bool PreventDoubleClick { get; set; } = false; // View Component equivalent behaviour diff --git a/TELBlazor.Components/Core/Compliance/IAccessibleComponent.cs b/TELBlazor.Components/Core/Compliance/IAccessibleComponent.cs index c1a2036..85379d4 100644 --- a/TELBlazor.Components/Core/Compliance/IAccessibleComponent.cs +++ b/TELBlazor.Components/Core/Compliance/IAccessibleComponent.cs @@ -21,11 +21,15 @@ public interface IAccessibleComponent /// /// The title attribute for the button, shown as a tooltip. + ///

😱😱😱😱😱 1st rule of aria don't use aria, second rule of aria ... 😱😱😱😱

+ /// qqqq ///
/// /// Use a brief message that reinforces the button's action. /// Example: "Click to save the current list". /// Tooltips can provide additional context or information to users hovering over the button. + /// For more info, see + /// For more info, see /// public string ToolTipTitle { get; set; } @@ -40,13 +44,14 @@ public interface IAccessibleComponent public string AssistiveText { get; set; } /// - /// The ARIA role of the button. + /// The ARIA role of the button /// /// /// Define the role of the button to provide additional context for assistive technologies. /// Example: "button". /// Use roles that accurately describe the element's function. If unsure, refer to /// the ARIA roles documentation to choose the appropriate role. + /// public string AriaRole { get; set; } @@ -61,6 +66,7 @@ public interface IAccessibleComponent /// Example: 0. /// The default is 0, which includes it in the natural tab order. /// This property helps with keyboard navigation. + /// public int TabIndex { get; set; } } diff --git a/TELBlazor.Components/OptionalImplementations/Core/DI/DI.cs b/TELBlazor.Components/OptionalImplementations/Core/DI/DI.cs index ecf309d..72edd36 100644 --- a/TELBlazor.Components/OptionalImplementations/Core/DI/DI.cs +++ b/TELBlazor.Components/OptionalImplementations/Core/DI/DI.cs @@ -9,11 +9,13 @@ using System.Text; using System.Threading.Tasks; using TELBlazor.Components.Core.Configuration; +using TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment; namespace TELBlazor.Components.OptionalImplementations.Core.DI { public static class DI { + private static IServiceCollection AddTELBlazorComponentServicessShared(this IServiceCollection services, ITELBlazorBaseComponentConfiguration TELBlazorBaseComponentConfiguration ) @@ -34,5 +36,12 @@ ITELBlazorBaseComponentConfiguration TELBlazorBaseComponentConfiguration { throw new NotImplementedException("This method is not implemented yet. See DI task"); } + public static IServiceCollection AddTELBlazorComponentServicesForTestComponents(this IServiceCollection services) + { + services.AddHttpClient(); + services.AddScoped(); + return services; + } + } } diff --git a/TELBlazor.Components/OptionalImplementations/TestComponents/CssSourceChecker.razor b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/CssSourceChecker.razor similarity index 100% rename from TELBlazor.Components/OptionalImplementations/TestComponents/CssSourceChecker.razor rename to TELBlazor.Components/OptionalImplementations/Test/TestComponents/CssSourceChecker.razor diff --git a/TELBlazor.Components/OptionalImplementations/TestComponents/CssSourceChecker.razor.css b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/CssSourceChecker.razor.css similarity index 100% rename from TELBlazor.Components/OptionalImplementations/TestComponents/CssSourceChecker.razor.css rename to TELBlazor.Components/OptionalImplementations/Test/TestComponents/CssSourceChecker.razor.css diff --git a/TELBlazor.Components/OptionalImplementations/TestComponents/LogLevelSwitcher.razor b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/LogLevelSwitcher.razor similarity index 100% rename from TELBlazor.Components/OptionalImplementations/TestComponents/LogLevelSwitcher.razor rename to TELBlazor.Components/OptionalImplementations/Test/TestComponents/LogLevelSwitcher.razor diff --git a/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/DummyData.cs b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/DummyData.cs new file mode 100644 index 0000000..ac565b4 --- /dev/null +++ b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/DummyData.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment +{ + public static class DummyData + { + public static readonly List AllTerms = new() +{ + "Dental care", "Dental hygiene", "Dental implants", "Dental braces", "Dental bleeding", + "Dental crowns", "Dental fillings", "Dental anxiety", "Dental pain", "Dental treatment", + "Dental checkup", "Dental cleaning", "Dental x-ray", "Dental surgery", "Dental emergency", + "Tooth extraction", "Toothache", "Tooth infection", "Tooth decay", "Tooth whitening", + "Gum disease", "Gum bleeding", "Gum recession", "Gum pain", "Gum treatment", + "False teeth", "Dentures", "Dental prosthetics", "Teeth grinding", "Teeth straightening", + "Teeth sensitivity", "Teeth whitening kits", "Wisdom teeth", "Root canal", "Jaw pain", + + "Mouth ulcers", "Mouth guards", "Mouthwash", "Oral hygiene", "Oral pain", "Oral cancer", + "Oral surgery", "Oral examination", "Oral infection", "Bad breath", "Plaque control", + + "Medical checkup", "Health checkup", "General examination", "Annual physical", + "Blood test", "Urine test", "X-ray chest", "CT scan", "MRI brain", "Eye examination", + "Vision test", "Hearing test", "Hearing aid fitting", "Skin check", "Skin biopsy", + + "Back pain", "Neck pain", "Joint pain", "Chest pain", "Abdominal pain", + "Leg swelling", "Arm numbness", "Foot infection", "Hand injury", "Hip replacement", + + "Heart checkup", "Heart surgery", "Heart failure", "Heart palpitations", "Chest tightness", + "Lung infection", "Pneumonia treatment", "Breathing problems", "Shortness of breath", "Asthma attack", + + "Skin rash", "Skin irritation", "Eczema treatment", "Psoriasis care", "Acne treatment", + "Wound dressing", "Burn treatment", "Scar removal", "Skin peeling", "Allergy treatment", + + "Ear infection", "Throat infection", "Sinus infection", "Eye infection", "Nasal bleeding", + "Frequent nosebleeds", "Throat pain", "Swollen lymph nodes", "Ear pain", "Voice loss", + + "Fever symptoms", "High temperature", "Cough treatment", "Cold remedies", "Flu vaccination", + "COVID-19 testing", "COVID-19 symptoms", "Headache relief", "Migraine treatment", "Dizziness causes", + + "Depression treatment", "Anxiety management", "Mental health support", "Counseling services", + "Sleep disorder", "Insomnia treatment", "Panic attacks", "Therapy sessions", "Psychiatric assessment", + + "Diabetes checkup", "Blood sugar test", "Insulin treatment", "Weight management", "Obesity care", + "Dietician referral", "Nutrition advice", "Cholesterol test", "Hypertension treatment", "Blood pressure check", + + "Cancer screening", "Mammogram exam", "Colon cancer test", "Prostate exam", "Biopsy procedure", + "Tumor removal", "Oncology referral", "Radiation therapy", "Chemotherapy care", "Cancer pain management", + + "Pregnancy test", "Prenatal checkup", "Fertility consultation", "Birth plan", "Ultrasound scan", + "Labour support", "Midwife appointment", "Postnatal care", "Infant checkup", "Child immunisation", + + "Vaccination record", "Tetanus shot", "Flu jab", "Hepatitis B shot", "Travel vaccines", + + "Infection treatment", "Antibiotic prescription", "Inflammation control", "Immune system check", + "Wound infection", "Blood poisoning", "Sepsis symptoms", "Fungal infection", "Viral infection", "Bacterial infection", + + "Pain management", "Bleeding control", "Emergency care", "Treatment plan", "Recovery plan", + "Follow-up visit", "Health advice", "Patient education", "Medication review", "Drug interaction" +}; + + } +} diff --git a/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/ISearchExperimentService.cs b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/ISearchExperimentService.cs new file mode 100644 index 0000000..9de77ec --- /dev/null +++ b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/ISearchExperimentService.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment +{ + public interface ISearchExperimentService + { + + /// + /// Gets search suggestions for a term + /// + /// The search term + /// Cancellation token + /// List of suggestion strings + public Task> GetSuggestionsAsync(string term, CancellationToken cancellationToken = default); + + /// + /// Performs a search and returns results + /// + /// The search term + /// Cancellation token + /// List of search result strings + public Task> SearchAsync(string searchTerm, CancellationToken cancellationToken = default); + } + + ///// + ///// Extended search service interface for more advanced scenarios + ///// If you need more complex results, implement this instead + ///// + //public interface IAdvancedSearchService + //{ + // /// + // /// Gets search suggestions with metadata + // /// + // /// The search term + // /// Cancellation token + // /// Search suggestions with metadata + // Task GetSuggestionsAsync(string term, CancellationToken cancellationToken = default); + + // /// + // /// Performs a search with pagination and filtering + // /// + // /// The search request + // /// Cancellation token + // /// Paginated search results + // Task SearchAsync(SearchRequest request, CancellationToken cancellationToken = default); + //} + + +} diff --git a/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/ReadMe.md b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/ReadMe.md new file mode 100644 index 0000000..3d5cb22 --- /dev/null +++ b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/ReadMe.md @@ -0,0 +1,12 @@ +# Search Component + +The search component is given a search service using the search interface. +The interface requires a search endpoint and a search suggestion endpoint. + +# Usage +For searching + +# NoJs + +The component has parameter for the search button action. On search it will hit the specified controller. +IT receives search information via the same interface so it can be populated in a view. \ No newline at end of file diff --git a/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperiment.razor b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperiment.razor new file mode 100644 index 0000000..8d7470d --- /dev/null +++ b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperiment.razor @@ -0,0 +1,204 @@ +@using System.Diagnostics + + + + +@inherits TELComponentBase + + + +@inject NavigationManager NavigationManager +@inject ISearchExperimentService searchExperimentService +@* @implements IDisposable *@ +@using static DummyData + + +
+

Search Results

+
    + @foreach (var result in searchResults) + { +
  • @result
  • + } +
+
+@code{ + + [Parameter] public Dictionary FormData { get; set; } = new();//shouldnt need + [EditorRequired, Parameter] public required string NoJSActionUrl { get; set; } = "nojsfallback/mvcendpoint/search"; // URL for the form submission + private List searchResults = new List(); + private string searchText = string.Empty; + private List suggestions = new List(); + protected ElementReference searchInput; + + /// + /// This is just for controller actions in a nojs environment + /// + [Parameter] public string NoJSControllerSetSearchText { get; set; } = String.Empty; + /// + /// This is just for controller actions in a nojs environment + /// + [Parameter] public List NoJSControllerSetSearchResults { get; set; } = new(); + /// + /// This is just for controller actions in a nojs environment + /// + [Parameter] public List NoJSControllerSetSuggestions { get; set; } = new(); + + private CancellationTokenSource _cts; + /*avoid potential memory leak from mvc taredown by counting down not up by not search on first 3 letters can initiate*/ + private Timer? _debounceTimer; + private readonly TimeSpan _debouncePeriod = TimeSpan.FromMilliseconds(500); + + // SendSearchRequest SendSuggestionRequest presumeably same end point but maybe a bool for no suggestions + + + protected override void OnParametersSet() + { + searchText = NoJSControllerSetSearchText; + searchResults = NoJSControllerSetSearchResults; + suggestions = NoJSControllerSetSuggestions; + } + + + private void StartDebounceCountdown() + { + // Dispose any existing timer first + _debounceTimer?.Dispose(); + + // Start a new one-shot countdown timer + _debounceTimer = new Timer(_ => + { + _debounceTimer?.Dispose(); + _debounceTimer = null; + }, + null, + _debouncePeriod, + Timeout.InfiniteTimeSpan); + } + + private async Task SendSearchRequest() + { + + suggestions.Clear(); + + _cts?.Cancel(); // cancel prior search + _cts = new CancellationTokenSource(); + + try + { + searchResults = (await searchExperimentService.SearchAsync(searchText)).ToList(); + } + catch (OperationCanceledException) + { + // Ignore cancelled searches + } + } + private async Task GetSuggestionsAsync() + { + _cts?.Cancel(); // cancel prior search + _cts = new CancellationTokenSource(); + + try + { + + suggestions = (await searchExperimentService.GetSuggestionsAsync(searchText)).ToList(); + + } + catch (OperationCanceledException) + { + // Ignore cancelled searches + } + } + + + public async Task HandleInput(KeyboardEventArgs args) + { + if (args.Key == null || args.Key.Equals("Enter")) + { + + /*fire forget?*/ + await SendSearchRequest(); + //_ = SendSearchRequest(); + } + else if (_debounceTimer != null || searchText.Length < 4 ) + { + StartDebounceCountdown(); + } + else + { + StartDebounceCountdown(); + /*fire forget?*/ + await GetSuggestionsAsync(); + // _ = GetSuggestionsAsync(); + } + } + /*careful mvc taredown wont call it so self dispose too*/ + // says not implement + // public void Dispose() + // { + // _debounceTimer?.Dispose(); + // } +} \ No newline at end of file diff --git a/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperimentModels.cs b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperimentModels.cs new file mode 100644 index 0000000..ca4cdab --- /dev/null +++ b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperimentModels.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment +{ + //qqqq no mvp + public class SearchRequest + { + public string SearchTerm { get; set; } = string.Empty; + public int PageIndex { get; set; } = 0; + public int PageSize { get; set; } = 10; + public Dictionary AdditionalParameters { get; set; } = new(); + } + + public class SearchResult + { + public IEnumerable Results { get; set; } = Enumerable.Empty(); + public int TotalCount { get; set; } + public int PageIndex { get; set; } + public int PageSize { get; set; } + public bool HasMore => (PageIndex + 1) * PageSize < TotalCount; + } + + public class SuggestionResult + { + public IEnumerable Suggestions { get; set; } = Enumerable.Empty(); + public int Count { get; set; } + } +} diff --git a/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperimentServiceOpenApi.cs b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperimentServiceOpenApi.cs new file mode 100644 index 0000000..16d352d --- /dev/null +++ b/TELBlazor.Components/OptionalImplementations/Test/TestComponents/SearchExperiment/SearchExperimentServiceOpenApi.cs @@ -0,0 +1,196 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Serilog.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text; +using System.Threading.Tasks; +using System.Text.Json; + +namespace TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment +{ + //curl -X 'GET' \ + // 'https://lh-openapi.dev.local/Search/GetAutoSuggestionResult/blood' \ + // -H 'accept: */*' \ + // -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkVCREI0RUNGNzZCRDk4Rjc2MzU1RkQxQzg3OTAzRTdGIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE3NTAyNDY1NzEsImV4cCI6MTc1MDI1MDE3MSwiaXNzIjoiaHR0cHM6Ly9saC1hdXRoLmRldi5sb2NhbCIsImF1ZCI6ImxlYXJuaW5naHViYXBpIiwiY2xpZW50X2lkIjoibGVhcm5pbmdodWJvcGVuYXBpIiwic3ViIjoiNTc1NDEiLCJhdXRoX3RpbWUiOjE3NTAyNDY0NjAsImlkcCI6ImxvY2FsIiwiZW1haWwiOiJwaGlsaXAudGF0ZUBuaHMubmV0IiwiZ2l2ZW5fbmFtZSI6IlRlc3RRUVFRRmlyc3QiLCJmYW1pbHlfbmFtZSI6IlRlc3RRUVFRTGFzdCIsInJvbGUiOiJBZG1pbmlzdHJhdG9yIiwiZWxmaF91c2VyTmFtZSI6IlRlc3RRUVFRIiwianRpIjoiQ0JBRTRFODY3QjM1M0FGMDcxNDQ2NUI5NEJBRjUyOEYiLCJzaWQiOiIxRURDNTUyRUREMDVFNEI2ODk3REFEQzI1MTVCQUZENSIsImlhdCI6MTc1MDI0NjU3MSwic2NvcGUiOlsibGVhcm5pbmdodWJhcGkiXSwiYW1yIjpbInB3ZCJdfQ.spMsOk4OAB9CDMEleutkmRygzOgOFXq15MFvHP9I0WmWHYWJOLwO-0GmfV5MzDvP-ovYQvuphE5XDJibpyzfOkGAMeFq58o2PwzHvmDfDNhqsgexBd8pXm6VEin_obj70hngvG_VFD1_JkndrT7_091fU4St6Z6_UfyVvKYD9uSOwpbr5UaBAtJYbYnB3p_0C5qtzLUhKK6Xjgbr_BMLKTo9d0RGRJ5an16_VYh8Vm3gOxBjdfizzEyOoGOns2tROPB3WO6z1RO5jODVALzZDORs_ad6wlMDq4-cORBHuRbOIwOQW6k7xLIIex5VKTEebFBwAP8ihS6SvsGa16Fs-A' + +// { +// "stats": { +// "totalHits": 23, +// "searchEngineTimeInMillis": 3, +// "searchEngineRoundTripTimeInMillis": 4, +// "searchProcessingTimeInMillis": 5 +// }, +// "catalogueDocument": { +// "id": "catalogues_documents", +// "totalHits": 0, +// "catalogueDocumentList": [] +// }, +// "conceptDocument": { +// "id": "concepts_documents", +// "totalHits": 6, +// "conceptDocumentList": [ +// { +// "id": "blood transfusion_hee-local", +// "title": "426 Blood Transfusion - Non Registered Staff, Mandatory Training Workbook 2019-20", +// "concept": "blood transfusion", +// "click": { +// "payload": { +// "searchSignal": { +// "stats": { +// "totalHits": 23 +// }, +// "searchId": "5b3e2cb9-2a6b-4d9a-8879-a7ba336abf3e", +// "profileSignature": { +// "applicationId": "HEE", +// "profileType": "SEARCHER", +// "profileId": "auto-suggestion-local" +// }, +// "userQuery": "q=blood", +// "query": "q=blood", +// "timeOfSearch": 1750247278498 +// }, +// "hitNumber": 4, +// "clickTargetUrl": "blood transfusion_hee-local", +// "documentFields": { +// "name": null, +// "title": "426 Blood Transfusion - Non Registered Staff, Mandatory Training Workbook 2019-20" +// }, +// "containerId": "concepts_documents", +// "timeOfClick": null +// }, +// "url": "/signals/hee/signal/click-hee" +// } +// } +// ] +// }, +// "resourceDocument": { +// "id": "resources_documents", +// "totalHits": 17, +// "resourceDocumentList": [ +// { + +// } +// ] +// } +//} + public class SearchExperimentServiceOpenApi :ISearchExperimentService + { + //qqqq can do alot better + private readonly HttpClient _httpClient; + private readonly string _baseUrl; + private readonly ILogger _logger; // logger + + public SearchExperimentServiceOpenApi(HttpClient httpClient, IConfiguration configuration, ILogger logger) + { + _httpClient = httpClient; + _baseUrl = configuration["SearchApi:BaseUrl"] ?? "https://lh-openapi.dev.local"; + _logger = logger; + + //var token = configuration["SearchApi:BearerToken"]; + //if (!string.IsNullOrEmpty(token)) + //{ + // _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + //} + } + public async Task> GetSuggestionsAsync(string term, CancellationToken cancellationToken = default) + { + try + { + var url = $"{_baseUrl}/Search/GetAutoSuggestionResult/{term}"; + var response = await _httpClient.GetFromJsonAsync(url); + _logger.LogWarning(JsonSerializer.Serialize(response)); + return response?.conceptDocument?.conceptDocumentList? + .Select(x => x.title) + .Where(x => !string.IsNullOrWhiteSpace(x)) + ?? Enumerable.Empty(); + } + catch (Exception ex) + { + Console.WriteLine($"Error in GetSuggestionsAsync: {ex.Message}"); + return Enumerable.Empty(); + } + } + + public async Task> SearchAsync(string term, CancellationToken cancellationToken = default) + { + try + { + var url = $"{_baseUrl}/Search/GetAutoSuggestionResult/{term}"; + var response = await _httpClient.GetFromJsonAsync(url); + _logger.LogWarning(JsonSerializer.Serialize(response)); + return response?.conceptDocument?.conceptDocumentList? + .Select(x => x.click?.payload?.clickTargetUrl) + .Where(url => !string.IsNullOrWhiteSpace(url)).DefaultIfEmpty("Soz nout found. ⚠️I'm actually just hitting the suggestion endpoint atm. coz t'uther bokked. ⚠️").ToList() + ?? new List() { "Soz nout found. ⚠I'm actually just hitting the suggestion endpoint atm.coz t'uther bokked. ⚠️" }; + } + catch (Exception ex) + { + Console.WriteLine($"Error in SearchAsync: {ex.Message}"); + return Enumerable.Empty(); + } + } + + private class SearchResponse + { + public ConceptDocument conceptDocument { get; set; } + // You could also add resourceDocument or catalogueDocument if needed + } + + private class ConceptDocument + { + public List conceptDocumentList { get; set; } + } + + private class ConceptItem + { + public string id { get; set; } + public string title { get; set; } + public string concept { get; set; } + public Click click { get; set; } + } + + private class Click + { + public Payload payload { get; set; } + public string url { get; set; } + } + + private class Payload + { + public string clickTargetUrl { get; set; } + } + + // // Minimal classes to match the API response shape + // private class SearchResponse + // { + // public ConceptDocument conceptDocument { get; set; } + // } + + // private class ConceptDocument + // { + // public List conceptDocumentList { get; set; } + // } + + // private class ConceptItem + // { + // public string title { get; set; } + // public Click click { get; set; } + // } + + // private class Click + // { + // public Payload payload { get; set; } + // } + + // private class Payload + // { + // public string clickTargetUrl { get; set; } + // } + } + + } + diff --git a/TELBlazor.Components/OptionalImplementations/TestComponents/SearchExperiment/SearchExperiment.razor b/TELBlazor.Components/OptionalImplementations/TestComponents/SearchExperiment/SearchExperiment.razor deleted file mode 100644 index 605f4da..0000000 --- a/TELBlazor.Components/OptionalImplementations/TestComponents/SearchExperiment/SearchExperiment.razor +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TELBlazor.Components/TELBlazor.Components.csproj b/TELBlazor.Components/TELBlazor.Components.csproj index aa7f8c3..da945a4 100644 --- a/TELBlazor.Components/TELBlazor.Components.csproj +++ b/TELBlazor.Components/TELBlazor.Components.csproj @@ -22,7 +22,7 @@ https://github.com/TechnologyEnhancedLearning/TELBlazor git README.md - $(NupkgOutputPath) + $(NugetPackagesOutputPath) Debug;Release @@ -48,12 +48,19 @@
+ + + + + + + @@ -70,6 +77,10 @@ --> + + + + diff --git a/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazor.cs b/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazor.cs index 20c743f..206cd28 100644 --- a/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazor.cs +++ b/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazor.cs @@ -1,7 +1,2 @@ -namespace TELBlazor.Components.TELBlazorPackageVersion -{ - public static partial class VersionInfo - { - public static readonly string TELBlazorPackageVersion = typeof(_Imports).Assembly.GetName().Version?.ToString() ?? "Unknown"; - } -} +namespace TELBlazor.Components.TELBlazorPackageVersion{public static partial class VersionInfo{public const string TELBlazorPackageVersion = "1.9.2-local" ; +} } diff --git a/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazorAssembly.cs b/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazorAssembly.cs new file mode 100644 index 0000000..b81e103 --- /dev/null +++ b/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.TELBlazorAssembly.cs @@ -0,0 +1,8 @@ +namespace TELBlazor.Components.TELBlazorPackageVersion +{ + public static partial class VersionInfo + { + //its not the package version its the assembly so no text at the end + public static readonly string TELBlazorPackageAssemblyVersion = typeof(_Imports).Assembly.GetName().Version?.ToString() ?? "Unknown"; + } +} diff --git a/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.cs b/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.cs deleted file mode 100644 index 4b1a9a6..0000000 --- a/TELBlazor.Components/TELBlazorPackageVersion/VersionInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -// VersionInfo.cs -namespace TELBlazor.Components.TELBlazorPackageVersion -{ - public static partial class VersionInfo { } -} \ No newline at end of file diff --git a/TELBlazor.Components/_Imports.razor b/TELBlazor.Components/_Imports.razor index 74150bd..a267336 100644 --- a/TELBlazor.Components/_Imports.razor +++ b/TELBlazor.Components/_Imports.razor @@ -7,7 +7,9 @@ @using TELBlazor.Components.Core.Extensions @using Microsoft.AspNetCore.Components.Forms @using TELBlazor.Components.Components.BaseComponents -@using TELBlazor.Components.OptionalImplementations.TestComponents +@using TELBlazor.Components.OptionalImplementations.Test +@using TELBlazor.Components.OptionalImplementations.Test.TestComponents +@using TELBlazor.Components.OptionalImplementations.Test.TestComponents.SearchExperiment @using Microsoft.Extensions.Logging @using TELBlazor.Components.Core.Services.HelperServices @using System.Diagnostics.CodeAnalysis \ No newline at end of file diff --git a/TELBlazor.sln b/TELBlazor.sln index b6cadbc..d2f65a3 100644 --- a/TELBlazor.sln +++ b/TELBlazor.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Packages.props = Directory.Packages.props global.json = global.json nuget.config = nuget.config + nuget.config.cicd = nuget.config.cicd + nuget.config.template = nuget.config.template package-lock.json = package-lock.json package.json = package.json PackageSettings.props = PackageSettings.props diff --git a/nuget.config b/nuget.config.cicd similarity index 74% rename from nuget.config rename to nuget.config.cicd index 586c212..9905465 100644 --- a/nuget.config +++ b/nuget.config.cicd @@ -1,18 +1,22 @@ + - - - + - + diff --git a/nuget.config.template b/nuget.config.template new file mode 100644 index 0000000..79f337b --- /dev/null +++ b/nuget.config.template @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/run-tests-and-report-with-env-values.ps1 b/run-tests-and-report-with-env-values.ps1 index e733be8..3ddac89 100644 --- a/run-tests-and-report-with-env-values.ps1 +++ b/run-tests-and-report-with-env-values.ps1 @@ -2,10 +2,9 @@ # book mark in chrome file:///C:/dev/repos/TELBlazor/coveragereport/index.html # or use nektos act on the specific job to locally run the pipeline job $env:TELBLAZOR_PACKAGE_VERSION = "10.9.9" -$env:NupkgOutputPath = "$PSScriptRoot\CICDPackageLocation" -$env:UseTELBlazorComponentsProjectReference = "true" -$env:TELBlazorPackageSource = "$PSScriptRoot\CICDPackageLocation" -$env:DisablePackageGeneration = "false" +$env:NUGET_PACKAGES_OUTPUT_PATH = "$PSScriptRoot\CICDPackageLocation" +$env:USE_TEL_BLAZOR_COMPONENTS_PROJECT_REFERENCE = "true" +$env:DISABLE_PACKAGE_GENERATION = "false" # Run Tests