summaryrefslogtreecommitdiff
path: root/CoreWiki/Pages
diff options
context:
space:
mode:
Diffstat (limited to 'CoreWiki/Pages')
-rw-r--r--CoreWiki/Pages/All.cshtml36
-rw-r--r--CoreWiki/Pages/All.cshtml.cs2
-rw-r--r--CoreWiki/Pages/Create.cshtml48
-rw-r--r--CoreWiki/Pages/Delete.cshtml41
-rw-r--r--CoreWiki/Pages/Details.cshtml28
-rw-r--r--CoreWiki/Pages/Details.cshtml.cs86
-rw-r--r--CoreWiki/Pages/Edit.cshtml19
-rw-r--r--CoreWiki/Pages/LatestChanges.cshtml18
-rw-r--r--CoreWiki/Pages/LatestChanges.cshtml.cs7
-rw-r--r--CoreWiki/Pages/Shared/_Layout.cshtml78
-rw-r--r--CoreWiki/Pages/_ViewImports.cshtml3
-rw-r--r--CoreWiki/Pages/_articleList.cshtml20
-rw-r--r--CoreWiki/Pages/_comments.cshtml35
-rw-r--r--CoreWiki/Pages/_commentsForm.cshtml32
-rw-r--r--CoreWiki/Pages/_commentsList.cshtml25
15 files changed, 318 insertions, 160 deletions
diff --git a/CoreWiki/Pages/All.cshtml b/CoreWiki/Pages/All.cshtml
index ca06cb5..6e3430e 100644
--- a/CoreWiki/Pages/All.cshtml
+++ b/CoreWiki/Pages/All.cshtml
@@ -5,40 +5,28 @@
ViewData["Title"] = "All articles";
}
-<h2>All articles</h2>
+<h1>All articles</h1>
-@foreach (var item in Model.Articles)
-{
- <div class="card border-primary my-2 mx-1">
- <div class="card-body">
- <h3 class="card-title">
- <a href="~/@item.Slug">@item.Topic</a>
- </h3>
- <h6 class="card-subtitle mb-2 text-muted">
- <span data-value="@item.Published" class="timeStampValue">@item.Published</span>
- </h6>
+<partial name="_articleList" model="Model.Articles"/>
- <a class="card-link" asp-page="./Edit" asp-route-slug="@item.Slug">Edit</a>
- <a class="card-link" asp-page="./Delete" asp-route-slug="@item.Slug">Delete</a>
- </div>
- </div>
-}
-
-<div class="col-md-12 d-flex">
- <nav class="flex-grow-1">
- <ul class="pagination">
+<div class="col-md-12 flex">
+ <nav class="flex-1">
+ <ul class="flex -space-x-px">
@for (var i = 1; i <= Model.TotalPages; i++)
{
var i1 = i;
- <li class="page-item @(i1 == Model.PageNumber ? "active" : "")">
- <a class="page-link" asp-page="All" asp-route-PageNumber="@i1" asp-route-PageSize="@Model.PageSize">@i1</a>
+ <li>
+ <a class="block p-3 text-sm leading-tight border border-gray-300 bg-white @(i1 == Model.PageNumber ? "bg-blue-100 text-blue-700" : "hover:bg-gray-50")"
+ asp-page="All"
+ asp-route-PageNumber="@i1"
+ asp-route-PageSize="@Model.PageSize">@i1</a>
</li>
}
</ul>
</nav>
- <form class="ms-auto" method="get" asp-page="All">
+ <form class="ml-auto" method="get" asp-page="All">
<input hidden name="@nameof(Model.PageNumber)" value="@Model.PageNumber"/>
- <select class="form-select" asp-for="@Model.PageSize" onchange="this.form.submit()">
+ <select class="bg-gray-50 border border-gray-300 text-gray-900 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full p-3" asp-for="@Model.PageSize" onchange="this.form.submit()">
<option value="2">2</option>
<option value="10">10</option>
<option value="25">25</option>
diff --git a/CoreWiki/Pages/All.cshtml.cs b/CoreWiki/Pages/All.cshtml.cs
index c41e216..d54e75b 100644
--- a/CoreWiki/Pages/All.cshtml.cs
+++ b/CoreWiki/Pages/All.cshtml.cs
@@ -14,7 +14,7 @@ public class All : PageModel
public int PageNumber { get; set; } = 1;
[BindProperty(SupportsGet = true)]
public int PageSize { get; set; } = 25;
- public IEnumerable<Article>? Articles { get; set; }
+ public IEnumerable<Article> Articles { get; set; }
public int TotalPages { get; set; }
public All(ApplicationDbContext context)
diff --git a/CoreWiki/Pages/Create.cshtml b/CoreWiki/Pages/Create.cshtml
index 4abbf11..153035a 100644
--- a/CoreWiki/Pages/Create.cshtml
+++ b/CoreWiki/Pages/Create.cshtml
@@ -5,45 +5,33 @@
ViewData["Title"] = "Create";
}
-<h1>Create</h1>
-
-<h4>Article</h4>
-<hr />
-<div class="row d-flex justify-content-center">
- <div class="col-md-8">
- <form method="post">
- <div asp-validation-summary="ModelOnly" class="text-danger"></div>
- <div class="form-group">
- <label asp-for="Article.Topic" class="control-label"></label>
- <input asp-for="Article.Topic" class="form-control" />
- <span asp-validation-for="Article.Topic" class="text-danger"></span>
- </div>
- <div class="form-group">
- <label asp-for="Article.Content" class="control-label"></label>
- <textarea asp-for="Article.Content" class="form-control"></textarea>
- <span asp-validation-for="Article.Content" class="text-danger"></span>
- </div>
- <div class="form-group">
- <input type="submit" value="Create" class="btn btn-primary" />
- </div>
- </form>
+<h1>Create Article</h1>
+<form class="space-y-3" method="post">
+ <div asp-validation-summary="ModelOnly" class="text-red-600"></div>
+ <div>
+ <label asp-for="Article.Topic" class="block mb-2 text-sm font-medium text-gray-900"></label>
+ <input asp-for="Article.Topic" class="block bg-gray-50 border border-gray-300 rounded focus:ring-blue-500 focus:border-blue-500 text-sm p-2 w-full"/>
+ <span asp-validation-for="Article.Topic" class="text-red-600"></span>
</div>
-</div>
-
-<div>
- <a asp-page="Index">Back to List</a>
-</div>
+ <div>
+ <label asp-for="Article.Content" class="block mb-2 text-sm font-medium text-gray-900"></label>
+ <textarea asp-for="Article.Content"></textarea>
+ <span asp-validation-for="Article.Content" class="text-red-600"></span>
+ </div>
+ <div class="flex space-x-2">
+ <input type="submit" value="Create" class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-1.5 focus:outline-none"/>
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none" asp-page="Details">Back to List</a>
+ </div>
+</form>
@section Scripts {
<script src="~/lib/simplemde/simplemde.min.js"></script>
<script>
var simplemde = new SimpleMDE();
</script>
- @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
-
}
@section Styles
{
<link href="~/lib/simplemde/simplemde.min.css" rel="stylesheet"/>
-}
+} \ No newline at end of file
diff --git a/CoreWiki/Pages/Delete.cshtml b/CoreWiki/Pages/Delete.cshtml
index 0b680b8..0c721bd 100644
--- a/CoreWiki/Pages/Delete.cshtml
+++ b/CoreWiki/Pages/Delete.cshtml
@@ -5,30 +5,19 @@
ViewData["Title"] = "Delete";
}
-<h1>Delete</h1>
-
-<h3>Are you sure you want to delete this?</h3>
-<div>
- <h4>Article</h4>
- <hr />
- <dl class="row">
- <dt class="col-sm-2">
- @Html.DisplayNameFor(model => model.Article.Published)
- </dt>
- <dd class="col-sm-10">
- @Html.DisplayFor(model => model.Article.Published)
- </dd>
- <dt class="col-sm-2">
- @Html.DisplayNameFor(model => model.Article.Content)
- </dt>
- <dd class="col-sm-10">
- @Html.DisplayFor(model => model.Article.Content)
- </dd>
- </dl>
-
- <form method="post">
- <input type="hidden" asp-for="Article.Slug" />
- <input type="submit" value="Delete" class="btn btn-danger" /> |
- <a href="/">Back to Home Page</a>
- </form>
+<h2 class="mb-3">Are you sure you want to delete this article?</h2>
+<div class="mb-3">
+ <div class="flex flex-col">
+ <span>@Html.DisplayNameFor(model => model.Article.Published)</span>
+ <span>@Html.DisplayFor(model => model.Article.Published)</span>
+ </div>
+ <div class="flex flex-col">
+ <span>@Html.DisplayNameFor(model => model.Article.Content)</span>
+ <span>@Html.DisplayFor(model => model.Article.Content)</span>
+ </div>
</div>
+<form method="post">
+ <input type="hidden" asp-for="Article.Id"/>
+ <input type="submit" value="Delete" class="text-white bg-red-500 hover:bg-red-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-1.5 focus:outline-none"/>
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none" href="/">Back to Home Page</a>
+</form> \ No newline at end of file
diff --git a/CoreWiki/Pages/Details.cshtml b/CoreWiki/Pages/Details.cshtml
index 9f48f9f..3b7dff1 100644
--- a/CoreWiki/Pages/Details.cshtml
+++ b/CoreWiki/Pages/Details.cshtml
@@ -5,16 +5,32 @@
ViewData["Title"] = Model.Article.Topic;
}
-<h1>@Model.Article.Topic</h1>
-<h5>Last Published: <span data-value="@Model.Article.Published" class="timeStampValue">@Model.Article.Published</span></h5>
+<div class="h-full">
+ <h2>@Model.Article.Topic</h2>
+ <p class="text-sm text-gray-500">Last Published: <span data-value="@Model.Article.Published" class="timeStampValue">@Model.Article.Published</span></p>
+ <p class="text-sm text-gray-500">Reading time: @Model.Article.EstimatedReadingTime minutes</p>
+ <p class="text-sm text-gray-500">Views: @Model.Article.ViewCount</p>
-<markdown markdown="Article.Content"/>
+ <markdown markdown="Article.Content"/>
+</div>
-<div>
- <a asp-page="./Edit" asp-route-slug="@Model.Article.Slug">Edit</a>
+<div class="mt-4">
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none"
+ asp-page="./Edit"
+ asp-route-slug="@Model.Article.Slug">Edit</a>
@if (Model.Article.Slug != "home-page")
{
- <text>| </text> <a href="~/">Back to Home</a>
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none"
+ href="~/">Back to Home</a>
}
</div>
+<br/>
+<div class="mt-auto cursor-pointer" x-data="{ formOpen: false, commentsOpen: false }" id="comments">
+ <partial name="_comments" model="@Model"/>
+</div>
+
+@section Styles
+{
+ <link href="~/lib/simplemde/simplemde.min.css" rel="stylesheet"/>
+} \ No newline at end of file
diff --git a/CoreWiki/Pages/Details.cshtml.cs b/CoreWiki/Pages/Details.cshtml.cs
index e4100ac..bdd576f 100644
--- a/CoreWiki/Pages/Details.cshtml.cs
+++ b/CoreWiki/Pages/Details.cshtml.cs
@@ -6,32 +6,108 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using CoreWiki.Models;
+using Htmx;
+using Microsoft.AspNetCore.Http.HttpResults;
+using NodaTime;
namespace CoreWiki.Pages
{
public class DetailsModel : PageModel
{
+ private readonly IClock _clock;
private readonly ApplicationDbContext _context;
- public DetailsModel(ApplicationDbContext context)
+ public DetailsModel(ApplicationDbContext context, IClock clock)
{
+ _clock = clock;
_context = context;
}
- public Article Article { get; set; } = default!;
+ private const int CommentsPerPage = 10;
- public async Task<IActionResult> OnGetAsync(string? slug)
+ public Article Article { get; set; } = default!;
+ [BindProperty] public Comment Comment { get; set; }
+
+ public async Task<IActionResult> OnGetAsync(string? slug, [FromQuery] int? pageNumber)
{
slug ??= "home-page";
+ var pagesToSkip = CommentsPerPage * ((pageNumber ?? 1) - 1);
- var article = await _context.Articles.FirstOrDefaultAsync(a => a.Slug == slug);
+ var article = await _context.Articles
+ .Include(a => a.Comments
+ .OrderByDescending(c => c.SubmittedDateTime)
+ .Skip(pagesToSkip)
+ .Take(CommentsPerPage))
+ .FirstOrDefaultAsync(a => a.Slug == slug);
if (article == null)
{
return NotFound();
}
Article = article;
+ if (Request.Cookies[Article.Slug] == null)
+ {
+ Article.ViewCount++;
+ Response.Cookies.Append(Article.Slug, "viewed", new CookieOptions()
+ {
+ Expires = DateTime.UtcNow.AddMinutes(5)
+ });
+
+ await _context.SaveChangesAsync();
+ }
+
+ if (Request.IsHtmx() && !Request.IsHtmxBoosted())
+ {
+ return Partial("_commentsList", new ArticleCommentListViewModel
+ {
+ Comments = Article.Comments,
+ Slug = slug,
+ PageNumber = pageNumber ?? 1
+ });
+ }
+
return Page();
}
+
+ public async Task<IActionResult> OnPostAsync(string? slug)
+ {
+ slug ??= "home-page";
+ var article = await _context.Articles.FirstOrDefaultAsync(a => a.Slug == slug);
+ if (article == null)
+ {
+ return NotFound();
+ }
+
+ Article = article;
+
+ ModelState.Remove("Comment.Article");
+ if (!ModelState.IsValid)
+ {
+ if (Request.IsHtmx() && !Request.IsHtmxBoosted())
+ {
+ return Partial("_commentsForm", this);
+ }
+
+ return Page();
+ }
+
+ Comment.Submitted = _clock.GetCurrentInstant();
+ article.Comments.Add(Comment);
+ await _context.SaveChangesAsync();
+
+ if (Request.IsHtmx() && !Request.IsHtmxBoosted())
+ {
+ Response.Htmx(h => { h.WithTrigger("commentsChanged"); });
+
+ Comment.DisplayName = String.Empty;
+ Comment.EMail = String.Empty;
+ Comment.Content = String.Empty;
+ ModelState.Clear();
+
+ return Partial("_commentsForm", this);
+ }
+
+ return RedirectToPage("Details", new { slug = slug == "home-page" ? "" : slug });
+ }
}
-}
+} \ No newline at end of file
diff --git a/CoreWiki/Pages/Edit.cshtml b/CoreWiki/Pages/Edit.cshtml
index 8793034..38c4891 100644
--- a/CoreWiki/Pages/Edit.cshtml
+++ b/CoreWiki/Pages/Edit.cshtml
@@ -8,24 +8,22 @@
<h1>Edit</h1>
-<br />
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
- <input type="hidden" asp-for="Article.Slug" />
- <input type="hidden" asp-for="Article.Topic" />
+ <input type="hidden" asp-for="Article.Id"/>
+ <input type="hidden" asp-for="Article.Slug"/>
+ <input type="hidden" asp-for="Article.Topic"/>
+ <input type="hidden" asp-for="Article.ViewCount"/>
+ <h3 class="mb-2">@Model.Article.Topic</h3>
<div class="form-group">
- <h3>@Model.Article.Topic</h3>
- </div>
- <div class="form-group">
- <label asp-for="Article.Content" class="control-label"></label>
<textarea asp-for="Article.Content" class="form-control"></textarea>
<span asp-validation-for="Article.Content" class="text-danger"></span>
</div>
<div class="form-group">
- <input type="submit" value="Save" class="btn btn-primary" />
- <a class="btn btn-outline-secondary" href="~/">Back to Home</a>
+ <input type="submit" value="Save" class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-1.5 focus:outline-none"/>
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none" href="~/">Back to Home</a>
</div>
</form>
</div>
@@ -36,10 +34,9 @@
<script>
var simplemde = new SimpleMDE();
</script>
- @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
@section Styles
{
<link href="~/lib/simplemde/simplemde.min.css" rel="stylesheet"/>
-}
+} \ No newline at end of file
diff --git a/CoreWiki/Pages/LatestChanges.cshtml b/CoreWiki/Pages/LatestChanges.cshtml
index 677887b..8657edb 100644
--- a/CoreWiki/Pages/LatestChanges.cshtml
+++ b/CoreWiki/Pages/LatestChanges.cshtml
@@ -7,19 +7,7 @@
<h1>Latest Changes</h1>
-<p>
- <a asp-page="Create">Create New</a>
-</p>
+<a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none my-3 w-fit"
+ asp-page="Create">Create New</a>
-@foreach (var item in Model.Article)
-{
- <div class="card border-primary m-1">
- <div class="card-body">
- <h3 class="card-title"><a href="~/@item.Slug">@item.Topic</a></h3>
- <h6 class="card-subtitle mb-2 text-muted"><span data-value="@item.Published" class="timeStampValue">@item.Published</span></h6>
-
- <a class="card-link" asp-page="./Edit" asp-route-slug="@item.Slug">Edit</a>
- <a class="card-link" asp-page="./Delete" asp-route-slug="@item.Slug">Delete</a>
- </div>
- </div>
-} \ No newline at end of file
+<partial name="_articleList" model="Model.Article"/> \ No newline at end of file
diff --git a/CoreWiki/Pages/LatestChanges.cshtml.cs b/CoreWiki/Pages/LatestChanges.cshtml.cs
index 83a70ab..21dde4c 100644
--- a/CoreWiki/Pages/LatestChanges.cshtml.cs
+++ b/CoreWiki/Pages/LatestChanges.cshtml.cs
@@ -18,14 +18,11 @@ namespace CoreWiki.Pages
_context = context;
}
- public IList<Article> Article { get;set; } = default!;
+ public IList<Article> Article { get; set; } = default!;
public async Task OnGetAsync()
{
- if (_context.Articles != null)
- {
- Article = await _context.Articles.OrderByDescending(a => a.PublishedDateTime).Take(10).ToListAsync();
- }
+ Article = await _context.Articles.OrderByDescending(a => a.PublishedDateTime).Take(10).ToListAsync();
}
}
}
diff --git a/CoreWiki/Pages/Shared/_Layout.cshtml b/CoreWiki/Pages/Shared/_Layout.cshtml
index ac74eba..03d8607 100644
--- a/CoreWiki/Pages/Shared/_Layout.cshtml
+++ b/CoreWiki/Pages/Shared/_Layout.cshtml
@@ -1,59 +1,65 @@
-<!DOCTYPE html>
+@using Htmx.TagHelpers
+<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+ <meta name="htmx-config" includeAspNetAntiforgeryToken="true"/>
<title>@ViewData["Title"] - CoreWiki</title>
- <link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.min.css"/>
- <link rel="stylesheet" href="~/css/site.css" asp-append-version="true"/>
- <link rel="stylesheet" href="~/CoreWiki.styles.css" asp-append-version="true"/>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
+ <link rel="stylesheet" href="~/css/output.css"/>
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
-<header>
- <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-light border-bottom box-shadow mb-3">
- <div class="container">
- <a class="navbar-brand" asp-page="Details">CoreWiki</a>
- <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
- aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggler-icon"></span>
- </button>
- <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
- <ul class="navbar-nav flex-grow-1">
- <li class="nav-item">
- <a class="nav-link" asp-page="Details">Home</a>
+<script src="https://unpkg.com/htmx.org@1.9.10"></script>
+<script src="//unpkg.com/alpinejs" defer></script>
+<div class="flex flex-col min-h-screen">
+ <header x-data="{ open: false }">
+ <nav class="flex flex-col md:flex-row w-full px-4 py-3 bg-gray-50 border-bottom shadow">
+ <div class="flex w-full md:w-fit">
+ <a class="text-lg mr-1" asp-page="Details">CoreWiki</a>
+ <button @@click="open = !open" class="inline md:hidden ml-auto">
+ <span class="ml-auto">Toggle</span>
+ </button>
+ </div>
+ <div :class="open || 'hidden'" class="w-full mt-4 md:block md:mt-0 md:w-fit md:ml-auto">
+ <ul class="flex flex-col space-y-3 md:flex-row md:space-x-4 md:space-y-0">
+ <li>
+ <a asp-page="Details">Home</a>
</li>
- <li class="nav-item">
- <a class="nav-link" asp-area="" asp-page="Create">Create</a>
+ <li>
+ <a asp-area="" asp-page="Create">Create</a>
</li>
- <li class="nav-item">
- <a class="nav-link" asp-page="All">All Articles</a>
+ <li>
+ <a hx-boost="true" asp-page="All">All Articles</a>
</li>
- <li class="nav-item">
- <a class="nav-link" asp-page="LatestChanges">Latest Changes</a>
+ <li>
+ <a hx-boost="true" asp-page="LatestChanges">Latest Changes</a>
</li>
</ul>
</div>
- </div>
- </nav>
-</header>
-<div class="container">
- <main role="main" class="pb-3">
+ </nav>
+ </header>
+ <main role="main" class="flex-1 flex flex-col px-8 md:px-48 py-8">
@RenderBody()
</main>
+ <footer class="flex justify-center text-gray-500 shadow border-t py-4 px-2">
+ <span>&copy; 2023 - CoreWiki - <a asp-area="" asp-page="/Privacy">Privacy</a></span>
+ </footer>
</div>
-<footer class="border-top footer text-muted">
- <div class="container">
- &copy; 2023 - CoreWiki - <a asp-area="" asp-page="/Privacy">Privacy</a>
- </div>
-</footer>
-
-<script src="~/lib/jquery/dist/jquery.min.js"></script>
-<script src="~/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
+@Html.HtmxAntiforgeryScript()
<script src="~/lib/momentjs/moment.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
+<script>
+ document.addEventListener("htmx:afterRequest", function (e) {
+ const timeStamps = document.querySelectorAll(".timeStampValue");
+ for (const ts of timeStamps) {
+ const thisTimeStamp = ts.getAttribute("data-value");
+ const date = new Date(thisTimeStamp);
+ ts.textContent = moment(date).format('LLL');
+ }
+ });
+</script>
@await RenderSectionAsync("Scripts", required: false)
</body>
diff --git a/CoreWiki/Pages/_ViewImports.cshtml b/CoreWiki/Pages/_ViewImports.cshtml
index 802f152..86f4a07 100644
--- a/CoreWiki/Pages/_ViewImports.cshtml
+++ b/CoreWiki/Pages/_ViewImports.cshtml
@@ -1,4 +1,5 @@
@using CoreWiki
@namespace CoreWiki.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
-@addTagHelper *, Westwind.AspNetCore.Markdown \ No newline at end of file
+@addTagHelper *, Westwind.AspNetCore.Markdown
+@addTagHelper *, Htmx.TagHelpers \ No newline at end of file
diff --git a/CoreWiki/Pages/_articleList.cshtml b/CoreWiki/Pages/_articleList.cshtml
new file mode 100644
index 0000000..7b8b038
--- /dev/null
+++ b/CoreWiki/Pages/_articleList.cshtml
@@ -0,0 +1,20 @@
+@model IEnumerable<CoreWiki.Models.Article>
+
+@foreach (var item in Model)
+{
+ <div class="flex flex-col border rounded mb-3">
+ <h3 class="bg-gray-50 p-2 border-b">
+ <a href="~/@item.Slug">@item.Topic</a>
+ </h3>
+ <div class="flex flex-col p-2">
+ <h6 class="font-normal card-subtitle mb-2 text-gray-500">
+ <span data-value="@item.Published" class="timeStampValue">@item.Published</span>
+ </h6>
+
+ <div class="flex space-x-2">
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none" asp-page="./Edit" asp-route-slug="@item.Slug">Edit</a>
+ <a class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none" asp-page="./Delete" asp-route-slug="@item.Slug">Delete</a>
+ </div>
+ </div>
+ </div>
+} \ No newline at end of file
diff --git a/CoreWiki/Pages/_comments.cshtml b/CoreWiki/Pages/_comments.cshtml
new file mode 100644
index 0000000..3abc114
--- /dev/null
+++ b/CoreWiki/Pages/_comments.cshtml
@@ -0,0 +1,35 @@
+@using CoreWiki.Models
+@model DetailsModel
+
+<div role="button" @@click="formOpen = !formOpen" class="flex items-center mb-2">
+ <span class="text-2xl">Add comment</span>
+ <svg x-show="!formOpen" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width: 1.25rem; height: 1.25rem;" class="ms-auto">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>
+ </svg>
+ <svg x-show="formOpen" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width: 1.25rem; height: 1.25rem;" class="ms-auto">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5"/>
+ </svg>
+</div>
+<hr class="mb-2"/>
+<div x-cloak x-show="formOpen">
+ <script src="~/lib/simplemde/simplemde.min.js"></script>
+ <script>
+ let simplemde;
+ </script>
+ <partial name="_commentsForm" model="Model"/>
+</div>
+<div role="button" @@click="commentsOpen = !commentsOpen" class="flex items-center mb-2">
+ <span class="text-2xl">Comments</span>
+ <svg x-show="!commentsOpen" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width: 1.25rem; height: 1.25rem;" class="ml-auto">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>
+ </svg>
+ <svg x-show="commentsOpen" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" style="width: 1.25rem; height: 1.25rem;" class="ml-auto">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5"/>
+ </svg>
+</div>
+<hr/>
+<div x-cloak x-show="commentsOpen">
+ <div hx-get hx-trigger="commentsChanged from:body" hx-page="Details" hx-route-slug="@Model.Article.Slug" class="d-flex flex-column gap-3 mt-2">
+ <partial name="_commentsList" model="@(new ArticleCommentListViewModel {Comments = Model.Article.Comments, Slug = Model.Article.Slug, PageNumber = 1})"/>
+ </div>
+</div> \ No newline at end of file
diff --git a/CoreWiki/Pages/_commentsForm.cshtml b/CoreWiki/Pages/_commentsForm.cshtml
new file mode 100644
index 0000000..f87da25
--- /dev/null
+++ b/CoreWiki/Pages/_commentsForm.cshtml
@@ -0,0 +1,32 @@
+@using Htmx.TagHelpers
+@using Microsoft.AspNetCore.Mvc.TagHelpers
+@model DetailsModel
+<form
+ hx-post
+ hx-page="Details"
+ hx-route-slug="@Model.Article.Slug"
+ hx-swap="outerHTML"
+ class="col-12 mb-3"
+ method="post">
+ <div class="mb-2 flex w-full gap-3">
+ <div class="flex-1 min-w-0">
+ <label class="block mb-2 text-sm font-medium text-gray-900" asp-for="Comment.DisplayName"></label>
+ <input class="block bg-gray-50 border border-gray-300 rounded focus:ring-blue-500 focus:border-blue-500 text-sm p-2 w-full" asp-for="Comment.DisplayName"/>
+ <span asp-validation-for="Comment.DisplayName" class="text-danger"></span>
+ </div>
+ <div class="flex-1 min-w-0">
+ <label class="block mb-2 text-sm font-medium text-gray-900" asp-for="Comment.EMail"></label>
+ <input class="block bg-gray-50 border border-gray-300 rounded focus:ring-blue-500 focus:border-blue-500 text-sm p-2 w-full" asp-for="Comment.EMail"/>
+ <span asp-validation-for="Comment.EMail" class="text-danger"></span>
+ </div>
+ </div>
+ <div class="form-group">
+ <label asp-for="Comment.Content" class="block mb-2 text-sm font-medium text-gray-900"></label>
+ <textarea id="contentInput" asp-for="Comment.Content"></textarea>
+ <span asp-validation-for="Comment.Content" class="text-danger"></span>
+ </div>
+ <input type="submit" class="text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded text-sm px-3.5 py-2 focus:outline-none" value="Submit"/>
+</form>
+<script>
+ simplemde = new SimpleMDE({ element: document.getElementById("contentInput"), forceSync: true });
+</script> \ No newline at end of file
diff --git a/CoreWiki/Pages/_commentsList.cshtml b/CoreWiki/Pages/_commentsList.cshtml
new file mode 100644
index 0000000..ba0eb85
--- /dev/null
+++ b/CoreWiki/Pages/_commentsList.cshtml
@@ -0,0 +1,25 @@
+@model CoreWiki.Models.ArticleCommentListViewModel
+
+@foreach (var item in Model.Comments)
+{
+ <div class="flex flex-col border rounded mb-3">
+ <div class="bg-gray-50 p-2 border-b flex items-center">
+ <img class="me-2" width="40" height="40" src="https://gravatar.com/avatar/@item.GravatarHash?d=identicon" alt="avatar"/>
+ <div class="d-flex flex-column">
+ <span class="text-blue-600">@item.DisplayName</span>
+ <div class="flex">
+ <span class="text-sm text-gray-500">Commented on <span data-value="@item.Submitted" class="timeStampValue text-muted">@item.Submitted</span></span>
+ </div>
+ </div>
+ </div>
+ <div class="py-4 px-2">
+ <markdown>@item.Content</markdown>
+ </div>
+ </div>
+}
+@if (Model.Comments.Count() == 10)
+{
+ <div class="h-100 align-items-center d-flex" hx-get hx-page="Details" hx-route-slug="@Model.Slug" hx-route-pageNumber="@(Model.PageNumber + 1)" hx-swap="outerHTML" hx-trigger="intersect once">
+ <div id="spinner" class="htmx-indicator spinner-border"></div>
+ </div>
+} \ No newline at end of file