Add view story page (#4)
This commit is contained in:
parent
a6fd891cc5
commit
46882bdfc6
|
@ -4,6 +4,7 @@ using NodaTime;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace JobsJobsJobs.Client
|
namespace JobsJobsJobs.Client
|
||||||
|
@ -18,6 +19,17 @@ namespace JobsJobsJobs.Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AppState
|
public class AppState
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The application version, as a nice display string
|
||||||
|
/// </summary>
|
||||||
|
public static Lazy<string> Version => new Lazy<string>(() =>
|
||||||
|
{
|
||||||
|
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||||
|
var display = $"v{version.Major}.{version.Minor}";
|
||||||
|
if (version.Build > 0) display += $".{version.Build}";
|
||||||
|
return display;
|
||||||
|
});
|
||||||
|
|
||||||
public event Action OnChange = () => { };
|
public event Action OnChange = () => { };
|
||||||
|
|
||||||
private UserInfo? _user = null;
|
private UserInfo? _user = null;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Story</th>
|
<th scope="col">Story</th>
|
||||||
<th scope="col">From</th>
|
<th scope="col">From</th>
|
||||||
|
<th scope="col">Found Here?</th>
|
||||||
<th scope="col">Recorded On</th>
|
<th scope="col">Recorded On</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -21,13 +22,30 @@
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
@if (story.HasStory)
|
||||||
|
{
|
||||||
<a href="/success-story/view/@story.Id">View</a>
|
<a href="/success-story/view/@story.Id">View</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<em>None</em>
|
||||||
|
}
|
||||||
@if (story.CitizenId == state.User!.Id)
|
@if (story.CitizenId == state.User!.Id)
|
||||||
{
|
{
|
||||||
<text> ~ <a href="/success-story/edit/@story.Id">Edit</a></text>
|
<text> ~ </text><a href="/success-story/edit/@story.Id">Edit</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>@story.CitizenName</td>
|
<td>@story.CitizenName</td>
|
||||||
|
<td>
|
||||||
|
@if (story.FromHere)
|
||||||
|
{
|
||||||
|
<strong>Yes</strong>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<text>No</text>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
<td><FullDate TheDate=@story.RecordedOn /></td>
|
<td><FullDate TheDate=@story.RecordedOn /></td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
|
19
src/JobsJobsJobs/Client/Pages/SuccessStory/ViewStory.razor
Normal file
19
src/JobsJobsJobs/Client/Pages/SuccessStory/ViewStory.razor
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
@page "/success-story/view/{Id}"
|
||||||
|
@inject HttpClient http
|
||||||
|
@inject AppState state
|
||||||
|
|
||||||
|
<PageTitle Title="Success Story" />
|
||||||
|
|
||||||
|
<Loading OnLoad=@RetrieveStory>
|
||||||
|
<h3>@Citizen.DisplayName’s Success Story</h3>
|
||||||
|
<h4><FullDateTime TheDate=@Story.RecordedOn /></h4>
|
||||||
|
@if (Story.FromHere)
|
||||||
|
{
|
||||||
|
<p><em><strong>Found via Jobs, Jobs, Jobs</strong></em></p>
|
||||||
|
}
|
||||||
|
<hr>
|
||||||
|
@if (Story.Story != null)
|
||||||
|
{
|
||||||
|
<div>@(new MarkupString(Story.Story.ToHtml()))</div>
|
||||||
|
}
|
||||||
|
</Loading>
|
|
@ -0,0 +1,69 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Domain = JobsJobsJobs.Shared;
|
||||||
|
|
||||||
|
namespace JobsJobsJobs.Client.Pages.SuccessStory
|
||||||
|
{
|
||||||
|
public partial class ViewStory : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the success story to display
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Id { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The success story to be displayed
|
||||||
|
/// </summary>
|
||||||
|
private Domain.Success Story { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The citizen who authorized this success story
|
||||||
|
/// </summary>
|
||||||
|
private Domain.Citizen Citizen { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve the success story
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errors">The error collection via which errors will be reported</param>
|
||||||
|
public async Task RetrieveStory(ICollection<string> errors)
|
||||||
|
{
|
||||||
|
ServerApi.SetJwt(http, state);
|
||||||
|
var story = await ServerApi.RetrieveOne<Domain.Success>(http, $"success/{Id}");
|
||||||
|
|
||||||
|
if (story.IsOk)
|
||||||
|
{
|
||||||
|
if (story.Ok == null)
|
||||||
|
{
|
||||||
|
errors.Add($"Success story {Id} not found");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Story = story.Ok;
|
||||||
|
var citizen = await ServerApi.RetrieveOne<Domain.Citizen>(http, $"citizen/get/{Story.CitizenId}");
|
||||||
|
if (citizen.IsOk)
|
||||||
|
{
|
||||||
|
if (citizen.Ok == null)
|
||||||
|
{
|
||||||
|
errors.Add($"Citizen ID {Story.CitizenId} not found");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Citizen = citizen.Ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errors.Add(citizen.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errors.Add(story.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ namespace JobsJobsJobs.Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static ServerApi()
|
static ServerApi()
|
||||||
{
|
{
|
||||||
|
|
||||||
var options = new JsonSerializerOptions
|
var options = new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
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.Shared
|
namespace JobsJobsJobs.Client.Shared
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
@using System.Reflection
|
|
||||||
@inject IJSRuntime js
|
|
||||||
@using Blazored.Toast.Configuration
|
@using Blazored.Toast.Configuration
|
||||||
|
@inject IJSRuntime js
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
|
@ -10,7 +9,7 @@
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="top-row px-4">
|
<div class="top-row px-4">
|
||||||
<em>(...and Jobs - <a class="audio" @onclick="PlayJobs">Let's Vote for Jobs!</a>)</em>
|
<em>(…and Jobs - <a class="audio" @onclick="PlayJobs">Let's Vote for Jobs!</a>)</em>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content px-4">
|
<div class="content px-4">
|
||||||
|
@ -20,7 +19,7 @@
|
||||||
<source src="/audio/pelosi-jobs.mp3">
|
<source src="/audio/pelosi-jobs.mp3">
|
||||||
</audio>
|
</audio>
|
||||||
|
|
||||||
<div class="app-version">Jobs, Jobs, Jobs @Version</div>
|
<div class="app-version">Jobs, Jobs, Jobs @AppState.Version.Value</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<BlazoredToasts Position="ToastPosition.BottomRight"
|
<BlazoredToasts Position="ToastPosition.BottomRight"
|
||||||
|
@ -28,14 +27,4 @@
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
async void PlayJobs() => await js.InvokeVoidAsync("Audio.play", "pelosijobs");
|
async void PlayJobs() => await js.InvokeVoidAsync("Audio.play", "pelosijobs");
|
||||||
|
|
||||||
private string Version { get; set; } = "";
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
|
||||||
Version = $"v{version.Major}.{version.Minor}";
|
|
||||||
if (version.Build > 0) Version += $".{version.Build}";
|
|
||||||
base.OnInitialized();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,20 @@
|
||||||
|
|
||||||
<div class="top-row pl-4 navbar navbar-dark">
|
<div class="top-row pl-4 navbar navbar-dark">
|
||||||
<a class="navbar-brand" href="">Jobs, Jobs, Jobs</a>
|
<a class="navbar-brand" href="">Jobs, Jobs, Jobs</a>
|
||||||
<button class="navbar-toggler" @onclick="ToggleNavMenu">
|
<button class="navbar-toggler" @onclick=@ToggleNavMenu>
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="@NavMenuCssClass" @onclick=@ToggleNavMenu>
|
<div class="@NavMenuCssClass" @onclick=@ToggleNavMenu>
|
||||||
<ul class="nav flex-column">
|
<ul class="nav flex-column">
|
||||||
|
@if (state.User == null)
|
||||||
|
{
|
||||||
<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>
|
||||||
@if (state.User == null)
|
|
||||||
{
|
|
||||||
<li class="nav-item px-3">
|
<li class="nav-item px-3">
|
||||||
<a class="nav-link" href="@AuthUrl">
|
<a class="nav-link" href="@AuthUrl">
|
||||||
<span class="oi oi-account-login" aria-hidden="true"></span> Log On
|
<span class="oi oi-account-login" aria-hidden="true"></span> Log On
|
||||||
|
|
|
@ -28,7 +28,8 @@ namespace JobsJobsJobs.Server.Data
|
||||||
await db.Successes
|
await db.Successes
|
||||||
.Join(db.Citizens, s => s.CitizenId, c => c.Id, (s, c) => new { Success = s, Citizen = c })
|
.Join(db.Citizens, s => s.CitizenId, c => c.Id, (s, c) => new { Success = s, Citizen = c })
|
||||||
.OrderByDescending(it => it.Success.RecordedOn)
|
.OrderByDescending(it => it.Success.RecordedOn)
|
||||||
.Select(it => new StoryEntry(it.Success.Id, it.Citizen.Id, it.Citizen.DisplayName, it.Success.RecordedOn))
|
.Select(it => new StoryEntry(it.Success.Id, it.Citizen.Id, it.Citizen.DisplayName,
|
||||||
|
it.Success.RecordedOn, it.Success.FromHere, it.Success.Story != null))
|
||||||
.ToListAsync().ConfigureAwait(false);
|
.ToListAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,11 @@ namespace JobsJobsJobs.Shared.Api
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An entry in the list of success stories
|
/// An entry in the list of success stories
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record StoryEntry(SuccessId Id, CitizenId CitizenId, string CitizenName, Instant RecordedOn);
|
public record StoryEntry(
|
||||||
|
SuccessId Id,
|
||||||
|
CitizenId CitizenId,
|
||||||
|
string CitizenName,
|
||||||
|
Instant RecordedOn,
|
||||||
|
bool FromHere,
|
||||||
|
bool HasStory);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user