Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save questions and anwers of the selected quiz #74

Merged
merged 1 commit into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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; }

Check warning on line 9 in QuizApp.Common/Request/SaveOrUpdateQuestionRequest.cs

View workflow job for this annotation

GitHub Actions / build_dotnet

Non-nullable property 'QuestionDTOs' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
}
Loading
Loading