Events-MVC (example with htmx)

This commit is contained in:
Boris Milašinović
2026-04-25 22:21:35 +02:00
parent eb04483417
commit 0ee1b22f61
114 changed files with 7966 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData[Constants.ViewDataKeys.Title] - Events</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body class="bg-body-tertiary">
<header class="border-bottom bg-white shadow-sm">
<nav class="navbar navbar-expand-lg">
<div class="container">
<a class="navbar-brand fw-semibold" asp-controller="Home" asp-action="Index">Events</a>
@if (ViewData[Constants.ViewDataKeys.HeaderActionLabel] is string headerActionLabel && ViewData[Constants.ViewDataKeys.HeaderActionTarget] is string headerActionTarget)
{
<div class="ms-3 d-none d-lg-flex align-items-center">
<button
type="button"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="collapse"
data-bs-target="@headerActionTarget"
aria-expanded="false"
aria-controls="@headerActionTarget.TrimStart('#')">
@headerActionLabel
</button>
</div>
}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNav">
@if (ViewData[Constants.ViewDataKeys.HeaderActionLabel] is string mobileHeaderActionLabel && ViewData[Constants.ViewDataKeys.HeaderActionTarget] is string mobileHeaderActionTarget)
{
<div class="d-lg-none mb-3">
<button
type="button"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="collapse"
data-bs-target="@mobileHeaderActionTarget"
aria-expanded="false"
aria-controls="@mobileHeaderActionTarget.TrimStart('#')">
@mobileHeaderActionLabel
</button>
</div>
}
<ul class="navbar-nav ms-auto gap-lg-2">
<li class="nav-item">
<a class="nav-link" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="Events" asp-action="Index">Events</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="Sports" asp-action="Index">Sports</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="Countries" asp-action="Index">Countries</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="People" asp-action="Index">People</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-controller="Registrations" asp-action="Index">Registrations</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main class="container py-4">
@RenderBody()
</main>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="app-toast" class="toast border-0 shadow-sm text-white" role="status" aria-live="polite" aria-atomic="true">
<div id="app-toast-header" class="toast-header">
<strong id="app-toast-title" class="me-auto">Notification</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div id="app-toast-body" class="toast-body"></div>
</div>
</div>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/htmx.org/dist/htmx.min.js"></script>
<script src="~/js/pager.js" asp-append-version="true"></script>
<script>
var toastVariantSuccess = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Constants.ToastVariants.Success));
var toastVariantError = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Constants.ToastVariants.Error));
var toastTitleNotification = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Constants.ToastTitles.Notification));
var toastTitleError = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Constants.ToastTitles.Error));
function showAppToast(options) {
var toastElement = document.getElementById("app-toast");
var headerElement = document.getElementById("app-toast-header");
var titleElement = document.getElementById("app-toast-title");
var bodyElement = document.getElementById("app-toast-body");
if (!toastElement || !headerElement || !titleElement || !bodyElement || !window.bootstrap) {
return;
}
var variant = options.variant || toastVariantSuccess;
toastElement.classList.remove("toast-success", "toast-error");
toastElement.classList.add(variant === toastVariantError ? "toast-error" : "toast-success");
headerElement.classList.remove("text-bg-success", "text-bg-danger");
headerElement.classList.add(variant === toastVariantError ? "text-bg-danger" : "text-bg-success");
titleElement.textContent = options.title || toastTitleNotification;
bodyElement.textContent = options.message || "";
var toast = bootstrap.Toast.getOrCreateInstance(toastElement, { delay: 2500 });
toast.show();
}
document.body.addEventListener(@Html.Raw(System.Text.Json.JsonSerializer.Serialize(Constants.HtmxEvents.ShowToast)), function (event) {
showAppToast(event.detail || {});
});
document.body.addEventListener("htmx:responseError", function (event) {
var xhr = event.detail.xhr;
var message = "An error occurred while processing the request.";
if (xhr && xhr.responseText) {
try {
var problem = JSON.parse(xhr.responseText);
message = problem.detail || problem.title || xhr.responseText;
}
catch {
message = xhr.responseText;
}
}
showAppToast({
variant: toastVariantError,
title: toastTitleError,
message: message
});
});
@if (TempData[Constants.TempDataKeys.ToastMessage] is string toastMessage)
{
var initialToastJson = System.Text.Json.JsonSerializer.Serialize(new
{
variant = TempData[Constants.TempDataKeys.ToastVariant] as string ?? Constants.ToastVariants.Success,
title = TempData[Constants.TempDataKeys.ToastTitle] as string ?? Constants.ToastTitles.Notification,
message = toastMessage
});
@:showAppToast(@Html.Raw(initialToastJson));
}
</script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>