can create and drag and drop pages

This commit is contained in:
2024-01-12 15:06:15 -07:00
parent a4179e6d52
commit 9f19704724
33 changed files with 651 additions and 319 deletions

View File

@@ -272,7 +272,7 @@ public class FileStorageTests
Pages = [
new () {
Name = "test page persistence",
DueDateForOrdering = new DateTime(),
DueAt = new DateTime(),
Text = "this is some\n## markdown\n"
}
]

View File

@@ -9,7 +9,7 @@ public class PageMarkdownTests
{
Name = "test title",
Text = "test text content",
DueDateForOrdering = new DateTime()
DueAt = new DateTime()
};
var pageMarkdown = page.ToMarkdown();

View File

@@ -4,7 +4,7 @@
@using CanvasModel.Courses
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@using LocalModels
@using Management.Web.Pages.Course.Module.Assignment
@using Management.Web.Pages.Course.Module.ModuleItems
@using Management.Web.Shared.Components
@inject FileStorageManager fileStorageManager

View File

@@ -3,7 +3,7 @@
@using CanvasModel.Courses
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@using LocalModels
@using Management.Web.Pages.Course.Module.Assignment
@using Management.Web.Pages.Course.Module.ModuleItems
@using Management.Web.Shared.Components
@inject FileStorageManager fileStorageManager

View File

@@ -1,4 +1,4 @@
@using Management.Web.Course.Module.Assignment
@using Management.Web.Course.Module.ModuleItems
@inject DragContainer dragContainer
@inject NavigationManager Navigation

View File

@@ -3,12 +3,12 @@
@inject CoursePlanner planner
@code
@code
{
[Parameter, EditorRequired]
public DateTime? date { get; set; } =
default!;
default!;
private bool isWeekDay {
get => date?.DayOfWeek != null;
}
@@ -25,8 +25,8 @@
{
planner.StateHasChanged -= reload;
}
private IEnumerable<LocalAssignment> TodaysAssignments
private IEnumerable<LocalAssignment> TodaysAssignments
{
get
{
@@ -39,7 +39,7 @@
}
}
private IEnumerable<LocalQuiz> todaysQuizzes
private IEnumerable<LocalQuiz> todaysQuizzes
{
get
{
@@ -51,11 +51,25 @@
.Where(q => q.DueAt.Date == date?.Date);
}
}
private IEnumerable<LocalCoursePage> todaysPages
{
get
{
if(planner.LocalCourse == null || date == null)
return Enumerable.Empty<LocalCoursePage>();
else
return planner.LocalCourse.Modules
.SelectMany(m => m.Pages)
.Where(q => q.DueAt.Date == date?.Date);
}
}
private string calculatedClass
{
get
{
var baseClasses = "col border rounded rounded-3 p-2 pb-4 m-1 ";
var baseClasses = "col border rounded rounded-3 p-2 pb-4 m-1 ";
if(dragging)
return baseClasses + " bg-secondary text-light ";
@@ -66,15 +80,15 @@
{
DayOfWeek? weekDay = date?.DayOfWeek;
DayOfWeek notNullDay = weekDay ?? default;
var isClassDay = planner.LocalCourse?.Settings.DaysOfWeek.Contains(notNullDay) ?? false;
var dayInSemester =
var dayInSemester =
isClassDay
&& date <= planner.LocalCourse?.Settings.EndDate
&& date >= planner.LocalCourse?.Settings.StartDate;
var totalClasses = dayInSemester
? "bg-light-subtle text-light " + baseClasses
var totalClasses = dayInSemester
? "bg-light-subtle text-light " + baseClasses
: " " + baseClasses;
return totalClasses;
@@ -105,9 +119,9 @@
dragContainer.DropCallback?.Invoke(d, null);
}
}
}
}
<div
<div
class="@calculatedClass"
@ondrop="@(() => OnDrop())"
@ondragenter="OnDragEnter"
@@ -118,13 +132,17 @@
<ul class="m-0 ps-3">
@foreach (var assignment in TodaysAssignments)
{
@* Console.WriteLine($"assignment: {assignment.Name}"); *@
<AssignmentInDay Assignment="assignment" @key="@assignment" />
<AssignmentInDay Assignment="assignment" @key="@assignment" />
}
@foreach(var quiz in todaysQuizzes)
{
<QuizInDay Quiz="quiz" @key="@quiz" />
}
@foreach(var page in todaysPages)
{
<PageInDay Page="page" @key="page" />
}
</ul>
</div>

View File

@@ -0,0 +1,55 @@
@using Management.Web.Course.Module.ModuleItems
@inject DragContainer dragContainer
@inject NavigationManager Navigation
@inject PageEditorContext pageContext
@inject MyLogger<PageInDay> logger
@inherits DroppablePage
@code {
protected override void OnInitialized()
{
planner.StateHasChanged += reload;
}
private void reload()
{
this.InvokeAsync(this.StateHasChanged);
}
public void Dispose()
{
planner.StateHasChanged -= reload;
}
private void HandleDragStart()
{
dragContainer.DropCallback = dropCallback;
}
private void HandleDragEnd()
{
dragContainer.DropCallback = null;
}
private void OnClick()
{
if(planner.LocalCourse != null)
{
pageContext.Page = Page;
Navigation.NavigateTo("/course/" + planner.LocalCourse.Settings.Name + "/page/" + Page.Name);
logger.Log("navigating to coursePage page");
}
}
}
<li
draggable="true"
@ondragstart="HandleDragStart"
@ondragend="HandleDragEnd"
@onclick="OnClick"
role="button"
>
@Page.Name
</li>

View File

@@ -1,201 +0,0 @@
@using Management.Web.Shared.Components
@using Management.Web.Course.Module.Assignment
@using CanvasModel.Assignments
@inject DragContainer dragContainer
@inject NavigationManager Navigation
@inject AssignmentEditorContext assignmentContext
@inherits DroppableAssignment
@code {
[Parameter]
[EditorRequired]
public LocalModule Module { get; set; } = new();
protected override void OnInitialized()
{
planner.StateHasChanged += reload;
}
private void reload()
{
this.InvokeAsync(this.StateHasChanged);
}
public void Dispose()
{
planner.StateHasChanged -= reload;
}
private bool showAll { get; set; } = false;
private void HandleDragStart()
{
dragContainer.DropCallback = DropCallback;
}
private void HandleDragEnd()
{
dragContainer.DropCallback = null;
}
private CanvasAssignment? assignmentInCanvas => planner
.CanvasAssignments?
.FirstOrDefault(
a => a.Name == Assignment.Name
);
private bool existsInCanvas =>
assignmentInCanvas != null;
private void OnClick()
{
assignmentContext.Assignment = Assignment;
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name + "/assignment/" + Assignment.Name);
}
private bool NeedsToBeUpdatedInCanvas => planner.LocalCourse != null
&& planner.LocalCourse.Settings.CanvasId != null
&& planner.CanvasAssignments != null
&& planner.CanvasModules != null
&& assignmentInCanvas != null
&& Assignment.NeedsUpdates(
(CanvasAssignment)assignmentInCanvas,
Assignment.GetCanvasAssignmentGroupId(planner.LocalCourse.Settings.AssignmentGroups)
);
}
<div
draggable="true"
@ondragstart="HandleDragStart"
@ondragend="HandleDragEnd"
@onclick="OnClick"
role="button"
>
<div class="card">
<div class="card-body p-0">
<div class="card-title pt-2 px-2 m-0">
<div class="row mx-1">
<div class="col offset-2 offset-lg-1 ">
<h4 class="text-center m-0">
@Assignment.Name
</h4>
</div>
<div class="col-2 col-lg-1 text-end">
@if(existsInCanvas)
{
@if(NeedsToBeUpdatedInCanvas)
{
<SyncIcon />
}
else
{
<CheckIcon />
}
}
else
{
<SyncIcon />
}
</div>
</div>
</div>
@if(
planner.LocalCourse != null
&& existsInCanvas
&& NeedsToBeUpdatedInCanvas
&& assignmentInCanvas != null
)
{
<div class="mx-3 text-body-tertiary">
@Assignment.GetUpdateReason(
(CanvasAssignment)assignmentInCanvas,
Assignment.GetCanvasAssignmentGroupId(planner.LocalCourse.Settings.AssignmentGroups))
</div>
}
@if(!existsInCanvas)
{
<div class="mx-3 text-body-tertiary">
no assignment with same name in canvas
</div>
}
@if(!showAll)
{
<div class="card-text overflow-hidden p-2" style="max-height: 5rem;">
<div>Points: @Assignment.PointsPossible</div>
<div>Due At: @Assignment.DueAt</div>
</div>
}
else
{
<div class="card-text">
<div class="px-3 py-1 bg-dark-subtle my-1">
@((MarkupString) @Assignment.GetDescriptionHtml())
</div>
<section class="px-3">
<div>Points: @Assignment.PointsPossible</div>
<div>Due At: @Assignment.DueAt</div>
<div>Lock At: @Assignment.LockAt</div>
<div>Submission Types:</div>
<ul>
@foreach(var type in Assignment.SubmissionTypes)
{
<li>
@type
</li>
}
</ul>
</section>
</div>
}
@if(!showAll)
{
<div
class="text-center fs-3 fw-bold lh-1 text-primary"
role="button"
@onclick:preventDefault="true"
@onclick:stopPropagation="true"
@onclick="() => showAll = true"
>
<svg
width="30"
height="30"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 10a2 2 0 11-4.001-.001A2 2 0 016 10zm6 0a2 2 0 11-4.001-.001A2 2 0 0112 10zm6 0a2 2 0 11-4.001-.001A2 2 0 0118 10z"
fill="var(--bs-primary)"
/>
</svg>
</div>
}
else
{
<div
class="text-center fs-3 fw-bold lh-1 text-primary"
role="button"
@onclick:preventDefault="true"
@onclick:stopPropagation="true"
@onclick="() => showAll = false"
>
<svg
width="30"
height="30"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 10a2 2 0 11-4.001-.001A2 2 0 016 10zm6 0a2 2 0 11-4.001-.001A2 2 0 0112 10zm6 0a2 2 0 11-4.001-.001A2 2 0 0118 10z"
fill="var(--bs-primary)"
/>
</svg>
</div>
}
</div>
</div>
</div>

View File

@@ -1,6 +1,8 @@
@using Management.Web.Shared.Components
@using Management.Web.Shared.Components.Quiz
@using Management.Web.Pages.Course.Module
@using Management.Web.Pages.Course.Module.ModuleItems
@using Management.Web.Pages.Course.Module.NewItemsButtons
@using LocalModels
@using BlazorMonaco
@using BlazorMonaco.Editor
@@ -153,6 +155,10 @@
<h5>Assignments</h5>
<div class="row">
@foreach(var p in Module.Pages)
{
<PageListItem Page=p />
}
@foreach (var a in Module.Assignments)
{
<AssignmentListItem Assignment="a" Module="Module" />

View File

@@ -0,0 +1,137 @@
@using Management.Web.Shared.Components
@using Management.Web.Course.Module.ModuleItems
@using CanvasModel.Assignments
@inject DragContainer dragContainer
@inject NavigationManager Navigation
@inject AssignmentEditorContext assignmentContext
@inherits DroppableAssignment
@code {
[Parameter]
[EditorRequired]
public LocalModule Module { get; set; } = new();
protected override void OnInitialized()
{
planner.StateHasChanged += reload;
}
private void reload()
{
this.InvokeAsync(this.StateHasChanged);
}
public void Dispose()
{
planner.StateHasChanged -= reload;
}
private bool showAll { get; set; } = false;
private void HandleDragStart()
{
dragContainer.DropCallback = DropCallback;
}
private void HandleDragEnd()
{
dragContainer.DropCallback = null;
}
private CanvasAssignment? assignmentInCanvas => planner
.CanvasAssignments?
.FirstOrDefault(
a => a.Name == Assignment.Name
);
private bool existsInCanvas =>
assignmentInCanvas != null;
private void OnClick()
{
assignmentContext.Assignment = Assignment;
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name + "/assignment/" + Assignment.Name);
}
private bool NeedsToBeUpdatedInCanvas => planner.LocalCourse != null
&& planner.LocalCourse.Settings.CanvasId != null
&& planner.CanvasAssignments != null
&& planner.CanvasModules != null
&& assignmentInCanvas != null
&& Assignment.NeedsUpdates(
(CanvasAssignment)assignmentInCanvas,
Assignment.GetCanvasAssignmentGroupId(planner.LocalCourse.Settings.AssignmentGroups)
);
}
<div
draggable="true"
@ondragstart="HandleDragStart"
@ondragend="HandleDragEnd"
@onclick="OnClick"
role="button"
>
<ModuleItemLayout Name=@Assignment.Name IsSyncedWithCanvas=@(existsInCanvas && !NeedsToBeUpdatedInCanvas)>
@if(
planner.LocalCourse != null
&& existsInCanvas
&& NeedsToBeUpdatedInCanvas
&& assignmentInCanvas != null
)
{
<div class="mx-3 text-body-tertiary">
@Assignment.GetUpdateReason(
(CanvasAssignment)assignmentInCanvas,
Assignment.GetCanvasAssignmentGroupId(planner.LocalCourse.Settings.AssignmentGroups))
</div>
}
@if(!existsInCanvas)
{
<div class="mx-3 text-body-tertiary">
no assignment with same name in canvas
</div>
}
@if(!showAll)
{
<div class="card-text overflow-hidden p-2" style="max-height: 5rem;">
<div>Points: @Assignment.PointsPossible</div>
<div>Due At: @Assignment.DueAt</div>
</div>
}
else
{
<div class="card-text">
<div class="px-3 py-1 bg-dark-subtle my-1">
@((MarkupString) @Assignment.GetDescriptionHtml())
</div>
<section class="px-3">
<div>Points: @Assignment.PointsPossible</div>
<div>Due At: @Assignment.DueAt</div>
<div>Lock At: @Assignment.LockAt</div>
<div>Submission Types:</div>
<ul>
@foreach(var type in Assignment.SubmissionTypes)
{
<li>
@type
</li>
}
</ul>
</section>
</div>
}
<div
class="text-center fs-3 fw-bold lh-1 text-primary"
role="button"
@onclick:preventDefault="true"
@onclick:stopPropagation="true"
@onclick="() => showAll = !showAll"
>
<MeatballsIcon />
</div>
</ModuleItemLayout>
</div>

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Components;
namespace Management.Web.Course.Module.Assignment;
namespace Management.Web.Course.Module.ModuleItems;
public class DroppableAssignment : ComponentBase
{
[Inject]

View File

@@ -0,0 +1,85 @@
using Microsoft.AspNetCore.Components;
namespace Management.Web.Course.Module.ModuleItems;
public class DroppablePage : ComponentBase
{
[Inject]
protected CoursePlanner planner { get; set; } = default!;
[Parameter, EditorRequired]
public LocalCoursePage Page { get; set; } = default!;
private void dropOnDate(DateTime dropDate)
{
if (planner.LocalCourse == null) return;
var currentModule = planner
.LocalCourse
.Modules
.First(m =>
m.Pages.Contains(Page)
) ?? throw new Exception("in drop page callback, could not find module");
var defaultDueTimeDate = new DateTime(
year: dropDate.Year,
month: dropDate.Month,
day: dropDate.Day,
hour: planner.LocalCourse.Settings.DefaultDueTime.Hour,
minute: planner.LocalCourse.Settings.DefaultDueTime.Minute,
second: 0
);
var moduleWithUpdatedPage = currentModule with
{
Pages = currentModule.Pages.Select(p =>
p.Name != Page.Name // we are only changing the due date, so the name should be the same
? p
: p with { DueAt = defaultDueTimeDate }
)
};
var updatedModules = planner.LocalCourse.Modules
.Select(m =>
m.Name == moduleWithUpdatedPage.Name
? moduleWithUpdatedPage
: m
);
var newCourse = planner.LocalCourse with
{
Modules = updatedModules
};
planner.LocalCourse = newCourse;
}
private void dropOnModule(LocalModule module)
{
if (planner.LocalCourse == null) return;
var newModules = planner.LocalCourse.Modules.Select(m =>
m.Name != module.Name
? m with
{
Pages = m.Pages.Where(p => p.Name != Page.Name).DistinctBy(p => p.Name)
}
: m with
{
Pages = m.Pages.Append(Page).DistinctBy(a => a.Name)
}
);
var newCourse = planner.LocalCourse with
{
Modules = newModules
};
planner.LocalCourse = newCourse;
}
protected void dropCallback(DateTime? dropDate, LocalModule? module)
{
if (module == null)
{
dropOnDate(dropDate ?? Page.DueAt);
}
else
{
dropOnModule(module);
}
}
}

View File

@@ -0,0 +1,32 @@
@using Management.Web.Shared.Components
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
[Parameter, EditorRequired]
public string Name { get; set; } = default!;
[Parameter, EditorRequired]
public bool IsSyncedWithCanvas { get; set; } = default!;
}
<div class="card">
<div class="card-body p-0">
<div class="card-title pt-2 px-2 m-0 d-flex justify-content-between">
<h4>@Name</h4>
@if(IsSyncedWithCanvas)
{
<CheckIcon />
}
else
{
<SyncIcon />
}
</div>
@ChildContent
</div>
</div>

View File

@@ -0,0 +1,48 @@
@using Management.Web.Shared.Components
@using Management.Web.Course.Module.ModuleItems
@inject DragContainer dragContainer
@inject NavigationManager Navigation
@inject PageEditorContext pageContext
@inherits DroppablePage
@code {
private void HandleDragStart()
{
dragContainer.DropCallback = dropCallback;
}
private void HandleDragEnd()
{
dragContainer.DropCallback = null;
}
private bool existsInCanvas => false;
private void OnClick()
{
pageContext.Page = Page;
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name + "/page/" + Page.Name);
}
}
<div
draggable="true"
@ondragstart="HandleDragStart"
@ondragend="HandleDragEnd"
@onclick="OnClick"
role="button"
>
<ModuleItemLayout Name=@Page.Name IsSyncedWithCanvas=existsInCanvas>
@if(!existsInCanvas)
{
<div class="mx-3 text-body-tertiary">
no page with same name in canvas
</div>
}
<div class="card-text overflow-hidden p-2">
<div>Due At: @Page.DueAt</div>
</div>
</ModuleItemLayout>
</div>

View File

@@ -0,0 +1,54 @@
@using Management.Web.Shared.Components
@using Management.Web.Shared.Components.Quiz
@inject DragContainer dragContainer
@inject QuizEditorContext quizContext
@inject NavigationManager Navigation
@inherits DroppableQuiz
@code {
private void HandleDragStart()
{
dragContainer.DropCallback = dropCallback;
}
private void HandleDragEnd()
{
dragContainer.DropCallback = null;
}
private bool existsInCanvas =>
planner.CanvasQuizzes != null
? Quiz.QuizIsCreated(planner.CanvasQuizzes)
: false;
private void OnClick()
{
quizContext.Quiz = Quiz;
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name + "/quiz/" + Quiz.Name);
}
}
<div
draggable="true"
@ondragstart="HandleDragStart"
@ondragend="HandleDragEnd"
@onclick="OnClick"
role="button"
>
<ModuleItemLayout Name=Quiz.Name IsSyncedWithCanvas=existsInCanvas>
@if(!existsInCanvas)
{
<div class="mx-3 text-body-tertiary">
no quiz with same name in canvas
</div>
}
<div class="card-text overflow-hidden p-2">
<div>Due At: @Quiz.DueAt</div>
</div>
</ModuleItemLayout>
</div>

View File

@@ -1,5 +1,6 @@
@using Management.Web.Pages.Course.Module
@using System.Linq
@using Management.Web.Pages.Course.Module.NewItemsButtons
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject CoursePlanner planner

View File

@@ -26,7 +26,7 @@
{
Name = Name,
Text = "",
DueDateForOrdering = DateTime.Now
DueAt = DateTime.Now
};
var newModules =planner.LocalCourse.Modules.Select(m =>

View File

@@ -1,68 +0,0 @@
@using Management.Web.Shared.Components
@using Management.Web.Shared.Components.Quiz
@inject DragContainer dragContainer
@inject QuizEditorContext quizContext
@inject NavigationManager Navigation
@inherits DroppableQuiz
@code {
private void HandleDragStart()
{
dragContainer.DropCallback = dropCallback;
}
private void HandleDragEnd()
{
dragContainer.DropCallback = null;
}
private bool existsInCanvas =>
planner.CanvasQuizzes != null
? Quiz.QuizIsCreated(planner.CanvasQuizzes)
: false;
private void OnClick()
{
quizContext.Quiz = Quiz;
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name + "/quiz/" + Quiz.Name);
}
}
<div
draggable="true"
@ondragstart="HandleDragStart"
@ondragend="HandleDragEnd"
@onclick="OnClick"
role="button"
>
<div class="card">
<div class="card-body p-0">
<div class="card-title pt-2 px-2 m-0 d-flex justify-content-between">
<h4>@Quiz.Name</h4>
@if(existsInCanvas)
{
<CheckIcon />
}
else
{
<SyncIcon />
}
</div>
@if(!existsInCanvas)
{
<div class="mx-3 text-body-tertiary">
no quiz with same name in canvas
</div>
}
<div class="card-text overflow-hidden p-2">
<div>Due At: @Quiz.DueAt</div>
</div>
</div>
</div>
</div>

View File

@@ -3,7 +3,7 @@
@using CanvasModel.Courses
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@using LocalModels
@using Management.Web.Pages.Course.Module.Assignment
@using Management.Web.Pages.Course.Module.ModuleItems
@using Management.Web.Shared.Components
@inject CanvasService canvas

View File

@@ -6,7 +6,7 @@
@using CanvasModel.Courses
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@using LocalModels
@using Management.Web.Pages.Course.Module.Assignment
@using Management.Web.Pages.Course.Module.ModuleItems
@inject FileStorageManager fileStorageManager
@inject CanvasService canvas

View File

@@ -85,6 +85,7 @@ builder.Services.AddScoped<FileStorageManager>();
builder.Services.AddScoped<CoursePlanner>();
builder.Services.AddScoped<AssignmentEditorContext>();
builder.Services.AddScoped<PageEditorContext>();
builder.Services.AddScoped<QuizEditorContext>();
builder.Services.AddScoped<DragContainer>();

View File

@@ -0,0 +1,11 @@
<svg
width="30"
height="30"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 10a2 2 0 11-4.001-.001A2 2 0 016 10zm6 0a2 2 0 11-4.001-.001A2 2 0 0112 10zm6 0a2 2 0 11-4.001-.001A2 2 0 0118 10z"
fill="var(--bs-primary)"
/>
</svg>

After

Width:  |  Height:  |  Size: 268 B

View File

@@ -0,0 +1,148 @@
using CanvasModel.Modules;
using LocalModels;
using Management.Planner;
using Management.Services;
using Management.Services.Canvas;
public class PageEditorContext(
CoursePlanner planner,
CanvasService canvas,
MyLogger<PageEditorContext> logger)
{
public event Action? StateHasChanged;
private CoursePlanner planner { get; } = planner;
private CanvasService canvas { get; } = canvas;
private readonly MyLogger<PageEditorContext> logger = logger;
private LocalCoursePage? _page;
private LocalModule? _module;
public LocalCoursePage? Page
{
get => _page;
set
{
if (_page == null && value != null && planner != null && planner.LocalCourse != null)
{
_module = getCurrentLocalModule(value, planner.LocalCourse);
}
_page = value;
StateHasChanged?.Invoke();
}
}
public void SavePage(LocalCoursePage newPage)
{
if (planner.LocalCourse != null && _module != null && Page != null)
{
// use Page not newPage because it is the version that was last stored
var updatedModules = planner.LocalCourse.Modules
.Select(
m =>
m.Name == _module.Name
? m with
{
Pages = m.Pages
.Select(p => p == Page ? newPage : p)
.ToArray()
}
: m
)
.ToArray();
planner.LocalCourse = planner.LocalCourse with { Modules = updatedModules };
Page = newPage;
}
}
public void DeletePage()
{
if (planner.LocalCourse != null && Page != null && _module != null)
{
// not dealing with canvas rn
var updatedModules = planner.LocalCourse.Modules
.Select(m => m.Name != _module.Name
? m
: m with
{
Pages = m.Pages.Where(p => p == Page).ToArray()
}
)
.ToArray();
planner.LocalCourse = planner.LocalCourse with { Modules = updatedModules };
Page = null;
}
}
public async Task AddPageToCanvas()
{
// logger.Log("started to add quiz to canvas");
// if (Quiz == null)
// {
// logger.Log("cannot add null quiz to canvas");
// return;
// }
// await planner.LoadCanvasData();
// if (planner.CanvasQuizzes == null)
// {
// logger.Log("cannot add quiz to canvas, failed to retrieve current quizzes");
// return;
// }
// if (planner.LocalCourse == null)
// {
// logger.Log("cannot add quiz to canvas, no course stored in planner");
// return;
// }
// var canvasQuizId = await planner.LocalCourse.AddQuizToCanvas(Quiz, canvas);
// var courseCanvasId = planner.LocalCourse.Settings.CanvasId;
// if (courseCanvasId == null)
// {
// logger.Log("was able to add quiz to canvas, but errored while making module item. CourseCanvasId is null");
// return;
// }
// var canvasModule = getCurrentCanvasModule(Quiz, planner.LocalCourse);
// await canvas.CreateModuleItem(
// (ulong)courseCanvasId,
// canvasModule.Id,
// Quiz.Name,
// "Quiz",
// (ulong)canvasQuizId
// );
// await planner.LocalCourse.Modules.First().SortModuleItems(
// (ulong)courseCanvasId,
// canvasModule.Id,
// canvas
// );
// logger.Log($"finished adding quiz {Quiz.Name} to canvas");
}
private static LocalModule getCurrentLocalModule(LocalCoursePage page, LocalCourse course)
{
return course.Modules.FirstOrDefault(
m => m.Pages.Contains(page)
)
?? throw new Exception("could not find current module in page editor context");
}
private CanvasModule getCurrentCanvasModule(LocalCoursePage quiz, LocalCourse course)
{
var localModule = getCurrentLocalModule(quiz, course);
var canvasModule = planner.CanvasModules?.FirstOrDefault(m => m.Name == localModule.Name)
?? throw new Exception($"error in page context, canvas module with name {localModule.Name} not found in planner");
return canvasModule;
}
}

View File

@@ -5,26 +5,20 @@ using Management.Planner;
using Management.Services;
using Management.Services.Canvas;
public class QuizEditorContext
public class QuizEditorContext(
CoursePlanner planner,
CanvasService canvas,
MyLogger<QuizEditorContext> logger)
{
public QuizEditorContext(
CoursePlanner planner,
CanvasService canvas,
MyLogger<CanvasAssignmentService> logger)
{
this.planner = planner;
this.canvas = canvas;
this.logger = logger;
}
public event Action? StateHasChanged;
private CoursePlanner planner { get; }
private CanvasService canvas { get; }
private CoursePlanner planner { get; } = planner;
private CanvasService canvas { get; } = canvas;
private readonly MyLogger<QuizEditorContext> logger = logger;
private LocalQuiz? _quiz;
private LocalModule? _module;
private readonly MyLogger<CanvasAssignmentService> logger;
public LocalQuiz? Quiz
{

View File

@@ -6,7 +6,7 @@ using YamlDotNet.Serialization;
namespace LocalModels;
public record LocalAssignment
public record LocalAssignment: IModuleItem
{
private string _name = "";
public string Name
@@ -28,6 +28,7 @@ public record LocalAssignment
public IEnumerable<RubricItem> Rubric { get; init; } = Array.Empty<RubricItem>();
public int PointsPossible => Rubric.Sum(r => r.IsExtraCredit ? 0 : r.Points);
public string GetDescriptionHtml()
{
return Markdig.Markdown.ToHtml(Description);

View File

@@ -0,0 +1,9 @@
namespace LocalModels;
public interface IModuleItem
{
public string Name { get; init; }
public DateTime DueAt { get; init; }
}

View File

@@ -1,14 +1,14 @@
namespace LocalModels;
public record LocalCoursePage
public record LocalCoursePage: IModuleItem
{
public required string Name { get; init; }
public required string Text { get; set; }
public DateTime? DueDateForOrdering { get; init; }
public DateTime DueAt { get; init; }
public string ToMarkdown()
{
var printableDueDate = DueDateForOrdering.ToString()?.Replace('\u202F', ' ');
var printableDueDate = DueAt.ToString()?.Replace('\u202F', ' ');
var settingsMarkdown = $"Name: {Name}\n"
+ $"DueDateForOrdering: {printableDueDate}\n"
+ "---\n";
@@ -20,9 +20,9 @@ public record LocalCoursePage
var name = MarkdownUtils.ExtractLabelValue(rawSettings, "Name");
var rawDate = MarkdownUtils.ExtractLabelValue(rawSettings, "DueDateForOrdering");
DateTime? parsedDate = DateTime.TryParse(rawDate, out DateTime parsedDueAt)
DateTime parsedDate = DateTime.TryParse(rawDate, out DateTime parsedDueAt)
? parsedDueAt
: null;
: throw new LocalPageMarkdownParseException($"could not parse due date: {rawDate}");
var text = pageMarkdown.Split("---\n")[1];
@@ -30,7 +30,7 @@ public record LocalCoursePage
return new LocalCoursePage
{
Name = name,
DueDateForOrdering = parsedDate,
DueAt = parsedDate,
Text = text
};
}

View File

@@ -3,7 +3,7 @@ using YamlDotNet.Serialization;
namespace LocalModels;
public record LocalQuiz
public record LocalQuiz: IModuleItem
{
public required string Name { get; init; }
public required string Description { get; init; }
@@ -27,7 +27,7 @@ public record LocalQuiz
.CanvasId;
public string GetDescriptionHtml() => Markdig.Markdown.ToHtml(Description);
public string ToYaml()
{
var serializer = new SerializerBuilder().DisableAliases().Build();
@@ -100,7 +100,7 @@ Description: {Description}
var rawLockAt = extractLabelValue(settings, "LockAt");
DateTime? lockAt = DateTime.TryParse(rawLockAt, out DateTime parsedLockAt)
DateTime? lockAt = DateTime.TryParse(rawLockAt, out DateTime parsedLockAt)
? parsedLockAt
: null;
@@ -153,6 +153,6 @@ public class QuizMarkdownParseException : Exception
{
public QuizMarkdownParseException(string message): base(message)
{
}
}
}

View File

@@ -131,6 +131,7 @@ public class CourseMarkdownLoader
}
private async Task<IEnumerable<LocalCoursePage>> loadPagesFromPath(string modulePath)
{
using var activity = DiagnosticsConfig.Source?.StartActivity("loading Pages from file");
var pagesPath = $"{modulePath}/pages";
if (!Directory.Exists(pagesPath))
{