Add common loading component

This has no specific issue associated, but reduces boilerplate in most page components
This commit is contained in:
Daniel J. Summers 2021-01-26 22:26:36 -05:00
parent 7f7eb191fb
commit a6fd891cc5
13 changed files with 299 additions and 300 deletions

View File

@ -6,46 +6,39 @@
<h3>Welcome, @state.User!.Name!</h3> <h3>Welcome, @state.User!.Name!</h3>
@if (RetrievingData) <Loading OnLoad=@LoadProfile Message=@(new MarkupString("Retrieving your employment profile&hellip;"))>
{ @if (Profile != null)
<p>Retrieving your employment profile...</p> {
}
else
{
<ErrorList Errors=@ErrorMessages>
@if (Profile != null)
{
<p>
Your employment profile was last updated <FullDateTime TheDate=@Profile.LastUpdatedOn />. Your profile currently
lists @Profile.Skills.Length skill@(Profile.Skills.Length != 1 ? "s" : "").
</p>
<p><a href="/profile/view/@state.User.Id">View Your Employment Profile</a></p>
@if (Profile.SeekingEmployment)
{
<p>
Your profile indicates that you are seeking employment. Once you find it,
<a href="/success-story/add">tell your fellow citizens about it!</a>
</p>
}
}
else
{
<p>
You do not have an employment profile established; click &ldquo;Edit Profile&rdquo; in the menu to get
started!
</p>
}
<hr>
<p> <p>
There @(ProfileCount == 1 ? "is" : "are") @(ProfileCount == 0 ? "no" : ProfileCount) employment Your employment profile was last updated <FullDateTime TheDate=@Profile.LastUpdatedOn />. Your profile currently
profile@(ProfileCount != 1 ? "s" : "") from citizens of Gitmo Nation. lists @Profile.Skills.Length skill@(Profile.Skills.Length != 1 ? "s" : "").
@if (ProfileCount > 0)
{
<text>Take a look around and see if you can help them find work!</text>
}
</p> </p>
</ErrorList> <p><a href="/profile/view/@state.User.Id">View Your Employment Profile</a></p>
} @if (Profile.SeekingEmployment)
{
<p>
Your profile indicates that you are seeking employment. Once you find it,
<a href="/success-story/add">tell your fellow citizens about it!</a>
</p>
}
}
else
{
<p>
You do not have an employment profile established; click &ldquo;Edit Profile&rdquo; in the menu to get
started!
</p>
}
<hr>
<p>
There @(ProfileCount == 1 ? "is" : "are") @(ProfileCount == 0 ? "no" : ProfileCount) employment
profile@(ProfileCount != 1 ? "s" : "") from citizens of Gitmo Nation.
@if (ProfileCount > 0)
{
<text>Take a look around and see if you can help them find work!</text>
}
</p>
</Loading>
<hr> <hr>
<h4> <h4>
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/3" target="_blank">Phase 3</a> &ndash; What Works <a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/3" target="_blank">Phase 3</a> &ndash; What Works

View File

@ -11,11 +11,6 @@ namespace JobsJobsJobs.Client.Pages.Citizen
/// </summary> /// </summary>
public partial class Dashboard : ComponentBase public partial class Dashboard : ComponentBase
{ {
/// <summary>
/// Whether the data is being retrieved
/// </summary>
private bool RetrievingData { get; set; } = true;
/// <summary> /// <summary>
/// The user's profile /// The user's profile
/// </summary> /// </summary>
@ -27,11 +22,10 @@ namespace JobsJobsJobs.Client.Pages.Citizen
private int ProfileCount { get; set; } private int ProfileCount { get; set; }
/// <summary> /// <summary>
/// Error messages from data access /// Load the user's profile information
/// </summary> /// </summary>
private IList<string> ErrorMessages { get; } = new List<string>(); /// <param name="errors">A collection to report errors that may be encountered</param>
public async Task LoadProfile(ICollection<string> errors)
protected override async Task OnInitializedAsync()
{ {
if (state.User != null) if (state.User != null)
{ {
@ -47,7 +41,7 @@ namespace JobsJobsJobs.Client.Pages.Citizen
} }
else else
{ {
ErrorMessages.Add(profileTask.Result.Error); errors.Add(profileTask.Result.Error);
} }
if (profileCountTask.Result.IsOk) if (profileCountTask.Result.IsOk)
@ -56,10 +50,8 @@ namespace JobsJobsJobs.Client.Pages.Citizen
} }
else else
{ {
ErrorMessages.Add(profileCountTask.Result.Error); errors.Add(profileCountTask.Result.Error);
} }
RetrievingData = false;
} }
} }
} }

View File

@ -8,118 +8,111 @@
<h3>Employment Profile</h3> <h3>Employment Profile</h3>
@if (AllLoaded) <Loading OnLoad=@SetUpProfile Message=@(new MarkupString("Loading Your Profile&hellip;"))>
{ <EditForm Model=@ProfileForm OnValidSubmit=@SaveProfile>
<ErrorList Errors=@ErrorMessages> <DataAnnotationsValidator />
<EditForm Model=@ProfileForm OnValidSubmit=@SaveProfile> <div class="form-row">
<DataAnnotationsValidator /> <div class="col">
<div class="form-row"> <div class="form-check">
<div class="col"> <InputCheckbox id="seeking" class="form-check-input" @bind-Value=@ProfileForm.IsSeekingEmployment />
<div class="form-check"> <label for="seeking" class="form-check-label">I am currently seeking employment</label>
<InputCheckbox id="seeking" class="form-check-input" @bind-Value=@ProfileForm.IsSeekingEmployment /> @if (IsSeeking)
<label for="seeking" class="form-check-label">I am currently seeking employment</label> {
@if (IsSeeking) <em>&nbsp; &nbsp; If you have found employment, consider
<a href="/success-story/add">telling your fellow citizens about it</a>
</em>
}
</div>
</div>
</div>
<div class="form-row">
<div class="col col-xs-12 col-sm-6 col-md-4">
<div class="form-group">
<label for="continentId" class="jjj-required">Continent</label>
<InputSelect id="continentId" @bind-Value=@ProfileForm.ContinentId class="form-control">
<option>&ndash; Select &ndash;</option>
@foreach (var (id, name) in Continents)
{ {
<em>&nbsp; &nbsp; If you have found employment, consider <option value="@id">@name</option>
<a href="/success-story/add">telling your fellow citizens about it</a>
</em>
} }
</div> </InputSelect>
<ValidationMessage For=@(() => ProfileForm.ContinentId) />
</div> </div>
</div> </div>
<div class="form-row"> <div class="col col-xs-12 col-sm-6 col-md-8">
<div class="col col-xs-12 col-sm-6 col-md-4"> <div class="form-group">
<div class="form-group"> <label for="region" class="jjj-required">Region</label>
<label for="continentId" class="jjj-required">Continent</label> <InputText id="region" @bind-Value=@ProfileForm.Region class="form-control"
<InputSelect id="continentId" @bind-Value=@ProfileForm.ContinentId class="form-control"> placeholder="Country, state, geographic area, etc." />
<option>&ndash; Select &ndash;</option> <ValidationMessage For=@(() => ProfileForm.Region) />
@foreach (var (id, name) in Continents)
{
<option value="@id">@name</option>
}
</InputSelect>
<ValidationMessage For=@(() => ProfileForm.ContinentId) />
</div>
</div>
<div class="col col-xs-12 col-sm-6 col-md-8">
<div class="form-group">
<label for="region" class="jjj-required">Region</label>
<InputText id="region" @bind-Value=@ProfileForm.Region class="form-control"
placeholder="Country, state, geographic area, etc." />
<ValidationMessage For=@(() => ProfileForm.Region) />
</div>
</div> </div>
</div> </div>
<div class="form-row"> </div>
<div class="col"> <div class="form-row">
<div class="form-group"> <div class="col">
<label for="bio" class="jjj-required">Professional Biography</label> <div class="form-group">
<MarkdownEditor Id="bio" @bind-Text=@ProfileForm.Biography /> <label for="bio" class="jjj-required">Professional Biography</label>
<ValidationMessage For=@(() => ProfileForm.Biography) /> <MarkdownEditor Id="bio" @bind-Text=@ProfileForm.Biography />
</div> <ValidationMessage For=@(() => ProfileForm.Biography) />
</div> </div>
</div> </div>
<div class="form-row"> </div>
<div class="col col-xs-12 col-sm-12 offset-md-2 col-md-4"> <div class="form-row">
<div class="form-check"> <div class="col col-xs-12 col-sm-12 offset-md-2 col-md-4">
<InputCheckbox id="isRemote" class="form-check-input" @bind-Value=@ProfileForm.RemoteWork /> <div class="form-check">
<label for="isRemote" class="form-check-label">I am looking for remote work</label> <InputCheckbox id="isRemote" class="form-check-input" @bind-Value=@ProfileForm.RemoteWork />
</div> <label for="isRemote" class="form-check-label">I am looking for remote work</label>
</div>
<div class="col col-xs-12 col-sm-12 col-md-4">
<div class="form-check">
<InputCheckbox id="isFull" class="form-check-input" @bind-Value=@ProfileForm.FullTime />
<label for="isFull" class="form-check-label">I am looking for full-time work</label>
</div>
</div> </div>
</div> </div>
<hr> <div class="col col-xs-12 col-sm-12 col-md-4">
<h4> <div class="form-check">
Skills &nbsp; <InputCheckbox id="isFull" class="form-check-input" @bind-Value=@ProfileForm.FullTime />
<button type="button" class="btn btn-outline-primary" @onclick=@AddNewSkill>Add a Skill</button> <label for="isFull" class="form-check-label">I am looking for full-time work</label>
</h4>
@foreach (var skill in ProfileForm.Skills)
{
<SkillEdit Skill=@skill OnRemove=@RemoveSkill />
}
<hr>
<h4>Experience</h4>
<p>
This application does not have a place to individually list your chronological job history; however, you can
use this area to list prior jobs, their dates, and anything else you want to include that&rsquo;s not already a
part of your Professional Biography above.
</p>
<div class="form-row">
<div class="col">
<MarkdownEditor Id="experience" @bind-Text=@ProfileForm.Experience />
</div> </div>
</div> </div>
<div class="form-row"> </div>
<div class="col"> <hr>
<div class="form-check"> <h4>
<InputCheckbox id="isPublic" class="form-check-input" @bind-Value=@ProfileForm.IsPublic /> Skills &nbsp;
<label for="isPublic" class="form-check-label"> <button type="button" class="btn btn-outline-primary" @onclick=@AddNewSkill>Add a Skill</button>
Allow my profile to be searched publicly (outside NA Social) </h4>
</label> @foreach (var skill in ProfileForm.Skills)
</div>
</div>
</div>
<div class="form-row">
<div class="col">
<br>
<button type="submit" class="btn btn-outline-primary">Save</button>
</div>
</div>
</EditForm>
@if (!IsNew)
{ {
<p> <SkillEdit Skill=@skill OnRemove=@RemoveSkill />
<br><a href="/profile/view/@state.User!.Id"><span class="oi oi-file"></span> View Your User Profile</a>
</p>
} }
</ErrorList> <hr>
} <h4>Experience</h4>
else <p>
{ This application does not have a place to individually list your chronological job history; however, you can
<p>Loading your profile...</p> use this area to list prior jobs, their dates, and anything else you want to include that&rsquo;s not already a
} part of your Professional Biography above.
</p>
<div class="form-row">
<div class="col">
<MarkdownEditor Id="experience" @bind-Text=@ProfileForm.Experience />
</div>
</div>
<div class="form-row">
<div class="col">
<div class="form-check">
<InputCheckbox id="isPublic" class="form-check-input" @bind-Value=@ProfileForm.IsPublic />
<label for="isPublic" class="form-check-label">
Allow my profile to be searched publicly (outside NA Social)
</label>
</div>
</div>
</div>
<div class="form-row">
<div class="col">
<br>
<button type="submit" class="btn btn-outline-primary">Save</button>
</div>
</div>
</EditForm>
@if (!IsNew)
{
<p>
<br><a href="/profile/view/@state.User!.Id"><span class="oi oi-file"></span> View Your User Profile</a>
</p>
}
</Loading>

View File

@ -18,11 +18,6 @@ namespace JobsJobsJobs.Client.Pages.Citizen
/// </summary> /// </summary>
private int _newSkillCounter = 0; private int _newSkillCounter = 0;
/// <summary>
/// A flag that indicates all the required API calls have completed, and the form is ready to be displayed
/// </summary>
private bool AllLoaded { get; set; } = false;
/// <summary> /// <summary>
/// Whether the citizen is seeking employment at the time the profile is loaded (used to show success story /// Whether the citizen is seeking employment at the time the profile is loaded (used to show success story
/// link) /// link)
@ -45,11 +40,10 @@ namespace JobsJobsJobs.Client.Pages.Citizen
private bool IsNew { get; set; } = false; private bool IsNew { get; set; } = false;
/// <summary> /// <summary>
/// Error messages from API access /// Set up the data needed to add or edit the user's profile
/// </summary> /// </summary>
private IList<string> ErrorMessages { get; } = new List<string>(); /// <param name="errors">The collection where errors can be reported</param>
public async Task SetUpProfile(ICollection<string> errors)
protected override async Task OnInitializedAsync()
{ {
ServerApi.SetJwt(http, state); ServerApi.SetJwt(http, state);
var continentTask = state.GetContinents(http); var continentTask = state.GetContinents(http);
@ -75,10 +69,8 @@ namespace JobsJobsJobs.Client.Pages.Citizen
} }
else else
{ {
ErrorMessages.Add(profileTask.Result.Error); errors.Add(profileTask.Result.Error);
} }
AllLoaded = true;
} }
/// <summary> /// <summary>
@ -118,6 +110,5 @@ namespace JobsJobsJobs.Client.Pages.Citizen
toast.ShowError($"{(int)res.StatusCode} {error}"); toast.ShowError($"{(int)res.StatusCode} {error}");
} }
} }
} }
} }

View File

@ -2,51 +2,44 @@
@inject HttpClient http @inject HttpClient http
@inject AppState state @inject AppState state
@if (IsLoading) <Loading OnLoad=@RetrieveProfile>
{
<p>Loading profile...</p>
}
else
{
<PageTitle Title=@($"Employment Profile for {Citizen.DisplayName}") /> <PageTitle Title=@($"Employment Profile for {Citizen.DisplayName}") />
<ErrorList Errors=@ErrorMessages> <h2><a href="@Citizen.ProfileUrl" target="_blank">@Citizen.DisplayName</a></h2>
<h2><a href="@Citizen.ProfileUrl" target="_blank">@Citizen.DisplayName</a></h2> <h4>@Profile.Continent!.Name, @Profile.Region</h4>
<h4>@Profile.Continent!.Name, @Profile.Region</h4> <p>@WorkTypes</p>
<p>@WorkTypes</p>
<hr> <hr>
<div> <div>
@(new MarkupString(Profile.Biography.ToHtml())) @(new MarkupString(Profile.Biography.ToHtml()))
</div> </div>
@if (Profile.Skills.Length > 0) @if (Profile.Skills.Length > 0)
{ {
<hr> <hr>
<h4>Skills</h4> <h4>Skills</h4>
<ul> <ul>
@foreach (var skill in Profile.Skills) @foreach (var skill in Profile.Skills)
{ {
var notes = skill.Notes == null ? "" : $" ({skill.Notes})"; var notes = skill.Notes == null ? "" : $" ({skill.Notes})";
<li>@skill.Description@notes</li> <li>@skill.Description@notes</li>
} }
</ul> </ul>
} }
@if (Profile.Experience != null) @if (Profile.Experience != null)
{ {
<hr> <hr>
<h4>Experience / Employment History</h4> <h4>Experience / Employment History</h4>
<div> <div>
@(new MarkupString(Profile.Experience.ToHtml())) @(new MarkupString(Profile.Experience.ToHtml()))
</div> </div>
} }
@if (Id == state.User!.Id.ToString()) @if (Id == state.User!.Id.ToString())
{ {
<hr> <hr>
<p><a href="/citizen/profile"><span class="oi oi-pencil"></span> Edit Your Profile</a></p> <p><a href="/citizen/profile"><span class="oi oi-pencil"></span> Edit Your Profile</a></p>
} }
</ErrorList> </Loading>
}

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Domain = JobsJobsJobs.Shared; using Domain = JobsJobsJobs.Shared;
@ -8,11 +7,6 @@ namespace JobsJobsJobs.Client.Pages.Profile
{ {
public partial class View : ComponentBase public partial class View : ComponentBase
{ {
/// <summary>
/// Whether data for this component is loading
/// </summary>
private bool IsLoading { get; set; } = true;
/// <summary> /// <summary>
/// The citizen whose profile is being displayed /// The citizen whose profile is being displayed
/// </summary> /// </summary>
@ -48,18 +42,17 @@ namespace JobsJobsJobs.Client.Pages.Profile
} }
} }
/// <summary>
/// Error messages from data retrieval
/// </summary>
private IList<string> ErrorMessages { get; } = new List<string>();
/// <summary> /// <summary>
/// The ID of the citizen whose profile should be displayed /// The ID of the citizen whose profile should be displayed
/// </summary> /// </summary>
[Parameter] [Parameter]
public string Id { get; set; } = default!; public string Id { get; set; } = default!;
protected override async Task OnInitializedAsync() /// <summary>
/// Retrieve the requested profile
/// </summary>
/// <param name="errors">A collection to report errors that may occur</param>
public async Task RetrieveProfile(ICollection<string> errors)
{ {
ServerApi.SetJwt(http, state); ServerApi.SetJwt(http, state);
var citizenTask = ServerApi.RetrieveOne<Domain.Citizen>(http, $"citizen/get/{Id}"); var citizenTask = ServerApi.RetrieveOne<Domain.Citizen>(http, $"citizen/get/{Id}");
@ -73,11 +66,11 @@ namespace JobsJobsJobs.Client.Pages.Profile
} }
else if (citizenTask.Result.IsOk) else if (citizenTask.Result.IsOk)
{ {
ErrorMessages.Add("Citizen not found"); errors.Add("Citizen not found");
} }
else else
{ {
ErrorMessages.Add(citizenTask.Result.Error); errors.Add(citizenTask.Result.Error);
} }
if (profileTask.Result.IsOk && profileTask.Result.Ok != null) if (profileTask.Result.IsOk && profileTask.Result.Ok != null)
@ -86,14 +79,12 @@ namespace JobsJobsJobs.Client.Pages.Profile
} }
else if (profileTask.Result.IsOk) else if (profileTask.Result.IsOk)
{ {
ErrorMessages.Add("Profile not found"); errors.Add("Profile not found");
} }
else else
{ {
ErrorMessages.Add(profileTask.Result.Error); errors.Add(profileTask.Result.Error);
} }
IsLoading = false;
} }
} }
} }

View File

@ -6,52 +6,44 @@
@inject IToastService toast @inject IToastService toast
<PageTitle Title=@Title /> <PageTitle Title=@Title />
<h3>@Title</h3> <h3>@Title</h3>
<ErrorList Errors=@ErrorMessages> <Loading OnLoad=@RetrieveStory>
@if (Loading) @if (IsNew)
{ {
<p>Loading...</p> <p>
Congratulations on your employment! Your fellow citizens would enjoy hearing how it all came about; tell us
about it below! <em>(These will be visible to other users, but not to the general public.)</em>
</p>
} }
else <EditForm Model=@Form OnValidSubmit=@SaveStory>
{ <div class="form-row">
@if (IsNew) <div class="col">
{ <div class="form-check">
<p> <InputCheckbox id="fromHere" class="form-check-input" @bind-Value=@Form.FromHere />
Congratulations on your employment! Your fellow citizens would enjoy hearing how it all came about; tell us <label for="fromHere" class="form-check-label">I found my employment here</label>
about it below! <em>(These will be visible to other users, but not to the general public.)</em>
</p>
}
<EditForm Model=@Form OnValidSubmit=@SaveStory>
<div class="form-row">
<div class="col">
<div class="form-check">
<InputCheckbox id="fromHere" class="form-check-input" @bind-Value=@Form.FromHere />
<label for="fromHere" class="form-check-label">I found my employment here</label>
</div>
</div> </div>
</div> </div>
<div class="form-row"> </div>
<div class="col"> <div class="form-row">
<div class="form-group"> <div class="col">
<label for="story" class="jjj-label">The Success Story</label> <div class="form-group">
<MarkdownEditor Id="story" @bind-Text=@Form.Story /> <label for="story" class="jjj-label">The Success Story</label>
</div> <MarkdownEditor Id="story" @bind-Text=@Form.Story />
</div> </div>
</div> </div>
<div class="form-row"> </div>
<div class="col"> <div class="form-row">
<br> <div class="col">
<button type="submit" class="btn btn-outline-primary">Save</button> <br>
@if (IsNew) <button type="submit" class="btn btn-outline-primary">Save</button>
{ @if (IsNew)
<p> {
<em>(Saving this will set &ldquo;Seeking Employment&rdquo; to &ldquo;No&rdquo; on your profile.)</em> <p>
</p> <em>(Saving this will set &ldquo;Seeking Employment&rdquo; to &ldquo;No&rdquo; on your profile.)</em>
} </p>
</div> }
</div> </div>
</EditForm> </div>
} </EditForm>
</ErrorList> </Loading>

View File

@ -16,11 +16,6 @@ namespace JobsJobsJobs.Client.Pages.SuccessStory
[Parameter] [Parameter]
public string? Id { get; set; } public string? Id { get; set; }
/// <summary>
/// Whether we are loading information
/// </summary>
private bool Loading { get; set; } = true;
/// <summary> /// <summary>
/// The page title / header /// The page title / header
/// </summary> /// </summary>
@ -37,11 +32,10 @@ namespace JobsJobsJobs.Client.Pages.SuccessStory
private bool IsNew => Form.Id == "new"; private bool IsNew => Form.Id == "new";
/// <summary> /// <summary>
/// Error messages from API access /// Retrieve the story
/// </summary> /// </summary>
private IList<string> ErrorMessages { get; } = new List<string>(); /// <param name="errors">A collection to use in reporting errors that may occur</param>
public async Task RetrieveStory(ICollection<string> errors)
protected override async Task OnInitializedAsync()
{ {
if (Id != null) if (Id != null)
{ {
@ -58,14 +52,13 @@ namespace JobsJobsJobs.Client.Pages.SuccessStory
} }
else if (story.IsOk) else if (story.IsOk)
{ {
ErrorMessages.Add($"The success story {Id} does not exist"); errors.Add($"The success story {Id} does not exist");
} }
else else
{ {
ErrorMessages.Add(story.Error); errors.Add(story.Error);
} }
} }
Loading = false;
} }
/// <summary> /// <summary>

View File

@ -3,15 +3,10 @@
@inject AppState state @inject AppState state
<PageTitle Title="Success Stories" /> <PageTitle Title="Success Stories" />
<h3>Success Stories</h3> <h3>Success Stories</h3>
<ErrorList Errors=@ErrorMessages> <Loading OnLoad=@LoadStories>
@if (Loading) @if (Stories.Any())
{
<p>Loading...</p>
}
else if (Stories.Any())
{ {
<table class="table table-sm table-hover"> <table class="table table-sm table-hover">
<thead> <thead>
@ -43,4 +38,4 @@
{ {
<p>There are no success stories recorded <em>(yet)</em></p> <p>There are no success stories recorded <em>(yet)</em></p>
} }
</ErrorList> </Loading>

View File

@ -1,30 +1,22 @@
using JobsJobsJobs.Shared.Api; using JobsJobsJobs.Shared.Api;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace JobsJobsJobs.Client.Pages.SuccessStory namespace JobsJobsJobs.Client.Pages.SuccessStory
{ {
public partial class ListStories : ComponentBase public partial class ListStories : ComponentBase
{ {
/// <summary>
/// Whether we are still loading data
/// </summary>
private bool Loading { get; set; } = true;
/// <summary> /// <summary>
/// The story entries /// The story entries
/// </summary> /// </summary>
private IEnumerable<StoryEntry> Stories { get; set; } = default!; private IEnumerable<StoryEntry> Stories { get; set; } = default!;
/// <summary> /// <summary>
/// Error messages encountered /// Load all success stories
/// </summary> /// </summary>
private IList<string> ErrorMessages { get; set; } = new List<string>(); /// <param name="errors">The collection into which errors can be reported</param>
public async Task LoadStories(ICollection<string> errors)
protected override async Task OnInitializedAsync()
{ {
ServerApi.SetJwt(http, state); ServerApi.SetJwt(http, state);
var stories = await ServerApi.RetrieveMany<StoryEntry>(http, "success/list"); var stories = await ServerApi.RetrieveMany<StoryEntry>(http, "success/list");
@ -35,10 +27,8 @@ namespace JobsJobsJobs.Client.Pages.SuccessStory
} }
else else
{ {
ErrorMessages.Add(stories.Error); errors.Add(stories.Error);
} }
Loading = false;
} }
} }
} }

View File

@ -0,0 +1,18 @@
@if (IsLoading)
{
<p>@Message</p>
}
else if (ErrorMessages.Count > 0)
{
<p>The following error@(ErrorMessages.Count == 1 ? "" : "s") occurred:</p>
<ul>
@foreach (var msg in ErrorMessages)
{
<li><pre>@msg</pre></li>
}
</ul>
}
else
{
@ChildContent
}

View File

@ -0,0 +1,58 @@
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace JobsJobsJobs.Client.Shared
{
public partial class Loading : ComponentBase
{
/// <summary>
/// The delegate to call to load the data for this page
/// </summary>
[Parameter]
public EventCallback<ICollection<string>> OnLoad { get; set; }
/// <summary>
/// The message to display when the page is loading (optional)
/// </summary>
[Parameter]
public MarkupString Message { get; set; } = new MarkupString("Loading&hellip;");
/// <summary>
/// The content to be displayed once the data has been loaded
/// </summary>
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
/// <summary>
/// Error messages that may arise from the data loading delegate
/// </summary>
private ICollection<string> ErrorMessages { get; set; } = new List<string>();
/// <summary>
/// Whether we are currently loading data
/// </summary>
private bool IsLoading { get; set; } = true;
protected override async Task OnInitializedAsync()
{
if (OnLoad.HasDelegate)
{
try
{
await OnLoad.InvokeAsync(ErrorMessages);
}
finally
{
IsLoading = false;
}
}
else
{
IsLoading = false;
}
}
}
}

View File

@ -9,10 +9,10 @@
</button> </button>
</div> </div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> <div class="@NavMenuCssClass" @onclick=@ToggleNavMenu>
<ul class="nav flex-column"> <ul class="nav flex-column">
<li class="nav-item px-3"> <li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <NavLink class="nav-link" href="" Match=@NavLinkMatch.All>
<span class="oi oi-home" aria-hidden="true"></span> Home <span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink> </NavLink>
</li> </li>