Complete public search (#5)
- Bump version - Define nav between profile and continent/skills - Remove redundant code
This commit is contained in:
parent
fb147888c5
commit
60ed7e1e79
|
@ -22,7 +22,7 @@ namespace JobsJobsJobs.Client
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The application version, as a nice display string
|
/// The application version, as a nice display string
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Lazy<string> Version => new Lazy<string>(() =>
|
public static Lazy<string> Version => new(() =>
|
||||||
{
|
{
|
||||||
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||||
var display = $"v{version.Major}.{version.Minor}";
|
var display = $"v{version.Major}.{version.Minor}";
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{
|
{
|
||||||
<p>
|
<p>
|
||||||
Your employment profile was last updated <FullDateTime TheDate=@Profile.LastUpdatedOn />. Your profile currently
|
Your employment profile was last updated <FullDateTime TheDate=@Profile.LastUpdatedOn />. Your profile currently
|
||||||
lists @Profile.Skills.Length skill@(Profile.Skills.Length != 1 ? "s" : "").
|
lists @Profile.Skills.Count skill@(Profile.Skills.Count != 1 ? "s" : "").
|
||||||
</p>
|
</p>
|
||||||
<p><a href="/profile/view/@state.User.Id">View Your Employment Profile</a></p>
|
<p><a href="/profile/view/@state.User.Id">View Your Employment Profile</a></p>
|
||||||
@if (Profile.SeekingEmployment)
|
@if (Profile.SeekingEmployment)
|
||||||
|
@ -41,7 +41,6 @@
|
||||||
</Loading>
|
</Loading>
|
||||||
<hr>
|
<hr>
|
||||||
<p>
|
<p>
|
||||||
To see what is currently done, and how this application works, check out “How It Works” in the sidebar.
|
To see how this application works, check out “How It Works” in the sidebar (last updated June
|
||||||
The application now has 4 of 5 phases complete towards version 1.0; the documentation was last updated January
|
14<sup>th</sup>, 2021).
|
||||||
31<sup>st</sup>, 2021.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -4,18 +4,6 @@
|
||||||
|
|
||||||
<h3>How It Works</h3>
|
<h3>How It Works</h3>
|
||||||
|
|
||||||
<p>
|
|
||||||
Phase 4 is complete, which means that the entire logged-in version of the application is now available. There are
|
|
||||||
GitHub issues for each one
|
|
||||||
(<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/1" target="_blank">Phase 1</a> •
|
|
||||||
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/2" target="_blank">Phase 2</a> •
|
|
||||||
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/3" target="_blank">Phase 3</a> •
|
|
||||||
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/4" target="_blank">Phase 4</a>), and if you run into any
|
|
||||||
issues with it, feel free to
|
|
||||||
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues" target="_blank">let us know on GitHub</a>, or look up
|
|
||||||
@("@")danieljsummers on No Agenda Social.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4>Completing Your Profile</h4>
|
<h4>Completing Your Profile</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
@ -44,11 +32,10 @@
|
||||||
would like it presented to fellow citizens.
|
would like it presented to fellow citizens.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/5" target="_blank">Phase 5</a> includes allowing
|
If you check the “Allow my profile to be searched publicly” checkbox, <strong>and</strong> you are
|
||||||
public access to the continent, region, and skills fields of Gitmo Nation citizens who indicate that they are both
|
seeking employment, your continent, region, and skills fields will be searchable and displayed to public users of
|
||||||
seeking employment <strong>and</strong> want their information disclosed to public users. The “Allow my
|
the site. They will not be tied to your No Agenda Social handle or real name; they are there to let people peek
|
||||||
profile to be searched publicly” checkbox, at the bottom of the page where you edit your employment profile,
|
behind the curtain a bit, and hopefully inspire them to join us.
|
||||||
is how you opt your profile in to this list.
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -77,19 +64,18 @@
|
||||||
to read it; if you submitted the story, there will also be an “Edit” link.
|
to read it; if you submitted the story, there will also be an “Edit” link.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Publicly Available Information</h4>
|
||||||
Publicly Available Information
|
|
||||||
<em><small>(coming in <a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/5" target="_blank">Phase 5</a>)</small></em>
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
The “public” page for profile information will display the following information:
|
The “Job Seekers” page for profile information will allow users to search for and display the continent,
|
||||||
|
region, skills, and notes of users who are seeking employment <strong>and</strong> have opted in to their information
|
||||||
|
being publicly searchable. If you are a public user, this information is always the latest we have; check out the link
|
||||||
|
at the top of the search results for how you can learn more about these fine human resources!
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
|
||||||
<li>A count of profiles where the citizen is seeking employment</li>
|
<h4>Help / Suggestions</h4>
|
||||||
<li>The citizen’s continent and region</li>
|
|
||||||
<li>The citizen’s skills and notes</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
<p>
|
||||||
This information will be pullled only from profiles where citizens have said can it be publicly available
|
This is open-source software
|
||||||
<strong>and</strong> are currently seeking employment.
|
<a href="https://github.com/bit-badger/jobs-jobs-jobs" _target="_blank">developed on Github</a>; feel free to
|
||||||
|
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues" target="_blank">create an issue there</a>, or look up
|
||||||
|
@("@")danieljsummers on No Agenda Social.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -23,10 +23,13 @@
|
||||||
<br>
|
<br>
|
||||||
@if (SearchResults.Any())
|
@if (SearchResults.Any())
|
||||||
{
|
{
|
||||||
|
<p>
|
||||||
|
These profiles match your search criteria. To learn more about these people, join the merry band of human
|
||||||
|
resources in the <a href="https://noagendashow.net" target="_blank">No Agenda</a> tribe!
|
||||||
|
</p>
|
||||||
<table class="table table-sm table-hover">
|
<table class="table table-sm table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Profile</th>
|
|
||||||
<th scope="col">Continent</th>
|
<th scope="col">Continent</th>
|
||||||
<th scope="col" class="text-center">Region</th>
|
<th scope="col" class="text-center">Region</th>
|
||||||
<th scope="col" class="text-center">Remote?</th>
|
<th scope="col" class="text-center">Remote?</th>
|
||||||
|
@ -37,12 +40,15 @@
|
||||||
@foreach (var profile in SearchResults)
|
@foreach (var profile in SearchResults)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/profile/bio/@profile.CitizenId">View</a></td>
|
<td>@profile.Continent</td>
|
||||||
<td class=@IsSeeking(profile)>@profile.DisplayName</td>
|
<td>@profile.Region</td>
|
||||||
<td class="text-center">@YesOrNo(profile.SeekingEmployment)</td>
|
|
||||||
<td class="text-center">@YesOrNo(profile.RemoteWork)</td>
|
<td class="text-center">@YesOrNo(profile.RemoteWork)</td>
|
||||||
<td class="text-center">@YesOrNo(profile.FullTime)</td>
|
<td>
|
||||||
<td><FullDate TheDate=@profile.LastUpdated /></td>
|
@foreach (var skill in profile.Skills)
|
||||||
|
{
|
||||||
|
@skill.Replace(" ()", "")<br>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The search results
|
/// The search results
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IEnumerable<ProfileSearchResult> SearchResults { get; set; } = Enumerable.Empty<ProfileSearchResult>();
|
private IEnumerable<PublicSearchResult> SearchResults { get; set; } = Enumerable.Empty<PublicSearchResult>();
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
@ -72,7 +72,7 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||||
{
|
{
|
||||||
var query = SearchQuery();
|
var query = SearchQuery();
|
||||||
query.Add("Searched", "True");
|
query.Add("Searched", "True");
|
||||||
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/search", query));
|
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/seeking", query));
|
||||||
await RetrieveProfiles();
|
await RetrieveProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||||
{
|
{
|
||||||
Searching = true;
|
Searching = true;
|
||||||
|
|
||||||
var searchResult = await ServerApi.RetrieveMany<ProfileSearchResult>(http,
|
var searchResult = await ServerApi.RetrieveMany<PublicSearchResult>(http,
|
||||||
QueryHelpers.AddQueryString("profile/search", SearchQuery()));
|
QueryHelpers.AddQueryString("profile/public-search", SearchQuery()));
|
||||||
|
|
||||||
if (searchResult.IsOk)
|
if (searchResult.IsOk)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@if (Profile.Skills.Length > 0)
|
@if (Profile.Skills.Count > 0)
|
||||||
{
|
{
|
||||||
<hr>
|
<hr>
|
||||||
<h4>Skills</h4>
|
<h4>Skills</h4>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
using JobsJobsJobs.Shared.Api;
|
using JobsJobsJobs.Shared.Api;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using NodaTime.Serialization.SystemTextJson;
|
using NodaTime.Serialization.SystemTextJson;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AssemblyVersion>0.9.1.0</AssemblyVersion>
|
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>0.9.1.0</FileVersion>
|
<FileVersion>1.0.0.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -97,10 +97,6 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("skills")]
|
|
||||||
public async Task<IActionResult> GetSkills() =>
|
|
||||||
Ok(await _db.FindSkillsByCitizen(CurrentCitizenId));
|
|
||||||
|
|
||||||
[HttpGet("count")]
|
[HttpGet("count")]
|
||||||
public async Task<IActionResult> GetProfileCount() =>
|
public async Task<IActionResult> GetProfileCount() =>
|
||||||
Ok(new Count(await _db.CountProfiles()));
|
Ok(new Count(await _db.CountProfiles()));
|
||||||
|
|
|
@ -83,8 +83,12 @@ namespace JobsJobsJobs.Server.Data
|
||||||
m.Property(e => e.LastUpdatedOn).HasColumnName("last_updated_on").IsRequired();
|
m.Property(e => e.LastUpdatedOn).HasColumnName("last_updated_on").IsRequired();
|
||||||
m.Property(e => e.Experience).HasColumnName("experience")
|
m.Property(e => e.Experience).HasColumnName("experience")
|
||||||
.HasConversion(Converters.OptionalMarkdownStringConverter);
|
.HasConversion(Converters.OptionalMarkdownStringConverter);
|
||||||
m.Ignore(e => e.Continent);
|
m.HasOne(e => e.Continent)
|
||||||
m.Ignore(e => e.Skills);
|
.WithMany()
|
||||||
|
.HasForeignKey(e => e.ContinentId);
|
||||||
|
m.HasMany(e => e.Skills)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey(e => e.CitizenId);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<Skill>(m =>
|
modelBuilder.Entity<Skill>(m =>
|
||||||
|
|
|
@ -18,24 +18,13 @@ namespace JobsJobsJobs.Server.Data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="citizenId">The ID of the citizen whose profile should be retrieved</param>
|
/// <param name="citizenId">The ID of the citizen whose profile should be retrieved</param>
|
||||||
/// <returns>The profile, or null if it does not exist</returns>
|
/// <returns>The profile, or null if it does not exist</returns>
|
||||||
public static async Task<Profile?> FindProfileByCitizen(this JobsDbContext db, CitizenId citizenId)
|
public static async Task<Profile?> FindProfileByCitizen(this JobsDbContext db, CitizenId citizenId) =>
|
||||||
{
|
await db.Profiles.AsNoTracking()
|
||||||
var profile = await db.Profiles.AsNoTracking()
|
.Include(p => p.Continent)
|
||||||
|
.Include(p => p.Skills)
|
||||||
.SingleOrDefaultAsync(p => p.Id == citizenId)
|
.SingleOrDefaultAsync(p => p.Id == citizenId)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (profile != null)
|
|
||||||
{
|
|
||||||
return profile with
|
|
||||||
{
|
|
||||||
Continent = await db.FindContinentById(profile.ContinentId).ConfigureAwait(false),
|
|
||||||
Skills = (await db.FindSkillsByCitizen(citizenId).ConfigureAwait(false)).ToArray()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save a profile
|
/// Save a profile
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -52,16 +41,6 @@ namespace JobsJobsJobs.Server.Data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve all skills for the given citizen
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="citizenId">The ID of the citizen whose skills should be retrieved</param>
|
|
||||||
/// <returns>The skills defined for this citizen</returns>
|
|
||||||
public static async Task<IEnumerable<Skill>> FindSkillsByCitizen(this JobsDbContext db, CitizenId citizenId) =>
|
|
||||||
await db.Skills.AsNoTracking()
|
|
||||||
.Where(s => s.CitizenId == citizenId)
|
|
||||||
.ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save a skill
|
/// Save a skill
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -166,29 +145,30 @@ namespace JobsJobsJobs.Server.Data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="search">The search parameters</param>
|
/// <param name="search">The search parameters</param>
|
||||||
/// <returns>The information for profiles matching the criteria</returns>
|
/// <returns>The information for profiles matching the criteria</returns>
|
||||||
public static async Task<IEnumerable<ProfileSearchResult>> SearchPublicProfiles(this JobsDbContext db,
|
public static async Task<IEnumerable<PublicSearchResult>> SearchPublicProfiles(this JobsDbContext db,
|
||||||
PublicSearch search)
|
PublicSearch search)
|
||||||
{
|
{
|
||||||
var query = db.Profiles
|
var query = db.Profiles
|
||||||
.Join(db.Citizens, p => p.Id, c => c.Id, (p, c) => new { Profile = p, Citizen = c })
|
.Include(it => it.Continent)
|
||||||
.Where(it => it.Profile.IsPublic);
|
.Include(it => it.Skills)
|
||||||
|
.Where(it => it.IsPublic);
|
||||||
|
|
||||||
var useIds = false;
|
var useIds = false;
|
||||||
var citizenIds = new List<CitizenId>();
|
var citizenIds = new List<CitizenId>();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(search.ContinentId))
|
if (!string.IsNullOrEmpty(search.ContinentId))
|
||||||
{
|
{
|
||||||
query = query.Where(it => it.Profile.ContinentId == ContinentId.Parse(search.ContinentId));
|
query = query.Where(it => it.ContinentId == ContinentId.Parse(search.ContinentId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(search.Region))
|
if (!string.IsNullOrEmpty(search.Region))
|
||||||
{
|
{
|
||||||
query = query.Where(it => it.Profile.Region.ToLower().Contains(search.Region.ToLower()));
|
query = query.Where(it => it.Region.ToLower().Contains(search.Region.ToLower()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(search.RemoteWork))
|
if (!string.IsNullOrEmpty(search.RemoteWork))
|
||||||
{
|
{
|
||||||
query = query.Where(it => it.Profile.RemoteWork == (search.RemoteWork == "yes"));
|
query = query.Where(it => it.RemoteWork == (search.RemoteWork == "yes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(search.Skill))
|
if (!string.IsNullOrEmpty(search.Skill))
|
||||||
|
@ -202,11 +182,11 @@ namespace JobsJobsJobs.Server.Data
|
||||||
|
|
||||||
if (useIds)
|
if (useIds)
|
||||||
{
|
{
|
||||||
query = query.Where(it => citizenIds.Contains(it.Citizen.Id));
|
query = query.Where(it => citizenIds.Contains(it.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return await query.Select(x => new ProfileSearchResult(x.Citizen.Id, x.Citizen.CitizenName,
|
return await query.Select(x => new PublicSearchResult(x.Continent!.Name, x.Region, x.RemoteWork,
|
||||||
x.Profile.SeekingEmployment, x.Profile.RemoteWork, x.Profile.FullTime, x.Profile.LastUpdatedOn))
|
x.Skills.Select(sk => $"{sk.Description} ({sk.Notes})")))
|
||||||
.ToListAsync().ConfigureAwait(false);
|
.ToListAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<ActiveDebugProfile>JobsJobsJobs.Server</ActiveDebugProfile>
|
<ActiveDebugProfile>JobsJobsJobs.Server</ActiveDebugProfile>
|
||||||
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
||||||
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
|
<Controller_SelectedScaffolderCategoryPath>root/Common/MVC/Controller</Controller_SelectedScaffolderCategoryPath>
|
||||||
<NameOfLastUsedPublishProfile>C:\Users\danie\Documents\sandbox\jobs-jobs-jobs\src\JobsJobsJobs\Server\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
|
<NameOfLastUsedPublishProfile>FolderProfile</NameOfLastUsedPublishProfile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||||
|
|
13
src/JobsJobsJobs/Shared/Api/PublicSearchResult.cs
Normal file
13
src/JobsJobsJobs/Shared/Api/PublicSearchResult.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace JobsJobsJobs.Shared.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A public profile search result
|
||||||
|
/// </summary>
|
||||||
|
public record PublicSearchResult(
|
||||||
|
string Continent,
|
||||||
|
string Region,
|
||||||
|
bool RemoteWork,
|
||||||
|
IEnumerable<string> Skills);
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace JobsJobsJobs.Shared
|
namespace JobsJobsJobs.Shared
|
||||||
{
|
{
|
||||||
|
@ -26,6 +26,6 @@ namespace JobsJobsJobs.Shared
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convenience property for skills associated with a profile
|
/// Convenience property for skills associated with a profile
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Skill[] Skills { get; set; } = Array.Empty<Skill>();
|
public ICollection<Skill> Skills { get; set; } = new List<Skill>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user