Migrate components

This commit is contained in:
Beau Findlay
2026-01-31 15:51:29 +00:00
parent ee136857d1
commit 2105d3b85d
32 changed files with 2003 additions and 692 deletions

View File

@@ -0,0 +1,41 @@
@page "/about"
<PageTitle>Beau Findlay - About</PageTitle>
<Title CssClass="text-center pb-4">This App</Title>
<Text>
Below is an overview of how this simple app is made and what
technologies are used. If you'd like to dive straight in, the full
project is available on my
<AnchorLink Href="https://github.com/bdfin/my-portfolio">
GitHub
</AnchorLink>.
</Text>
<section>
<Subtitle>App</Subtitle>
<Text>
This app was originally made using
<AnchorLink Href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">.NET Blazor WASM</AnchorLink>
and then re-written in
<AnchorLink Href="https://react.dev/">React</AnchorLink>
with
<AnchorLink Href="https://www.typescriptlang.org/">TypeScript</AnchorLink>
as a learning exercise. I've now migrated it back to .NET Blazor to take advantage of static server-side
rendering for maximum performance and to remove unnecessary dependencies on large JS and CSS libraries.
</Text>
<Text>
This version uses pure vanilla CSS with CSS variables for theming, eliminating all JavaScript and external
dependencies; the mobile menu uses a CSS checkbox hack for zero-JavaScript interactivity.
</Text>
</section>
<section class="mt-8">
<Subtitle>Hosting & Deployment</Subtitle>
<Text>
TODO: This section
</Text>
</section>

View File

@@ -15,22 +15,28 @@
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
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>
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>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
protected override void OnInitialized()
{
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}
}

View File

@@ -0,0 +1,107 @@
@page "/experience"
<PageTitle>Beau Findlay - Experience</PageTitle>
<Title CssClass="text-center">Experience</Title>
<p class="text-center text-xl font-semibold mb-10 ">
Software Engineer since 2018
</p>
<ol class="timeline">
@foreach (var item in experienceTimelineItems)
{
<li class="timeline-item">
<time class="timeline-date">
@item.StartDate - @(item.EndDate ?? "Present")
</time>
<h3 class="timeline-title">
@item.Title @("@") <AnchorLink Href="@item.CompanyUrl">@item.CompanyName</AnchorLink>
</h3>
@foreach (var content in item.Content)
{
<Text>@content</Text>
}
</li>
}
</ol>
<Contact/>
@code {
private readonly List<WorkTimelineItem> experienceTimelineItems =
[
new()
{
StartDate = "September 2021",
Title = "CTO",
CompanyName = "un:hurd music",
CompanyUrl = "https://unhurdmusic.com",
Content =
[
"As one of the founding developers at un:hurd music and now Chief Technology Officer, I built and scaled un:hurd's back-end and cloud infrastructure that serves automated marketing soloutions for tens-of-thousands of artists and musicians.",
"I lead a small but incredibly talented multi-disciplinary team building on the Azure cloud using a .NET backend, React web front-end and a Swift native iOS app."
]
},
new()
{
StartDate = "August 2020",
EndDate = "September 2021",
Title = "Software Development Lead",
CompanyName = "Vouch",
CompanyUrl = "https://vouch.co.uk/",
Content =
[
"At Vouch I lead the backend build of a new version of their tenant referencing software - an AI enhanced chat-bot based system utlising Azure Cognitive Services and various supporting serverless APIs written in .NET Core and hosted on Microsoft Azure."
]
},
new()
{
StartDate = "May 2020",
EndDate = "July 2020",
Title = "Software Developer",
CompanyName = "Paragon ID",
CompanyUrl = "https://www.paragon-id.com/en",
Content =
[
"I joined Paragon ID on a short-term contract where I wrote and deployed two key projects: A complex dashboard for a large construction equipment manufacturer to track assets across various manufacturing stages and a medical assets tracking dashboard deployed and used in multiple hospitals across the UK."
]
},
new()
{
StartDate = "July 2019",
EndDate = "May 2020",
Title = "Software Developer",
CompanyName = "Osborne Technologies",
CompanyUrl = "https://www.osbornetechnologies.co.uk/",
Content =
[
"I joined Osborne Technologies as the only cloud cloud-specialist and lead a project creating the first web-based version of their flag ship visitor management software utilising ASP.NET Core MVC and Microsoft SQL Server on the Microsoft Azure cloud."
]
},
new()
{
StartDate = "September 2018",
EndDate = "September 2019",
Title = " MSc Computing Student",
CompanyName = "Sheffield Hallam University",
CompanyUrl = "https://www.shu.ac.uk/courses/computing/msc-computing/full-time",
Content =
[
"I joined Sheffield Hallam University to study for a Master of Science in Computing. During my time there I completed modules in computer programming and web development, databases and big data, computer hardware, project management and my software development thesis; a .NET web application that compiles astronomy and space exploration data from various APIs into an accessible calendar."
]
}
];
private class WorkTimelineItem
{
public string StartDate { get; init; } = string.Empty;
public string? EndDate { get; init; }
public string Title { get; init; } = string.Empty;
public string CompanyName { get; init; } = string.Empty;
public string CompanyUrl { get; init; } = string.Empty;
public string[] Content { get; init; } = [];
}
}

View File

@@ -1,7 +1,18 @@
@page "/"
<PageTitle>Home</PageTitle>
<PageTitle>Beau Findlay - Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<Title CssClass="text-center mb-8">Hi, I'm Beau.</Title>
<Text>
I'm a UK-based software engineer and I love building cool stuff.
</Text>
<Text>
I specialise in C#/.NET development and I've built systems that scale for hundreds-of-thousands of global users.
</Text>
<Text>
I've worked with businesses at all sizes and stages and I'm currently heading up the tech as CTO at a cool startup called
<AnchorLink Href="https://unhurdmusic.com">un:hurd music</AnchorLink>.
</Text>
<Text>
I believe in a privacy-first, information-focussed and performant internet. You won't find any trackers, analytics or the need for a cookie consent policy here.
</Text>

View File

@@ -1,5 +1,13 @@
@page "/not-found"
@layout MainLayout
<h3>Not Found</h3>
<p>Sorry, the content you are looking for does not exist.</p>
<PageTitle>Beau Findlay - Not Found</PageTitle>
<main class="grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8">
<div class="text-center">
<p class="text-base font-semibold">404</p>
<h1 class="mt-4 text-4xl font-bold tracking-tight">
Page not found
</h1>
<p class="mt-6 text-base leading-7">Sorry, this page doesn't exist.</p>
</div>
</main>

View File

@@ -1,64 +0,0 @@
@page "/weather"
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th aria-label="Temperature in Celsius">Temp. (C)</th>
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}