1
0

Compare commits

...

21 Commits

Author SHA1 Message Date
a25e767b29 Use ProtectedSessionStorage to for add server wizard.
And replace the URL query string with it.
2025-03-30 19:56:47 +02:00
28c3449e24 Add Wizard to add a new server, using URL query strings to pass data. 2025-03-30 16:08:48 +02:00
705e9493fe Rename Add button label to: Add Server 2025-03-30 15:34:30 +02:00
8cf581efca Add query string to AddEditServer page to prefill city. 2025-03-29 19:37:04 +01:00
86e41b22ff Consolidate Edit and Add server pages into single routable component. 2025-03-29 19:26:52 +01:00
d6df2a448d Navigate back to same selected city on servers page after editing a server. 2025-03-29 19:00:01 +01:00
ae3f096722 Use NavigationLock on EditServer page to prevent user from navigating away before confirmation. 2025-03-29 18:44:39 +01:00
2da0063331 Add refresh button and automatic refreshing timer to update state.
As effect, this keeps updating the amount of people online.
2025-03-29 18:10:07 +01:00
b041d7d244 Use SetParametersAsync to only trigger OnParametersSet when necessary. 2025-03-29 16:55:57 +01:00
f2b9daac52 Initialize servers in OnAfterRender. 2025-03-29 16:45:16 +01:00
b39211b1d5 Add Dispose function to CityListComponent. 2025-03-29 16:34:07 +01:00
b9e4f44d5d Create component for to-do item. 2025-03-29 11:54:29 +01:00
5f7b63071e Create component for to-do list. 2025-03-29 11:50:00 +01:00
23d20f2f04 Allow HTML attributes on SearchBar. 2025-03-28 23:20:00 +01:00
ea9213f9c1 Add quickgrid page with paginator. 2025-03-28 21:45:43 +01:00
820682a8ba Install Nuget package: ASP.NET Core QuickGrid 9.0.3 version.
https://www.nuget.org/packages/Microsoft.AspNetCore.Components.QuickGrid
2025-03-28 21:13:48 +01:00
2c8b50e8c5 Replace ul with table for server items. 2025-03-28 21:07:32 +01:00
e4ac3ab0d9 Create templated generic Repeater component for server list items.
This also is usefull to reduce the amount of null checking if-statements.
2025-03-28 20:56:05 +01:00
6b3b08b07a Create templated generic component for Fields for EditServer page. 2025-03-28 20:39:30 +01:00
9396e98eea Unstyle bullet list for server components. 2025-03-28 12:21:42 +01:00
f24cc284f7 Use Cascading Parameter to provide server component with selected city parameter to set a background color. 2025-03-28 12:20:07 +01:00
22 changed files with 708 additions and 223 deletions

View File

@ -1,3 +1,4 @@
@implements IDisposable
@if (cities != null && cities.Count > 0) @if (cities != null && cities.Count > 0)
{ {
@ -9,18 +10,19 @@
city="@city" city="@city"
selectedCity="@this.selectedCity" selectedCity="@this.selectedCity"
SelectCityCallBack="HandleCitySelection"> SelectCityCallBack="HandleCitySelection">
</CityComponent> </CityComponent>
} }
</div> </div>
</div> </div>
} }
@code { @code {
private string selectedCity = "Eindhoven";
private List<string> cities = ServersRepository.GetCities(); private List<string> cities = ServersRepository.GetCities();
[Parameter] [Parameter]
public EventCallback<string> SelectCityCallBack { get; set; } public EventCallback<string> SelectCityCallBack { get; set; }
[Parameter]
public string? selectedCity { get; set; } = "Eindhoven";
public void ClearSelection() public void ClearSelection()
{ {
@ -31,4 +33,10 @@
this.selectedCity = cityName; this.selectedCity = cityName;
SelectCityCallBack.InvokeAsync(cityName); SelectCityCallBack.InvokeAsync(cityName);
} }
public void Dispose()
{
var guid = Guid.NewGuid();
Console.WriteLine($"CityListComponent: {nameof(Dispose)} : {guid}");
}
} }

View File

@ -0,0 +1,24 @@
@namespace ServerManagement.Components.Controls.Generic
<div class="row mb-3">
<div class="col-2">
<label class="col-form-label">@Label</label>
</div>
<div class="col-6 input-width">
@Control
</div>
<div class="col">
@ValidationControl
</div>
</div>
@code {
[Parameter]
public string? Label { get; set; }
[Parameter]
public RenderFragment? Control { get; set; }
[Parameter]
public RenderFragment? ValidationControl { get; set; }
}

View File

@ -0,0 +1,24 @@
@typeparam TItem
@Header
@if (Items != null && Items.Count > 0 && Row != null)
{
<Virtualize Items="this.Items" Context="item">
@Row(item)
</Virtualize>
}
@Footer
@code {
[Parameter]
public List<TItem>? Items { get; set; }
[Parameter]
public RenderFragment<TItem>? Row { get; set; }
[Parameter]
public RenderFragment? Header { get; set; }
[Parameter]
public RenderFragment? Footer { get; set; }
}

View File

@ -1,4 +1,4 @@
<div class="input-group mb-3 input-width"> <div class="input-group mb-3 input-width" @attributes="OtherAttributes">
<input type="text" class="form-control" placeholder="Search servers" <input type="text" class="form-control" placeholder="Search servers"
@bind-value="serverFilter" @bind-value="serverFilter"
@bind-value:event="oninput" /> @bind-value:event="oninput" />
@ -18,6 +18,10 @@
[Parameter] [Parameter]
public EventCallback<string> FilterSearchTerm { get; set; } public EventCallback<string> FilterSearchTerm { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
// Enables other HTML attributes like class, style, data-x, etc.
public Dictionary<string, object>? OtherAttributes { get; set; }
private void HandleSearch() private void HandleSearch()
{ {
FilterSearchTerm.InvokeAsync(serverFilter); FilterSearchTerm.InvokeAsync(serverFilter);

View File

@ -4,53 +4,66 @@
@if (Server != null) @if (Server != null)
{ {
<li @key="Server.Id"> <tr @key="Server.Id" style="background-color: @GetBackgroundColor();">
@Server.Name in @Server.City is <td>
<span style="color:@(Server.IsOnline ? "green" : "red")"> @Server.Name
@(Server.IsOnline ? "online" : "offline") </td>
</span>; <td>
&nbsp; @Server.City
@if (Server.IsOnline) </td>
{ <td>
<button type="button" class="btn btn-outline-danger" <span style="color:@(Server.IsOnline ? "green" : "red")">
@onclick="@(() => { Server.IsOnline = false; })"> @(Server.IsOnline ? "online" : "offline")
Turn off </span>
</button> </td>
} <td>
else @if (Server.IsOnline)
{ {
<button type="button" class="btn btn-outline-success" Random random = new Random();
@onclick="@(() => { Server.IsOnline = true; })"> int randomNumber = random.Next(0, 500);
Turn on <text>@randomNumber users online</text>
</button> }
} else
&nbsp; {
@if (Server.IsOnline) <text>N/A</text>
{ }
Random random = new Random(); </td>
int randomNumber = random.Next(0, 500); <td>
<text>@randomNumber users online</text> @if (Server.IsOnline)
} {
else <button type="button" class="btn btn-outline-danger"
{ @onclick="@(() => { Server.IsOnline = false; })">
<text>N/A</text> Turn off
} </button>
&nbsp; }
<a href="@($"/servers/{Server.Id}")" class="btn btn-primary">Edit</a> else
&nbsp; {
<EditForm <button type="button" class="btn btn-outline-success"
Model="Server" @onclick="@(() => { Server.IsOnline = true; })">
FormName="@($"formDeleteServer{Server.Id}")" Turn on
OnValidSubmit="@(() => { DeleteServer(Server.Id); })"> </button>
<button type="submit" class="btn btn-danger">Delete</button> }
</EditForm> &nbsp;
</li> <a href="@($"/server/{Server.Id}")" class="btn btn-primary">Edit</a>
</td>
<td>
<EditForm
Model="Server"
FormName="@($"formDeleteServer{Server.Id}")"
OnValidSubmit="@(() => { DeleteServer(Server.Id); })">
<button type="submit" class="btn btn-danger">Delete</button>
</EditForm>
</td>
</tr>
} }
@code { @code {
[Parameter] [Parameter]
public Server? Server { get; set; } public Server? Server { get; set; }
[CascadingParameter(Name="SelectedCity")]
public string? SelectedCity { get; set; }
private void DeleteServer(int serverId) private void DeleteServer(int serverId)
{ {
if (serverId > 0) if (serverId > 0)
@ -59,4 +72,24 @@
NavigationManager.Refresh(forceReload: true); NavigationManager.Refresh(forceReload: true);
} }
} }
private string GetBackgroundColor()
{
if (SelectedCity != null) {
switch (this.SelectedCity)
{
case "Eindhoven": return "lightskyblue";
case "Helmond": return "lightcoral";
case "Oosterhout": return "lightgreen";
case "Roosendaal": return "lightsalmon";
case "Deurne": return "lightpink";
default:
return "white";
}
}
else
{
return "white";
}
}
} }

View File

@ -1,20 +1,62 @@
@if (this.servers != null && this.servers.Count > 0) @using System.Threading;
{
<ul> <table class="table table-striped">
<Virtualize Items="this.servers" Context="server"> <RepeaterComponent Items="this.servers">
<Header>
<thead>
<tr>
<th>Name</th>
<th>City</th>
<th>Status</th>
<th>People online</th>
<th></th>
<th></th>
</tr>
</thead>
</Header>
<Row Context="server">
<ServerComponent server="server"></ServerComponent> <ServerComponent server="server"></ServerComponent>
</Virtualize> </Row>
</ul> <Footer>
}
</Footer>
</RepeaterComponent>
</table>
<button type="button" class="btn btn-primary" onclick=@(() => {base.InvokeAsync(StateHasChanged);})"">Refresh</button>
@code { @code {
private List<Server>? servers; private List<Server>? servers;
private Timer? Timer;
[Parameter] [Parameter]
public string? CityName { get; set; } public string? CityName { get; set; }
[Parameter] [Parameter]
public string SearchFilter { get; set; } = ""; public string SearchFilter { get; set; } = "";
public override Task SetParametersAsync(ParameterView parameters)
{
// Only trigger OnParametersSet if the parameter matches and has actually changed.
// This saves expensive calls to the data repository.
if (parameters.TryGetValue<string>("CityName", out var cityName))
{
if (cityName != CityName)
{
base.SetParametersAsync(parameters);
}
}
if (parameters.TryGetValue<string>("SearchFilter", out var searchFilter))
{
if (searchFilter != SearchFilter)
{
base.SetParametersAsync(parameters);
}
}
return Task.CompletedTask;
}
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
if (string.IsNullOrWhiteSpace(this.SearchFilter)) if (string.IsNullOrWhiteSpace(this.SearchFilter))
@ -26,4 +68,27 @@
servers = ServersRepository.SearchServers(SearchFilter); servers = ServersRepository.SearchServers(SearchFilter);
} }
} }
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
if (firstRender)
{
if (string.IsNullOrWhiteSpace(this.SearchFilter))
{
servers = ServersRepository.GetServersByCity(CityName ?? "Eindhoven");
}
else
{
servers = ServersRepository.SearchServers(SearchFilter);
}
// Render the component again by letting it know the state changed.
StateHasChanged();
Timer = new Timer(_ => {
base.InvokeAsync(StateHasChanged);
}, null, 4000, 4000);
}
}
} }

View File

@ -0,0 +1,32 @@
@if (Item != null)
{
<li @key="Item.Id">
<div class="row mb-2">
<div class="col-1" style="width: 30px;">
<input type="checkbox" class="form-check-input" style="vertical-align: middle" @bind-value="Item.IsCompleted" checked="@Item.IsCompleted"/>
</div>
<div class="col">
@if (Item.IsCompleted)
{
<input type="text" class="form-control border-0 text-decoration-line-through" style="vertical-align: middle" @bind-value="Item.Name" disabled/>
}
else
{
<input type="text" class="form-control border-0" style="vertical-align: middle" @bind-value="Item.Name"/>
}
</div>
<div class="col">
@if (Item.IsCompleted)
{
<text>Completed at: @Item.DateCompleted.ToLongDateString()</text>
}
</div>
</div>
</li>
}
@code {
[Parameter]
public ToDoItem? Item { get; set; }
}

View File

@ -0,0 +1,12 @@
@if (Items != null)
{
@foreach (var item in Items)
{
<ToDoItemComponent Item="item"></ToDoItemComponent>
}
}
@code {
[Parameter]
public List<ToDoItem>? Items { get; set; }
}

View File

@ -0,0 +1,121 @@
@page "/server/{id:int?}"
@inject NavigationManager NavigationManager
@inject IJSRuntime JSRuntime
<NavigationLock
OnBeforeInternalNavigation="OnBeforeInternalNavigation"
ConfirmExternalNavigation="true">
</NavigationLock>
@if (Id.HasValue)
{
<h3>Edit server</h3>
}
else
{
<h3>Add server</h3>
}
@if (server != null)
{
<EditForm Model="server" FormName="formServer" OnValidSubmit="SubmitServer">
<DataAnnotationsValidator></DataAnnotationsValidator>
<ValidationSummary></ValidationSummary>
@if (server.Id > 0)
{
<InputNumber @bind-Value="server.Id" hidden></InputNumber>
}
<FieldComponent Label="Name">
<Control>
<InputText @bind-Value="server.Name" class="form-control"></InputText>
</Control>
<ValidationControl>
<ValidationMessage For="() => server.Name"></ValidationMessage>
</ValidationControl>
</FieldComponent>
<FieldComponent Label="City">
<Control>
<InputText @bind-Value="server.City" class="form-control"></InputText>
</Control>
<ValidationControl>
<ValidationMessage For="() => server.City"></ValidationMessage>
</ValidationControl>
</FieldComponent>
<FieldComponent Label="Online">
<Control>
<InputCheckbox @bind-Value="server.IsOnline" class="form-check-input"></InputCheckbox>
</Control>
</FieldComponent>
<br/>
@if (server.Id > 0)
{
<button class="btn btn-primary" type="submit">Update</button>
}
else
{
<button class="btn btn-primary" type="submit">Save</button>
}
&nbsp;
<a href="/servers" class="btn btn-primary">Close</a>
</EditForm>
}
@code {
[Parameter]
public int? Id { get; set; }
[SupplyParameterFromForm]
private Server? server { get; set; }
[SupplyParameterFromQuery]
public string? City { get; set; }
protected override void OnParametersSet()
{
if (this.Id.HasValue)
{
server ??= ServersRepository.GetServerById(this.Id.Value);
}
else
{
server ??= new Server() { IsOnline = false };
}
if (server != null && !string.IsNullOrEmpty(this.City))
{
server.City = this.City;
}
}
private void SubmitServer()
{
if (server != null)
{
if (this.Id.HasValue)
{
ServersRepository.UpdateServer(server.Id, server);
}
else
{
ServersRepository.AddServer(server);
}
}
// An exception is raised when debugging from VS Code, but not when using dotnet watch.
NavigationManager.NavigateTo($"/servers/back_from/{this.server?.City}");
}
private async Task OnBeforeInternalNavigation(LocationChangingContext context)
{
var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm", "Are you sure you want to leave this page?");
if (!isConfirmed)
{
context.PreventNavigation();
}
}
}

View File

@ -1,59 +0,0 @@
@page "/servers/add"
@using System.ComponentModel.DataAnnotations
@attribute [ExcludeFromInteractiveRouting]
@inject NavigationManager NavigationManager
<h3>Add server</h3>
<br/>
<br/>
<EditForm Model="server" FormName="formServer" OnValidSubmit="SubmitServer">
<DataAnnotationsValidator></DataAnnotationsValidator>
<ValidationSummary></ValidationSummary>
<div class="row mb-3">
<div class="col-2">
<label class="col-form-label">Name</label>
</div>
<div class="col-6">
<InputText @bind-Value="server.Name" class="form-control"></InputText>
</div>
<div class="col">
<ValidationMessage For="() => server.Name"></ValidationMessage>
</div>
</div>
<div class="row mb-3">
<div class="col-2">
<label class="col-form-label">City</label>
</div>
<div class="col-6">
<InputText @bind-Value="server.City" class="form-control"></InputText>
</div>
<div class="col">
<ValidationMessage For="() => server.City"></ValidationMessage>
</div>
</div>
<br/>
<button class="btn btn-primary" type="submit">Add</button>
&nbsp;
<a href="/servers" class="btn btn-primary">Close</a>
</EditForm>
@code {
[SupplyParameterFromForm(FormName = "formServer")]
private Server server { get; set; } = new Server() { IsOnline = false };
private void SubmitServer()
{
if (server != null)
{
ServersRepository.AddServer(server);
}
// An exception is raised when debugging from VS Code, but not when using dotnet watch.
NavigationManager.NavigateTo("/servers");
}
}

View File

@ -1,78 +0,0 @@
@page "/servers/{id:int}"
@* Route constraints: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-9.0#route-constraints *@
@attribute [ExcludeFromInteractiveRouting]
@inject NavigationManager NavigationManager
<h3>Edit server</h3>
<br/>
<br/>
@if (server != null)
{
<EditForm Model="server" FormName="formServer" OnValidSubmit="SubmitServer">
<DataAnnotationsValidator></DataAnnotationsValidator>
<ValidationSummary></ValidationSummary>
<InputNumber @bind-Value="server.Id" hidden></InputNumber>
<div class="row mb-3">
<div class="col-2">
<label class="col-form-label">Name</label>
</div>
<div class="col-6 input-width">
<InputText @bind-Value="server.Name" class="form-control"></InputText>
</div>
<div class="col">
<ValidationMessage For="() => server.Name"></ValidationMessage>
</div>
</div>
<div class="row mb-3">
<div class="col-2">
<label class="col-form-label">City</label>
</div>
<div class="col-6 input-width">
<InputText @bind-Value="server.City" class="form-control"></InputText>
</div>
<div class="col">
<ValidationMessage For="() => server.City"></ValidationMessage>
</div>
</div>
<div class="row mb-3">
<div class="col-2">
<label class="col-form-label">Online</label>
</div>
<div class="col-6">
<InputCheckbox @bind-Value="server.IsOnline" class="form-check-input"></InputCheckbox>
</div>
</div>
<br/>
<button class="btn btn-primary" type="submit">Update</button>
&nbsp;
<a href="/servers" class="btn btn-primary">Close</a>
</EditForm>
}
@code {
[Parameter] // This is a root parameter.
public int Id { get; set; }
[SupplyParameterFromForm(FormName = "formServer")]
private Server? server { get; set; }
protected override void OnParametersSet()
{
server ??= ServersRepository.GetServerById(this.Id);
}
private void SubmitServer()
{
if (server != null)
{
ServersRepository.UpdateServer(server.Id, server);
}
// An exception is raised when debugging from VS Code, but not when using dotnet watch.
NavigationManager.NavigateTo("/servers");
}
}

View File

@ -0,0 +1,76 @@
@page "/quickgrid"
@using Microsoft.AspNetCore.Components.QuickGrid
@inject NavigationManager NavigationManager
<h3>QuickGrid demo</h3>
<br/>
@if (servers != null)
{
<QuickGrid Items="servers.AsQueryable()" Pagination="paginationState">
<PropertyColumn Property="s => s.Name" Sortable="true"></PropertyColumn>
<PropertyColumn Property="s => s.City" Sortable="true"></PropertyColumn>
<TemplateColumn Title="Status" Sortable="true" SortBy="GridSort<Server>.ByAscending(s => s.IsOnline)">
<div style="color: @(context.IsOnline ? "green" : "red")">
@(context.IsOnline ? "Online" : "Offline")
</div>
</TemplateColumn>
<TemplateColumn Title="People online">
@if (context.IsOnline)
{
Random random = new Random();
int randomNumber = random.Next(0, 500);
<text>@randomNumber users online</text>
}
else
{
<text>N/A</text>
}
</TemplateColumn>
<TemplateColumn>
@if (context.IsOnline)
{
<button type="button" class="btn btn-outline-danger"
@onclick="@(() => { context.IsOnline = false; })">
Turn off
</button>
}
else
{
<button type="button" class="btn btn-outline-success"
@onclick="@(() => { context.IsOnline = true; })">
Turn on
</button>
}
</TemplateColumn>
<TemplateColumn>
<a href="@($"/servers/{context.Id}")" class="btn btn-primary">Edit</a>
</TemplateColumn>
<TemplateColumn>
<ChildContent Context="server">
<EditForm
Model="server"
FormName="@($"formDeleteServer{server.Id}")"
OnValidSubmit="@(() => { DeleteServer(server.Id); })">
<button type="submit" class="btn btn-danger">Delete</button>
</EditForm>
</ChildContent>
</TemplateColumn>
</QuickGrid>
<Paginator State="paginationState"></Paginator>
}
@code {
private List<Server>? servers = ServersRepository.GetServers();
private PaginationState paginationState = new PaginationState { ItemsPerPage = 5 };
private void DeleteServer(int serverId)
{
if (serverId > 0)
{
ServersRepository.DeleteServer(serverId);
NavigationManager.Refresh(forceReload: true);
}
}
}

View File

@ -1,23 +1,38 @@
@page "/servers" @page "/servers"
@page "/servers/back_from/{cityName}"
@using ServerManagement.Components.Controls @using ServerManagement.Components.Controls
@inject NavigationManager NavigationManager
<h3>Servers</h3> <h3>Servers</h3>
<br/> <br/>
<br/> <br/>
<CityListComponent @ref="cityListComponent" SelectCityCallBack="HandleCitySelection"></CityListComponent> <CityListComponent
@ref="cityListComponent"
SelectCityCallBack="HandleCitySelection"
selectedCity="@this.selectedCity">
</CityListComponent>
<br/> <br/>
<SearchBarComponent @ref="searchBarComponent" FilterSearchTerm="HandleSearch"></SearchBarComponent> <SearchBarComponent
@ref="searchBarComponent"
FilterSearchTerm="HandleSearch"
style="width: 600px">
</SearchBarComponent>
<br/> <br/>
<a href="@($"/servers/add")" class="btn btn-primary">Add</a> <a href="@($"/server")" class="btn btn-primary">Add Server</a>
&nbsp;
<a href="@($"/servername")" class="btn btn-primary">Wizard</a>
<br/> <br/>
<ServerListComponent <CascadingValue Name="SelectedCity" Value="@selectedCity">
CityName="@this.selectedCity" <ServerListComponent
SearchFilter="@this.searchFilter"> CityName="@this.selectedCity"
SearchFilter="@this.searchFilter">
</ServerListComponent> </ServerListComponent>
</CascadingValue>
@code { @code {
[Parameter]
public string? CityName { get; set; }
private string selectedCity = "Eindhoven"; private string selectedCity = "Eindhoven";
private string searchFilter = ""; private string searchFilter = "";
private CityListComponent? cityListComponent; private CityListComponent? cityListComponent;
@ -35,4 +50,16 @@
this.searchFilter = searchFilter; this.searchFilter = searchFilter;
cityListComponent?.ClearSelection(); cityListComponent?.ClearSelection();
} }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
if (NavigationManager.Uri.Contains("back_from") && !string.IsNullOrWhiteSpace(CityName))
{
selectedCity = CityName;
StateHasChanged();
}
}
}
} }

View File

@ -10,32 +10,7 @@
@if (items != null && items.Count > 0) @if (items != null && items.Count > 0)
{ {
<ul class="list-unstyled"> <ul class="list-unstyled">
@foreach (var item in items) <ToDoItemListComponent Items="items"></ToDoItemListComponent>
{
<li @key="item.Id">
<div class="row mb-2">
<div class="col-1" style="width: 30px;">
<input type="checkbox" class="form-check-input" style="vertical-align: middle" @bind-value="item.IsCompleted" checked="@item.IsCompleted"/>
</div>
<div class="col">
@if (item.IsCompleted)
{
<input type="text" class="form-control border-0 text-decoration-line-through" style="vertical-align: middle" @bind-value="item.Name" disabled/>
}
else
{
<input type="text" class="form-control border-0" style="vertical-align: middle" @bind-value="item.Name"/>
}
</div>
<div class="col">
@if (item.IsCompleted)
{
<text>Completed at: @item.DateCompleted.ToLongDateString()</text>
}
</div>
</div>
</li>
}
</ul> </ul>
} }

View File

@ -0,0 +1,65 @@
@page "/cityname"
@using ServerManagement.StateStore
@inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage
<h3>City Name</h3>
<br/>
@if (!string.IsNullOrWhiteSpace(errorMessage))
{
<div class="alert alert-danger">
@errorMessage
</div>
}
@if (server != null)
{
<FieldComponent Label="City name">
<Control>
<input type="text" @bind-value="server.City" class="form-control"></input>
</Control>
</FieldComponent>
<br/>
<button type="button" class="btn btn-primary" @onclick="GoNext">Next</button>
}
@code {
private Server? server;
private string? errorMessage;
[SupplyParameterFromQuery]
private string? ServerName { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
this.server = await sessionStorage.GetServerAsync();
StateHasChanged();
}
}
private async Task GoNext()
{
if (server != null)
{
if (string.IsNullOrWhiteSpace(server.City))
{
this.errorMessage = "City name is required.";
}
else
{
await this.sessionStorage.SetServerAsync(server);
NavigationManager.NavigateTo($"/serverstatus");
}
}
}
}

View File

@ -0,0 +1,59 @@
@page "/servername"
@using ServerManagement.StateStore
@inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage
<h3>Server Name</h3>
<br/>
@if (!string.IsNullOrWhiteSpace(errorMessage))
{
<div class="alert alert-danger">
@errorMessage
</div>
}
@if (server != null)
{
<FieldComponent Label="Server name">
<Control>
<input type="text" @bind-value="server.Name" class="form-control"></input>
</Control>
</FieldComponent>
<br/>
<button type="button" class="btn btn-primary" @onclick="GoNext">Next</button>
}
@code {
private Server? server;
private string? errorMessage;
protected override void OnInitialized()
{
base.OnInitialized();
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
server = new Server();
StateHasChanged();
}
}
private async Task GoNext()
{
if (string.IsNullOrWhiteSpace(server?.Name))
{
this.errorMessage = "Server name is required.";
}
else
{
await sessionStorage.SetServerAsync(server);
NavigationManager.NavigateTo($"/cityname");
}
}
}

View File

@ -0,0 +1,55 @@
@page "/serverstatus"
@using ServerManagement.StateStore
@inject NavigationManager NavigationManager
@inject SessionStorage sessionStorage
<h3>Server Status</h3>
<br/>
@if (server != null)
{
<FieldComponent Label="Server status online">
<Control>
@if (server.IsOnline)
{
<input type="checkbox" @bind-value="server.IsOnline" class="form-check-input"></input>
}
else
{
<input type="checkbox" @bind-value="server.IsOnline" class="form-check-input" checked></input>
}
</Control>
</FieldComponent>
<br/>
<button type="button" class="btn btn-primary" @onclick="Save">Save</button>
}
@code {
private Server? server;
protected override void OnInitialized()
{
base.OnInitialized();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
this.server = await sessionStorage.GetServerAsync();
StateHasChanged();
}
}
private async Task Save()
{
if (server != null)
{
await sessionStorage.SetServerAsync(null);
ServersRepository.AddServer(server);
NavigationManager.NavigateTo($"/servers/back_from/{server?.City}");
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using ServerManagement.Models;
namespace ServerManagement.StateStore
{
public class SessionStorage
{
private readonly ProtectedSessionStorage protectedSessionStorage;
public SessionStorage(ProtectedSessionStorage protectedSessionStorage)
{
this.protectedSessionStorage = protectedSessionStorage;
}
public async Task<Server?> GetServerAsync()
{
var result = await this.protectedSessionStorage.GetAsync<Server>("server");
if (result.Success)
{
return result.Value;
}
else
{
return null;
}
}
public async Task SetServerAsync(Server? server)
{
await this.protectedSessionStorage.SetAsync("server", server);
}
}
}

View File

@ -9,4 +9,6 @@
@using ServerManagement @using ServerManagement
@using ServerManagement.Components @using ServerManagement.Components
@using ServerManagement.Components.Controls @using ServerManagement.Components.Controls
@using ServerManagement.Models @using ServerManagement.Components.Controls.Generic
@using ServerManagement.Models
@using ServerManagement.StateStore

View File

@ -1,4 +1,5 @@
using ServerManagement.Components; using ServerManagement.Components;
using ServerManagement.StateStore;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -6,6 +7,8 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents() builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(); // Provides server interactivity. .AddInteractiveServerComponents(); // Provides server interactivity.
builder.Services.AddTransient<SessionStorage>();
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.

View File

@ -10,4 +10,8 @@
<RazorComponent Include="Components\Controls\ServerComponent.razor" /> <RazorComponent Include="Components\Controls\ServerComponent.razor" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="9.0.3" />
</ItemGroup>
</Project> </Project>