mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
restructured components so that there are more components in the pages
This commit is contained in:
82
Management.Web/Pages/QuizForm/MarkdownQuestionPreview.razor
Normal file
82
Management.Web/Pages/QuizForm/MarkdownQuestionPreview.razor
Normal file
@@ -0,0 +1,82 @@
|
||||
@using Markdig
|
||||
|
||||
@code {
|
||||
[Parameter, EditorRequired]
|
||||
public LocalQuizQuestion Question { get; set; } = default!;
|
||||
|
||||
}
|
||||
|
||||
<div class="row justify-content-between text-secondary">
|
||||
<div class="col">
|
||||
points: @Question.Points
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@Question.QuestionType
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@((MarkupString)Question.HtmlText)
|
||||
|
||||
@if(Question.QuestionType == QuestionType.MATCHING)
|
||||
{
|
||||
@foreach(var answer in Question.Answers)
|
||||
{
|
||||
<div class="mx-3 mb-1 bg-dark px-2 rounded rounded-2 border row">
|
||||
<div
|
||||
class="col text-end my-auto p-1"
|
||||
>
|
||||
@answer.Text
|
||||
</div>
|
||||
<div
|
||||
class="col my-auto"
|
||||
>
|
||||
@answer.MatchedText
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach(var answer in Question.Answers)
|
||||
{
|
||||
string answerPreview = answer.HtmlText.StartsWith("<p>")
|
||||
? answer.HtmlText.Replace("<p>", "<p class='m-0'>")
|
||||
: answer.HtmlText;
|
||||
|
||||
<div class="mx-3 mb-1 bg-dark px-2 rounded rounded-2 d-flex flex-row border">
|
||||
@if(answer.Correct)
|
||||
{
|
||||
<svg
|
||||
style="width: 1em;"
|
||||
class="me-1 my-auto"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M4 12.6111L8.92308 17.5L20 6.5"
|
||||
stroke="var(--bs-success)"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div
|
||||
class="me-1 my-auto"
|
||||
style="width: 1em;"
|
||||
>
|
||||
@if(Question.QuestionType == QuestionType.MULTIPLE_ANSWERS)
|
||||
{
|
||||
<span>[ ]</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="markdownQuizAnswerPreview p-1">
|
||||
@((MarkupString)answerPreview)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
79
Management.Web/Pages/QuizForm/MarkdownQuizForm.razor
Normal file
79
Management.Web/Pages/QuizForm/MarkdownQuizForm.razor
Normal file
@@ -0,0 +1,79 @@
|
||||
@using Management.Web.Shared.Components
|
||||
|
||||
@inject QuizEditorContext quizContext
|
||||
|
||||
@code {
|
||||
private Modal? modal { get; set; }
|
||||
|
||||
private LocalQuiz? testQuiz;
|
||||
|
||||
private string? error { get; set; } = null;
|
||||
private string _quizMarkdownInput { get; set; } = "";
|
||||
private string quizMarkdownInput
|
||||
{
|
||||
get => _quizMarkdownInput;
|
||||
set
|
||||
{
|
||||
_quizMarkdownInput = value;
|
||||
|
||||
try
|
||||
{
|
||||
var newQuiz = LocalQuiz.ParseMarkdown(_quizMarkdownInput);
|
||||
error = null;
|
||||
testQuiz = newQuiz;
|
||||
quizContext.SaveQuiz(newQuiz);
|
||||
}
|
||||
catch (QuizMarkdownParseException e)
|
||||
{
|
||||
error = e.Message;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
reload();
|
||||
quizContext.StateHasChanged += reload;
|
||||
}
|
||||
private void reload()
|
||||
{
|
||||
if (quizContext.Quiz != null)
|
||||
{
|
||||
if (quizMarkdownInput == "")
|
||||
{
|
||||
quizMarkdownInput = quizContext.Quiz.ToMarkdown();
|
||||
}
|
||||
this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
quizContext.StateHasChanged -= reload;
|
||||
}
|
||||
}
|
||||
|
||||
<div class="d-flex flex-column h-100">
|
||||
<div class="d-flex flex-row h-100 p-2">
|
||||
<div class="row flex-grow-1">
|
||||
<div class="col-6">
|
||||
<MonacoTextArea
|
||||
Value="@quizMarkdownInput"
|
||||
OnChange="@((v) => quizMarkdownInput = v)"
|
||||
/>
|
||||
|
||||
</div>
|
||||
<div class="col-6 h-100 overflow-y-auto">
|
||||
@if (error != null)
|
||||
{
|
||||
<p class="text-danger text-truncate">Error: @error</p>
|
||||
}
|
||||
@if(testQuiz != null)
|
||||
{
|
||||
<QuizPreview Quiz="testQuiz" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
249
Management.Web/Pages/QuizForm/QuizFormPage.razor
Normal file
249
Management.Web/Pages/QuizForm/QuizFormPage.razor
Normal file
@@ -0,0 +1,249 @@
|
||||
@page "/course/{CourseName}/quiz/{QuizName}"
|
||||
|
||||
@using CanvasModel.EnrollmentTerms
|
||||
@using CanvasModel.Quizzes
|
||||
@using Management.Web.Shared.Components
|
||||
@using CanvasModel.Courses
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@using LocalModels
|
||||
@using Management.Web.Shared.Module.Assignment
|
||||
@using Management.Web.Shared.Components
|
||||
|
||||
@inject FileStorageManager fileStorageManager
|
||||
@inject CanvasService canvas
|
||||
@inject CoursePlanner planner
|
||||
@inject QuizEditorContext quizContext
|
||||
@inject MyLogger<QuizFormPage> logger
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string? CourseName { get; set; } = default!;
|
||||
[Parameter]
|
||||
public string? QuizName { get; set; } = default!;
|
||||
|
||||
private bool loading { get; set; } = true;
|
||||
private bool addingQuizToCanvas = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
quizContext.StateHasChanged += reload;
|
||||
}
|
||||
private void reload()
|
||||
{
|
||||
this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
quizContext.StateHasChanged -= reload;
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (loading)
|
||||
{
|
||||
loading = false;
|
||||
logger.Log($"loading quiz {CourseName} {QuizName}");
|
||||
if (planner.LocalCourse == null)
|
||||
{
|
||||
var courses = await fileStorageManager.LoadSavedCourses();
|
||||
planner.LocalCourse = courses.First(c => c.Settings.Name == CourseName);
|
||||
logger.Log($"set course to '{planner.LocalCourse?.Settings.Name}'");
|
||||
}
|
||||
|
||||
if (quizContext.Quiz == null)
|
||||
{
|
||||
var quiz = planner
|
||||
.LocalCourse?
|
||||
.Modules
|
||||
.SelectMany(m => m.Quizzes)
|
||||
.FirstOrDefault(q => q.Name == QuizName);
|
||||
|
||||
quizContext.Quiz = quiz;
|
||||
logger.Log($"set quiz to '{quizContext.Quiz?.Name}'");
|
||||
}
|
||||
StateHasChanged();
|
||||
|
||||
if (planner.CanvasQuizzes == null)
|
||||
{
|
||||
await planner.LoadCanvasData();
|
||||
}
|
||||
|
||||
base.OnInitialized();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteQuiz()
|
||||
{
|
||||
quizContext.DeleteQuiz();
|
||||
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name);
|
||||
}
|
||||
|
||||
private async Task addToCanvas()
|
||||
{
|
||||
addingQuizToCanvas = true;
|
||||
await quizContext.AddQuizToCanvas();
|
||||
await planner.LoadCanvasData();
|
||||
addingQuizToCanvas = false;
|
||||
}
|
||||
private void done()
|
||||
{
|
||||
quizContext.Quiz = null;
|
||||
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name);
|
||||
}
|
||||
|
||||
private CanvasQuiz? quizInCanvas => planner.CanvasQuizzes?.FirstOrDefault(q => q.Title == quizContext.Quiz?.Name);
|
||||
|
||||
private string canvasQuizUrl =>
|
||||
$"https://snow.instructure.com/courses/{planner.LocalCourse?.Settings.CanvasId}/quizzes/{quizInCanvas?.Id}";
|
||||
|
||||
private int? quizPoints => quizContext.Quiz?.Questions.Sum(q => q.Points);
|
||||
private bool showHelp = false;
|
||||
|
||||
private readonly static string exampleMarkdownQuestion = @"QUESTION REFERENCE
|
||||
---
|
||||
Points: 2
|
||||
this is a question?
|
||||
*a) correct
|
||||
b) not correct
|
||||
---
|
||||
points: 1
|
||||
question goes here
|
||||
[*] correct
|
||||
[ ] not correct
|
||||
[] not correct
|
||||
---
|
||||
the points default to 1?
|
||||
*a) true
|
||||
b) false
|
||||
---
|
||||
Markdown is supported
|
||||
|
||||
- like
|
||||
- this
|
||||
- list
|
||||
|
||||
[*] true
|
||||
[ ] false
|
||||
---
|
||||
This is a one point essay question
|
||||
essay
|
||||
---
|
||||
points: 4
|
||||
this is a short answer question
|
||||
short_answer
|
||||
---
|
||||
points: 4
|
||||
the underscore is optional
|
||||
short answer
|
||||
---
|
||||
this is a matching question
|
||||
^ left answer - right dropdown
|
||||
^ other thing - another option
|
||||
";
|
||||
}
|
||||
|
||||
<div class="d-flex flex-column py-3" style="height: 100vh;">
|
||||
|
||||
<section>
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto my-auto">
|
||||
<button class="btn btn-outline-secondary" @onclick="done">
|
||||
← go back
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto my-auto">
|
||||
<h2>
|
||||
@quizContext.Quiz?.Name
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@if (quizContext.Quiz == null)
|
||||
{
|
||||
<div class="col-auto">
|
||||
<Spinner />
|
||||
</div>
|
||||
}
|
||||
<div class="col-auto me-3">
|
||||
<h3>
|
||||
Questions: @quizContext.Quiz?.Questions.Count() - Points: @quizPoints
|
||||
</h3>
|
||||
@if (quizInCanvas != null)
|
||||
{
|
||||
@if (quizInCanvas?.Published == true)
|
||||
{
|
||||
<div class="text-success">
|
||||
Published!
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-danger">
|
||||
Not Published
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="flex-grow-1 w-100 d-flex justify-content-center border rounded-4 bg-dark-subtle"
|
||||
style="min-height: 10%; max-width: 100%;"
|
||||
>
|
||||
|
||||
@if(showHelp)
|
||||
{
|
||||
<pre class="bg-dark-subtle me-3 pe-5 ps-3 rounded rounded-3">
|
||||
@exampleMarkdownQuestion
|
||||
</pre>
|
||||
}
|
||||
<div class="w-100" style="max-width: 120em; max-height: 100%;">
|
||||
@if (quizContext.Quiz != null)
|
||||
{
|
||||
<MarkdownQuizForm />
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-outline-secondary mt-3"
|
||||
@onclick="@(() => showHelp = !showHelp)"
|
||||
>
|
||||
toggle help
|
||||
</button>
|
||||
</div>
|
||||
<section class="p-2">
|
||||
@if (quizContext.Quiz != null)
|
||||
{
|
||||
<div class="row justify-content-end">
|
||||
<div class="col-auto">
|
||||
<ConfirmationModal
|
||||
Label="Delete"
|
||||
Class="btn btn-danger"
|
||||
OnConfirm="deleteQuiz"
|
||||
Disabled="@addingQuizToCanvas"
|
||||
/>
|
||||
<button class="btn btn-outline-secondary me-1" @onclick="addToCanvas" disabled="@addingQuizToCanvas">
|
||||
Add to Canvas
|
||||
</button>
|
||||
@if (quizInCanvas != null)
|
||||
{
|
||||
<a class="btn btn-outline-secondary me-1" href="@canvasQuizUrl" target="_blank">
|
||||
View in Canvas
|
||||
</a>
|
||||
}
|
||||
<button class="btn btn-primary" @onclick="done" disabled="@addingQuizToCanvas">
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (addingQuizToCanvas)
|
||||
{
|
||||
<Spinner />
|
||||
}
|
||||
</section>
|
||||
</div>
|
||||
72
Management.Web/Pages/QuizForm/QuizPreview.razor
Normal file
72
Management.Web/Pages/QuizForm/QuizPreview.razor
Normal file
@@ -0,0 +1,72 @@
|
||||
@using Management.Web.Shared.Components
|
||||
|
||||
@inject QuizEditorContext quizContext
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public LocalQuiz Quiz { get; set; } = default!;
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
quizContext.StateHasChanged += reload;
|
||||
}
|
||||
private void reload()
|
||||
{
|
||||
this.InvokeAsync(this.StateHasChanged);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
quizContext.StateHasChanged -= reload;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@if(Quiz != null)
|
||||
{
|
||||
<div class="row justify-content-start">
|
||||
<div class="col-auto" style="min-width: 35em;">
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">Name: </div>
|
||||
<div class="col-6">@Quiz.Name</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">Due At: </div>
|
||||
<div class="col-6">@Quiz.DueAt</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">Lock At: </div>
|
||||
<div class="col-6">@Quiz.LockAt</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">Shuffle Answers: </div>
|
||||
<div class="col-6">@Quiz.ShuffleAnswers</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">Allowed Attempts: </div>
|
||||
<div class="col-6">@Quiz.AllowedAttempts</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">One question at a time: </div>
|
||||
<div class="col-6">@Quiz.OneQuestionAtATime</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 text-end">Assignment Group: </div>
|
||||
<div class="col-6">@Quiz.LocalAssignmentGroupName</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3" style="white-space: pre-wrap;">@Quiz.Description</div>
|
||||
|
||||
@foreach(var question in Quiz.Questions)
|
||||
{
|
||||
|
||||
<div class="bg-dark-subtle mt-1 p-1 ps-2 rounded rounded-2">
|
||||
<MarkdownQuestionPreview
|
||||
Question="question"
|
||||
@key="question"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user