@page
@model IndexModel
@{
ViewData["Title"] = "Gallery";
}
<div class="d-flex flex-wrap gap-2 align-items-end mt-3">
<form method="post" asp-page-handler="Update" class="mb-0">
<button type="submit" class="btn btn-success">Update</button>
</form>
<form method="get" class="row g-2 align-items-end flex-grow-1 mb-0">
<div class="col-12 col-sm-6 col-md-4">
<input id="keywordInput"
name="keyword"
class="form-control"
value="@Model.Keyword"
placeholder="Search by keyword"
aria-label="Keyword" />
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary">Search</button>
<a class="btn btn-outline-secondary" href="@Url.Page("Index")">Clear</a>
</div>
</form>
</div>
@if (!string.IsNullOrWhiteSpace(Model.StatusMessage))
{
<div class="alert alert-info mt-3">@Model.StatusMessage</div>
}
<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-4 g-3 mt-3">
@if (Model.Folders.Count == 0)
{
<div class="col">
<div class="alert alert-secondary mb-0">No records yet.</div>
</div>
}
else
{
foreach (var card in Model.Folders)
{
var item = card.Folder;
<div class="col">
<a class="card h-100 text-start shadow-sm text-decoration-none"
data-folder-link="true"
data-folder-id="@item.Id"
href="@Url.Page("Folder", null, new { id = item.Id, keyword = Model.Keyword, p = Model.PageNumber > 1 ? Model.PageNumber : (int?)null })">
@if (!string.IsNullOrWhiteSpace(card.CoverUrl))
{
<img src="@card.CoverUrl" alt="@card.CoverFileName" class="card-img-top" />
}
<div class="card-body d-flex flex-column">
<h5 class="card-title mb-1">@item.Title</h5>
<div class="text-muted small">@item.Date.ToString("yyyy-MM-dd") · @item.Author</div>
@if (!string.IsNullOrWhiteSpace(item.Keywords))
{
<div class="mt-2 text-body-secondary small">@item.Keywords</div>
}
<div class="mt-auto pt-2">
<span class="badge text-bg-light">@((item.FileCount ?? 0).ToString()) files</span>
</div>
</div>
</a>
</div>
}
}
</div>
@if (Model.TotalPages > 1)
{
<nav class="mt-3" aria-label="Pagination">
<ul class="pagination mb-0">
<li class="page-item @(Model.PageNumber <= 1 ? "disabled" : "")">
<a class="page-link"
href="@Url.Page("Index", null, new { p = Model.PageNumber - 1, keyword = Model.Keyword })"
aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
@for (var i = 1; i <= Model.TotalPages; i++)
{
<li class="page-item @(i == Model.PageNumber ? "active" : "")">
<a class="page-link" href="@Url.Page("Index", null, new { p = i, keyword = Model.Keyword })">@i</a>
</li>
}
<li class="page-item @(Model.PageNumber >= Model.TotalPages ? "disabled" : "")">
<a class="page-link"
href="@Url.Page("Index", null, new { p = Model.PageNumber + 1, keyword = Model.Keyword })"
aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
}
<div id="thumbWait" class="position-fixed top-0 start-0 w-100 h-100 d-none" style="z-index: 1080;">
<div class="d-flex align-items-center justify-content-center w-100 h-100 bg-dark bg-opacity-50">
<div class="text-center text-white">
<div class="spinner-border text-light" role="status" aria-hidden="true"></div>
<div class="mt-2">Please wait, generating thumbnails...</div>
</div>
</div>
</div>
@section Scripts {
<script>
const waitOverlay = document.getElementById("thumbWait");
document.querySelectorAll("[data-folder-link='true']").forEach(link => {
link.addEventListener("click", async event => {
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
return;
}
const folderId = link.dataset.folderId;
if (!folderId) {
return;
}
event.preventDefault();
waitOverlay?.classList.remove("d-none");
try {
await fetch(`?handler=EnsureThumbnails&id=${encodeURIComponent(folderId)}`);
} catch (error) {
console.warn("Thumbnail generation failed", error);
}
window.location.href = link.href;
});
});
</script>
}