+ @foreach ( var color in ThemeColors.Items.Values )
+ {
+
+ @foreach ( var shade in color.Shades.Values )
+ {
+ var temp = shade.Value;
+
+
OnThemeColorSelect(temp))">
+ }
+
+ }
+
+@code{
+ [Parameter]
+ public string? Value { get; set; }
+
+ [Parameter]
+ public EventCallback ValueChanged { get; set; }
+
+ Task OnThemeColorSelect( string value )
+ {
+ Value = value;
+ return ValueChanged.InvokeAsync(value);
+ }
+}
\ No newline at end of file
diff --git a/Visor/Visor/Components/Layout/ThemeColorSelector.razor.css b/Visor/Visor/Components/Layout/ThemeColorSelector.razor.css
new file mode 100644
index 0000000..0d2ad7b
--- /dev/null
+++ b/Visor/Visor/Components/Layout/ThemeColorSelector.razor.css
@@ -0,0 +1,16 @@
+.demo-theme-color-item {
+ display: table-cell;
+ width: 36px;
+ height: 36px;
+ vertical-align: top;
+ text-align: center;
+ color: white;
+ cursor: pointer;
+}
+
+ .demo-theme-color-item .material-icons {
+ display: flex;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+ }
diff --git a/Visor/Visor/Components/Layout/TopMenu.razor b/Visor/Visor/Components/Layout/TopMenu.razor
new file mode 100644
index 0000000..6dd6782
--- /dev/null
+++ b/Visor/Visor/Components/Layout/TopMenu.razor
@@ -0,0 +1,163 @@
+@using Blazorise.Localization
+
+
+
+
+
+
+ Visor
+
+
+
+
+
+
+
+ Home
+
+
+ Documentation
+
+
+
+ More
+
+
+ Quick-Start Guide
+
+
+
+ Usage
+
+
+
+
+
+
+ Layout
+
+
+ @if ( LayoutType == "fixed-header" )
+ {
+
+ }
+ Fixed Header
+
+
+ @if ( LayoutType == "fixed-header-footer-only" )
+ {
+
+ }
+ Fixed Header and Footer only
+
+
+ @if ( LayoutType == "sider-with-header-on-top" )
+ {
+
+ }
+ Sider with Header on top
+
+
+
+
+
+
+
+
+
+
+
+
+ @foreach ( var cultureInfo in LocalizationService!.AvailableCultures )
+ {
+
+ @if ( cultureInfo.IsNeutralCulture )
+ {
+ @cultureInfo.EnglishName
+ }
+ else
+ {
+ @cultureInfo.Parent.EnglishName
+ }
+
+ }
+
+
+
+
+
+ Theme
+
+
+
+
+ Theme enabled
+
+
+
+
+
+
+ Gradient colors
+
+
+ Rounded elements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GitHub
+
+
+
+
+@code {
+ protected override async Task OnInitializedAsync()
+ {
+ await SelectCulture( "en-US" );
+
+ await base.OnInitializedAsync();
+ }
+
+ Task SelectCulture( string name )
+ {
+ LocalizationService!.ChangeLanguage( name );
+
+ return Task.CompletedTask;
+ }
+
+ private bool topbarVisible = false;
+
+ Task OnLayoutTypeChecked( string layoutType )
+ {
+ LayoutType = layoutType;
+
+ return LayoutTypeChanged.InvokeAsync( layoutType );
+ }
+
+ [Parameter] public EventCallback ThemeEnabledChanged { get; set; }
+
+ [Parameter] public EventCallback ThemeGradientChanged { get; set; }
+
+ [Parameter] public EventCallback ThemeRoundedChanged { get; set; }
+
+ [Parameter] public EventCallback ThemeColorChanged { get; set; }
+
+ [Parameter] public string? LayoutType { get; set; }
+
+ [Parameter] public EventCallback LayoutTypeChanged { get; set; }
+
+ [Inject] protected ITextLocalizerService? LocalizationService { get; set; }
+
+ [CascadingParameter] protected Theme? Theme { get; set; }
+}
\ No newline at end of file
diff --git a/Visor/Visor/Components/TodoApp/BaseTodoItems.cs b/Visor/Visor/Components/TodoApp/BaseTodoItems.cs
new file mode 100644
index 0000000..16b2d41
--- /dev/null
+++ b/Visor/Visor/Components/TodoApp/BaseTodoItems.cs
@@ -0,0 +1,68 @@
+using Microsoft.AspNetCore.Components;
+
+namespace Visor.Components.TodoApp
+{
+ public abstract class BaseTodoItems : ComponentBase
+ {
+ protected Validations? validations;
+
+ protected string? description;
+
+ protected Filter filter = Filter.All;
+
+ protected List todos = new()
+ {
+ new() { Description = "Buy milk" },
+ new() { Description = "Call John regarding the meeting" },
+ new() { Description = "Walk a dog" },
+ };
+
+ protected IEnumerable Todos
+ {
+ get
+ {
+ var query = from t in todos select t;
+
+ if (filter == Filter.Active)
+ query = from q in query where !q.Completed select q;
+
+ if (filter == Filter.Completed)
+ query = from q in query where q.Completed select q;
+
+ return query;
+ }
+ }
+
+ protected void SetFilter(Filter filter)
+ {
+ this.filter = filter;
+ }
+
+ protected void OnCheckAll(bool isChecked)
+ {
+ todos.ForEach(x => x.Completed = isChecked);
+ }
+
+ protected async Task OnAddTodo()
+ {
+ if (await validations!.ValidateAll())
+ {
+ todos.Add(new() { Description = description });
+ description = null;
+
+ await validations.ClearAll();
+ }
+ }
+
+ protected void OnClearCompleted()
+ {
+ todos.RemoveAll(x => x.Completed);
+ filter = Filter.All;
+ }
+
+ protected Task OnTodoStatusChanged(bool isChecked)
+ {
+ return InvokeAsync(StateHasChanged);
+ }
+ }
+}
diff --git a/Visor/Visor/Components/TodoApp/Filter.cs b/Visor/Visor/Components/TodoApp/Filter.cs
new file mode 100644
index 0000000..e4d2542
--- /dev/null
+++ b/Visor/Visor/Components/TodoApp/Filter.cs
@@ -0,0 +1,9 @@
+namespace Visor.Components.TodoApp
+{
+ public enum Filter
+ {
+ All,
+ Active,
+ Completed,
+ }
+}
diff --git a/Visor/Visor/Components/TodoApp/Todo.cs b/Visor/Visor/Components/TodoApp/Todo.cs
new file mode 100644
index 0000000..8f28939
--- /dev/null
+++ b/Visor/Visor/Components/TodoApp/Todo.cs
@@ -0,0 +1,9 @@
+namespace Visor.Components.TodoApp
+{
+ public class Todo
+ {
+ public bool Completed { get; set; }
+
+ public string? Description { get; set; }
+ }
+}
diff --git a/Visor/Visor/Components/TodoApp/TodoItem.razor b/Visor/Visor/Components/TodoApp/TodoItem.razor
new file mode 100644
index 0000000..a1b9356
--- /dev/null
+++ b/Visor/Visor/Components/TodoApp/TodoItem.razor
@@ -0,0 +1,25 @@
+@if (Todo is not null)
+{
+
+
+
+
+
+
+ @Todo.Description
+
+
+
+}
+@code {
+ Task OnCheckedChanged(bool isChecked)
+ {
+ Todo!.Completed = isChecked;
+
+ return StatusChanged?.Invoke(isChecked) ?? Task.CompletedTask;
+ }
+
+ [Parameter] public Todo? Todo { get; set; }
+
+ [Parameter] public Func? StatusChanged { get; set; }
+}
\ No newline at end of file
diff --git a/Visor/Visor/Components/TodoApp/TodoItems.razor b/Visor/Visor/Components/TodoApp/TodoItems.razor
new file mode 100644
index 0000000..934e33e
--- /dev/null
+++ b/Visor/Visor/Components/TodoApp/TodoItems.razor
@@ -0,0 +1,57 @@
+@inherits BaseTodoItems
+
+
+
+
+
+ Todo List
+
+
+
+
+ All
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @foreach ( var todo in Todos )
+ {
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Visor/Visor/GlobalUsings.cs b/Visor/Visor/GlobalUsings.cs
new file mode 100644
index 0000000..be71087
--- /dev/null
+++ b/Visor/Visor/GlobalUsings.cs
@@ -0,0 +1,2 @@
+global using Blazorise;
+global using Blazorise.DataGrid;
diff --git a/Visor/Visor/Layouts/MainLayout.razor b/Visor/Visor/Layouts/MainLayout.razor
new file mode 100644
index 0000000..b0e5a37
--- /dev/null
+++ b/Visor/Visor/Layouts/MainLayout.razor
@@ -0,0 +1,69 @@
+@using Visor.Components.Layout
+
+@inherits LayoutComponentBase
+
+
+@if ( layoutType == "fixed-header" )
+{
+
+
+
+
+
+
+
+
+
+
+
+ @Body
+
+
+
+}
+else if ( layoutType == "fixed-header-footer-only" )
+{
+
+
+
+
+
+ @Body
+
+
+
+
+
+}
+else if ( layoutType == "sider-with-header-on-top" )
+{
+
+
+
+
+
+
+
+
+
+
+
+
+ @Body
+
+
+
+
+}
\ No newline at end of file
diff --git a/Visor/Visor/Layouts/MainLayout.razor.cs b/Visor/Visor/Layouts/MainLayout.razor.cs
new file mode 100644
index 0000000..4b2de2e
--- /dev/null
+++ b/Visor/Visor/Layouts/MainLayout.razor.cs
@@ -0,0 +1,89 @@
+using Blazorise;
+using Blazorise.Localization;
+
+using Microsoft.AspNetCore.Components;
+
+namespace Visor.Layouts
+{
+ public partial class MainLayout
+ {
+ [Inject] protected ITextLocalizerService? LocalizationService { get; set; }
+
+ [CascadingParameter] protected Theme? Theme { get; set; }
+
+ protected string layoutType = "fixed-header";
+
+ protected override async Task OnInitializedAsync()
+ {
+ await SelectCulture("en-US");
+
+ await base.OnInitializedAsync();
+ }
+
+ private Task SelectCulture(string name)
+ {
+ LocalizationService!.ChangeLanguage(name);
+
+ return Task.CompletedTask;
+ }
+
+ Task OnThemeEnabledChanged(bool value)
+ {
+ if (Theme is null)
+ return Task.CompletedTask;
+
+ Theme.Enabled = value;
+
+ return InvokeAsync(Theme.ThemeHasChanged);
+ }
+
+ Task OnThemeGradientChanged(bool value)
+ {
+ if (Theme is null)
+ return Task.CompletedTask;
+
+ Theme.IsGradient = value;
+
+ return InvokeAsync(Theme.ThemeHasChanged);
+ }
+
+ Task OnThemeRoundedChanged(bool value)
+ {
+ if (Theme is null)
+ return Task.CompletedTask;
+
+ Theme.IsRounded = value;
+
+ return InvokeAsync(Theme.ThemeHasChanged);
+ }
+
+ Task OnThemeColorChanged(string value)
+ {
+ if (Theme is null)
+ return Task.CompletedTask;
+
+ Theme.ColorOptions ??= new();
+
+ Theme.BackgroundOptions ??= new();
+
+ Theme.TextColorOptions ??= new();
+
+ Theme.ColorOptions.Primary = value;
+ Theme.BackgroundOptions.Primary = value;
+ Theme.TextColorOptions.Primary = value;
+
+ Theme.InputOptions ??= new();
+
+ Theme.InputOptions.CheckColor = value;
+ Theme.InputOptions.SliderColor = value;
+
+ Theme.SpinKitOptions ??= new();
+
+ Theme.SpinKitOptions.Color = value;
+
+ return InvokeAsync(Theme.ThemeHasChanged);
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/Visor/Visor/Pages/Dashboard.razor b/Visor/Visor/Pages/Dashboard.razor
new file mode 100644
index 0000000..b735c21
--- /dev/null
+++ b/Visor/Visor/Pages/Dashboard.razor
@@ -0,0 +1,36 @@
+@page "/"
+@inject IVersionProvider VersionProvider
+Blazorise
+
+
+ Blazorise is a component library built on top of Blazor and CSS frameworks like Bootstrap, FluentUI2, Tailwind, Bulma, Ant Design, and Material. It can be used to build responsive, single-page web applications.
+
+
+
+
+ This is a Blazorise Starting Template allowing you to quickly get started building your project!
+
+
+
+ The following Blazorise packages have been installed for you:
+
+
+
+ Blazorise @($"v{VersionProvider.MilestoneVersion}")
+ The Blazorise Bootstrap5 Provider
+ The Blazorise FontAwesome Icon Provider
+ The Blazorise DataGrid extension
+
+
+
+ However Blazorise has many more extensions at your disposal.
+ You can find them here.
+
+
+
+ Please visit the official Blazorise Demo for component examples.
+
+
+ Please visit the official Blazorise Documentation to learn more about the available components.
+
+
diff --git a/Visor/Visor/Pages/Error.cshtml b/Visor/Visor/Pages/Error.cshtml
new file mode 100644
index 0000000..91c9f5d
--- /dev/null
+++ b/Visor/Visor/Pages/Error.cshtml
@@ -0,0 +1,37 @@
+@page
+@model Visor.Pages.ErrorModel
+
+
+
+
+
+
+
+ Error
+
+
+
+
Error.
+
An error occurred while processing your request.
+
+ @if (Model.ShowRequestId)
+ {
+
+ Request ID:@Model.RequestId
+
+ }
+
+
Development Mode
+
+ Swapping to the Development environment displays detailed information about the error that occurred.
+
+
+ The Development environment shouldn't be enabled for deployed applications.
+ It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
+ and restarting the app.
+
+
+
+
+
diff --git a/Visor/Visor/Pages/Error.cshtml.cs b/Visor/Visor/Pages/Error.cshtml.cs
new file mode 100644
index 0000000..35f5504
--- /dev/null
+++ b/Visor/Visor/Pages/Error.cshtml.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using System.Diagnostics;
+
+namespace Visor.Pages
+{
+ [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
+ [IgnoreAntiforgeryToken]
+ public class ErrorModel : PageModel
+ {
+ public string? RequestId { get; set; }
+
+ public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
+
+ private readonly ILogger _logger;
+
+ public ErrorModel(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public void OnGet()
+ {
+ RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
+ }
+ }
+}
diff --git a/Visor/Visor/Pages/SimpleDataGridPage.razor b/Visor/Visor/Pages/SimpleDataGridPage.razor
new file mode 100644
index 0000000..f6d6082
--- /dev/null
+++ b/Visor/Visor/Pages/SimpleDataGridPage.razor
@@ -0,0 +1,75 @@
+@page "/simple-datagrid"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ private List data = new()
+ {
+ new("Harry Potter and the Sorcerer's Stone", "J.K. Rowling","9780590353427", new(1997,6,26), "Scholastic Press" ),
+ new("To Kill a Mockingbird", "Harper Lee", "9780061120084", new(1960,7,11), "J.B. Lippincott & Co." ),
+ new("The Catcher in the Rye", "J.D. Salinger","9780316769488", new(1951,7,16), "Little, Brown and Company" ),
+ new("The Great Gatsby", "F. Scott Fitzgerald","9780743273565", new(1925,4,10), "Charles Scribner's Sons" ),
+ new("The Lord of the Rings", "J.R.R. Tolkien", "9780544003415", new(1954,7,29), "George Allen & Unwin"),
+ new("The Hobbit", "J.R.R. Tolkien", "9780547928227", new(1937,9,21), "George Allen & Unwin"),
+ new("The Chronicles of Narnia: The Lion, the Witch, and the Wardrobe", "C.S. Lewis", "9780064471073", new(1950,10,16), "Geoffrey Bles"),
+ new("Animal Farm", "George Orwell", "978451219811", new(1945,8,17), "Secker & Warburg"),
+ new("The Adventures of Huckleberry Finn", "Mark Twain", "9780142437254", new(1884,12,10), "Chatto & Windus"),
+ new("The Diary of a Young Girl", "Anne Frank", "9780385473788", new(1947,6,25), "Doubleday"),
+ new("The Hunger Games", "Suzanne Collins", "9780439023481", new(2008,9,14), "Scholastic Press"),
+ new("The Maze Runner", "James Dashner", "9780385737965", new(2009,10,6), "Delacorte Press"),
+ new("The Secret Life of Bees", "Sue Monk Kidd", "9780670034852", new(2002,5,5), "Viking Press"),
+ new("The Strange Case of Dr. Jekyll and Mr. Hyde", "Robert Louis Stevenson", "9781593080171", new(1886,1,5), "Longmans, Green and Co"),
+ new("Harry Potter and the Chamber of Secrets", "J.K. Rowling", "9780439064866", new(1998,7,2), "Scholastic Press"),
+ new("Harry Potter and the Prisoner of Azkaban", "J.K. Rowling", "9780439136365", new(1999,7,8), "Scholastic Press"),
+ new("Catching Fire", "Suzanne Collins", "9780439023498", new(2009,9,1), "Scholastic Press"),
+ new("Mockingjay", "Suzanne Collins", "9780439023528", new(2010,8,24), "Scholastic Press"),
+ new("The Children of Hurin", "J.R.R. Tolkien", "9780544003422", new(2007,4,17), "Houghton Mifflin Harcourt"),
+ new("Beren and Luthien", "J.R.R. Tolkien", "9780544116813", new(2017,5,23), "Houghton Mifflin Harcourt")
+ };
+
+ private record Book( string Title, string Author, string Isbn, DateOnly PublicationDate, string Publisher );
+
+
+}
\ No newline at end of file
diff --git a/Visor/Visor/Pages/SimpleFormPage.razor b/Visor/Visor/Pages/SimpleFormPage.razor
new file mode 100644
index 0000000..8a607bc
--- /dev/null
+++ b/Visor/Visor/Pages/SimpleFormPage.razor
@@ -0,0 +1,124 @@
+@page "/simple-form"
+
+
+
+
+ Basic Example
+
+
+
+
+
+ Email address
+
+ We'll never share your email with anyone else.
+
+
+
+
+ Password
+
+
+
+
+
+ Remember me?
+
+
+
+
+
+
+
+
+
+ Horizontal Form
+
+
+
+
+
+ Email address
+
+
+
+
+
+
+
+ Password
+
+
+
+
+
+
+
+ Re Password
+
+
+
+
+
+
+
+ Remember me?
+
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ Validations? validationsBasicExampleRef;
+ Validations? validationsHorizontalFormRef;
+ string? password;
+
+ void ValidatePassword( ValidatorEventArgs e )
+ {
+ e.Status = Convert.ToString( e.Value )?.Length >= 6 ? ValidationStatus.Success : ValidationStatus.Error;
+ }
+
+ void ValidatePassword2( ValidatorEventArgs e )
+ {
+ var password2 = Convert.ToString( e.Value );
+
+ if ( password2?.Length < 6 )
+ {
+ e.Status = ValidationStatus.Error;
+ e.ErrorText = "Password must be at least 6 characters long!";
+ }
+ else if ( password2 != password )
+ {
+ e.Status = ValidationStatus.Error;
+ }
+ else
+ {
+ e.Status = ValidationStatus.Success;
+ }
+ }
+
+ async Task SubmitBasicExample()
+ {
+ if ( await validationsBasicExampleRef!.ValidateAll() )
+ {
+ await validationsBasicExampleRef.ClearAll();
+ }
+ }
+
+ async Task SubmitHorizontalForm()
+ {
+ if ( await validationsHorizontalFormRef!.ValidateAll() )
+ {
+ await validationsHorizontalFormRef.ClearAll();
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/Visor/Visor/Pages/TodoAppPage.razor b/Visor/Visor/Pages/TodoAppPage.razor
new file mode 100644
index 0000000..9a9ab4f
--- /dev/null
+++ b/Visor/Visor/Pages/TodoAppPage.razor
@@ -0,0 +1,3 @@
+@using Visor.Components.TodoApp
+@page "/apps/todo"
+
\ No newline at end of file
diff --git a/Visor/Visor/Pages/_Host.cshtml b/Visor/Visor/Pages/_Host.cshtml
new file mode 100644
index 0000000..0156356
--- /dev/null
+++ b/Visor/Visor/Pages/_Host.cshtml
@@ -0,0 +1,8 @@
+@page "/"
+@namespace Visor.Pages
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+@{
+ Layout = "_Layout";
+}
+
+
diff --git a/Visor/Visor/Pages/_Layout.cshtml b/Visor/Visor/Pages/_Layout.cshtml
new file mode 100644
index 0000000..3aeec43
--- /dev/null
+++ b/Visor/Visor/Pages/_Layout.cshtml
@@ -0,0 +1,41 @@
+@using Microsoft.AspNetCore.Components.Web
+@namespace Visor.Pages
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+
+
+
+
+
+
+ Visor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @RenderBody()
+
+
+
+ An error has occurred. This application may no longer respond until reloaded.
+
+
+ An unhandled exception has occurred. See browser dev tools for details.
+
+ Reload
+ 🗙
+
+
+
+
+
diff --git a/Visor/Visor/Program.cs b/Visor/Visor/Program.cs
new file mode 100644
index 0000000..093540f
--- /dev/null
+++ b/Visor/Visor/Program.cs
@@ -0,0 +1,41 @@
+using Blazorise.Bootstrap5;
+using Blazorise.Icons.FontAwesome;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+builder.Services.AddRazorPages();
+builder.Services.AddServerSideBlazor();
+
+AddBlazorise(builder.Services);
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (!app.Environment.IsDevelopment())
+{
+ app.UseExceptionHandler("/Error");
+ // 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.UseHttpsRedirection();
+
+app.UseStaticFiles();
+
+app.UseRouting();
+
+app.MapBlazorHub();
+app.MapFallbackToPage("/_Host");
+
+app.Run();
+
+
+void AddBlazorise(IServiceCollection services)
+{
+ services
+ .AddBlazorise();
+ services
+ .AddBootstrap5Providers()
+ .AddFontAwesomeIcons();
+}
diff --git a/Visor/Visor/Properties/launchSettings.json b/Visor/Visor/Properties/launchSettings.json
new file mode 100644
index 0000000..a59427d
--- /dev/null
+++ b/Visor/Visor/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:61646",
+ "sslPort": 44332
+ }
+ },
+ "profiles": {
+ "Visor": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7297;http://localhost:5255",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Visor/Visor/Visor.csproj b/Visor/Visor/Visor.csproj
new file mode 100644
index 0000000..8cab721
--- /dev/null
+++ b/Visor/Visor/Visor.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net8.0
+ enable
+ enable
+ 2.2.1
+ Megabit
+ Megabit
+
+ This is a Blazorise Template for a Blazor Server Application.
+ Blazorise is a component library built on top of Blazor and CSS frameworks like Bootstrap, FluentUI2, Tailwind, Bulma and Material.
+
+ Copyright 2018-2026 Megabit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Visor/Visor/_Imports.razor b/Visor/Visor/_Imports.razor
new file mode 100644
index 0000000..e467b30
--- /dev/null
+++ b/Visor/Visor/_Imports.razor
@@ -0,0 +1,12 @@
+@using System.Net.Http
+@using Microsoft.AspNetCore.Authorization
+@using Microsoft.AspNetCore.Components.Authorization
+@using Microsoft.AspNetCore.Components.Forms
+@using Microsoft.AspNetCore.Components.Routing
+@using Microsoft.AspNetCore.Components.Web
+@using Microsoft.AspNetCore.Components.Web.Virtualization
+@using Microsoft.JSInterop
+
+@using Visor
+@using Blazorise
+@using Blazorise.DataGrid
diff --git a/Visor/Visor/appsettings.Development.json b/Visor/Visor/appsettings.Development.json
new file mode 100644
index 0000000..770d3e9
--- /dev/null
+++ b/Visor/Visor/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "DetailedErrors": true,
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/Visor/Visor/appsettings.json b/Visor/Visor/appsettings.json
new file mode 100644
index 0000000..10f68b8
--- /dev/null
+++ b/Visor/Visor/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/Visor/Visor/wwwroot/brand-logo.png b/Visor/Visor/wwwroot/brand-logo.png
new file mode 100644
index 0000000..3746003
Binary files /dev/null and b/Visor/Visor/wwwroot/brand-logo.png differ
diff --git a/Visor/Visor/wwwroot/css/blazor-ui.css b/Visor/Visor/wwwroot/css/blazor-ui.css
new file mode 100644
index 0000000..db706f5
--- /dev/null
+++ b/Visor/Visor/wwwroot/css/blazor-ui.css
@@ -0,0 +1,28 @@
+#blazor-error-ui {
+ background: #ffffe0;
+ bottom: 0;
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, .2);
+ display: none;
+ left: 0;
+ padding: .6rem 1.25rem .7rem 1.25rem;
+ position: fixed;
+ right: 0;
+ z-index: 1000
+}
+
+#blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: .75rem;
+ top: .5rem
+}
+
+.blazor-error-boundary {
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ padding: 1rem 1rem 1rem 3.7rem;
+ color: white;
+}
+
+.blazor-error-boundary::after {
+ content: "An error has occurred."
+}
\ No newline at end of file
diff --git a/Visor/Visor/wwwroot/favicon.ico b/Visor/Visor/wwwroot/favicon.ico
new file mode 100644
index 0000000..f47a486
Binary files /dev/null and b/Visor/Visor/wwwroot/favicon.ico differ
diff --git a/lsSoft.slnx b/lsSoft.slnx
index 7413f25..3d55c97 100644
--- a/lsSoft.slnx
+++ b/lsSoft.slnx
@@ -5,6 +5,7 @@
+