Skip to content

Commit

Permalink
Merge pull request #74 from davishoang96/save-questions
Browse files Browse the repository at this point in the history
Save questions and anwers of the selected quiz
  • Loading branch information
davishoang96 authored Jan 5, 2025
2 parents 9a59193 + 10ee3cd commit 2edc47c
Show file tree
Hide file tree
Showing 11 changed files with 573 additions and 1 deletion.
187 changes: 187 additions & 0 deletions QuizApp.Api/QuizApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ public partial interface IQuizApiClient
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<QuestionDTO>> GetAllQuestionsEndpointAsync(System.Threading.CancellationToken cancellationToken);

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<QuestionDTO>> GetQuestionsByQuizIdEndpointAsync(int quizId);

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<System.Collections.Generic.ICollection<QuestionDTO>> GetQuestionsByQuizIdEndpointAsync(int quizId, System.Threading.CancellationToken cancellationToken);

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<int> SaveOrUpdateQuestionAsync(SaveOrUpdateQuestionRequest saveOrUpdateQuestionRequest);

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
System.Threading.Tasks.Task<int> SaveOrUpdateQuestionAsync(SaveOrUpdateQuestionRequest saveOrUpdateQuestionRequest, System.Threading.CancellationToken cancellationToken);

}

[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")]
Expand Down Expand Up @@ -892,6 +910,175 @@ public virtual async System.Threading.Tasks.Task<bool> DeleteQuizEndpointAsync(D
}
}

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<System.Collections.Generic.ICollection<QuestionDTO>> GetQuestionsByQuizIdEndpointAsync(int quizId)
{
return GetQuestionsByQuizIdEndpointAsync(quizId, System.Threading.CancellationToken.None);
}

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<QuestionDTO>> GetQuestionsByQuizIdEndpointAsync(int quizId, System.Threading.CancellationToken cancellationToken)
{
if (quizId == null)
throw new System.ArgumentNullException("quizId");

var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));

var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "question/getquestionbyquizid"
urlBuilder_.Append("question/getquestionbyquizid");
urlBuilder_.Append('?');
urlBuilder_.Append(System.Uri.EscapeDataString("QuizId")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(quizId, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
urlBuilder_.Length--;

PrepareRequest(client_, request_, urlBuilder_);

var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

PrepareRequest(client_, request_, url_);

var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}

ProcessResponse(client_, response_);

var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<System.Collections.Generic.ICollection<QuestionDTO>>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}

/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<int> SaveOrUpdateQuestionAsync(SaveOrUpdateQuestionRequest saveOrUpdateQuestionRequest)
{
return SaveOrUpdateQuestionAsync(saveOrUpdateQuestionRequest, System.Threading.CancellationToken.None);
}

/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>Success</returns>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<int> SaveOrUpdateQuestionAsync(SaveOrUpdateQuestionRequest saveOrUpdateQuestionRequest, System.Threading.CancellationToken cancellationToken)
{
if (saveOrUpdateQuestionRequest == null)
throw new System.ArgumentNullException("saveOrUpdateQuestionRequest");

var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(saveOrUpdateQuestionRequest, JsonSerializerSettings);
var content_ = new System.Net.Http.StringContent(json_);
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));

var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "question/savequestions"
urlBuilder_.Append("question/savequestions");

PrepareRequest(client_, request_, urlBuilder_);

var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);

PrepareRequest(client_, request_, url_);

var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}

ProcessResponse(client_, response_);

var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<int>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}

protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
Expand Down
90 changes: 90 additions & 0 deletions QuizApp.Client/Pages/AddQuestion.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
@page "/add-question/{quizId:int}"
@using QuizApp.Api
@using QuizApp.Common.DTO
@using System.Collections.ObjectModel
@inject IQuizApiClient apiClient
@inject DialogService DialogService
@inject NotificationService NotificationService
@inject NavigationManager NavigationManager

<h3>
Add Questions & Answers
</h3>

<RadzenStack AlignItems="AlignItems.Start" JustifyContent="JustifyContent.Left">

<RadzenButton Click="AddNewQuestion">Add question</RadzenButton>

<RadzenDataGrid ColumnWidth="200px" AllowFiltering="true" AllowPaging="true" AllowSorting="true" Data="@Questions" TItem="QuestionDTO">
<Columns>
<RadzenDataGridColumn Property="Title" Title="Question" Width="300px" />
<RadzenDataGridColumn Property="Answers" Title="Answers">
<Template Context="data">
<ul>
@foreach (var answer in data.Answers)
{
<li>
@answer.Text
<span class="badge bg-@(answer.IsCorrect ? "success" : "secondary")">
@(answer.IsCorrect ? "Correct" : "Incorrect")
</span>
</li>
}
</ul>
</Template>
</RadzenDataGridColumn>
</Columns>
</RadzenDataGrid>

<RadzenButton Click="SaveQuestion">Save</RadzenButton>

</RadzenStack>

@code {
[Parameter] public int QuizId { get; set; }

private ObservableCollection<QuestionDTO> Questions = new ObservableCollection<QuestionDTO>();

private async Task SaveQuestion()
{
var result = await apiClient.SaveOrUpdateQuestionAsync(new Common.Request.SaveOrUpdateQuestionRequest
{
QuizId = QuizId,
QuestionDTOs = Questions,
});

if(result > 0)
{
NotificationService.Notify(NotificationSeverity.Success, "Save questions successfully");
}
}

private async Task AddNewQuestion()
{
DialogService.OnClose += AddNewQuestionClosed;
await DialogService.OpenAsync<AddQuestionDialog>("Add New Question");
}

private void AddNewQuestionClosed(dynamic obj)
{
var questions = obj as QuestionDTO;
if (questions is not null)
{
Questions.Add(questions);
StateHasChanged();
}
}

protected override async Task OnInitializedAsync()
{
try
{
var questionDTOs = await apiClient.GetQuestionsByQuizIdEndpointAsync(QuizId);
Questions = new ObservableCollection<QuestionDTO>(questionDTOs);
}
catch(Exception ex)
{

}
}
}
49 changes: 49 additions & 0 deletions QuizApp.Client/Pages/AddQuestionDialog.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@using QuizApp.Common.DTO
@using System.Collections.ObjectModel
@inject DialogService dialogService

<RadzenStack>
<RadzenTextArea Rows="1" @bind-Value="@TheQuestion.Title" Placeholder="Title"></RadzenTextArea>
<RadzenDataGrid AllowFiltering="false" AllowPaging="false" AllowSorting="false" Data="@Answers" TItem="AnswerDTO">
<Columns>
<RadzenDataGridColumn Width="70%" Property="Text" Title="Answer(s)">
<Template Context="data">
<RadzenTextBox Style="width: 100%" @bind-Value="data.Text" />
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn TextAlign="TextAlign.Center" Width="15%" Property="IsCorrect" Title="Correct">
<Template Context="data">
<RadzenCheckBox @bind-Value="data.IsCorrect" />
</Template>
</RadzenDataGridColumn>
<RadzenDataGridColumn Width="15%" TextAlign="TextAlign.Center">
<Template Context="data">
<RadzenButton Icon="delete" ButtonStyle="ButtonStyle.Danger" Click="(() => RemoveAnswer(data))" />
</Template>
</RadzenDataGridColumn>
</Columns>
</RadzenDataGrid>
<RadzenButton Click="AddAnswer">Add Answer</RadzenButton>
<RadzenButton Click="Save">Save</RadzenButton>
</RadzenStack>

@code {
private QuestionDTO TheQuestion = new QuestionDTO();
private ObservableCollection<AnswerDTO> Answers { get; set; } = new ObservableCollection<AnswerDTO>();

private async Task AddAnswer()
{
Answers.Add(new AnswerDTO { Text = string.Empty, IsCorrect = false });
}

private void RemoveAnswer(AnswerDTO answer)
{
Answers.Remove(answer);
}

private void Save()
{
TheQuestion.Answers = Answers;
dialogService.Close(TheQuestion);
}
}
11 changes: 10 additions & 1 deletion QuizApp.Client/Pages/QuizPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@inject IQuizApiClient apiClient
@inject DialogService DialogService
@inject NotificationService NotificationService
@inject NavigationManager NavigationManager

<h3>Quiz Manager</h3>

Expand All @@ -20,7 +21,7 @@ else if (!Quizzes.Any())
}
else
{
<RadzenDataGrid ColumnWidth="200px" AllowFiltering="true" AllowPaging="true" AllowSorting="true" Data="@Quizzes" TItem="QuizDTO">
<RadzenDataGrid RowClick="OnRowClick" ColumnWidth="200px" AllowFiltering="true" AllowPaging="true" AllowSorting="true" Data="@Quizzes" TItem="QuizDTO">
<Columns>
<RadzenDataGridColumn Property="@(nameof(QuizDTO.Name))" Title="Name" Width="140px" />
<RadzenDataGridColumn Property="@(nameof(QuizDTO.Description))" Title="Description" Filterable="false" />
Expand All @@ -43,6 +44,14 @@ else
Quizzes = new ObservableCollection<QuizDTO>(a);
}

private void OnRowClick(DataGridRowMouseEventArgs<QuizDTO> selectedItem)
{
if (selectedItem.Data?.Id.HasValue == true)
{
NavigationManager.NavigateTo($"/add-question/{selectedItem.Data.Id.Value}");
}
}

private async Task AddNewQuiz()
{
DialogService.OnClose += AddNewQuizClosed;
Expand Down
6 changes: 6 additions & 0 deletions QuizApp.Common/Request/GetQuestionByQuizIdRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace QuizApp.Common.Request;

public class GetQuestionByQuizIdRequest
{
public int QuizId { get; set; }
}
10 changes: 10 additions & 0 deletions QuizApp.Common/Request/SaveOrUpdateQuestionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using QuizApp.Common.DTO;

namespace QuizApp.Common.Request;

public class SaveOrUpdateQuestionRequest
{
public int QuizId { get; set; }
public int QuestionId { get; set; }
public IEnumerable<QuestionDTO> QuestionDTOs { get; set; }
}
Loading

0 comments on commit 2edc47c

Please sign in to comment.