Restructure into feature folders with code-behind and scoped CSS #4

Merged
Tom merged 1 commits from refactor/feature-folders into main 2026-09-21 22:01:58 +02:00
25 changed files with 250 additions and 203 deletions

No files matched your search

@@ -1,46 +0,0 @@
@page "/beitraege/{Slug}"
@using System.Globalization
@using Elternbeirat.Web.Services
@inject PostService PostService
@if (_post is null)
{
<PageTitle>Nicht gefunden</PageTitle>
}
else
{
<PageTitle>@_post.Title</PageTitle>
<article>
<p class="post-meta">
<a href="/beitraege">&larr; Alle Beiträge</a>
<time datetime="@_post.Date.ToString("yyyy-MM-dd")">
@_post.Date.ToString("d. MMMM yyyy", _german)
</time>
</p>
@((MarkupString)_post.ContentHtml)
</article>
}
@code {
private static readonly CultureInfo _german = CultureInfo.GetCultureInfo("de-DE");
[Parameter]
public string Slug { get; set; } = "";
private Post? _post;
protected override void OnParametersSet()
{
_post = PostService.Find(Slug);
// Unknown slug -> 404, so UseStatusCodePagesWithReExecute serves the
// /not-found page instead of an empty 200 response.
if (_post is null && HttpContext is not null)
{
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
}
}
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
}
File renamed without changes.
@@ -1,6 +1,4 @@
@page "/termine"
@using System.Globalization
@using Elternbeirat.Web.Services
@inject EventService EventService
<PageTitle>Termine</PageTitle>
@@ -57,35 +55,3 @@ else
}
</ul>
}
@code {
private static readonly CultureInfo _german = CultureInfo.GetCultureInfo("de-DE");
// Formats the entry's date range for display: a single day, a date with a
// time, or a span across days.
private static string Format(Event ev)
{
var start = ev.HasTime
? ev.Start.ToString("dddd, d. MMMM yyyy, HH:mm", _german) + " Uhr"
: ev.Start.ToString("dddd, d. MMMM yyyy", _german);
if (ev.End is not { } end)
{
return start;
}
// Same day: append just the end time. Different days: append the full
// end date.
if (end.Date == ev.Start.Date)
{
return ev.HasTime
? start + "–" + end.ToString("HH:mm", _german) + " Uhr"
: start;
}
var endText = ev.HasTime
? end.ToString("d. MMMM yyyy, HH:mm", _german) + " Uhr"
: end.ToString("d. MMMM yyyy", _german);
return start + " – " + endText;
}
}
@@ -0,0 +1,37 @@
using System.Globalization;
using Elternbeirat.Web.Services;
namespace Elternbeirat.Web.Features.Events;
public partial class EventList
{
private static readonly CultureInfo German = CultureInfo.GetCultureInfo("de-DE");
// Formats the entry's date range for display: a single day, a date with a
// time, or a span across days.
private static string Format(Event ev)
{
var start = ev.HasTime
? ev.Start.ToString("dddd, d. MMMM yyyy, HH:mm", German) + " Uhr"
: ev.Start.ToString("dddd, d. MMMM yyyy", German);
if (ev.End is not { } end)
{
return start;
}
// Same day: append just the end time. Different days: append the full
// end date.
if (end.Date == ev.Start.Date)
{
return ev.HasTime
? start + "–" + end.ToString("HH:mm", German) + " Uhr"
: start;
}
var endText = ev.HasTime
? end.ToString("d. MMMM yyyy, HH:mm", German) + " Uhr"
: end.ToString("d. MMMM yyyy", German);
return start + " – " + endText;
}
}
@@ -0,0 +1,42 @@
.event-list {
list-style: none;
padding: 0;
}
.event-list li {
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}
.event-list time {
display: block;
color: #777;
font-size: 0.85rem;
}
.event-title {
font-size: 1.1rem;
font-weight: 600;
}
.event-location::before {
content: " · ";
color: #777;
}
.event-location {
color: #555;
}
.event-note {
margin: 0.25rem 0 0;
color: #444;
}
.event-list-past {
color: #777;
}
.event-list-past .event-title {
font-weight: 400;
}
@@ -1,6 +1,4 @@
@page "/"
@using System.Globalization
@using Elternbeirat.Web.Services
@inject PostService PostService
<PageTitle>Elternbeirat der IGMH</PageTitle>
@@ -27,7 +25,7 @@
<li>
<a href="@($"/beitraege/{post.Slug}")">@post.Title</a>
<time datetime="@post.Date.ToString("yyyy-MM-dd")">
@post.Date.ToString("d. MMMM yyyy", _german)
@post.Date.ToString("d. MMMM yyyy", German)
</time>
</li>
}
@@ -35,15 +33,3 @@
<p><a href="/beitraege">Alle Beiträge &rarr;</a></p>
}
</section>
@code {
private static readonly CultureInfo _german = CultureInfo.GetCultureInfo("de-DE");
private IReadOnlyList<Post> _latest = [];
protected override void OnInitialized()
{
// Show the three most recent posts as a teaser on the home page.
_latest = PostService.All.Take(3).ToList();
}
}
@@ -0,0 +1,17 @@
using System.Globalization;
using Elternbeirat.Web.Services;
namespace Elternbeirat.Web.Features.Home;
public partial class Home
{
private static readonly CultureInfo German = CultureInfo.GetCultureInfo("de-DE");
private IReadOnlyList<Post> _latest = [];
protected override void OnInitialized()
{
// Show the three most recent posts as a teaser on the home page.
_latest = [.. PostService.All.Take(3)];
}
}
@@ -0,0 +1,29 @@
.intro {
font-size: 1.1rem;
color: #444;
}
.home-news {
margin-top: 2rem;
}
.post-list {
list-style: none;
padding: 0;
}
.post-list li {
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}
.post-list a {
font-size: 1.1rem;
text-decoration: none;
}
.post-list time {
display: block;
color: #777;
font-size: 0.85rem;
}
@@ -0,0 +1,14 @@
@page "/{Slug}"
@inject PageService PageService
@if (_page is null)
{
<PageTitle>Nicht gefunden</PageTitle>
}
else
{
<PageTitle>@_page.Title</PageTitle>
<article>
@((MarkupString)_page.ContentHtml)
</article>
}
@@ -1,20 +1,11 @@
@page "/{Slug}"
@using Elternbeirat.Web.Services
@inject PageService PageService
using Elternbeirat.Web.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
@if (_page is null)
{
<PageTitle>Nicht gefunden</PageTitle>
}
else
{
<PageTitle>@_page.Title</PageTitle>
<article>
@((MarkupString)_page.ContentHtml)
</article>
}
namespace Elternbeirat.Web.Features.Pages;
@code {
public partial class ContentPage
{
[Parameter]
public string Slug { get; set; } = "";
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,20 @@
@page "/beitraege/{Slug}"
@inject PostService PostService
@if (_post is null)
{
<PageTitle>Nicht gefunden</PageTitle>
}
else
{
<PageTitle>@_post.Title</PageTitle>
<article>
<p class="post-meta">
<a href="/beitraege">&larr; Alle Beiträge</a>
<time datetime="@_post.Date.ToString("yyyy-MM-dd")">
@_post.Date.ToString("d. MMMM yyyy", German)
</time>
</p>
@((MarkupString)_post.ContentHtml)
</article>
}
@@ -0,0 +1,31 @@
using System.Globalization;
using Elternbeirat.Web.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
namespace Elternbeirat.Web.Features.Posts;
public partial class PostDetail
{
private static readonly CultureInfo German = CultureInfo.GetCultureInfo("de-DE");
[Parameter]
public string Slug { get; set; } = "";
private Post? _post;
protected override void OnParametersSet()
{
_post = PostService.Find(Slug);
// Unknown slug -> 404, so UseStatusCodePagesWithReExecute serves the
// /not-found page instead of an empty 200 response.
if (_post is null && HttpContext is not null)
{
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
}
}
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
}
@@ -0,0 +1,13 @@
.post-meta {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 1rem;
margin-bottom: 1rem;
}
.post-meta time {
display: block;
color: #777;
font-size: 0.85rem;
}
@@ -1,6 +1,4 @@
@page "/beitraege"
@using System.Globalization
@using Elternbeirat.Web.Services
@inject PostService PostService
<PageTitle>Beiträge</PageTitle>
@@ -19,13 +17,9 @@ else
<li>
<a href="@($"/beitraege/{post.Slug}")">@post.Title</a>
<time datetime="@post.Date.ToString("yyyy-MM-dd")">
@post.Date.ToString("d. MMMM yyyy", _german)
@post.Date.ToString("d. MMMM yyyy", German)
</time>
</li>
}
</ul>
}
@code {
private static readonly CultureInfo _german = CultureInfo.GetCultureInfo("de-DE");
}
@@ -0,0 +1,8 @@
using System.Globalization;
namespace Elternbeirat.Web.Features.Posts;
public partial class PostList
{
private static readonly CultureInfo German = CultureInfo.GetCultureInfo("de-DE");
}
@@ -0,0 +1,20 @@
.post-list {
list-style: none;
padding: 0;
}
.post-list li {
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}
.post-list a {
font-size: 1.1rem;
text-decoration: none;
}
.post-list time {
display: block;
color: #777;
font-size: 0.85rem;
}
+10
View File
@@ -0,0 +1,10 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Elternbeirat.Web
@using Elternbeirat.Web.Services
+1 -86
View File
@@ -22,91 +22,6 @@ h1:focus {
outline: none;
}
/* Home page */
.intro {
font-size: 1.1rem;
color: #444;
}
.home-news {
margin-top: 2rem;
}
/* News list on /beitraege */
.post-list {
list-style: none;
padding: 0;
}
.post-list li {
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}
.post-list a {
font-size: 1.1rem;
text-decoration: none;
}
.post-list time,
.post-meta time {
display: block;
color: #777;
font-size: 0.85rem;
}
.post-meta {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 1rem;
margin-bottom: 1rem;
}
/* Event list on /termine */
.event-list {
list-style: none;
padding: 0;
}
.event-list li {
padding: 0.75rem 0;
border-bottom: 1px solid #eee;
}
.event-list time {
display: block;
color: #777;
font-size: 0.85rem;
}
.event-title {
font-size: 1.1rem;
font-weight: 600;
}
.event-location::before {
content: " · ";
color: #777;
}
.event-location {
color: #555;
}
.event-note {
margin: 0.25rem 0 0;
color: #444;
}
.event-list-past {
color: #777;
}
.event-list-past .event-title {
font-weight: 400;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
@@ -140,4 +55,4 @@ h1:focus {
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
text-align: start;
}
}