203 lines
9.9 KiB
Plaintext
203 lines
9.9 KiB
Plaintext
@model StoryCalendarViewModel
|
|
@using System.Globalization
|
|
@{
|
|
var selectedMonthValue = Model.SelectedMonth?.ToString("yyyy-MM", CultureInfo.InvariantCulture);
|
|
var showBookLabels = !Model.SelectedBookID.HasValue;
|
|
var weekdays = new[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
|
|
}
|
|
|
|
<div class="page-heading story-calendar-heading">
|
|
<div>
|
|
<p class="eyebrow">Story time</p>
|
|
<h1>Calendar</h1>
|
|
<p class="lead-text">@Model.StoryRangeLabel</p>
|
|
</div>
|
|
<div class="story-calendar-stats" aria-label="Calendar summary">
|
|
<span><strong>@Model.SceneCount</strong> dated scenes</span>
|
|
<span><strong>@Model.ActiveStoryDayCount</strong> story days</span>
|
|
<span><strong>@Model.UnplacedScenes.Count</strong> unplaced</span>
|
|
</div>
|
|
</div>
|
|
|
|
<section class="card mb-3 story-calendar-filter">
|
|
<div class="card-body">
|
|
<form asp-action="ApplyFilters" method="post" class="row g-2 align-items-end" data-story-calendar-filter data-story-calendar-index-url="@Url.Action("Index", "StoryCalendar")">
|
|
<input type="hidden" name="projectId" value="@Model.Project.ProjectID" />
|
|
<div class="col-md-5 col-lg-4">
|
|
<label class="form-label" for="bookId">Book</label>
|
|
<select class="form-select" id="bookId" name="bookId" asp-items="Model.BookOptions"></select>
|
|
</div>
|
|
<div class="col-md-4 col-lg-3">
|
|
<label class="form-label" for="month">Month</label>
|
|
<select class="form-select" id="month" name="month" disabled="@(!Model.MonthOptions.Any())">
|
|
@if (!Model.MonthOptions.Any())
|
|
{
|
|
<option value="">No dated scenes</option>
|
|
}
|
|
else
|
|
{
|
|
@foreach (var option in Model.MonthOptions)
|
|
{
|
|
<option value="@option.Value" selected="@option.Selected">@option.Text</option>
|
|
}
|
|
}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 col-lg-2">
|
|
<button class="btn btn-primary w-100" type="submit">Apply</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
|
|
@if (!Model.HasDatedScenes)
|
|
{
|
|
<section class="card story-calendar-empty">
|
|
<div class="card-body">
|
|
<h2>No dated scenes yet</h2>
|
|
<p class="muted mb-0">Scenes will appear here once canonical scene chronology has a resolved start date.</p>
|
|
</div>
|
|
</section>
|
|
}
|
|
else
|
|
{
|
|
<section class="story-calendar-shell">
|
|
<div class="story-calendar-toolbar">
|
|
<div class="button-row">
|
|
<a class="btn btn-outline-secondary btn-sm"
|
|
asp-action="Index"
|
|
asp-route-projectId="@Model.Project.ProjectID"
|
|
asp-route-bookId="@Model.SelectedBookID"
|
|
asp-route-month="@Model.PreviousMonth?.ToString("yyyy-MM", CultureInfo.InvariantCulture)"
|
|
data-story-calendar-nav>Previous month</a>
|
|
<a class="btn btn-outline-secondary btn-sm"
|
|
asp-action="Index"
|
|
asp-route-projectId="@Model.Project.ProjectID"
|
|
asp-route-bookId="@Model.SelectedBookID"
|
|
asp-route-month="@Model.NextMonth?.ToString("yyyy-MM", CultureInfo.InvariantCulture)"
|
|
data-story-calendar-nav>Next month</a>
|
|
@if (Model.FirstStoryMonth.HasValue)
|
|
{
|
|
<a class="btn btn-outline-secondary btn-sm"
|
|
asp-action="Index"
|
|
asp-route-projectId="@Model.Project.ProjectID"
|
|
asp-route-bookId="@Model.SelectedBookID"
|
|
asp-route-month="@Model.FirstStoryMonth.Value.ToString("yyyy-MM", CultureInfo.InvariantCulture)"
|
|
data-story-calendar-nav>First dated scene</a>
|
|
}
|
|
</div>
|
|
<div class="story-calendar-story-months">
|
|
@if (Model.PreviousStoryMonth.HasValue)
|
|
{
|
|
<a asp-action="Index"
|
|
asp-route-projectId="@Model.Project.ProjectID"
|
|
asp-route-bookId="@Model.SelectedBookID"
|
|
asp-route-month="@Model.PreviousStoryMonth.Value.ToString("yyyy-MM", CultureInfo.InvariantCulture)"
|
|
data-story-calendar-nav>Previous story month</a>
|
|
}
|
|
@if (Model.NextStoryMonth.HasValue)
|
|
{
|
|
<a asp-action="Index"
|
|
asp-route-projectId="@Model.Project.ProjectID"
|
|
asp-route-bookId="@Model.SelectedBookID"
|
|
asp-route-month="@Model.NextStoryMonth.Value.ToString("yyyy-MM", CultureInfo.InvariantCulture)"
|
|
data-story-calendar-nav>Next story month</a>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="story-calendar-layout">
|
|
<div class="story-calendar-grid-wrap">
|
|
<div class="story-calendar-month" aria-label="@Model.SelectedMonthLabel">
|
|
<div class="story-calendar-month-title">@Model.SelectedMonthLabel</div>
|
|
<div class="story-calendar-weekdays">
|
|
@foreach (var weekday in weekdays)
|
|
{
|
|
<div>@weekday</div>
|
|
}
|
|
</div>
|
|
<div class="story-calendar-grid">
|
|
@foreach (var week in Model.Weeks)
|
|
{
|
|
@foreach (var day in week.Days)
|
|
{
|
|
var visibleScenes = day.Scenes.Take(3).ToList();
|
|
var hiddenCount = Math.Max(0, day.Scenes.Count - visibleScenes.Count);
|
|
<a class="story-calendar-day @(day.IsInSelectedMonth ? "" : "is-overflow") @(day.IsSelected ? "is-selected" : "")"
|
|
asp-action="Index"
|
|
asp-route-projectId="@Model.Project.ProjectID"
|
|
asp-route-bookId="@Model.SelectedBookID"
|
|
asp-route-month="@selectedMonthValue"
|
|
asp-route-date="@day.QueryValue"
|
|
data-story-calendar-day
|
|
data-story-calendar-date="@day.QueryValue"
|
|
aria-current="@(day.IsSelected ? "date" : null)"
|
|
aria-label="@day.DayLabel">
|
|
<span class="story-calendar-day-number">@day.DayNumber</span>
|
|
<span class="story-calendar-day-scenes">
|
|
@foreach (var scene in visibleScenes)
|
|
{
|
|
<span class="story-calendar-scene-chip">
|
|
@if (showBookLabels)
|
|
{
|
|
<span class="story-calendar-book">@scene.BookLabel</span>
|
|
}
|
|
<span>@scene.SceneLabel</span>
|
|
<strong>@scene.Title</strong>
|
|
</span>
|
|
}
|
|
@if (hiddenCount > 0)
|
|
{
|
|
<span class="story-calendar-more">+ @hiddenCount more</span>
|
|
}
|
|
</span>
|
|
</a>
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<aside class="story-calendar-detail" aria-label="Selected day" aria-live="polite" data-story-calendar-detail>
|
|
<partial name="_StoryCalendarDayDetails" model="Model.DayDetails" />
|
|
</aside>
|
|
</div>
|
|
</section>
|
|
}
|
|
|
|
<section class="card story-calendar-unplaced">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<strong>Unplaced scenes</strong>
|
|
<button class="btn btn-outline-secondary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#story-calendar-unplaced" aria-expanded="false" aria-controls="story-calendar-unplaced">
|
|
@Model.UnplacedScenes.Count scenes
|
|
</button>
|
|
</div>
|
|
<div class="collapse" id="story-calendar-unplaced">
|
|
<div class="card-body">
|
|
@if (!Model.UnplacedScenes.Any())
|
|
{
|
|
<p class="muted mb-0">All scenes in this scope have resolved story dates.</p>
|
|
}
|
|
else
|
|
{
|
|
<div class="story-calendar-unplaced-list">
|
|
@foreach (var scene in Model.UnplacedScenes)
|
|
{
|
|
<article>
|
|
<div>
|
|
<strong>@scene.Title</strong>
|
|
<span>@scene.BookTitle · Chapter @scene.ChapterNumber:g · Scene @scene.SceneNumber:g</span>
|
|
@if (!string.IsNullOrWhiteSpace(scene.RelativeTimeText))
|
|
{
|
|
<small>@scene.RelativeTimeText</small>
|
|
}
|
|
</div>
|
|
<a class="btn btn-outline-primary btn-sm" asp-controller="Scenes" asp-action="Edit" asp-route-id="@scene.SceneID">Open scene</a>
|
|
</article>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</section>
|