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>
|
||||
/// The application version, as a nice display string
|
||||
/// </summary>
|
||||
public static Lazy<string> Version => new Lazy<string>(() =>
|
||||
public static Lazy<string> Version => new(() =>
|
||||
{
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||
var display = $"v{version.Major}.{version.Minor}";
|
||||
|
@ -11,7 +11,7 @@
|
||||
{
|
||||
<p>
|
||||
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><a href="/profile/view/@state.User.Id">View Your Employment Profile</a></p>
|
||||
@if (Profile.SeekingEmployment)
|
||||
@ -41,7 +41,6 @@
|
||||
</Loading>
|
||||
<hr>
|
||||
<p>
|
||||
To see what is currently done, and how this application works, check out “How It Works” in the sidebar.
|
||||
The application now has 4 of 5 phases complete towards version 1.0; the documentation was last updated January
|
||||
31<sup>st</sup>, 2021.
|
||||
To see how this application works, check out “How It Works” in the sidebar (last updated June
|
||||
14<sup>th</sup>, 2021).
|
||||
</p>
|
||||
|
@ -4,18 +4,6 @@
|
||||
|
||||
<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>
|
||||
<ul>
|
||||
<li>
|
||||
@ -44,11 +32,10 @@
|
||||
would like it presented to fellow citizens.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/bit-badger/jobs-jobs-jobs/issues/5" target="_blank">Phase 5</a> includes allowing
|
||||
public access to the continent, region, and skills fields of Gitmo Nation citizens who indicate that they are both
|
||||
seeking employment <strong>and</strong> want their information disclosed to public users. The “Allow my
|
||||
profile to be searched publicly” checkbox, at the bottom of the page where you edit your employment profile,
|
||||
is how you opt your profile in to this list.
|
||||
If you check the “Allow my profile to be searched publicly” checkbox, <strong>and</strong> you are
|
||||
seeking employment, your continent, region, and skills fields will be searchable and displayed to public users of
|
||||
the site. They will not be tied to your No Agenda Social handle or real name; they are there to let people peek
|
||||
behind the curtain a bit, and hopefully inspire them to join us.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -77,19 +64,18 @@
|
||||
to read it; if you submitted the story, there will also be an “Edit” link.
|
||||
</p>
|
||||
|
||||
<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>
|
||||
<h4>Publicly Available Information</h4>
|
||||
<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>
|
||||
<ul>
|
||||
<li>A count of profiles where the citizen is seeking employment</li>
|
||||
<li>The citizen’s continent and region</li>
|
||||
<li>The citizen’s skills and notes</li>
|
||||
</ul>
|
||||
|
||||
<h4>Help / Suggestions</h4>
|
||||
<p>
|
||||
This information will be pullled only from profiles where citizens have said can it be publicly available
|
||||
<strong>and</strong> are currently seeking employment.
|
||||
This is open-source software
|
||||
<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>
|
||||
|
@ -23,10 +23,13 @@
|
||||
<br>
|
||||
@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">
|
||||
<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>
|
||||
@ -37,12 +40,15 @@
|
||||
@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>@profile.Continent</td>
|
||||
<td>@profile.Region</td>
|
||||
<td class="text-center">@YesOrNo(profile.RemoteWork)</td>
|
||||
<td class="text-center">@YesOrNo(profile.FullTime)</td>
|
||||
<td><FullDate TheDate=@profile.LastUpdated /></td>
|
||||
<td>
|
||||
@foreach (var skill in profile.Skills)
|
||||
{
|
||||
@skill.Replace(" ()", "")<br>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
@ -39,7 +39,7 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
/// <summary>
|
||||
/// The search results
|
||||
/// </summary>
|
||||
private IEnumerable<ProfileSearchResult> SearchResults { get; set; } = Enumerable.Empty<ProfileSearchResult>();
|
||||
private IEnumerable<PublicSearchResult> SearchResults { get; set; } = Enumerable.Empty<PublicSearchResult>();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@ -72,7 +72,7 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
{
|
||||
var query = SearchQuery();
|
||||
query.Add("Searched", "True");
|
||||
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/search", query));
|
||||
nav.NavigateTo(QueryHelpers.AddQueryString("/profile/seeking", query));
|
||||
await RetrieveProfiles();
|
||||
}
|
||||
|
||||
@ -83,8 +83,8 @@ namespace JobsJobsJobs.Client.Pages.Profiles
|
||||
{
|
||||
Searching = true;
|
||||
|
||||
var searchResult = await ServerApi.RetrieveMany<ProfileSearchResult>(http,
|
||||
QueryHelpers.AddQueryString("profile/search", SearchQuery()));
|
||||
var searchResult = await ServerApi.RetrieveMany<PublicSearchResult>(http,
|
||||
QueryHelpers.AddQueryString("profile/public-search", SearchQuery()));
|
||||
|
||||
if (searchResult.IsOk)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
@if (Profile.Skills.Length > 0)
|
||||
@if (Profile.Skills.Count > 0)
|
||||
{
|
||||
<hr>
|
||||
<h4>Skills</h4>
|
||||
|
@ -2,7 +2,6 @@
|
||||
using JobsJobsJobs.Shared.Api;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.SystemTextJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyVersion>0.9.1.0</AssemblyVersion>
|
||||
<FileVersion>0.9.1.0</FileVersion>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
@ -97,10 +97,6 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("skills")]
|
||||
public async Task<IActionResult> GetSkills() =>
|
||||
Ok(await _db.FindSkillsByCitizen(CurrentCitizenId));
|
||||
|
||||
[HttpGet("count")]
|
||||
public async Task<IActionResult> GetProfileCount() =>
|
||||
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.Experience).HasColumnName("experience")
|
||||
.HasConversion(Converters.OptionalMarkdownStringConverter);
|
||||
m.Ignore(e => e.Continent);
|
||||
m.Ignore(e => e.Skills);
|
||||
m.HasOne(e => e.Continent)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.ContinentId);
|
||||
m.HasMany(e => e.Skills)
|
||||
.WithOne()
|
||||
.HasForeignKey(e => e.CitizenId);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Skill>(m =>
|
||||
|
@ -18,24 +18,13 @@ namespace JobsJobsJobs.Server.Data
|
||||
/// </summary>
|
||||
/// <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>
|
||||
public static async Task<Profile?> FindProfileByCitizen(this JobsDbContext db, CitizenId citizenId)
|
||||
{
|
||||
var profile = await db.Profiles.AsNoTracking()
|
||||
public static async Task<Profile?> FindProfileByCitizen(this JobsDbContext db, CitizenId citizenId) =>
|
||||
await db.Profiles.AsNoTracking()
|
||||
.Include(p => p.Continent)
|
||||
.Include(p => p.Skills)
|
||||
.SingleOrDefaultAsync(p => p.Id == citizenId)
|
||||
.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>
|
||||
/// Save a profile
|
||||
/// </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>
|
||||
/// Save a skill
|
||||
/// </summary>
|
||||
@ -166,29 +145,30 @@ namespace JobsJobsJobs.Server.Data
|
||||
/// </summary>
|
||||
/// <param name="search">The search parameters</param>
|
||||
/// <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)
|
||||
{
|
||||
var query = db.Profiles
|
||||
.Join(db.Citizens, p => p.Id, c => c.Id, (p, c) => new { Profile = p, Citizen = c })
|
||||
.Where(it => it.Profile.IsPublic);
|
||||
.Include(it => it.Continent)
|
||||
.Include(it => it.Skills)
|
||||
.Where(it => it.IsPublic);
|
||||
|
||||
var useIds = false;
|
||||
var citizenIds = new List<CitizenId>();
|
||||
|
||||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
query = query.Where(it => it.Profile.RemoteWork == (search.RemoteWork == "yes"));
|
||||
query = query.Where(it => it.RemoteWork == (search.RemoteWork == "yes"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(search.Skill))
|
||||
@ -202,11 +182,11 @@ namespace JobsJobsJobs.Server.Data
|
||||
|
||||
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,
|
||||
x.Profile.SeekingEmployment, x.Profile.RemoteWork, x.Profile.FullTime, x.Profile.LastUpdatedOn))
|
||||
return await query.Select(x => new PublicSearchResult(x.Continent!.Name, x.Region, x.RemoteWork,
|
||||
x.Skills.Select(sk => $"{sk.Description} ({sk.Notes})")))
|
||||
.ToListAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<ActiveDebugProfile>JobsJobsJobs.Server</ActiveDebugProfile>
|
||||
<Controller_SelectedScaffolderID>MvcControllerEmptyScaffolder</Controller_SelectedScaffolderID>
|
||||
<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 Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<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 System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JobsJobsJobs.Shared
|
||||
{
|
||||
@ -26,6 +26,6 @@ namespace JobsJobsJobs.Shared
|
||||
/// <summary>
|
||||
/// Convenience property for skills associated with a profile
|
||||
/// </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