Merge pull request 'housekeeping updates' (#7) from dev into main
All checks were successful
deploy / deploy (push) Successful in 3m10s
All checks were successful
deploy / deploy (push) Successful in 3m10s
Reviewed-on: #7
This commit is contained in:
commit
ab666e4efe
24 changed files with 367 additions and 334 deletions
|
@ -13,4 +13,6 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: docker build
|
||||
run: docker build -t nseguin42/website:${{ github.sha }} .
|
||||
run: docker build -t nseguin42/website:${{ github.sha }} .
|
||||
- name: cleanup old images
|
||||
run: docker image prune -f --filter "until=24h"
|
|
@ -1,19 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
@using Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
@using JetBrains.Annotations
|
||||
@inject IFileVersionProvider FileVersionProvider
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--suppress HtmlRequiredTitleElement -->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/"/>
|
||||
<link rel="stylesheet" href="css/styles.css" asp-append-version="true"/>
|
||||
<link rel="stylesheet" href="app.css"/>
|
||||
<link rel="stylesheet" href="NSeguin.Dev.Web.styles.css"/>
|
||||
<base href="@PathBase"/>
|
||||
<link rel="stylesheet" href="@AddVersionToPath("css/styles.css")"/>
|
||||
<link rel="stylesheet" href="@AddVersionToPath("app.css")"/>
|
||||
<link rel="icon" type="image/png" href="favicon.ico"/>
|
||||
<HeadOutlet/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes/>
|
||||
@* ReSharper disable once Html.PathError *@
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="@AddVersionToPath("js/app.js")"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@code {
|
||||
private const string PathBase = "/";
|
||||
|
||||
private string AddVersionToPath([PathReference] string path) => FileVersionProvider.AddFileVersionToPath(PathBase, path);
|
||||
}
|
|
@ -1,15 +1,26 @@
|
|||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<main>
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
<div class="fixed inset-y-0 right-0 left-[40rem] hidden lg:block xl:left-[50rem] z-0">
|
||||
<svg
|
||||
viewBox="0 0 1080 957"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
class="absolute inset-0 h-full w-full"
|
||||
preserveAspectRatio="xMinYMid slice">
|
||||
<path fill="#2e3440" d="M0 0h1080v957H0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
<main>
|
||||
<div class="px-4 py-10 sm:py-28 sm:px-6 lg:px-8 xl:py-32 xl:px-28">
|
||||
<div class="mx-auto max-w-xl lg:mx-0">
|
||||
<article class="px-4">
|
||||
<ErrorBoundary>
|
||||
@Body
|
||||
</ErrorBoundary>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<BlazorErrorUI/>
|
|
@ -1,92 +0,0 @@
|
|||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-row.auth ::deep a:first-child {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.top-row, article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
65
src/NSeguin.Dev.Web/Components/Pages/Attributions.razor
Normal file
65
src/NSeguin.Dev.Web/Components/Pages/Attributions.razor
Normal file
|
@ -0,0 +1,65 @@
|
|||
@page "/attributions"
|
||||
@using System.Text.Json
|
||||
|
||||
<PageTitle>Attributions</PageTitle>
|
||||
|
||||
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
|
||||
Attributions
|
||||
</h1>
|
||||
|
||||
<p class="mt-4 text-base text-slate-900">
|
||||
The source code for this website is available on
|
||||
<a href="https://www.git.nseguin.dev/nseguin/website" class="text-blue-600">Forgejo (git.nseguin.dev).</a>
|
||||
</p>
|
||||
|
||||
<p class="mt-4 text-base text-slate-900">
|
||||
It is licensed under the <a href="@LicenseUrl" class="text-blue-600">MIT License</a>.
|
||||
</p>
|
||||
|
||||
<ul class="list-disc mt-4 text-base leading-7 text-slate-900">
|
||||
@if (AttributionsList != null)
|
||||
{
|
||||
@foreach (var attribution in AttributionsList)
|
||||
{
|
||||
<li class="mt-4" key="@attribution.Name">
|
||||
<h2 class="text-lg font-bold">@attribution.Name</h2>
|
||||
@if (!string.IsNullOrWhiteSpace(attribution.Url?.ToString()))
|
||||
{
|
||||
<p class="mt-2">URL: <a href="@attribution.Url.ToString()" class="text-blue-600">@attribution.Url</a></p>
|
||||
}
|
||||
@if (attribution.Authors?.Any() ?? false)
|
||||
{
|
||||
<p class="mt-2">Author(s): @string.Join(", ", attribution.Authors)</p>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(attribution.License))
|
||||
{
|
||||
<p class="mt-2">License: <a href="@attribution.LicenseUrl?.ToString()" class="text-blue-600">@attribution.License</a></p>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
|
||||
@code {
|
||||
|
||||
public record Attribution(string Name, Uri Url, string? License, Uri? LicenseUrl, string[]? Authors);
|
||||
|
||||
private string? AttributionsJson { get; set; }
|
||||
|
||||
private List<Attribution>? AttributionsList { get; set; }
|
||||
|
||||
private string? LicenseUrl => "static/LICENSE.txt";
|
||||
|
||||
[Inject]
|
||||
private IWebHostEnvironment HostEnvironment { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var attributionsFile = HostEnvironment.WebRootFileProvider.GetFileInfo("static/attributions.json");
|
||||
using var reader = new StreamReader(attributionsFile.CreateReadStream());
|
||||
AttributionsJson = await reader.ReadToEndAsync();
|
||||
AttributionsList = JsonSerializer.Deserialize<List<Attribution>>(AttributionsJson);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
@page "/authentication/{action}"
|
||||
@using System.Security.Claims
|
||||
|
||||
@code{
|
||||
private const string Login = "login";
|
||||
private const string Logout = "logout";
|
||||
public ClaimsPrincipal? User { get; private set; }
|
||||
|
||||
[CascadingParameter]
|
||||
public required Task<AuthenticationState> AuthenticationState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Action { get; set; }
|
||||
|
||||
[Inject]
|
||||
public required NavigationManager NavigationManager { get; set; }
|
||||
|
||||
private void SignOut()
|
||||
{
|
||||
NavigationManager.NavigateTo("MicrosoftIdentity/Account/SignOut", true);
|
||||
}
|
||||
|
||||
private void SignIn()
|
||||
{
|
||||
NavigationManager.NavigateTo("MicrosoftIdentity/Account/SignIn", true);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var authenticationState = await AuthenticationState;
|
||||
User = authenticationState.User;
|
||||
if (Action == Logout)
|
||||
{
|
||||
SignOut();
|
||||
}
|
||||
else if (Action == Login)
|
||||
{
|
||||
SignIn();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +1,49 @@
|
|||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
@using Microsoft.AspNetCore.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-semibold text-[2rem] leading-10 tracking-tighter">Error</h1>
|
||||
|
||||
<p class="text-slate text-xl mt-4">
|
||||
An error occurred while processing your request.
|
||||
</p>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<p class="text-slate text-md mt-4 mb-4">
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
@if (ShowException)
|
||||
{
|
||||
<p class="text-slate mt-4 mb-4">
|
||||
<h2 class="text-2xl font-bold">Exception</h2>
|
||||
<pre class="text-md">
|
||||
<code>@(Exception?.ToString())</code>
|
||||
</pre>
|
||||
</p>
|
||||
}
|
||||
|
||||
@code{
|
||||
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
[Inject]
|
||||
private IHostEnvironment HostEnvironment { get; set; } = default!;
|
||||
|
||||
private Exception? Exception { get; set; }
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
private bool ShowException => Exception is not null && HostEnvironment.IsDevelopment();
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
Exception = HttpContext?.Features.Get<IExceptionHandlerFeature>()?.Error ?? new Exception("An error occurred while processing your request.");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,84 +1,68 @@
|
|||
@page "/"
|
||||
@using NSeguin.Dev.Web.Components.Shared.HeroIcons
|
||||
@layout SimpleLayout
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
<div class="fixed inset-y-0 right-0 left-[40rem] hidden lg:block xl:left-[50rem] z-0">
|
||||
<svg
|
||||
viewBox="0 0 1080 957"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
class="absolute inset-0 h-full w-full"
|
||||
preserveAspectRatio="xMinYMid slice">
|
||||
<path fill="#2e3440" d="M0 0h1080v957H0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="mt-10 flex items-center text-sm font-semibold leading-6 text-brand">
|
||||
<a href="https://git.nseguin.dev/nseguin/website">nseguin/website</a>
|
||||
<small class="ml-3 rounded-full bg-brand/5 px-2 text-[0.8125rem] font-medium leading-6">
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<div class="px-4 py-10 sm:py-28 sm:px-6 lg:px-8 xl:py-32 xl:px-28">
|
||||
<div class="mx-auto max-w-xl lg:mx-0">
|
||||
<h1 class="mt-10 flex items-center text-sm font-semibold leading-6 text-brand">
|
||||
<a href="https://git.nseguin.dev/nseguin/website">nseguin/website</a>
|
||||
<small class="ml-3 rounded-full bg-brand/5 px-2 text-[0.8125rem] font-medium leading-6">
|
||||
</small>
|
||||
</h1>
|
||||
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
|
||||
Nick Seguin
|
||||
</h1>
|
||||
|
||||
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
|
||||
Nick Seguin
|
||||
</h1>
|
||||
<p class="mt-4 text-base leading-7 text-slate-900">
|
||||
I'm a software developer based in Iowa.
|
||||
I'm currently working on a few projects, including this website.
|
||||
Thanks for visiting!
|
||||
</p>
|
||||
|
||||
<p class="mt-4 text-base leading-7 text-slate-900">
|
||||
I'm a software developer based in Iowa.
|
||||
I'm currently working on a few projects, including this website.
|
||||
Thanks for visiting!
|
||||
</p>
|
||||
|
||||
<div class="flex">
|
||||
<div class="w-full sm:w-auto">
|
||||
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
|
||||
<a
|
||||
href="/resume"
|
||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
|
||||
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
|
||||
</span>
|
||||
<span class="relative flex items-center gap-4 sm:flex-col">
|
||||
<div class="w-6 h-6">
|
||||
<HeroIcon Kind="HeroIcon.DocumentText"/>
|
||||
</div>
|
||||
Resumé
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/nseguin42"
|
||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
|
||||
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
|
||||
</span>
|
||||
<span class="relative flex items-center gap-4 sm:flex-col">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 0C5.37 0 0 5.506 0 12.303c0 5.445 3.435 10.043 8.205 11.674.6.107.825-.262.825-.585 0-.292-.015-1.261-.015-2.291C6 21.67 5.22 20.346 4.98 19.654c-.135-.354-.72-1.446-1.23-1.738-.42-.23-1.02-.8-.015-.815.945-.015 1.62.892 1.845 1.261 1.08 1.86 2.805 1.338 3.495 1.015.105-.8.42-1.338.765-1.645-2.67-.308-5.46-1.37-5.46-6.075 0-1.338.465-2.446 1.23-3.307-.12-.308-.54-1.569.12-3.26 0 0 1.005-.323 3.3 1.26.96-.276 1.98-.415 3-.415s2.04.139 3 .416c2.295-1.6 3.3-1.261 3.3-1.261.66 1.691.24 2.952.12 3.26.765.861 1.23 1.953 1.23 3.307 0 4.721-2.805 5.767-5.475 6.075.435.384.81 1.122.81 2.276 0 1.645-.015 2.968-.015 3.383 0 .323.225.707.825.585a12.047 12.047 0 0 0 5.919-4.489A12.536 12.536 0 0 0 24 12.304C24 5.505 18.63 0 12 0Z"
|
||||
fill="#18181B"/>
|
||||
</svg>
|
||||
GitHub
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:mail@nseguin.dev"
|
||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
|
||||
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
|
||||
</span>
|
||||
<span class="relative flex items-center gap-4 sm:flex-col text-black">
|
||||
<div class="w-6 h-6">
|
||||
<HeroIcon Kind="HeroIcon.Envelope"/>
|
||||
</div>
|
||||
Contact
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="w-full sm:w-auto">
|
||||
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
|
||||
<a
|
||||
href="/resume"
|
||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
|
||||
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
|
||||
</span>
|
||||
<span class="relative flex items-center gap-4 sm:flex-col">
|
||||
<div class="w-6 h-6">
|
||||
<HeroIcon Kind="HeroIcon.DocumentText"/>
|
||||
</div>
|
||||
Resumé
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/nseguin42"
|
||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
|
||||
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
|
||||
</span>
|
||||
<span class="relative flex items-center gap-4 sm:flex-col">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 0C5.37 0 0 5.506 0 12.303c0 5.445 3.435 10.043 8.205 11.674.6.107.825-.262.825-.585 0-.292-.015-1.261-.015-2.291C6 21.67 5.22 20.346 4.98 19.654c-.135-.354-.72-1.446-1.23-1.738-.42-.23-1.02-.8-.015-.815.945-.015 1.62.892 1.845 1.261 1.08 1.86 2.805 1.338 3.495 1.015.105-.8.42-1.338.765-1.645-2.67-.308-5.46-1.37-5.46-6.075 0-1.338.465-2.446 1.23-3.307-.12-.308-.54-1.569.12-3.26 0 0 1.005-.323 3.3 1.26.96-.276 1.98-.415 3-.415s2.04.139 3 .416c2.295-1.6 3.3-1.261 3.3-1.261.66 1.691.24 2.952.12 3.26.765.861 1.23 1.953 1.23 3.307 0 4.721-2.805 5.767-5.475 6.075.435.384.81 1.122.81 2.276 0 1.645-.015 2.968-.015 3.383 0 .323.225.707.825.585a12.047 12.047 0 0 0 5.919-4.489A12.536 12.536 0 0 0 24 12.304C24 5.505 18.63 0 12 0Z"
|
||||
fill="#18181B"/>
|
||||
</svg>
|
||||
GitHub
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:mail@nseguin.dev"
|
||||
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
|
||||
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
|
||||
</span>
|
||||
<span class="relative flex items-center gap-4 sm:flex-col text-black">
|
||||
<div class="w-6 h-6">
|
||||
<HeroIcon Kind="HeroIcon.Envelope"/>
|
||||
</div>
|
||||
Contact
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,34 +1,17 @@
|
|||
@page "/NotFound"
|
||||
@using Microsoft.AspNetCore.WebUtilities
|
||||
@layout SimpleLayout
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<PageTitle>Not Found</PageTitle>
|
||||
|
||||
<div class="fixed inset-y-0 right-0 left-[40rem] hidden lg:block xl:left-[50rem] z-0">
|
||||
<svg
|
||||
viewBox="0 0 1080 957"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
class="absolute inset-0 h-full w-full"
|
||||
preserveAspectRatio="xMinYMid slice">
|
||||
<path fill="#2e3440" d="M0 0h1080v957H0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-semibold text-[2rem] leading-10 tracking-tighter">Not Found</h1>
|
||||
<p class="text-slate text-xl mt-4">
|
||||
No page was found at <code>@Path</code>.
|
||||
</p>
|
||||
|
||||
<div class="px-4 py-10 sm:py-28 sm:px-6 lg:px-8 xl:py-32 xl:px-28">
|
||||
<div class="mx-auto max-w-xl lg:mx-0">
|
||||
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-semibold text-[2rem] leading-10 tracking-tighter">Not Found</h1>
|
||||
<p class="text-slate text-xl mt-4">
|
||||
No page was found at <code>@Path</code>.<br/>
|
||||
</p>
|
||||
|
||||
<p class="text-blue-600 text-xl mt-4">
|
||||
<a href="/" aria-label="Home" class="underline">Go to home page</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-blue-600 text-xl mt-4">
|
||||
<a href="/" aria-label="Home" class="underline">Go to home page</a>
|
||||
</p>
|
||||
|
||||
@code {
|
||||
private string Path { get; set; }
|
||||
|
|
|
@ -1,24 +1,5 @@
|
|||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] {typeof(Client._Imports).Assembly}">
|
||||
<Found Context="routeData">
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(SimpleLayout)">
|
||||
<NotAuthorized>
|
||||
@if (context.User.Identity?.IsAuthenticated != true)
|
||||
{
|
||||
<RedirectToLogin/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p role="alert">You are not authorized to access this resource.</p>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(SimpleLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] {typeof(Client._Imports).Assembly, typeof(AsyncEventHandler).Assembly}">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(SimpleLayout)"/>
|
||||
</Found>
|
||||
</Router>
|
|
@ -1,14 +1,21 @@
|
|||
@inject IJSRuntime JSRuntime
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public Func<ElementReference> GetControl { get; set; }
|
||||
public Func<ElementReference>? GetControl { get; set; }
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (GetControl is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(GetControl));
|
||||
}
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("App.scrollIntoView", GetControl(), true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
src/NSeguin.Dev.Web/Components/Shared/BlazorErrorUI.razor
Normal file
11
src/NSeguin.Dev.Web/Components/Shared/BlazorErrorUI.razor
Normal file
|
@ -0,0 +1,11 @@
|
|||
@inject IHostEnvironment HostEnvironment
|
||||
|
||||
<div id="blazor-error-ui"
|
||||
class="bg-yellow-400 bottom-0 shadow-md fixed left-0 py-2 px-4 w-full z-50 hidden"
|
||||
role="alertdialog"
|
||||
tabindex="-1"
|
||||
data-nosnippet>
|
||||
An unhandled error has occurred.
|
||||
<a href="#" class="reload underline">Reload</a>
|
||||
<button class="dismiss" type="button">🗙</button>
|
||||
</div>
|
|
@ -1,20 +0,0 @@
|
|||
@inject NavigationManager Navigation
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
Hello, @context.User.Identity?.Name!
|
||||
<button class="nav-link btn btn-link" @onclick="BeginLogout">Log out</button>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<a href="authentication/login">Log in</a>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code{
|
||||
|
||||
private void BeginLogout(MouseEventArgs args)
|
||||
{
|
||||
Navigation.NavigateTo("authentication/logout");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
@inject NavigationManager Navigation
|
||||
|
||||
@code {
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
|
||||
}
|
||||
|
||||
}
|
|
@ -38,6 +38,12 @@
|
|||
<AdditionalFiles Include="pnpm-lock.yaml" CopyToOutputDirectory="Always"/>
|
||||
<Content Remove="libman.json"/>
|
||||
<AdditionalFiles Include="libman.json" CopyToOutputDirectory="Always"/>
|
||||
<Content Update="wwwroot\static\attributions.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\static\LICENSE.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
2
src/NSeguin.Dev.Web/NSeguin.Dev.Web.csproj.DotSettings
Normal file
2
src/NSeguin.Dev.Web/NSeguin.Dev.Web.csproj.DotSettings
Normal file
|
@ -0,0 +1,2 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=components_005Cmodels/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -1,6 +1,8 @@
|
|||
using Azure.Identity;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Identity.Web;
|
||||
using Microsoft.Identity.Web.UI;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
|
@ -30,6 +32,20 @@ builder.Services.AddRazorComponents()
|
|||
.AddInteractiveServerComponents()
|
||||
.AddInteractiveWebAssemblyComponents();
|
||||
|
||||
builder.Services.AddProblemDetails(
|
||||
options =>
|
||||
{
|
||||
options.CustomizeProblemDetails = ctx =>
|
||||
{
|
||||
ctx.ProblemDetails.Extensions.TryAdd("traceId", ctx.HttpContext.TraceIdentifier);
|
||||
if (ctx.HttpContext.RequestServices.GetRequiredService<IHostEnvironment>()
|
||||
.IsDevelopment())
|
||||
{
|
||||
ctx.ProblemDetails.Extensions.TryAdd("exception", ctx.Exception?.ToString());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (builder.Environment.IsStaging() || builder.Environment.IsProduction())
|
||||
{
|
||||
builder.Configuration.AddAzureAppConfiguration(
|
||||
|
@ -97,14 +113,21 @@ app.UseAuthorization();
|
|||
app.UseAntiforgery();
|
||||
app.MapRazorComponents<App>()
|
||||
.AddAdditionalAssemblies(typeof(Counter).Assembly)
|
||||
.AddAdditionalAssemblies(typeof(AsyncEventHandler).Assembly)
|
||||
.AddInteractiveServerRenderMode()
|
||||
.AddInteractiveWebAssemblyRenderMode();
|
||||
|
||||
app.MapControllers();
|
||||
app.UseExceptionHandler();
|
||||
app.MapFallback(
|
||||
ctx =>
|
||||
{
|
||||
ctx.Response.Redirect($"/NotFound?path={ctx.Request.Path}");
|
||||
ctx.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
var headers = ctx.Request.GetTypedHeaders();
|
||||
if (headers.Accept.Any(h => h.MatchesMediaType("text/html")))
|
||||
{
|
||||
ctx.Response.Redirect($"/NotFound?path={ctx.Request.Path}");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"name": "nseguin.dev.web",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"bun-types": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
|
@ -12,7 +11,11 @@
|
|||
"css:watch": "pnpm tailwindcss -i ./wwwroot/css/site.css -o ./wwwroot/css/styles.css -c ./tailwind.config.ts --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"tailwind-nord": "^1.3.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
"tailwindcss": "^3.4.1",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"tailwind-nord": "^1.3.0"
|
||||
}
|
||||
}
|
|
@ -5,6 +5,18 @@ settings:
|
|||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@tailwindcss/aspect-ratio':
|
||||
specifier: ^0.4.2
|
||||
version: 0.4.2(tailwindcss@3.4.1)
|
||||
'@tailwindcss/container-queries':
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1(tailwindcss@3.4.1)
|
||||
'@tailwindcss/forms':
|
||||
specifier: ^0.5.7
|
||||
version: 0.5.7(tailwindcss@3.4.1)
|
||||
'@tailwindcss/typography':
|
||||
specifier: ^0.5.10
|
||||
version: 0.5.10(tailwindcss@3.4.1)
|
||||
tailwind-nord:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
|
@ -15,11 +27,6 @@ dependencies:
|
|||
specifier: ^5.0.0
|
||||
version: 5.3.3
|
||||
|
||||
devDependencies:
|
||||
bun-types:
|
||||
specifier: latest
|
||||
version: 1.0.15
|
||||
|
||||
packages:
|
||||
|
||||
/@alloc/quick-lru@5.2.0:
|
||||
|
@ -78,6 +85,43 @@ packages:
|
|||
fastq: 1.15.0
|
||||
dev: false
|
||||
|
||||
/@tailwindcss/aspect-ratio@0.4.2(tailwindcss@3.4.1):
|
||||
resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
|
||||
dependencies:
|
||||
tailwindcss: 3.4.1
|
||||
dev: false
|
||||
|
||||
/@tailwindcss/container-queries@0.1.1(tailwindcss@3.4.1):
|
||||
resolution: {integrity: sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.2.0'
|
||||
dependencies:
|
||||
tailwindcss: 3.4.1
|
||||
dev: false
|
||||
|
||||
/@tailwindcss/forms@0.5.7(tailwindcss@3.4.1):
|
||||
resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
|
||||
dependencies:
|
||||
mini-svg-data-uri: 1.4.4
|
||||
tailwindcss: 3.4.1
|
||||
dev: false
|
||||
|
||||
/@tailwindcss/typography@0.5.10(tailwindcss@3.4.1):
|
||||
resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || insiders'
|
||||
dependencies:
|
||||
lodash.castarray: 4.4.0
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.merge: 4.6.2
|
||||
postcss-selector-parser: 6.0.10
|
||||
tailwindcss: 3.4.1
|
||||
dev: false
|
||||
|
||||
/any-promise@1.3.0:
|
||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||
dev: false
|
||||
|
@ -117,10 +161,6 @@ packages:
|
|||
fill-range: 7.0.1
|
||||
dev: false
|
||||
|
||||
/bun-types@1.0.15:
|
||||
resolution: {integrity: sha512-XkEvWLV1JIhcVIpf2Lu6FXnZUxRUkQVJmgY+VT7os6Tk5X1nkXx11q4Rtu6txsqpDJZfUeZXblnnD59K+6wsVA==}
|
||||
dev: true
|
||||
|
||||
/camelcase-css@2.0.1:
|
||||
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
@ -296,6 +336,18 @@ packages:
|
|||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: false
|
||||
|
||||
/lodash.castarray@4.4.0:
|
||||
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
|
||||
dev: false
|
||||
|
||||
/lodash.isplainobject@4.0.6:
|
||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||
dev: false
|
||||
|
||||
/lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
dev: false
|
||||
|
||||
/merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -309,6 +361,11 @@ packages:
|
|||
picomatch: 2.3.1
|
||||
dev: false
|
||||
|
||||
/mini-svg-data-uri@1.4.4:
|
||||
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
dependencies:
|
||||
|
@ -427,6 +484,14 @@ packages:
|
|||
postcss-selector-parser: 6.0.13
|
||||
dev: false
|
||||
|
||||
/postcss-selector-parser@6.0.10:
|
||||
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
cssesc: 3.0.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: false
|
||||
|
||||
/postcss-selector-parser@6.0.13:
|
||||
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
|
@ -145,7 +145,11 @@ export default {
|
|||
}
|
||||
},
|
||||
plugins: [
|
||||
require('tailwind-nord')
|
||||
require('tailwind-nord'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
require('@tailwindcss/container-queries')
|
||||
]
|
||||
}
|
||||
} satisfies Config
|
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,15 @@
|
|||
var App = App || {};
|
||||
const App = App || {};
|
||||
|
||||
App.setFocus = function (element) {
|
||||
element.focus();
|
||||
}
|
||||
|
||||
App.scrollIntoView = function (element, args) {
|
||||
if (element === undefined) {
|
||||
console.error('Element is undefined');
|
||||
} else if (element === null) {
|
||||
console.error('Element is null');
|
||||
}
|
||||
|
||||
element.scrollIntoView(args);
|
||||
}
|
22
src/NSeguin.Dev.Web/wwwroot/static/LICENSE.txt
Normal file
22
src/NSeguin.Dev.Web/wwwroot/static/LICENSE.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Nicholas Seguin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
2
src/NSeguin.Dev.Web/wwwroot/static/attributions.json
Normal file
2
src/NSeguin.Dev.Web/wwwroot/static/attributions.json
Normal file
|
@ -0,0 +1,2 @@
|
|||
[
|
||||
]
|
Loading…
Reference in a new issue