diff --git a/Elternbeirat.Web/Components/Pages/PostDetail.razor b/Elternbeirat.Web/Components/Pages/PostDetail.razor deleted file mode 100644 index 5e678a7..0000000 --- a/Elternbeirat.Web/Components/Pages/PostDetail.razor +++ /dev/null @@ -1,46 +0,0 @@ -@page "/beitraege/{Slug}" -@using System.Globalization -@using Elternbeirat.Web.Services -@inject PostService PostService - -@if (_post is null) -{ - Nicht gefunden -} -else -{ - @_post.Title -
-

- ← Alle Beiträge - -

- @((MarkupString)_post.ContentHtml) -
-} - -@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; } -} diff --git a/Elternbeirat.Web/Services/Event.cs b/Elternbeirat.Web/Features/Events/Event.cs similarity index 100% rename from Elternbeirat.Web/Services/Event.cs rename to Elternbeirat.Web/Features/Events/Event.cs diff --git a/Elternbeirat.Web/Components/Pages/EventList.razor b/Elternbeirat.Web/Features/Events/EventList.razor similarity index 59% rename from Elternbeirat.Web/Components/Pages/EventList.razor rename to Elternbeirat.Web/Features/Events/EventList.razor index 0fdee26..5d71ca1 100644 --- a/Elternbeirat.Web/Components/Pages/EventList.razor +++ b/Elternbeirat.Web/Features/Events/EventList.razor @@ -1,6 +1,4 @@ @page "/termine" -@using System.Globalization -@using Elternbeirat.Web.Services @inject EventService EventService Termine @@ -57,35 +55,3 @@ else } } - -@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; - } -} diff --git a/Elternbeirat.Web/Features/Events/EventList.razor.cs b/Elternbeirat.Web/Features/Events/EventList.razor.cs new file mode 100644 index 0000000..a17a7bd --- /dev/null +++ b/Elternbeirat.Web/Features/Events/EventList.razor.cs @@ -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; + } +} diff --git a/Elternbeirat.Web/Features/Events/EventList.razor.css b/Elternbeirat.Web/Features/Events/EventList.razor.css new file mode 100644 index 0000000..1b33ecc --- /dev/null +++ b/Elternbeirat.Web/Features/Events/EventList.razor.css @@ -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; +} diff --git a/Elternbeirat.Web/Services/EventService.cs b/Elternbeirat.Web/Features/Events/EventService.cs similarity index 100% rename from Elternbeirat.Web/Services/EventService.cs rename to Elternbeirat.Web/Features/Events/EventService.cs diff --git a/Elternbeirat.Web/Services/IcsCalendar.cs b/Elternbeirat.Web/Features/Events/IcsCalendar.cs similarity index 100% rename from Elternbeirat.Web/Services/IcsCalendar.cs rename to Elternbeirat.Web/Features/Events/IcsCalendar.cs diff --git a/Elternbeirat.Web/Components/Pages/Home.razor b/Elternbeirat.Web/Features/Home/Home.razor similarity index 65% rename from Elternbeirat.Web/Components/Pages/Home.razor rename to Elternbeirat.Web/Features/Home/Home.razor index 829e27e..b811c0e 100644 --- a/Elternbeirat.Web/Components/Pages/Home.razor +++ b/Elternbeirat.Web/Features/Home/Home.razor @@ -1,6 +1,4 @@ @page "/" -@using System.Globalization -@using Elternbeirat.Web.Services @inject PostService PostService Elternbeirat der IGMH @@ -27,7 +25,7 @@
  • @post.Title
  • } @@ -35,15 +33,3 @@

    Alle Beiträge →

    } - -@code { - private static readonly CultureInfo _german = CultureInfo.GetCultureInfo("de-DE"); - - private IReadOnlyList _latest = []; - - protected override void OnInitialized() - { - // Show the three most recent posts as a teaser on the home page. - _latest = PostService.All.Take(3).ToList(); - } -} diff --git a/Elternbeirat.Web/Features/Home/Home.razor.cs b/Elternbeirat.Web/Features/Home/Home.razor.cs new file mode 100644 index 0000000..8ad1eef --- /dev/null +++ b/Elternbeirat.Web/Features/Home/Home.razor.cs @@ -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 _latest = []; + + protected override void OnInitialized() + { + // Show the three most recent posts as a teaser on the home page. + _latest = [.. PostService.All.Take(3)]; + } +} diff --git a/Elternbeirat.Web/Features/Home/Home.razor.css b/Elternbeirat.Web/Features/Home/Home.razor.css new file mode 100644 index 0000000..016ae11 --- /dev/null +++ b/Elternbeirat.Web/Features/Home/Home.razor.css @@ -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; +} diff --git a/Elternbeirat.Web/Features/Pages/ContentPage.razor b/Elternbeirat.Web/Features/Pages/ContentPage.razor new file mode 100644 index 0000000..dd09e47 --- /dev/null +++ b/Elternbeirat.Web/Features/Pages/ContentPage.razor @@ -0,0 +1,14 @@ +@page "/{Slug}" +@inject PageService PageService + +@if (_page is null) +{ + Nicht gefunden +} +else +{ + @_page.Title +
    + @((MarkupString)_page.ContentHtml) +
    +} diff --git a/Elternbeirat.Web/Components/Pages/ContentPage.razor b/Elternbeirat.Web/Features/Pages/ContentPage.razor.cs similarity index 67% rename from Elternbeirat.Web/Components/Pages/ContentPage.razor rename to Elternbeirat.Web/Features/Pages/ContentPage.razor.cs index 76f4b2e..0d37292 100644 --- a/Elternbeirat.Web/Components/Pages/ContentPage.razor +++ b/Elternbeirat.Web/Features/Pages/ContentPage.razor.cs @@ -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) -{ - Nicht gefunden -} -else -{ - @_page.Title -
    - @((MarkupString)_page.ContentHtml) -
    -} +namespace Elternbeirat.Web.Features.Pages; -@code { +public partial class ContentPage +{ [Parameter] public string Slug { get; set; } = ""; diff --git a/Elternbeirat.Web/Services/Page.cs b/Elternbeirat.Web/Features/Pages/Page.cs similarity index 100% rename from Elternbeirat.Web/Services/Page.cs rename to Elternbeirat.Web/Features/Pages/Page.cs diff --git a/Elternbeirat.Web/Services/PageService.cs b/Elternbeirat.Web/Features/Pages/PageService.cs similarity index 100% rename from Elternbeirat.Web/Services/PageService.cs rename to Elternbeirat.Web/Features/Pages/PageService.cs diff --git a/Elternbeirat.Web/Services/Post.cs b/Elternbeirat.Web/Features/Posts/Post.cs similarity index 100% rename from Elternbeirat.Web/Services/Post.cs rename to Elternbeirat.Web/Features/Posts/Post.cs diff --git a/Elternbeirat.Web/Features/Posts/PostDetail.razor b/Elternbeirat.Web/Features/Posts/PostDetail.razor new file mode 100644 index 0000000..ef31e63 --- /dev/null +++ b/Elternbeirat.Web/Features/Posts/PostDetail.razor @@ -0,0 +1,20 @@ +@page "/beitraege/{Slug}" +@inject PostService PostService + +@if (_post is null) +{ + Nicht gefunden +} +else +{ + @_post.Title +
    + + @((MarkupString)_post.ContentHtml) +
    +} diff --git a/Elternbeirat.Web/Features/Posts/PostDetail.razor.cs b/Elternbeirat.Web/Features/Posts/PostDetail.razor.cs new file mode 100644 index 0000000..e2d1e99 --- /dev/null +++ b/Elternbeirat.Web/Features/Posts/PostDetail.razor.cs @@ -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; } +} diff --git a/Elternbeirat.Web/Features/Posts/PostDetail.razor.css b/Elternbeirat.Web/Features/Posts/PostDetail.razor.css new file mode 100644 index 0000000..f9ff28c --- /dev/null +++ b/Elternbeirat.Web/Features/Posts/PostDetail.razor.css @@ -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; +} diff --git a/Elternbeirat.Web/Components/Pages/PostList.razor b/Elternbeirat.Web/Features/Posts/PostList.razor similarity index 68% rename from Elternbeirat.Web/Components/Pages/PostList.razor rename to Elternbeirat.Web/Features/Posts/PostList.razor index 1b2ee6c..0301a89 100644 --- a/Elternbeirat.Web/Components/Pages/PostList.razor +++ b/Elternbeirat.Web/Features/Posts/PostList.razor @@ -1,6 +1,4 @@ @page "/beitraege" -@using System.Globalization -@using Elternbeirat.Web.Services @inject PostService PostService Beiträge @@ -19,13 +17,9 @@ else
  • @post.Title
  • } } - -@code { - private static readonly CultureInfo _german = CultureInfo.GetCultureInfo("de-DE"); -} diff --git a/Elternbeirat.Web/Features/Posts/PostList.razor.cs b/Elternbeirat.Web/Features/Posts/PostList.razor.cs new file mode 100644 index 0000000..f582b7e --- /dev/null +++ b/Elternbeirat.Web/Features/Posts/PostList.razor.cs @@ -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"); +} diff --git a/Elternbeirat.Web/Features/Posts/PostList.razor.css b/Elternbeirat.Web/Features/Posts/PostList.razor.css new file mode 100644 index 0000000..ac756fa --- /dev/null +++ b/Elternbeirat.Web/Features/Posts/PostList.razor.css @@ -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; +} diff --git a/Elternbeirat.Web/Services/PostService.cs b/Elternbeirat.Web/Features/Posts/PostService.cs similarity index 100% rename from Elternbeirat.Web/Services/PostService.cs rename to Elternbeirat.Web/Features/Posts/PostService.cs diff --git a/Elternbeirat.Web/Services/FrontMatter.cs b/Elternbeirat.Web/Shared/FrontMatter.cs similarity index 100% rename from Elternbeirat.Web/Services/FrontMatter.cs rename to Elternbeirat.Web/Shared/FrontMatter.cs diff --git a/Elternbeirat.Web/_Imports.razor b/Elternbeirat.Web/_Imports.razor new file mode 100644 index 0000000..57b29e7 --- /dev/null +++ b/Elternbeirat.Web/_Imports.razor @@ -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 diff --git a/Elternbeirat.Web/wwwroot/app.css b/Elternbeirat.Web/wwwroot/app.css index 161563d..e49babf 100644 --- a/Elternbeirat.Web/wwwroot/app.css +++ b/Elternbeirat.Web/wwwroot/app.css @@ -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; -} \ No newline at end of file +}