2026-06-12 19:45:22 +01:00

183 lines
9.2 KiB
Plaintext

@model ProjectIndexViewModel
@{
ViewData["Title"] = "Projects";
}
<div class="page-heading">
<div>
<p class="eyebrow">Series planning</p>
<h1>Projects <help-icon key="projects.overview" /></h1>
</div>
<div class="button-row">
<a class="btn btn-outline-primary" asp-controller="Imports" asp-action="Index">Import JSON</a>
<a class="btn btn-primary" asp-action="Create">New project</a>
</div>
</div>
@if (TempData["ArchiveMessage"] is string archiveMessage)
{
<div class="alert alert-success">@archiveMessage</div>
}
@if (TempData["ProjectExportMessage"] is string projectExportMessage)
{
<div class="alert alert-warning">@projectExportMessage</div>
}
@if (!Model.ActiveProjects.Any() && !Model.ArchivedProjects.Any())
{
<section class="empty-panel">
<h2>Start with a series or standalone novel</h2>
<p>Create a project, then add books, chapters and scenes in narrative order.</p>
<a class="btn btn-primary" asp-action="Create">Create your first project</a>
</section>
}
else
{
<section class="list-section">
<h2>Active Projects</h2>
@if (!Model.ActiveProjects.Any())
{
<p class="muted">No active projects.</p>
}
else
{
<div class="content-grid">
@foreach (var project in Model.ActiveProjects)
{
<article class="plain-card">
<h3>@project.ProjectName</h3>
<p class="eyebrow">@project.AccessRole</p>
<p>@(string.IsNullOrWhiteSpace(project.Description) ? "No description yet." : project.Description)</p>
<div class="button-row">
<a class="btn btn-outline-primary btn-sm" asp-action="Details" asp-route-id="@project.ProjectID">Open</a>
<a class="btn btn-outline-secondary btn-sm" asp-action="Edit" asp-route-id="@project.ProjectID">Edit</a>
@if (project.IsOwner)
{
<a class="btn btn-outline-secondary btn-sm" asp-controller="Exports" asp-action="ProjectBackup" asp-route-projectId="@project.ProjectID">Download backup</a>
<form asp-action="Archive" method="post" class="archive-button-form" data-confirm-message="Archive this project?&#10;&#10;Archived projects are hidden from your active workspace but remain stored within PlotDirector. Export your project first if you wish to maintain an external backup.">
<input type="hidden" name="id" value="@project.ProjectID" />
<button class="btn btn-outline-danger btn-sm" type="submit">Archive</button>
</form>
}
</div>
</article>
}
</div>
}
</section>
<section class="list-section">
<h2>Archived Projects</h2>
@if (!Model.ArchivedProjects.Any())
{
<p class="muted">No archived projects.</p>
}
else
{
<div class="content-grid">
@foreach (var project in Model.ArchivedProjects)
{
var archivedDateLabel = project.ArchivedDate?.ToLocalTime().ToString("yyyy-MM-dd");
<article class="plain-card">
<h3>@project.ProjectName</h3>
<p class="eyebrow">
Archived
@if (!string.IsNullOrWhiteSpace(archivedDateLabel))
{
<span> / @archivedDateLabel</span>
}
</p>
<p>@(string.IsNullOrWhiteSpace(project.Description) ? "No description yet." : project.Description)</p>
<div class="button-row">
<a class="btn btn-outline-secondary btn-sm" asp-controller="Exports" asp-action="ProjectBackup" asp-route-projectId="@project.ProjectID">Download backup</a>
<form asp-action="Restore" method="post" class="archive-button-form" data-confirm-message="Restore this project?&#10;&#10;It will return to your active project list.">
<input type="hidden" name="id" value="@project.ProjectID" />
<button class="btn btn-outline-primary btn-sm" type="submit">Restore</button>
</form>
@if (project.IsOwner)
{
<button class="btn btn-outline-danger btn-sm"
type="button"
data-hard-delete-project
data-project-id="@project.ProjectID"
data-project-name="@project.ProjectName">
Delete Permanently
</button>
}
</div>
</article>
}
</div>
}
</section>
<div class="modal fade" id="hardDeleteProjectModal" tabindex="-1" aria-labelledby="hardDeleteProjectModalTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content plotline-confirm-modal">
<div class="modal-header">
<h2 class="modal-title fs-5" id="hardDeleteProjectModalTitle">Permanently delete this project?</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
</div>
<form asp-controller="Projects" asp-action="HardDeleteArchivedProject" method="post" asp-antiforgery="true" data-confirm-skip="true" data-hard-delete-project-form>
<div class="modal-body">
<p>This will permanently delete <strong data-hard-delete-project-name></strong> and all associated data, including books, chapters, scenes, characters, locations, assets, plot threads, notes, metrics, attachments and uploaded files.</p>
<p class="mb-3">This action cannot be undone.</p>
<input type="hidden" name="projectId" data-hard-delete-project-id />
<label class="form-label" for="hardDeleteProjectConfirmation">Type the project name to continue</label>
<input class="form-control"
id="hardDeleteProjectConfirmation"
name="confirmationName"
autocomplete="off"
data-hard-delete-project-confirmation />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger" disabled data-hard-delete-project-submit>Delete Permanently</button>
</div>
</form>
</div>
</div>
</div>
}
@section Scripts {
<script>
(() => {
const modalElement = document.getElementById("hardDeleteProjectModal");
if (!modalElement || !window.bootstrap) return;
const modal = new bootstrap.Modal(modalElement);
const projectNameLabel = modalElement.querySelector("[data-hard-delete-project-name]");
const projectIdInput = modalElement.querySelector("[data-hard-delete-project-id]");
const confirmationInput = modalElement.querySelector("[data-hard-delete-project-confirmation]");
const submitButton = modalElement.querySelector("[data-hard-delete-project-submit]");
let selectedProjectName = "";
const updateSubmitState = () => {
if (!submitButton || !confirmationInput) return;
submitButton.disabled = confirmationInput.value.trim() !== selectedProjectName;
};
document.querySelectorAll("[data-hard-delete-project]").forEach((button) => {
button.addEventListener("click", () => {
selectedProjectName = button.dataset.projectName || "";
if (projectNameLabel) projectNameLabel.textContent = selectedProjectName;
if (projectIdInput) projectIdInput.value = button.dataset.projectId || "";
if (confirmationInput) confirmationInput.value = "";
updateSubmitState();
modal.show();
});
});
confirmationInput?.addEventListener("input", updateSubmitState);
modalElement.addEventListener("shown.bs.modal", () => confirmationInput?.focus());
modalElement.addEventListener("hidden.bs.modal", () => {
selectedProjectName = "";
if (projectIdInput) projectIdInput.value = "";
if (confirmationInput) confirmationInput.value = "";
updateSubmitState();
});
})();
</script>
}