Add public search page / initial API (#5)
This commit is contained in:
@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.WebUtilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
@@ -56,10 +55,10 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
{
|
||||
if (query.TryGetValue(part, out var partValue)) func(partValue);
|
||||
}
|
||||
setPart("ContinentId", x => Criteria.ContinentId = x);
|
||||
setPart("Skill", x => Criteria.Skill = x);
|
||||
setPart("BioExperience", x => Criteria.BioExperience = x);
|
||||
setPart("RemoteWork", x => Criteria.RemoteWork = x);
|
||||
setPart(nameof(Criteria.ContinentId), x => Criteria.ContinentId = x);
|
||||
setPart(nameof(Criteria.Skill), x => Criteria.Skill = x);
|
||||
setPart(nameof(Criteria.BioExperience), x => Criteria.BioExperience = x);
|
||||
setPart(nameof(Criteria.RemoteWork), x => Criteria.RemoteWork = x);
|
||||
|
||||
await RetrieveProfiles();
|
||||
}
|
||||
@@ -100,6 +99,11 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
Searching = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a CSS class if the user is actively seeking work
|
||||
/// </summary>
|
||||
/// <param name="profile">The result in question</param>
|
||||
/// <returns>A string with the appropriate CSS class, if actively seeking work</returns>
|
||||
private static string? IsSeeking(ProfileSearchResult profile) =>
|
||||
profile.SeekingEmployment ? "font-weight-bold" : null;
|
||||
|
||||
@@ -124,10 +128,10 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
if (!string.IsNullOrEmpty(func(Criteria))) dict.Add(name, func(Criteria));
|
||||
}
|
||||
|
||||
part("ContinentId", it => it.ContinentId);
|
||||
part("Skill", it => it.Skill);
|
||||
part("BioExperience", it => it.BioExperience);
|
||||
part("RemoteWork", it => it.RemoteWork);
|
||||
part(nameof(Criteria.ContinentId), it => it.ContinentId);
|
||||
part(nameof(Criteria.Skill), it => it.Skill);
|
||||
part(nameof(Criteria.BioExperience), it => it.BioExperience);
|
||||
part(nameof(Criteria.RemoteWork), it => it.RemoteWork);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
56
src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor
Normal file
56
src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor
Normal file
@@ -0,0 +1,56 @@
|
||||
@page "/profile/seeking"
|
||||
@inject HttpClient http
|
||||
@inject NavigationManager nav
|
||||
@inject AppState state
|
||||
|
||||
<PageTitle Title="People Seeking Work" />
|
||||
<h3>People Seeking Work</h3>
|
||||
|
||||
<ErrorList Errors=@ErrorMessages>
|
||||
@if (Searching)
|
||||
{
|
||||
<p>Searching profiles...</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Searched)
|
||||
{
|
||||
<p>Enter one or more criteria to filter results, or just click “Search” to list all profiles.</p>
|
||||
}
|
||||
<Collapsible HeaderText="Search Criteria" Collapsed=@(Searched && SearchResults.Any())>
|
||||
<PublicSearchForm Criteria=@Criteria OnSearch=@DoSearch Continents=@Continents />
|
||||
</Collapsible>
|
||||
<br>
|
||||
@if (SearchResults.Any())
|
||||
{
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Profile</th>
|
||||
<th scope="col">Continent</th>
|
||||
<th scope="col" class="text-center">Region</th>
|
||||
<th scope="col" class="text-center">Remote?</th>
|
||||
<th scope="col" class="text-center">Skills</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var profile in SearchResults)
|
||||
{
|
||||
<tr>
|
||||
<td><a href="/profile/bio/@profile.CitizenId">View</a></td>
|
||||
<td class=@IsSeeking(profile)>@profile.DisplayName</td>
|
||||
<td class="text-center">@YesOrNo(profile.SeekingEmployment)</td>
|
||||
<td class="text-center">@YesOrNo(profile.RemoteWork)</td>
|
||||
<td class="text-center">@YesOrNo(profile.FullTime)</td>
|
||||
<td><FullDate TheDate=@profile.LastUpdated /></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else if (Searched)
|
||||
{
|
||||
<p>No results found for the specified criteria</p>
|
||||
}
|
||||
}
|
||||
</ErrorList>
|
||||
134
src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor.cs
Normal file
134
src/JobsJobsJobs/Client/Pages/Profiles/Seeking.razor.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using JobsJobsJobs.Shared;
|
||||
using JobsJobsJobs.Shared.Api;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
{
|
||||
public partial class Seeking : ComponentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether a search has been performed
|
||||
/// </summary>
|
||||
private bool Searched { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a request for matching profiles is in progress
|
||||
/// </summary>
|
||||
private bool Searching { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The search criteria
|
||||
/// </summary>
|
||||
private PublicSearch Criteria { get; set; } = new PublicSearch();
|
||||
|
||||
/// <summary>
|
||||
/// Error messages encountered while searching for profiles
|
||||
/// </summary>
|
||||
private IList<string> ErrorMessages { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// All continents
|
||||
/// </summary>
|
||||
private IEnumerable<Continent> Continents { get; set; } = Enumerable.Empty<Continent>();
|
||||
|
||||
/// <summary>
|
||||
/// The search results
|
||||
/// </summary>
|
||||
private IEnumerable<ProfileSearchResult> SearchResults { get; set; } = Enumerable.Empty<ProfileSearchResult>();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Continents = await state.GetContinents(http);
|
||||
|
||||
// Determine if we have searched before
|
||||
var query = QueryHelpers.ParseQuery(nav.ToAbsoluteUri(nav.Uri).Query);
|
||||
|
||||
if (query.TryGetValue("Searched", out var searched))
|
||||
{
|
||||
Searched = Convert.ToBoolean(searched);
|
||||
void setPart(string part, Action<string> func)
|
||||
{
|
||||
if (query.TryGetValue(part, out var partValue)) func(partValue);
|
||||
}
|
||||
setPart(nameof(Criteria.ContinentId), x => Criteria.ContinentId = x);
|
||||
setPart(nameof(Criteria.Region), x => Criteria.Region = x);
|
||||
setPart(nameof(Criteria.Skill), x => Criteria.Skill = x);
|
||||
setPart(nameof(Criteria.RemoteWork), x => Criteria.RemoteWork = x);
|
||||
|
||||
await RetrieveProfiles();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do a search
|
||||
/// </summary>
|
||||
/// <remarks>This navigates with the parameters in the URL; this should trigger a search</remarks>
|
||||
private async Task DoSearch()
|
||||
{
|
||||
var query = SearchQuery();
|
||||
query.Add("Searched", "True");
|
||||
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/search", query));
|
||||
await RetrieveProfiles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retreive profiles matching the current search criteria
|
||||
/// </summary>
|
||||
private async Task RetrieveProfiles()
|
||||
{
|
||||
Searching = true;
|
||||
|
||||
var searchResult = await ServerApi.RetrieveMany<ProfileSearchResult>(http,
|
||||
QueryHelpers.AddQueryString("profile/search", SearchQuery()));
|
||||
|
||||
if (searchResult.IsOk)
|
||||
{
|
||||
SearchResults = searchResult.Ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessages.Add(searchResult.Error);
|
||||
}
|
||||
|
||||
Searched = true;
|
||||
Searching = false;
|
||||
}
|
||||
|
||||
private static string? IsSeeking(ProfileSearchResult profile) =>
|
||||
profile.SeekingEmployment ? "font-weight-bold" : null;
|
||||
|
||||
/// <summary>
|
||||
/// Return "Yes" for true and "No" for false
|
||||
/// </summary>
|
||||
/// <param name="condition">The condition in question</param>
|
||||
/// <returns>"Yes" for true, "No" for false</returns>
|
||||
private static string YesOrNo(bool condition) => condition ? "Yes" : "No";
|
||||
|
||||
/// <summary>
|
||||
/// Create a search query string from the currently-entered criteria
|
||||
/// </summary>
|
||||
/// <returns>The query string for the currently-entered criteria</returns>
|
||||
private IDictionary<string, string?> SearchQuery()
|
||||
{
|
||||
var dict = new Dictionary<string, string?>();
|
||||
if (Criteria.IsEmptySearch) return dict;
|
||||
|
||||
void part(string name, Func<PublicSearch, string?> func)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(func(Criteria))) dict.Add(name, func(Criteria));
|
||||
}
|
||||
|
||||
part(nameof(Criteria.ContinentId), it => it.ContinentId);
|
||||
part(nameof(Criteria.Region), it => it.Region);
|
||||
part(nameof(Criteria.Skill), it => it.Skill);
|
||||
part(nameof(Criteria.RemoteWork), it => it.RemoteWork);
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user